Skip to content

Commit

Permalink
cf changelog 1.6
Browse files Browse the repository at this point in the history
  • Loading branch information
Alessandro ZANNI committed Sep 5, 2016
1 parent 0c7268c commit e5f1971
Show file tree
Hide file tree
Showing 96 changed files with 311 additions and 1,013 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
LaZagne 1.6 (05/09/2016)
- Only Windows:
* Internet Explorer history retrieved using powershell - no more dll written on the disk (all in memory)
* Internet Explorer passwords stored in the credential manager retrieved (for Win8 and higher)
* Wifi bug fixed

LaZagne 1.5 (01/08/2016)
- Only Windows:
* New modules (thanks to righettod => https://github.com/righettod):
Expand Down
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,6 @@ It uses two ways to do that:
* If a process from another user is launched (using runas or if many users are connected to the same host), it manages to steal a process token to launch laZagne with its privileges (this is the best way). It could retrieve passwords stored encrypted with the Windows API.
* If no process has been launched but other user exists (visible on the file system in C:\Users\...), it browses the file system in order to retrieve passwords from these users. However, it could not retrieve passwords encrypted with the Windows API (we have to be on the same context as the user to decrypt these passwords). Only few passwords could be retrieved (Firefox, Jitsi, Dbvis, etc.).

IE Browser history
----
Internet Explorer passwords (from IE7 and before Windows 8) can only be decrypted using the URL of the website. This one is used as an argument of the Win32CryptUnprotectData api. Thus, using the browsing history of ie will permit to decrypt many passwords.
To do that, I used a dll written in C code (the code is in the "browser_history_dll" directory) and it is directly embedded to the Python code as a Base64 string (c.f. ie.py). Once launched, the dll is written on the disk, a wrapper is used to call dll functions and then the dll file is removed from the disk.

Windows hashes
----
To dump windows hashes and LSA Secrets, the impacket library has been used: https://github.com/CoreSecurity/impacket
Expand All @@ -77,8 +72,8 @@ To do that, some code standards are to be met:

* Add on the config.manageModules.py file your class name and your import

* The output containing all passwords has to be send to the "print_output" function - ex: print_output(software_name, password_list)
* password_list has to be an array of dictionnaries.
* The run function has to return an array of dictionnaries
* ex: [{"Username": "emiliano", "Password":"ZapaTa", "URL": "http://mail.com"}]

* Optional: you could use the function "print_debug" to print your output
* ex: print_debug("ERROR", "Failed to load ...")
Expand Down
21 changes: 14 additions & 7 deletions Windows/src/LaZagne/laZagne.py → Windows/laZagne.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

# Softwares that passwords can be retrieved without needed to be in the user environmment
from lazagne.softwares.browsers.mozilla import Mozilla
from lazagne.softwares.wifi.wifipass import WifiPass
from lazagne.softwares.wifi.wifi import Wifi
from lazagne.softwares.windows.secrets import Secrets
from lazagne.softwares.chats.jitsi import Jitsi
from lazagne.softwares.chats.pidgin import Pidgin
Expand Down Expand Up @@ -428,8 +428,12 @@ def error(self, message):
if isChild:
constant.output = 'json'
try:
Secrets().run()
WifiPass().run()
if "windows" in argv or "all" in argv:
Secrets().run()

elif "wifi" in argv or "all" in argv:
pwdFound = Wifi().run()
print_output('Wifi', pwdFound)
except Exception,e:
print_debug('ERROR', e)
pass
Expand All @@ -447,8 +451,12 @@ def error(self, message):
# Get all privilege passwords
print '\n\n########## User: SYSTEM ##########\n'
try:
Secrets().run()
WifiPass().run()
if "windows" in argv or "all" in argv:
Secrets().run()

elif "wifi" in argv or "all" in argv:
pwdFound = Wifi().run()
print_output('Wifi', pwdFound)
stdoutRes.append(constant.finalResults)
except Exception,e:
print_debug('ERROR', e)
Expand Down Expand Up @@ -502,7 +510,6 @@ def error(self, message):
json.dump(json.dumps(chld), f)
print '[+] File written: ' + constant.folder_name + os.sep + constant.file_name_results + '.json'


# Write to a txt file
if constant.output != 'json':
with open(constant.folder_name + os.sep + constant.file_name_results + '.txt', 'a+b') as f:
Expand Down Expand Up @@ -562,4 +569,4 @@ def error(self, message):
print_footer()

elapsed_time = time.time() - start_time
print 'elapsed time = ' + str(elapsed_time)
print '\nelapsed time = ' + str(elapsed_time)
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class constant():
folder_name = 'results'
file_name_results = 'credentials' # the extention is added depending on the user output choice
MAX_HELP_POSITION = 27
CURRENT_VERSION = '1.5'
CURRENT_VERSION = '1.6'
output = None
file_logger = None

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from lazagne.softwares.chats.jitsi import Jitsi
# wifi
from lazagne.softwares.wifi.wifi import Wifi
from lazagne.softwares.wifi.wifipass import WifiPass
# mails
from lazagne.softwares.mails.outlook import Outlook
# databases
Expand Down Expand Up @@ -84,7 +83,6 @@ def get_modules():
Squirrel(),
Turba(),
Wifi(),
WifiPass(),
WinSCP(),
GitForWindows(),
MavenRepositories(),
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
282 changes: 282 additions & 0 deletions Windows/lazagne/softwares/browsers/ie.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
import win32con, win32api, win32cred
import subprocess
import struct, hashlib, os, base64
from ctypes import *
from ctypes.wintypes import DWORD
from lazagne.config.constant import *
from lazagne.config.write_output import print_debug
from lazagne.config.moduleInfo import ModuleInfo

memcpy = cdll.msvcrt.memcpy
LocalFree = windll.kernel32.LocalFree
CryptUnprotectData = windll.crypt32.CryptUnprotectData
CRYPTPROTECT_UI_FORBIDDEN = 0x01
pwdFound = []

class DATA_BLOB(Structure):
_fields_ = [
('cbData', DWORD),
('pbData', POINTER(c_char))
]

class IE(ModuleInfo):
def __init__(self):
options = {'command': '-e', 'action': 'store_true', 'dest': 'Internet Explorer', 'help': 'internet explorer (stored in registry and using the credential manager)'}
suboptions = [{'command': '-l', 'action': 'store', 'dest': 'historic', 'help': 'text file with a list of websites', 'title': 'Advanced ie option'}]
ModuleInfo.__init__(self, 'ie', 'browsers', options, suboptions)

def getData(self, blobOut):
cbData = int(blobOut.cbData)
pbData = blobOut.pbData
buffer = c_buffer(cbData)

memcpy(buffer, pbData, cbData)
LocalFree(pbData);
return buffer.raw

def Win32CryptUnprotectData(self, cipherText, entropy):
bufferIn = c_buffer(cipherText, len(cipherText))
blobIn = DATA_BLOB(len(cipherText), bufferIn)
bufferEntropy = c_buffer(entropy, len(entropy))
blobEntropy = DATA_BLOB(len(entropy), bufferEntropy)
blobOut = DATA_BLOB()
if CryptUnprotectData(byref(blobIn), None, byref(blobEntropy), None, None, 0, byref(blobOut)):
return self.getData(blobOut)
else:
return 'failed'

def get_hash_table(self, lists):
# get the url list
urls = self.get_history()
urls = urls + lists

# calculate the hash for all urls found on the history
hash_tables = []
for u in range(len(urls)):
try:
h = (urls[u] + '\0').encode('UTF-16LE')
hash_tables.append([h, hashlib.sha1(h).hexdigest().lower()])
except Exception,e:
print_debug('DEBUG', '{0}'.format(e))
return hash_tables

def get_history(self):
urls = self.history_from_regedit()
try:
urls = urls + self.history_from_powershell()
except Exception,e:
print_debug('DEBUG', '{0}'.format(e))
print_debug('ERROR', 'Browser history failed to load, only few url will be tried')

urls = urls + ['https://www.facebook.com/', 'https://www.gmail.com/', 'https://accounts.google.com/', 'https://accounts.google.com/servicelogin']
return urls

def history_from_powershell(self):
# From https://richardspowershellblog.wordpress.com/2011/06/29/ie-history-to-csv/
cmdline = '''
function get-iehistory {
[CmdletBinding()]
param ()
$shell = New-Object -ComObject Shell.Application
$hist = $shell.NameSpace(34)
$folder = $hist.Self
$hist.Items() |
foreach {
if ($_.IsFolder) {
$siteFolder = $_.GetFolder
$siteFolder.Items() |
foreach {
$site = $_
if ($site.IsFolder) {
$pageFolder = $site.GetFolder
$pageFolder.Items() |
foreach {
$visit = New-Object -TypeName PSObject -Property @{
URL = $($pageFolder.GetDetailsOf($_,0))
}
$visit
}
}
}
}
}
}
get-iehistory
'''
command=['powershell.exe', '/c', cmdline]
res = subprocess.check_output(command, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, universal_newlines=True)
urls = []
for r in res.split('\n'):
if r.startswith('http'):
urls.append(r.strip())
return urls

def history_from_regedit(self):
urls = []

# open the registry
accessRead = win32con.KEY_READ | win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE
keyPath = 'Software\\Microsoft\\Internet Explorer\\TypedURLs'

try:
hkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, keyPath, 0, accessRead)
except Exception,e:
print_debug('DEBUG', '{0}'.format(e))
return []

num = win32api.RegQueryInfoKey(hkey)[1]
for x in range(0, num):
k = win32api.RegEnumValue(hkey, x)
if k:
urls.append(k[1])
return urls

def decipher_password(self, cipher_text, u):
pfound = []
# deciper the password
pwd = self.Win32CryptUnprotectData(cipher_text, u)
a = None
for i in range(len(pwd)):
try:
a = pwd[i:].decode('UTF-16LE')
a = a.decode('utf-8')
break
except Exception,e:
pass
result = ''

# the last one is always equal to 0
secret = a.split('\x00')
if secret[len(secret)-1] == '':
secret = secret[:len(secret)-1]

# define the length of the tab
if len(secret) % 2 == 0:
length = len(secret)
else:
length = len(secret)-1

values = {}
# list username / password in clear text
for s in range(length):
try:
if s % 2 != 0:
values = {}
values['Website'] = u.decode('UTF-16LE')
values['Username'] = secret[length - s]
values['Password'] = password
pfound.append(values)
else:
password = secret[length - s]
except Exception,e:
print_debug('DEBUG', '{0}'.format(e))

return pfound

# get credential manager passwords
def windows_vault_ie(self):
# From : https://gallery.technet.microsoft.com/Manipulate-credentials-in-58e0f761
cmdline = '''
try
{
#Load the WinRT projection for the PasswordVault
$Script:vaultType = [Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]
$Script:vault = new-object Windows.Security.Credentials.PasswordVault -ErrorAction silentlycontinue
}
catch
{
throw "This module relies on functionality provided in Windows 8 or Windows 2012 and above."
}
#endregion
function Get-VaultCredential
{
process
{
try
{
&{
$Script:vault.RetrieveAll()
} | foreach-Object { $_.RetrievePassword() ; "Username......";$_.UserName;"######";"Password......";$_.Password;"######";"Website......";$_.Resource;"_________" }
}
catch
{
Write-Error -ErrorRecord $_ -RecommendedAction "Check your search input - user: $UserName resource: $Resource"
}
}
end
{
Write-Debug "[$cmdName] Exiting function"
}
}
Get-VaultCredential
'''

command=['powershell.exe', '/c', cmdline]
results = subprocess.check_output(command, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, universal_newlines=True)
passwords = []
for result in results.replace('\n', '').split('_________'):
values = {}
if result:
for res in result.split('######'):
values[res.split('......')[0]] = res.split('......')[1]
passwords.append(values)
return passwords

def run(self, historic=''):
pwdFound = []

# ----------------- For Win7 and before (passwords stored on registry) -----------------
# open the registry
accessRead = win32con.KEY_READ | win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE
keyPath = 'Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2'

failed = False
try:
hkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, keyPath, 0, accessRead)
except Exception,e:
print_debug('DEBUG', '{0}'.format(e))
failed = True

if failed == False:
nb_site = 0
nb_pass_found = 0
lists = []
if historic:
if os.path.exists(historic):
f = open(historic, 'r')
for line in f:
lists.append(line.strip())
else:
print_debug('WARNING', 'The text file %s does not exist' % historic)

# retrieve the urls from the history
hash_tables = self.get_hash_table(lists)

num = win32api.RegQueryInfoKey(hkey)[1]
for x in range(0, num):
k = win32api.RegEnumValue(hkey, x)
if k:
nb_site +=1
for h in hash_tables:
# both hash are similar, we can decipher the password
if h[1] == k[0][:40].lower():
nb_pass_found += 1
cipher_text = k[1]
pwdFound += self.decipher_password(cipher_text, h[0])
break

# manage errors
if nb_site > nb_pass_found:
print_debug('ERROR', '%s hashes have not been decrypted, the associate website used to decrypt the passwords has not been found' % str(nb_site - nb_pass_found))

# ----------------- For Win8 and after (passwords stored on the credential manager) -----------------
try:
pwdFound += self.windows_vault_ie()
except:
print_debug('DEBUG', '{0}'.format(e))

return pwdFound
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit e5f1971

Please sign in to comment.