Skip to content

Commit

Permalink
Merge pull request #133 from skelsec/main
Browse files Browse the repository at this point in the history
Main
  • Loading branch information
skelsec authored Oct 5, 2023
2 parents 3ea56c3 + ba86774 commit 9c79b68
Show file tree
Hide file tree
Showing 9 changed files with 46 additions and 27 deletions.
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
![Supported Python versions](https://img.shields.io/badge/python-3.7+-blue.svg) [![Twitter](https://img.shields.io/twitter/follow/skelsec?label=skelsec&style=social)](https://twitter.com/intent/follow?screen_name=skelsec)

:triangular_flag_on_post: This is the public repository of PyPyKatz, for latest version and updates please consider supporting us through https://porchetta.industries/

# pypykatz
Mimikatz implementation in pure Python. At least a part of it :)
Runs on all OS's which support python>=3.6
![pypy_card](https://user-images.githubusercontent.com/19204702/71646030-221fe200-2ce1-11ea-9e2a-e587ea4790d7.jpg)

## :triangular_flag_on_post: Sponsors

If you want to sponsors this project and have the latest updates on this project, latest issues fixed, latest features, please support us on https://porchetta.industries/

## Official Discord Channel

Come hang out on Discord!
Expand Down
2 changes: 1 addition & 1 deletion pypykatz/_version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

__version__ = "0.6.8"
__version__ = "0.6.9"
__banner__ = \
"""
# pypyKatz %s
Expand Down
15 changes: 10 additions & 5 deletions pypykatz/lsadecryptor/package_commons.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,16 @@ def walk_list(self, entry_ptr, callback, max_walk = 255, override_ptr = None):
max_walk = max_walk
self.log_ptr(entry_ptr.value, 'List entry -%s-' % entry_ptr.finaltype.__name__ if not override_ptr else override_ptr.__name__)
while True:
if override_ptr:
entry = entry_ptr.read(self.reader, override_ptr)
else:
entry = entry_ptr.read(self.reader)

try:
if override_ptr:
entry = entry_ptr.read(self.reader, override_ptr)
else:
entry = entry_ptr.read(self.reader)
except Exception as e:
self.log('Exception while reading list entry: %s' % e)
break # uncomment this line to discard the exception and continue parsing
raise

if not entry:
break

Expand Down
11 changes: 9 additions & 2 deletions pypykatz/registry/security/acommon.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,13 @@ def __str__(self):
return '=== LSA DPAPI secret ===\r\nHistory: %s\r\nMachine key (hex): %s\r\nUser key(hex): %s' % (self.history, self.machine_key.hex(), self.user_key.hex())

class LSADCCSecret:
def __init__(self, version, domain, username, hash_value, iteration = None):
def __init__(self, version, domain, username, hash_value, iteration = 10240, last_write_ts = None):
self.version = version
self.domain = domain
self.username = username
self.iteration = iteration
self.hash_value = hash_value
self.last_write_ts = last_write_ts

def to_dict(self):
t = {}
Expand All @@ -220,7 +221,13 @@ def to_dict(self):
t['username'] = self.username
t['iteration'] = self.iteration
t['hash_value'] = self.hash_value
t['lastwrite'] = self.get_lastwrite()
return t

def get_lastwrite(self, default = None):
if self.last_write_ts is None:
return default
return self.last_write_ts.strftime("%Y-%m-%d %H:%M:%S")

def __str__(self):
return self.to_lopth()
Expand All @@ -229,4 +236,4 @@ def to_lopth(self):
if self.version == 1:
return "%s/%s:%s:%s" % (self.domain, self.username, self.hash_value.hex(), self.username)
else:
return "%s/%s:$DCC2$%s#%s#%s" % (self.domain, self.username, self.iteration, self.username, self.hash_value.hex())
return "%s/%s:*%s*$DCC2$%s#%s#%s" % (self.domain, self.username, self.get_lastwrite(''), self.iteration, self.username, self.hash_value.hex())
2 changes: 1 addition & 1 deletion pypykatz/registry/security/asecurity.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ async def dump_dcc(self):
domain = blob.read(record.DnsDomainNameLength).decode('utf-16-le')

version = 2 if self.lsa_secret_key_vista_type is True else 1
secret = LSADCCSecret(version, domain, username, dcc_hash, iteration = self.dcc_iteration_count)
secret = LSADCCSecret(version, domain, username, dcc_hash, iteration = self.dcc_iteration_count, last_write_ts=record.LastWrite)
self.dcc_hashes.append(secret)

return self.dcc_hashes
Expand Down
11 changes: 9 additions & 2 deletions pypykatz/registry/security/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,13 @@ def __str__(self):
return '=== LSA DPAPI secret ===\r\nHistory: %s\r\nMachine key (hex): %s\r\nUser key(hex): %s' % (self.history, self.machine_key.hex(), self.user_key.hex())

class LSADCCSecret:
def __init__(self, version, domain, username, hash_value, iteration = None):
def __init__(self, version, domain, username, hash_value, iteration = 10240, last_write_ts = None):
self.version = version
self.domain = domain
self.username = username
self.iteration = iteration
self.hash_value = hash_value
self.last_write_ts = last_write_ts

def to_dict(self):
t = {}
Expand All @@ -220,7 +221,13 @@ def to_dict(self):
t['username'] = self.username
t['iteration'] = self.iteration
t['hash_value'] = self.hash_value
t['lastwrite'] = self.get_lastwrite()
return t

def get_lastwrite(self, default = None):
if self.last_write_ts is None:
return default
return self.last_write_ts.strftime("%Y-%m-%d %H:%M:%S")

def __str__(self):
return self.to_lopth()
Expand All @@ -229,4 +236,4 @@ def to_lopth(self):
if self.version == 1:
return "%s/%s:%s:%s" % (self.domain, self.username, self.hash_value.hex(), self.username)
else:
return "%s/%s:$DCC2$%s#%s#%s" % (self.domain, self.username, self.iteration, self.username, self.hash_value.hex())
return "%s/%s:*%s*$DCC2$%s#%s#%s" % (self.domain, self.username, self.get_lastwrite(''), self.iteration, self.username, self.hash_value.hex())
4 changes: 2 additions & 2 deletions pypykatz/registry/security/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,9 @@ def dump_dcc(self):
username = blob.read(record.UserLength).decode('utf-16-le')
blob.seek(self.__pad(record.UserLength) + self.__pad(record.DomainNameLength))
domain = blob.read(record.DnsDomainNameLength).decode('utf-16-le')

version = 2 if self.lsa_secret_key_vista_type is True else 1
secret = LSADCCSecret(version, domain, username, dcc_hash, iteration = self.dcc_iteration_count)
secret = LSADCCSecret(version, domain, username, dcc_hash, iteration = self.dcc_iteration_count, last_write_ts=record.LastWrite)
self.dcc_hashes.append(secret)

return self.dcc_hashes
Expand Down
6 changes: 6 additions & 0 deletions pypykatz/registry/security/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#
import enum
import io
from pypykatz.commons.filetime import filetime_to_dt

class LSA_SECRET_BLOB:
def __init__(self):
Expand Down Expand Up @@ -157,6 +158,11 @@ def from_buffer(buff):
nl.IV = buff.read(16)
nl.CH = buff.read(16)
nl.EncryptedData = buff.read()

try:
nl.LastWrite = filetime_to_dt(nl.LastWrite)
except Exception as e:
nl.LastWrite = None

return nl

Expand Down
16 changes: 8 additions & 8 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@
"Operating System :: OS Independent",
],
install_requires=[
'unicrypto==0.0.10',
'minidump==0.0.21',
'minikerberos==0.4.1',
'aiowinreg==0.0.10',
'msldap==0.5.5',
'winacl==0.1.7',
'aiosmb==0.4.6',
'aesedb==0.1.4',
'unicrypto>=0.0.10,<=0.1.0',
'minidump>=0.0.21,<=0.1.0',
'minikerberos>=0.4.1,<=0.5.0',
'aiowinreg>=0.0.10,<=0.1.0',
'msldap>=0.5.7,<=0.6.0',
'winacl>=0.1.7,<=0.2.0',
'aiosmb>=0.4.8,<=0.5.0',
'aesedb>=0.1.4,<=0.2.0',
'tqdm',
],

Expand Down

0 comments on commit 9c79b68

Please sign in to comment.