前言
- 学习记录
ret2text
ret2text
1 | from pwn import * |
cgpwn2
file & checksec
Analyse
- hello 函数中存在 gets 栈溢出
EXP
1 | from pwn import * |
ret2shellcode
ret2shellcode-bamboofox
1 | from pwn import * |
sniperoj-pwn100-shellcode-x86-64
1 | from pwn import * |
ret2syscall
ret2syscall-bamboofox
1 | from pwn import * |
Recho rctf2017
file & checksec
Analyse
- 存在一个很明显的问题,就第二次可以输入的字符串大小是根据第一次输入的数字大小确定的,所以存在一个明显的栈溢出
- 但是第一个问题是这个循环是个死循环,如果直接使用 close() 函数程序会直接退出,查询资料后发现 pwn 库中提供了 shutdown() 函数,他可以关闭 IO 流,即让循环正常退出
- 既然使用 shutdown 函数,我们必须一次性将 payload 发送过去,这就导致我们不能使用内存泄漏来泄漏 system 函数,查询了一下字符串发现有一个 flag ,所以我们考虑使用系统调用完成,alarm,open,read,write函数的实现都是通过 syscall 实现的,我们选择将 alarm_got 覆盖成 syscall ,通过调试发现 syscall 就在 alarm 函数中 +5 的位置,那我们只需要让 alarm_got 指向的地址 +5 即可
- 然后使用 open 系统调用打开文件流,再用 read 函数读取(fd是3,0,1,2已经分别被标准输入、标准输出、标准错误所占用),然后使用 write 或者 printf 输出
EXP
1 | from pwn import * |
ret2libc
ret2libc1
1 | from pwn import * |
ret2libc2
1 | from pwn import * |
ret2libc3
demo
1 | from pwn import * |
train.cs.nctu.edu.tw: ret2libc
1 | from pwn import * |
ret2__libc_csu_init
hitcon-level5
1 | from pwn import * |
Format String
int_overflow
file & checksec
Analyse
- check_passwd 函数 v3 是 uint8 ,对 buf 进行了低字节截断
- 3 =< v3 <= 8 ,所以 0x103 =< buf_size <= 0x108
EXP
1 | from pwn import * |
CGfsb
file & checksec
Analyse
- 23 行有个格式化字符串漏洞
- 利用
%k$n
覆盖pwnme
,需要找到偏移量,经过调试,如下图,偏移量是 10
- 所以利用
%nc
与%k$n
达到覆盖pwnme
的目的,可构造[addr of pwnme]%4c%10$n
EXP
1 | from pwn import * |
seed_overflow
dice_game
file & checksec
Analyse
- rand() 生成的随机数和随机种子 seed() 有关,通过观察题目,可以发现存在溢出漏洞,通过输入可以覆盖到 seed() ,实现一个可预测的随机数列
EXP
1 | from pwn import * |
guess_num
file & checksec
Analyse
- rand() 生成的随机数和随机种子 seed() 有关,通过观察题目,可以发现存在溢出漏洞,通过输入可以覆盖到 seed(0) ,实现一个可预测的随机数列,连续猜中 10 次
EXP
1 | from pwn import * |
BROP
XMan 2017 Baaa
EXP
- 盲打栈溢出
1 | from pwn import * |
ret2dlresolve
2015-XDCTF-pwn200 no-relro
- 直接修改 .dynamic
- 修改 .dynamic 节中字符串表的地址为伪造的地址
- 在伪造的地址处构造好字符串表,将 read 字符串替换为 system 字符串
- 在特定的位置读取 /bin/sh 字符串
- 调用 read 函数的 plt 的第二条指令,触发 _dl_runtime_resolve 进行函数解析,从而执行 system 函数
EXP1
- 自己构造 payload ,不使用其他模块
1 | from pwn import * |
EXP2
- 使用 ROP 模块
1 | from pwn import * |
2015-XDCTF-pwn200 partial-relro
- .dynamic 节只读,不能修改 .dynamic ,可以通过伪造重定位表项的方式来调用目标函数
- 一步一步手工伪造
EXP1
1 | from pwn import * |
EXP2
- 使用 roputil
1 | from roputils import * |
EXP3
- 使用 pwntools 的 Ret2dlresolvePayload 模块
1 | from pwn import * |
SROP
论文例子学习
- 首先利用栈溢出漏洞,将返回地址覆盖为一个指向
sigreturn gadget
的指针。如果只有syscall
,则将RAX
设置为0xf
,效果是一样的。在栈上覆盖上fake frame
。其中:- RSP: 一个可写的内存地址
- RIP:
syscall;retn
的地址 - RAX: read 的系统调用号
- RDI: 文件描述符,即从哪儿读入
- RSI: 可写内存的地址,即写入到哪儿
- RDX: 读入的字节数,这里是 306
sigreturn gadget
执行完之后,因为设置了RIP
,所以会再次执行syscall;retn
gadget。payload 的第二部分就是通过这里读入文件描述符的。这一部分包含3
个syscall;retn
、fake frame
和其他的代码或数据- 接收完数据后,
read
函数返回,返回值即读入的字节数被放到rax
中。我们的可写内存被这些数据所覆盖,并且RSP
指向了它的开头。然后syscall;retn
被执行,由于RAX=306
,即syncfs
的系统调用号,该调用总是返回0
,而0
又是read
的调用号 - 再次执行
syscall;retn
,即read
系统调用。这一次,读入的内容不重要,重要的是数量,让它等于15
,即sigreturn
的调用号 - 执行第三个
syscall;retn
,即sigreturn
系统调用。从第二个fake frame
中恢复寄存器,这里是execve("/bin/sh",...)
。另外还可以调用 mprotect 将某段数据变为可执行的。 - 执行
execve
,拿到shell
。