| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,6 +5,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | |||
| 5 | 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | |
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | + ## [1.11.1] - 2021-03-05 | ||
| 9 | + | ||
| 10 | + ### Fixed | ||
| 11 | + * **SharpChrome** project | ||
| 12 | + * Chrome statekey usage bug when triaging multiple users | ||
| 13 | + | ||
| 14 | + ### Changed | ||
| 15 | + * **SharpChrome** project | ||
| 16 | + * Default cookie/logins/statekeys triage behavior is now to triage the current user if elevated, unless pvk/password/masterkeys specified | ||
| 17 | + * `/target:X` can now be a `C:\Users\USER\` folder for any specified triage | ||
| 18 | + * Added Brave statekey triage to `statekeys` | ||
| 19 | + * Cleaned up Chromium triage code | ||
| 20 | + * Removed out of date SharpDPAPI.cna aggressor script | ||
| 21 | + | ||
| 22 | + | ||
| 8 | 23 | ## [1.11.0] - 2021-03-01 | |
| 9 | 24 | ||
| 10 | 25 | ### Added | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -29,7 +29,6 @@ SharpDPAPI is licensed under the BSD 3-Clause license. | |||
| 29 | 29 | - [Operational Usage](#operational-usage) | |
| 30 | 30 | - [SharpDPAPI](#sharpdpapi-1) | |
| 31 | 31 | - [SharpChrome](#sharpchrome) | |
| 32 | - - [Cobalt Strike Usage](#cobalt-strike-usage) | ||
| 33 | 32 | - [SharpDPAPI Commands](#sharpdpapi-commands) | |
| 34 | 33 | - [User Triage](#user-triage) | |
| 35 | 34 | - [masterkeys](#masterkeys) | |
@@ -164,6 +163,7 @@ SharpDPAPI is licensed under the BSD 3-Clause license. | |||
| 164 | 163 | ||
| 165 | 164 | Targeting: | |
| 166 | 165 | /target:FILE - triage a specific 'Cookies', 'Login Data', or 'Local State' file location | |
| 166 | + /target:C:\Users\X\ - triage a specific user folder for any specified command | ||
| 167 | 167 | /server:SERVER - triage a remote server, assuming admin access (note: must use with /pvk:KEY) | |
| 168 | 168 | /browser:X - triage 'chrome' (the default) or (chromium-based) 'edge' | |
| 169 | 169 | ||
@@ -205,21 +205,14 @@ SharpChrome is a Chrome-specific implementation of SharpDPAPI capable of **cooki | |||
| 205 | 205 | ||
| 206 | 206 | Since Chrome Cookies/Login Data are saved without CRYPTPROTECT_SYSTEM, CryptUnprotectData() is back on the table. If SharpChrome is run from an unelevated contect, it will attempt to decrypt any logins/cookies for the current user using CryptUnprotectData(). A `/pvk:[BASE64|file.pvk]`, {GUID}:SHA1 lookup table, `/password:X`, or `/mkfile:FILE` of {GUID}:SHA1 values can also be used to decrypt values. Also, the [C# SQL library](https://github.com/akveo/digitsquare/tree/a251a1220ef6212d1bed8c720368435ee1bfdfc2/plugins/com.brodysoft.sqlitePlugin/src/wp) used (with a few modifications) supports [lockless opening](https://github.com/gentilkiwi/mimikatz/pull/199), meaning that Chrome does not have to be closed/target files do not have to be copied to another location. | |
| 207 | 207 | ||
| 208 | - If Chrome is version 80+, an AES state key is stored in *AppData\Local\Google\Chrome\User Data\Local State* - this key is protected with DPAPI, so we can use CryptUnprotectData()/pvk/masterkey lookup tables to decrypt it. This AES key is then used to protect new cookie and login data entries. This is also the process when `/browser:edge` is specified, for newer Chromium-based Edge browser triage. | ||
| 208 | + If Chrome is version 80+, an AES state key is stored in *AppData\Local\Google\Chrome\User Data\Local State* - this key is protected with DPAPI, so we can use CryptUnprotectData()/pvk/masterkey lookup tables to decrypt it. This AES key is then used to protect new cookie and login data entries. This is also the process when `/browser:edge` or `/browser:brave` is specified, for newer Chromium-based Edge browser triage. | ||
| 209 | 209 | ||
| 210 | 210 | By default, cookies and logins are displayed as a csv - this can be changed with `/format:table` for table output, and `/format:json` for cookies specifically. The json option outputs cookies in a json format that can be imported into the [EditThisCookie](https://chrome.google.com/webstore/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg?hl=en) Chrome extension for easy reuse. | |
| 211 | 211 | ||
| 212 | 212 | The **cookies** command also has `/cookie:REGEX` and `/url:REGEX` arguments to only return cookie names or urls matching the supplied regex. This is useful with `/format:json` to easily clone access to specific sites. | |
| 213 | 213 | ||
| 214 | - ### Cobalt Strike Usage | ||
| 214 | + Specific cookies/logins/statekey files can be specified with `/target:X`, and a user folder can be specified with `/target:C:\Users\USER\` for any triage command. | ||
| 215 | 215 | ||
| 216 | - SharpDPAPI has an Aggressor script (**SharpDPAPI.cna**) that automates the usage of SharpDPAPI through Cobalt Strike. Before usage, replace `$SharpDPAPI::AssemblyPath` in the .cna with the location of your compiled SharpDPAPI assembly. | ||
| 217 | - | ||
| 218 | - Loading **SharpDPAPI.cna** will register a new **sharpDPAPI** Beacon command. If **beacon> sharpDPAPI -dump** is run, the current Beacon will execute `sekurlsa::dpapi` Mimikatz command to extract any DPAPI keys from LSASS (assuming elevation) followed by `dpapi::cache` to display the {GUID}:SHA1 mappings. The decrypted master key SHA1s are stored in the credential store. | ||
| 219 | - | ||
| 220 | - Running **beacon> sharpDPAPI** will execute SharpDPAPI with the `triage` command with any GUID:SHA1 masterkey mappings extracted for that host. This allows for effective triage of all Credentials and Vaults on a host _for any currently logged in users_. | ||
| 221 | - | ||
| 222 | - _TODO: implement machine key triage functions in SharpDPAPI.cna_ | ||
| 223 | 216 | ||
| 224 | 217 | ## SharpDPAPI Commands | |
| 225 | 218 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -150,24 +150,26 @@ public void Execute(Dictionary<string, string> arguments) | |||
| 150 | 150 | { | |
| 151 | 151 | Console.WriteLine("[*] Target 'Cookies' File: {0}\r\n", target); | |
| 152 | 152 | } | |
| 153 | - | ||
| 154 | 153 | Chrome.ParseChromeCookies(masterkeys, target, displayFormat, showAll, unprotect, cookieRegex, urlRegex, setneverexpire, stateKeyBytes, quiet); | |
| 155 | 154 | } | |
| 155 | + else if (Directory.Exists(target) && target.ToLower().Contains("users")) | ||
| 156 | + { | ||
| 157 | + Chrome.TriageChromeCookies(masterkeys, server, target, displayFormat, showAll, unprotect, cookieRegex, urlRegex, setneverexpire, stateKey, browser, quiet); | ||
| 158 | + } | ||
| 156 | 159 | else | |
| 157 | 160 | { | |
| 158 | - Console.WriteLine("\r\n[X] '{0}' is not a valid file.", target); | ||
| 161 | + Console.WriteLine("\r\n[X] '{0}' is not a valid file or user directory.", target); | ||
| 159 | 162 | } | |
| 160 | 163 | } | |
| 161 | 164 | else | |
| 162 | 165 | { | |
| 163 | - if (arguments.ContainsKey("/server") && !arguments.ContainsKey("/pvk") && !arguments.ContainsKey("/password") && !quiet) | ||
| 166 | + if (arguments.ContainsKey("/server") && (masterkeys.Count == 0)) | ||
| 164 | 167 | { | |
| 165 | - Console.WriteLine("[X] The '/server:X' argument must be used with '/pvk:BASE64...' or '/password:X' !"); | ||
| 168 | + Console.WriteLine("[X] The '/server:X' argument must be used with '/pvk:BASE64...', '/password:X' , or masterkey specification !"); | ||
| 166 | 169 | } | |
| 167 | 170 | else | |
| 168 | 171 | { | |
| 169 | - // last "true" -> indicates we want to triage Edge | ||
| 170 | - Chrome.TriageChromeCookies(masterkeys, server, displayFormat, showAll, unprotect, cookieRegex, urlRegex, setneverexpire, stateKey, browser, quiet); | ||
| 172 | + Chrome.TriageChromeCookies(masterkeys, server, "", displayFormat, showAll, unprotect, cookieRegex, urlRegex, setneverexpire, stateKey, browser, quiet); | ||
| 171 | 173 | } | |
| 172 | 174 | } | |
| 173 | 175 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,6 +19,7 @@ public void Execute(Dictionary<string, string> arguments) | |||
| 19 | 19 | bool quiet = false; // don't display headers/logos/etc. (for csv/json output) | |
| 20 | 20 | string stateKey = ""; // decrypted AES statekey to use for cookie decryption | |
| 21 | 21 | string browser = "chrome"; // alternate Chromiun browser to specify, currently supported: "chrome", "edge", "brave" | |
| 22 | + string target = ""; // target file/user folder to triage | ||
| 22 | 23 | ||
| 23 | 24 | ||
| 24 | 25 | if (arguments.ContainsKey("/quiet")) | |
@@ -106,7 +107,7 @@ public void Execute(Dictionary<string, string> arguments) | |||
| 106 | 107 | ||
| 107 | 108 | if (arguments.ContainsKey("/target")) | |
| 108 | 109 | { | |
| 109 | - string target = arguments["/target"].Trim('"').Trim('\''); | ||
| 110 | + target = arguments["/target"].Trim('"').Trim('\''); | ||
| 110 | 111 | byte[] stateKeyBytes = null; | |
| 111 | 112 | ||
| 112 | 113 | if (!String.IsNullOrEmpty(stateKey)) | |
@@ -122,21 +123,23 @@ public void Execute(Dictionary<string, string> arguments) | |||
| 122 | 123 | } | |
| 123 | 124 | Chrome.ParseChromeLogins(masterkeys, target, displayFormat, showAll, unprotect, stateKeyBytes, quiet); | |
| 124 | 125 | } | |
| 126 | + else if(Directory.Exists(target) && target.ToLower().Contains("users")) | ||
| 127 | + { | ||
| 128 | + Chrome.TriageChromeLogins(masterkeys, server, target, displayFormat, showAll, unprotect, stateKey, browser, quiet); | ||
| 129 | + } | ||
| 125 | 130 | else | |
| 126 | 131 | { | |
| 127 | - Console.WriteLine("\r\n[X] '{0}' is not a valid file.", target); | ||
| 132 | + Console.WriteLine("\r\n[X] '{0}' is not a valid file or user directory.", target); | ||
| 128 | 133 | } | |
| 129 | 134 | } | |
| 130 | 135 | else | |
| 131 | 136 | { | |
| 132 | - if (arguments.ContainsKey("/server") && !arguments.ContainsKey("/pvk") && !arguments.ContainsKey("/password")) | ||
| 133 | - { | ||
| 134 | - Console.WriteLine("[X] The '/server:X' argument must be used with '/pvk:BASE64...' or '/password:X' !"); | ||
| 135 | - } | ||
| 136 | - else | ||
| 137 | + if (arguments.ContainsKey("/server") && (masterkeys.Count == 0)) | ||
| 137 | 138 | { | |
| 138 | - Chrome.TriageChromeLogins(masterkeys, server, displayFormat, showAll, unprotect, stateKey, browser, quiet); | ||
| 139 | + Console.WriteLine("[!] Warning: the '/server:X' argument must be used with '/pvk:BASE64...', '/password:X' , or masterkey specification for successful decryption!"); | ||
| 139 | 140 | } | |
| 141 | + | ||
| 142 | + Chrome.TriageChromeLogins(masterkeys, server, target, displayFormat, showAll, unprotect, stateKey, browser, quiet); | ||
| 140 | 143 | } | |
| 141 | 144 | } | |
| 142 | 145 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -11,7 +11,7 @@ public class Statekeys : ICommand | |||
| 11 | 11 | public void Execute(Dictionary<string, string> arguments) | |
| 12 | 12 | { | |
| 13 | 13 | Console.WriteLine("\r\n[*] Action: Chromium Statekey Extraction\r\n"); | |
| 14 | - arguments.Remove("cookies"); | ||
| 14 | + arguments.Remove("statekeys"); | ||
| 15 | 15 | ||
| 16 | 16 | string server = ""; // used for remote server specification | |
| 17 | 17 | bool unprotect = false; // whether to force CryptUnprotectData() | |
@@ -67,21 +67,22 @@ public void Execute(Dictionary<string, string> arguments) | |||
| 67 | 67 | { | |
| 68 | 68 | Chrome.TriageStateKeys(masterkeys, server, unprotect, target); | |
| 69 | 69 | } | |
| 70 | + else if (Directory.Exists(target) && target.ToLower().Contains("users")) | ||
| 71 | + { | ||
| 72 | + Chrome.TriageStateKeys(masterkeys, server, unprotect, "", target); | ||
| 73 | + } | ||
| 70 | 74 | else | |
| 71 | 75 | { | |
| 72 | - Console.WriteLine("\r\n[X] '{0}' is not a valid file.", target); | ||
| 76 | + Console.WriteLine("\r\n[X] '{0}' is not a valid file or user directory.", target); | ||
| 73 | 77 | } | |
| 74 | 78 | } | |
| 75 | 79 | else | |
| 76 | 80 | { | |
| 77 | - if (arguments.ContainsKey("/server") && !arguments.ContainsKey("/pvk") && !arguments.ContainsKey("/password")) | ||
| 78 | - { | ||
| 79 | - Console.WriteLine("[X] The '/server:X' argument must be used with '/pvk:BASE64...' or '/password:X' !"); | ||
| 80 | - } | ||
| 81 | - else | ||
| 81 | + if (arguments.ContainsKey("/server") && (masterkeys.Count == 0)) | ||
| 82 | 82 | { | |
| 83 | - Chrome.TriageStateKeys(masterkeys, server, unprotect); | ||
| 83 | + Console.WriteLine("[!] Warning: the '/server:X' argument must be used with '/pvk:BASE64...', '/password:X' , or masterkey specification for successful decryption!"); | ||
| 84 | 84 | } | |
| 85 | + Chrome.TriageStateKeys(masterkeys, server, unprotect); | ||
| 85 | 86 | } | |
| 86 | 87 | } | |
| 87 | 88 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,7 +18,7 @@ public static void ShowUsage() | |||
| 18 | 18 | string usage = @" | |
| 19 | 19 | Retrieve a domain controller's DPAPI backup key, optionally specifying a DC and output file: | |
| 20 | 20 | ||
| 21 | - SharpChrome backupkey [/server:SERVER.domain] [/file:key.pvk] | ||
| 21 | + SharpChrome backupkey [/nowrap] [/server:SERVER.domain] [/file:key.pvk] | ||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | 24 | Global arguments for the 'cookies', 'logins', and 'statekeys' commands: | |
@@ -34,6 +34,7 @@ public static void ShowUsage() | |||
| 34 | 34 | ||
| 35 | 35 | Targeting: | |
| 36 | 36 | /target:FILE - triage a specific 'Cookies', 'Login Data', or 'Local State' file location | |
| 37 | + /target:C:\Users\X\ - triage a specific user folder for any specified command | ||
| 37 | 38 | /server:SERVER - triage a remote server, assuming admin access (note: must use with /pvk:KEY) | |
| 38 | 39 | /browser:X - triage 'chrome' (the default) or (chromium-based) 'edge'/'brave' | |
| 39 | 40 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments