第一次接触 mips架构,而且有一道很巧妙的格式化字符串利用链漏洞,学到新知识。
mmutag
程序分析
delete
函数里存在 double_free
漏洞。并且实 libc2.23版本。还存在一个后门函数可以输入 0x20的 content
并输出。
利用分析
首先利用后门函数 泄露 cannary
,然后在 栈上布置一个 fake_chunk size
,随后利用 fastbin attack
分配伪造堆块到栈上。然后通过栈溢出,执行ROP链先泄露 libc
地址,再修改 free_got
的值为 system
,随后在一个堆块里部署上 /bin/sh\x00
,即可 getshell.
EXP
1 | from pwn import * |
ezhttpd
程序分析
程序功能是伪造的一个简单的 http
流程,我们的输入里必须有Cookie token method
和参数,满足输入要求即可执行 create
、delete
、edit
等操作。
在 delete
函数里存在 double_free
漏洞,并且版本是 glibc 2.27
存在tcache
,所以利用较简单。
利用分析
- 劫持
tcache_perthread_struct
首先程序一开始会给出堆地址,我们利用tcache dup
可以再分配堆块到 tcache_perthread_struct
,然后通过修改 tcache_perthread_struct
结构就能实现分配任意地址的堆块。
- 多次修改
_IO_2_1_stdout_
结构体
然后再构造一个堆块分配到 unsortedbin
,使其fd 指针含有 main_arena+88
的地址,然后修改后两位,再将该对堆块地址放到 tcache_perthread_struct
处,就可以分配堆块到 _IO_2_1_stdout_
,因为此处分配堆块时会将堆块内容全部填满,所以我们每次只能申请0x20大小的堆块,然后多次修改 _IO_2_1_stdout_
的 flag
和 write_ptr
值。
SROP
和shellcode
得到flag
由于程序还存在沙箱,所以需要先将 free_hook
改为 setcontext+53
的地址,然后在堆上部署 SROP
链,实现mprotect
函数修改 内存属性为可执行,再在堆上布置 shellcode
来 ORW
得到flag
。
EXP
1 | from pwn import * |
managesystem
使用 Ghidra
即能够很好的对 Arm和MIPS
架构进行反编译。
程序分析
什么保护都没开。
在 edit
函数里,存在一个堆溢出,可以溢出 8 字节。
利用分析
unlink 劫持note_list-0xc 地址
将 note_list 地址覆盖为 一个 got 表地址,然后用 show 功能泄露地址;
最后将 free 地址覆盖为 system地址,在堆里布上
/bin/sh\x00
,再释放堆即可getshell。
EXP
环境还有点问题,加上还不知道如何将 pwntools 和我的 mips_qemu 虚拟机结合起来,调试脚本的时候有点麻烦。还有这脚本抄的大佬的作业。。。(xmzyshypnc NB)
1 | #coding=utf-8 |
2019 D^3CTF unprintableV
程序分析
程序功能是一个很简单的格式化字符串漏洞,但是不同在于通过 close(1)
把 stdout
这个结构体给关掉了,也就是我们的 printf
函数虽然可以正常执行,但是不会再有输出了。
同时程序,还开启了沙箱,不能够执行 execve
函数,只能够通过 orw
读取 flag
。
利用分析
- 修改
stdout@Glibc
指针指向stderr
结构体
以前知道执行 IO
输出函数时,函数最终会去遍历 FILE
结构体,例如 printf
函数肯定会最后调用 _IO_2_1_stdout_
结构体,但是printf
是如何找到 FILE
结构体的,源码里可以看到是需要先传入一个 stdout
指针,而且这个指针是全局变量,也就是我们能够在汇编中找到这个 stdout
指针。如下所示:
也就是说是这三个全局变量里会存储 各自FILE
结构体的位置,类似于 GOT
表。如果我们能够修改 stdout@GLIBC
指针指向 _IO_2_1_stderr_
结构体,那么 printf
函数根据 stdout@GLIBC
去找的 FILE
结构体为 _IO_2_1_stderr_
,该结构体并没有被关闭,能够正常输出,我们就能够通过该方法输出数据了。
但是如何修改?我们可以先看一下执行 printf
函数是的栈环境:
上图中的地址,我只用 后两字节来表示,例如 rbp 地址 0x7fffffffde60
表示为 0xde60
修改 0xde60
指向的地址 0xde80
值为 0xde78
修改 0xde80
指向的地址 0xde78
的值为 0x6020
(stdout@GLIBC
地址)
修改 0xde78
指向的地址 0x6020
(stdout@GLIBC
地址)的值后两字节为 stderr
(FILE
结构体位置)
那么即 可正常输出:
- 栈迁移到 buf
然后为了执行我们输入的 ROP 链,我们需要将程序栈迁移到我们输入的位置 buf
地址。栈迁移的原理就是修改 rbp
指针指向的值,在执行完 leave
指令,即 mov rsp, rbp; pop rbp
,此时 rbp
会指向我们修改的地址 fake_addr
,然后再此执行 leave
指令时,rsp
就会被修改到 我们的 fake_addr
,执行 ret
指令时,就会从 fake_addr+8
处开始执行,即可以执行我们的 rop 链。
目标是修改 rbp
的值最终指向 buf_addr
,我们选择的修改链是:
修改 0x6820
指向的地址 0x6918
的值为 rbp
的地址 0x67f0
修改 0x6918
指向的地址 0x67f0
的值为 buf_addr
的地址 0x202060
由于修改 rbp
的值为 buf_addr
时,我们输入有限制每次只能修改2字节,所以需要多次利用这个修改链,进行多次修改。
- 构造 orw 链
随后就是在 buf中布置 orw
链:
1 | orw = flat([ |
EXP
1 | from pwn import * |
noleakfmt
程序分析
这道西湖论剑的题和上面这道题类似,都是关闭了 stdout
输出,但是有一个格式化字符串漏洞。
不同点在于这个 buf
并没有与 stdout@GLIBC
结构体十分靠近。这道题的难点就在于如何找到 stdout
指针,并将其指向 stderr
结构体。
利用分析
- 迁移栈帧
首先我们需要在栈上找到一个 stdout
指针,这样我们就可以通过修改这个指针得后两位,去指向 stdout
结构体中 fileno
变量,将其改为 2 就可以将 stdout
改为 stderr
变量了。
我们进入 printf
函数,可以在其中发现 stdout
地址。但是这个地址,在我们当前的 printf
的 低地址处, printf
不能从高往低修改。我们需要先将我们的函数栈迁移到低地址。方法是 将 printf
函数的返回地址修改为 start
函数地址,这样函数栈帧就会向低地址生长,当我们再次进入 main
函数时,栈地址就已经比之前得 stdout
地址高了。
1 | fake_rbp_offset = 0x17b0 |
- 覆盖
stdout
的fileno
变量
随后,我们只需要覆盖 上图中 _IO_2_1_stdout
结构体的地址为 _IO_2_1_stdout
中 fileno
变量的地址,并将其覆盖为 0x2,即可实现将 _IO_2_1_stdout
重定向到 _IO_2_1_stderr
- 覆盖
malloc_hook
为gadget
地址
运用上面的方法,将malloc_hook
为 gadget
。随后因为 printf
函数输出极大时,会重新调用 malloc
增大缓冲区,即可 getshell
。
EXP
然后,脸比较黑,爆破了很久都没有成功,有点难受。
1 | from pwn import * |
参考
- 本文作者: A1ex
- 本文链接: http://yoursite.com/2020/10/09/2020西湖论剑/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!