Skip to content

Commit ead02d8

Browse files
authored
Merge pull request Pennyw0rth#286 from termanix/New_Module_Bitlocker
2 parents cce5c32 + 8bbab76 commit ead02d8

2 files changed

Lines changed: 133 additions & 0 deletions

File tree

nxc/modules/bitlocker.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import re
2+
from impacket.dcerpc.v5.dcom import wmi
3+
from impacket.dcerpc.v5.dtypes import NULL
4+
from impacket.dcerpc.v5.dcomrt import DCOMConnection
5+
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY
6+
7+
class NXCModule:
8+
name = "bitlocker"
9+
description = "Enumerating BitLocker Status on target(s) If it is enabled or disabled."
10+
supported_protocols = ["smb", "wmi"]
11+
opsec_safe = True
12+
multiple_hosts = True
13+
14+
def __init__(self, context=None, module_options=None):
15+
self.context = context
16+
self.module_options = module_options
17+
18+
def options(self, context, module_options):
19+
"""
20+
USAGE:
21+
22+
NetExec smb <IP> -u <username> -p <password> -M bitlocker
23+
NetExec wmi <IP> -u <username> -p <password> -M bitlocker (Better option to use on real life.)
24+
"""
25+
26+
def on_admin_login(self, context, connection):
27+
if context.protocol == "smb":
28+
bitlocker_smb = BitLockerSMB(context, connection)
29+
bitlocker_smb.check_bitlocker_status()
30+
elif context.protocol == "wmi":
31+
bitlocker_wmi = BitLockerWMI(context, connection)
32+
bitlocker_wmi.check_bitlocker_status()
33+
34+
35+
class BitLockerSMB:
36+
def __init__(self, context, connection):
37+
self.context = context
38+
self.connection = connection
39+
40+
def check_bitlocker_status(self):
41+
# PowerShell command to check BitLocker volumes status.
42+
check_bitlocker_command_str = 'powershell.exe "Get-BitLockerVolume | Select-Object MountPoint, EncryptionMethod, ProtectionStatus"'
43+
44+
try:
45+
# Executing the PowerShell command to get BitLocker volumes status.
46+
check_bitlocker_command_str_output = self.connection.execute(check_bitlocker_command_str, True)
47+
48+
if "'Get-BitLockerVolume' is not recognized" in check_bitlocker_command_str_output:
49+
self.context.log.fail("BitLockerVolume not found on target.")
50+
return
51+
52+
# Splitting the output into lines.
53+
lines = str(check_bitlocker_command_str_output).splitlines()
54+
data_lines = [line for line in lines if re.match(r"\w:", line)]
55+
56+
for line in data_lines:
57+
# Checking every line for starting with drive
58+
if line[1] == ":":
59+
parts = line.split()
60+
MountPoint, EncryptionMethod, protection_status = parts[0], parts[1], parts[2]
61+
62+
# Checking if BitLocker is enabled.
63+
if protection_status == "On":
64+
self.context.log.highlight(f"BitLocker is enabled on drive {MountPoint} (Encryption Method: {EncryptionMethod})")
65+
else:
66+
self.context.log.highlight(f"BitLocker is disabled on drive {MountPoint}")
67+
except Exception as e:
68+
self.context.log.exception(f"Exception occurred: {e}")
69+
70+
71+
class BitLockerWMI:
72+
def __init__(self, context, connection):
73+
self.context = context
74+
self.connection = connection
75+
76+
def check_bitlocker_status(self):
77+
try:
78+
# Create a DCOM connection
79+
dcom_conn = DCOMConnection(
80+
self.connection.host,
81+
self.connection.username,
82+
self.connection.password,
83+
self.connection.domain,
84+
self.connection.lmhash,
85+
self.connection.nthash,
86+
oxidResolver=True,
87+
doKerberos=self.connection.kerberos,
88+
kdcHost=self.connection.kdcHost)
89+
90+
try:
91+
# CoCreateInstanceEx for WMI login
92+
i_interface = dcom_conn.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
93+
iWbemLevel1Login = wmi.IWbemLevel1Login(i_interface)
94+
95+
# Specify the namespace for BitLocker
96+
bitlockerNamespace = "root\\CIMv2\\Security\\MicrosoftVolumeEncryption"
97+
98+
# NTLM login for WMI
99+
iWbemServices = iWbemLevel1Login.NTLMLogin(bitlockerNamespace, NULL, NULL)
100+
101+
# Set authentication level
102+
iWbemServices.get_dce_rpc().set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
103+
104+
# Query to get BitLocker status
105+
classQuery = "SELECT DriveLetter, ProtectionStatus, EncryptionMethod FROM Win32_EncryptableVolume"
106+
iEnumWbemClassObject = iWbemServices.ExecQuery(classQuery)
107+
encryptionTypeMapping = {0: "None", 1: "AES_256_WITH_DIFFUSER", 2: "AES_256_WITH_DIFFUSER", 3: "AES_128", 4: "AES_256", 5: "HARDWARE_ENCRYPTION", 6: "XTS_AES_128", 7: "XTS_AES_256"}
108+
109+
try:
110+
while True:
111+
iWbemClassObject = iEnumWbemClassObject.Next(0xffffffff, 1)
112+
encryptionMethod = int(iWbemClassObject[0].EncryptionMethod)
113+
if iWbemClassObject[0].ProtectionStatus == 1:
114+
self.context.log.highlight(f"BitLocker is enabled on drive {iWbemClassObject[0].DriveLetter} (Encryption Method: {encryptionTypeMapping.get(encryptionMethod, 'Unknown')})")
115+
else:
116+
if encryptionMethod == 0: # Should be 0 if disabled
117+
self.context.log.highlight(f"BitLocker is disabled on drive {iWbemClassObject[0].DriveLetter}")
118+
except Exception:
119+
pass # Using pass because if try to log or printing, getting "WMI Session Error: code: 0x1 - WBEM_S_FALSE"
120+
121+
# Release resources
122+
iWbemLevel1Login.RemRelease()
123+
iWbemServices.RemRelease()
124+
dcom_conn.disconnect()
125+
except Exception as e:
126+
if "WBEM_E_INVALID_NAMESPACE" in str(e):
127+
self.context.log.fail("BitLockerNamespace not found on target.")
128+
dcom_conn.disconnect()
129+
except Exception as e:
130+
self.context.log.error(f"Error occurred during BitLocker check: {e}")
131+
dcom_conn.disconnect()

tests/e2e_commands.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -L
6262
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M add-computer -o NAME="BADPC" PASSWORD="Password1"
6363
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M add-computer -o NAME="BADPC" PASSWORD="Password2" CHANGEPW=True
6464
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M add-computer -o NAME="BADPC" DELETE=True
65+
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M bitlocker
6566
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M dfscoerce
6667
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M drop-sc
6768
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M drop-sc -o CLEANUP=True
@@ -153,6 +154,7 @@ netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M enum_dns
153154
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get_netconnections
154155
#netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M rdp -o ACTION=enable
155156
#netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M rdp -o ACTION=disable
157+
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M bitlocker
156158
##### LDAP
157159
netexec {DNS} ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
158160
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --users

0 commit comments

Comments
 (0)