确定偏移
- 利用pwntools提供的FmtStr(exec_fmt),获取offset
读/写栈上数据
- 读:计算出要读的地址是第xxx个不定参数,然后利用%xxx$x 读取(x-十六进制读,lx长整读取(64位))
- 写:首先泄露栈上rbp的值,然后根据rbp与返回地址之间的差值,得出返回地址所在的栈地址,利用任意地址写即可覆盖返回地址,如下图所示,可泄露show函数栈帧中rbp1的值,然后利用rbp1与printf函数返回地址之间的差值,计算可到printf返回地址所在的地址,覆盖之(绕过FULL RELRO,泄露show函数的返回地址,可以绕过PIE,泄露main函数的返回地址,即
__libc\_start_main_ret
,可以leak libc)
读/写任意地址数据
读:
发送:payload = START + %xx$s + END+ addr
接收:
1
2
3
4recvuntil(START)
data = recvuntil(END, drop=1, timeout=1)
if not data:
data = '\x00'读取addr地址处的字符串,xxx是根据addr在栈上的地址计算出的不定参数的值recvuntil(START)
- 写:
payload = %yyyc+%xxx$hn+\[padding\]+addr
- xxx计算方法同上,yyy为要写入的数据,可以用
fmtstr_payload(offset, writes)
实现
DynELF时leak函数至少返回1字节,并且addr地址中可以出现\x00,
格式化串不在栈上
要实现任意地址读写,只能通过其他操作控制栈上的数据(比如输入数字或者字符串来构造想要的地址),然后确认偏移
printf_chk
编译时如果开启了FORTIFY_SOURCE=2的编译选项,glibc在调用printf时,更改为调用printf_chk函数,将开启两个保护:
- 限制%n, 如果%n写入一个拥有写权限的内存空间,如栈、bss段,就会终止报错
- 无法使用
%N$
来跳过之前的不定参数,但可以采用%4$x%3$x%2$x%1$x
这种方式