ret2lic1
程序给出了system
的plt和字符串/bin/sh
,直接构造payload,可以使用IDA Pro手动找system
函数的plt表项地址,也可以使用pwntools的ELF对象自动找
完整exp如下
1 | #! /usr/bin/env python |
ret2lic2
这里缺少/bin/sh
字符串,没有现成的,甚至sh\x00
都没有,只能在构造ROP链中加上gets
函数输入一个/bin/sh
,第一个gadget读取字符串(可以使用gets
函数的plt),第二个gadget执行 system("/bin/sh")
,这里因为ROP链只有两个函数,所以可以不加pop_ret
来平衡栈,即
1 | payload = b'a'*(108+4) + p32(gets_addr) + p32(system_addr) + p32(bin_sh_addr) + p32(bin_sh_addr) |
若寻找pop_ret
如下(只要是通用寄存器都行,不一定是ebx,不选ebp和esp担心影响函数栈帧)
1 | (pwn) ~/Desktop/buuctf/ctf-wiki-20:52 >>> ROPgadget --bin ret2libc2 --only 'pop|ret' | grep 'ebx' |
完整exp如下
1 | #! /usr/bin/env python |
ret2lic3
system
的plt和字符串/bin/sh
都没有给,由于延迟绑定机制,如果程序中没有调用到system
函数,就不会有system
函数的plt表项,那就只能找到system
函数在libc中的真实地址
首先使用一些方法泄露其他函数的GOT表项的地址(即Libc中该函数的地址),然后再泄露远程主机的Libc版本(比如LibcSearcher),就可以通过偏移量计算出远程主机所使用的Libc下system
函数的地址,CTF Wiki给我总结了流程:
- 泄露
__libc_start_main
地址 - 获取 libc 版本
- 获取
system
地址与/bin/sh
的地址 - 返回程序入口,再次执行源程序
- 触发栈溢出执行
system("/bin/sh")
因为开启了ASLR但没有开启PIE,可以在ROP链中使用puts
函数将要泄露的地址输出到前端,从IDA Pro中找到GOT表项地址,将地址中存储的内容(要泄露的libc地址)输出在前端
因为__libc_start_main
是程序的入口,GOT表中肯定存在__libc_start_main
这一项,所以一般选取__libc_start_main
作为要泄露的地址,返回地址的选取有main
函数和_start
函数两种,注意PLT表中只有链接的标准函数,所以不会有_start
和main
表项,这两个作为程序入口是放在程序代码段的,但libc_start_main
是动态链接的函数
在程序的执行过程中,
_start
调用了libc_start_main
,libc_start_main
调用了main
__start
这个符号是程序的起始;main
是被标准库调用的一个符号
Linux x86 Program Start Up (dbp-consulting.com)这篇文章把整个程序运行到main
函数被调用的过程都讲了一遍,有时间看完后再总结一篇博客,_start
函数如下(汇编实现的)
1 | 080482e0 <_start>: |
重点在于第三和第四行,直接引用原文
The mov puts argv into %ecx without moving the stack pointer. Then we and the stack pointer with a mask that clears off the bottom four bits
也就是操作系统会使用execve
函数开启进程执行程序,pop %esi
将参数argc
赋给esi
,此时esp
指向第二个参数argv
,使用mov
指令赋给ecx
,使用AND
逻辑操作将esp
的最后四个字节变成0,esp
会减小0-15个字节大小,起到了内存对齐的作用,有利于*“aligned for memory and cache efficiency”*,最重要的是这个语句起到了平衡堆栈的作用
所以这里使用_start
函数作为返回地址时,第二个发生payload的填充数据长度和第一次相同,而选择main
函数的话,main
没有平衡堆栈的操作,会使esp的值变大,相当于栈空间减小了,填充数据的长度也减小了,因为我们的libc版本
使用gdb跟进调试会发现返回main
函数后,栈的空间是0xfff7c150 - 0xfff7c0d0 = 0x80 = 128,栈的空间减小了8,所以填充数据长度从112变成了104
1 | EBP 0xfff7c150 ◂— 0x61616161 ('aaaa') |
原来大小是0xffffcf08 - 0xffffce80 = 0x88 = 136
1 | EBP 0xffffcf08 ◂— 0x0 |
这里可能因为LibcSeacher找不到正确的版本,没有打通
我的exp如下
1 | #! /usr/bin/env python |
本着互联网开源的性质,欢迎分享这篇文章,以帮助到更多的人,谢谢!