pwnhub公开赛二期PWN-random
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
地址:
|
|
接下来直接对栈进行溢出,触发stack smash
:
|
|
完整exp
|
|
最后远程打的flag
为:
引用与参考
stack smash
: https://ctf-wiki.org/pwn/linux/stackoverflow/fancy-rop/#stack-smash