Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pass-the-sha1 for non domain joined machines #21

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ SharpDPAPI is licensed under the BSD 3-Clause license.
/pvk:key.pvk - use a DPAPI domain private key file to first decrypt reachable user masterkeys
/password:X - first decrypt the current user's masterkeys using a plaintext password or NTLM hash (works remotely)
/server:SERVER - triage a remote server, assuming admin access
/local - machine is non domain-joined. Keys use SHA1 instead of NTLM. Password may be supplied in plaintext or as a SHA1 hash


Arguments for the credentials|vaults|rdg|keepass|triage|blob|ps commands:
Expand Down
4 changes: 3 additions & 1 deletion SharpChrome/SharpChrome.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SharpChrome</RootNamespace>
<AssemblyName>SharpChrome</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
Expand All @@ -23,6 +23,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
Expand All @@ -33,6 +34,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>0</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
Expand Down
2 changes: 1 addition & 1 deletion SharpChrome/app.config
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>
7 changes: 6 additions & 1 deletion SharpDPAPI/Commands/Blob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace SharpDPAPI.Commands
{
Expand Down Expand Up @@ -63,7 +64,11 @@ public void Execute(Dictionary<string, string> arguments)
else if (arguments.ContainsKey("/password"))
{
string password = arguments["/password"];
Console.WriteLine("[*] Will decrypt user masterkeys with password: {0}\r\n", password);

Console.WriteLine("[*] Will decrypt user masterkeys with {0}: {1}\r\n",
Regex.IsMatch(password, @"^([a-f0-9]{32}|[a-f0-9]{40})$", RegexOptions.IgnoreCase)
? "hash" : "password", password);

if (arguments.ContainsKey("/server"))
{
masterkeys = Triage.TriageUserMasterKeys(null, true, arguments["/server"], password);
Expand Down
7 changes: 6 additions & 1 deletion SharpDPAPI/Commands/Credentials.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

namespace SharpDPAPI.Commands
{
Expand Down Expand Up @@ -44,7 +45,11 @@ public void Execute(Dictionary<string, string> arguments)
else if (arguments.ContainsKey("/password"))
{
string password = arguments["/password"];
Console.WriteLine("[*] Will decrypt user masterkeys with password: {0}\r\n", password);

Console.WriteLine("[*] Will decrypt user masterkeys with {0}: {1}\r\n",
Regex.IsMatch(password, @"^([a-f0-9]{32}|[a-f0-9]{40})$", RegexOptions.IgnoreCase)
? "hash" : "password", password);

if (arguments.ContainsKey("/server"))
{
masterkeys = Triage.TriageUserMasterKeys(null, true, arguments["/server"], password);
Expand Down
14 changes: 10 additions & 4 deletions SharpDPAPI/Commands/Masterkeys.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

namespace SharpDPAPI.Commands
{
Expand Down Expand Up @@ -46,7 +47,11 @@ public void Execute(Dictionary<string, string> arguments)
else if (arguments.ContainsKey("/password"))
{
password = arguments["/password"];
Console.WriteLine("[*] Will decrypt user masterkeys with password: {0}\r\n", password);

Console.WriteLine("[*] Will decrypt user masterkeys with {0}: {1}\r\n",
Regex.IsMatch(password, @"^([a-f0-9]{32}|[a-f0-9]{40})$", RegexOptions.IgnoreCase)
? "hash" : "password", password);

if (arguments.ContainsKey("/server"))
{
mappings = Triage.TriageUserMasterKeys(null, true, arguments["/server"], password);
Expand All @@ -55,12 +60,13 @@ public void Execute(Dictionary<string, string> arguments)
{
if (!arguments.ContainsKey("/sid"))
{
Console.WriteLine("[X] When using /password:X with /target:X, a /sid:X (domain user SID) is required!");
Console.WriteLine("[X] When using /password:X with /target:X, a /sid:X (user SID) is required!");
return;
}
else {
Console.WriteLine("[*] Triaging masterkey target: {0}\r\n", arguments["/target"]);
mappings = Triage.TriageUserMasterKeys(null, true, "", password, arguments["/target"], arguments["/sid"]);
mappings = Triage.TriageUserMasterKeys(null, true, "", password, arguments["/target"],
arguments["/sid"], false, arguments.ContainsKey("/local"));
}
}
else
Expand All @@ -79,7 +85,7 @@ public void Execute(Dictionary<string, string> arguments)
{
if (!arguments.ContainsKey("/sid"))
{
Console.WriteLine("[X] When dumping hashes with /target:X, a /sid:X (domain user SID) is required!");
Console.WriteLine("[X] When dumping hashes with /target:X, a /sid:X (user SID) is required!");
return;
}
else
Expand Down
119 changes: 119 additions & 0 deletions SharpDPAPI/Commands/Protect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static SharpDPAPI.Crypto;

namespace SharpDPAPI.Commands
{
public class Protect : ICommand
{
public static string CommandName => "protect";

public void Execute(Dictionary<string, string> arguments)
{
Console.WriteLine("\r\n[*] Action: Encrypt DPAPI blob");

if (!arguments.ContainsKey("/mkfile"))
{
Console.WriteLine("[!] Error: Provide a master key file using /mkfile:<path>");
return;
}

if (!arguments.ContainsKey("/password"))
{
Console.WriteLine("[!] Error: Provide a password");
return;
}

if (!arguments.ContainsKey("/input"))
{
Console.WriteLine("[!] Error: provide an input file path or base64 using /input:<file>");
return;
}

if (!arguments.ContainsKey("/output"))
{
Console.WriteLine("[!] Error: provide an output file path using /output:<file>");
return;
}

byte[] plainBytes;
byte[] entropy = null;

string inputFile = arguments["/input"].Trim('"').Trim('\'');
string outputFile = arguments["/output"].Trim('"').Trim('\'');
string sid = arguments.ContainsKey("/sid") ? arguments["/sid"] : string.Empty;
string masterKeyFile = arguments["/mkfile"].Trim('"').Trim('\'');
string password = arguments["/password"];
bool isLocalMachine = arguments.ContainsKey("/local");
string description = arguments.ContainsKey("/description") ? arguments["/description"] : string.Empty;

if (arguments.ContainsKey("/entropy"))
{
entropy = Helpers.StringToByteArray(arguments["/entropy"]);
}

if (File.Exists(inputFile))
{
plainBytes = File.ReadAllBytes(inputFile);
}
else
{
plainBytes = Convert.FromBase64String(inputFile);
}

Console.WriteLine("[*] Using masterkey: {0}", masterKeyFile);

string userSID = string.Empty;
Dictionary<string, string> keyDict;
KeyValuePair<string, string> keyPair = default;

byte[] masterKeyBytes = null;
Guid masterKeyGuid = default;

try
{
if (!isLocalMachine)
{
userSID = string.IsNullOrEmpty(sid) ? sid : Dpapi.ExtractSidFromPath(masterKeyFile);
keyDict = Triage.TriageUserMasterKeys(null, password: password, target: masterKeyFile, local: true, userSID: userSID);
if (keyDict.Count == 1)
{
keyPair = keyDict.First();
masterKeyBytes = Helpers.StringToByteArray(keyPair.Value);
masterKeyGuid = new Guid(keyPair.Key);
}
}
else
{
keyPair = Dpapi.DecryptMasterKeyWithSha(File.ReadAllBytes(masterKeyFile), Helpers.StringToByteArray(password));
masterKeyBytes = Helpers.StringToByteArray(keyPair.Value);
masterKeyGuid = new Guid(keyPair.Key);
}
}
catch
{
}

if (masterKeyBytes == null || masterKeyGuid == null)
{
Console.WriteLine("[!] Failed to decrypt masterkey. Wrong password?");
return;
}

byte[] enc = Dpapi.CreateDPAPIBlob(plainBytes, masterKeyBytes,
EncryptionAlgorithm.CALG_AES_256,
HashAlgorithm.CALG_SHA_512,
masterKeyGuid,
isLocalMachine: isLocalMachine,
entropy: entropy,
description: description
);

File.WriteAllBytes(outputFile, enc);
Console.WriteLine("[+] Done! Wrote {0} bytes to: {1}", enc.Length, outputFile);
Console.WriteLine("[*] {0}", Convert.ToBase64String(enc));
}
}
}
1 change: 1 addition & 0 deletions SharpDPAPI/Domain/CommandCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public CommandCollection()
_availableCommands.Add(Certificate.CommandName, () => new Certificate());
_availableCommands.Add(Search.CommandName, () => new Search());
_availableCommands.Add(SCCM.CommandName, () => new SCCM());
_availableCommands.Add(Protect.CommandName, () => new Protect());
}

public bool ExecuteCommand(string commandName, Dictionary<string, string> arguments)
Expand Down
16 changes: 13 additions & 3 deletions SharpDPAPI/Domain/Info.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ public static void ShowUsage()
/target:FILE/folder - triage a specific masterkey, or a folder full of masterkeys (otherwise triage local masterkeys)
/pvk:BASE64... - use a base64'ed DPAPI domain private key file to first decrypt reachable user masterkeys
/pvk:key.pvk - use a DPAPI domain private key file to first decrypt reachable user masterkeys
/password:X - first decrypt the current user's masterkeys using a plaintext password (works remotely)
/password:X - first decrypt the current user's masterkeys using a plaintext password or hash (works remotely)
/server:SERVER - triage a remote server, assuming admin access


Arguments for the credentials|vaults|rdg|keepass|triage|blob|ps commands:

Decryption:
/unprotect - force use of CryptUnprotectData() for 'ps', 'rdg', or 'blob' commands
/password:X - first decrypt the current user's masterkeys using a plaintext password. Works with any function, as well as remotely.
/password:X - first decrypt the current user's masterkeys using a plaintext password or hash. Works with any function, as well as remotely.
GUID1:SHA1 ... - use a one or more GUID:SHA1 masterkeys for decryption
/mkfile:FILE - use a file of one or more GUID:SHA1 masterkeys for decryption
/pvk:BASE64... - use a base64'ed DPAPI domain private key file to first decrypt reachable user masterkeys
Expand All @@ -73,7 +73,17 @@ public static void ShowUsage()
/machine - use the local machine store for certificate triage
/mkfile | /target - for /machine triage
/pvk | /mkfile | /password | /server | /target - for user triage



Encryption:
Arguments for the 'protect' command:
/input - the input file or base64 encoded string you want to protect using DPAPI
/output - the output file the encrypted blob will be written to
/mkfile - the path to the masterkey file to use for encryption
/password - the password, or password hash (SHA1 or NTLM) to decrypt the masterkey file
/local - encrypt the data with the local machine context instead of the user (requires SYSTEM DPAPI key)
/sid - provide the SID to use for decrypting the masterkey (by default this is guessed from the path)


Note: in most cases, just use *triage* if you're targeting user DPAPI secrets and *machinetriage* if you're going after SYSTEM DPAPI secrets.
These functions wrap all the other applicable functions that can be automatically run.
Expand Down
3 changes: 2 additions & 1 deletion SharpDPAPI/SharpDPAPI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SharpDPAPI</RootNamespace>
<AssemblyName>SharpDPAPI</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
Expand Down Expand Up @@ -72,6 +72,7 @@
<Compile Include="Commands\Machinemasterkeys.cs" />
<Compile Include="Commands\Machinetriage.cs" />
<Compile Include="Commands\Machinevaults.cs" />
<Compile Include="Commands\Protect.cs" />
<Compile Include="Commands\SCCM.cs" />
<Compile Include="Commands\Search.cs" />
<Compile Include="Commands\PS.cs" />
Expand Down
2 changes: 1 addition & 1 deletion SharpDPAPI/app.config
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>
Loading