2578 lines
136 KiB
C#
2578 lines
136 KiB
C#
using CredentialManagement;
|
|
using Microsoft.Win32;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Web.Script.Serialization;
|
|
using System.Xml;
|
|
|
|
|
|
namespace winPEAS
|
|
{
|
|
class KnownFileCredsInfo
|
|
{
|
|
public static List<string> GetFirefoxDbs()
|
|
{
|
|
List<string> results = new List<string>();
|
|
// checks if Firefox has a history database
|
|
try
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive"));
|
|
string[] dirs = Directory.GetDirectories(userFolder);
|
|
foreach (string dir in dirs)
|
|
{
|
|
string[] parts = dir.Split('\\');
|
|
string userName = parts[parts.Length - 1];
|
|
if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")))
|
|
{
|
|
string userFirefoxBasePath = String.Format("{0}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\", dir);
|
|
if (System.IO.Directory.Exists(userFirefoxBasePath))
|
|
{
|
|
string[] directories = Directory.GetDirectories(userFirefoxBasePath);
|
|
foreach (string directory in directories)
|
|
{
|
|
string firefoxCredentialFile3 = String.Format("{0}\\{1}", directory, "key3.db");
|
|
if (System.IO.File.Exists(firefoxCredentialFile3))
|
|
results.Add(firefoxCredentialFile3);
|
|
|
|
string firefoxCredentialFile4 = String.Format("{0}\\{1}", directory, "key4.db");
|
|
if (System.IO.File.Exists(firefoxCredentialFile4))
|
|
results.Add(firefoxCredentialFile3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string userName = Environment.GetEnvironmentVariable("USERNAME");
|
|
string userFirefoxBasePath = String.Format("{0}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
|
|
if (System.IO.Directory.Exists(userFirefoxBasePath))
|
|
{
|
|
string[] directories = Directory.GetDirectories(userFirefoxBasePath);
|
|
foreach (string directory in directories)
|
|
{
|
|
string firefoxCredentialFile3 = String.Format("{0}\\{1}", directory, "key3.db");
|
|
if (System.IO.File.Exists(firefoxCredentialFile3))
|
|
results.Add(firefoxCredentialFile3);
|
|
|
|
string firefoxCredentialFile4 = String.Format("{0}\\{1}", directory, "key4.db");
|
|
if (System.IO.File.Exists(firefoxCredentialFile4))
|
|
results.Add(firefoxCredentialFile4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
public static List<string> ParseFirefoxHistory(string path, string user)
|
|
{
|
|
List<string> results = new List<string>();
|
|
// parses a Firefox history file via regex
|
|
if (System.IO.Directory.Exists(path))
|
|
{
|
|
string[] directories = Directory.GetDirectories(path);
|
|
foreach (string directory in directories)
|
|
{
|
|
string firefoxHistoryFile = String.Format("{0}\\{1}", directory, "places.sqlite");
|
|
Regex historyRegex = new Regex(@"(http|ftp|https|file)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?");
|
|
|
|
try
|
|
{
|
|
using (StreamReader r = new StreamReader(firefoxHistoryFile))
|
|
{
|
|
string line;
|
|
while ((line = r.ReadLine()) != null)
|
|
{
|
|
Match m = historyRegex.Match(line);
|
|
if (m.Success)
|
|
results.Add(m.Groups[0].ToString().Trim());
|
|
}
|
|
}
|
|
}
|
|
catch (System.IO.IOException exception)
|
|
{
|
|
Console.WriteLine("\r\n [x] IO exception, places.sqlite file likely in use (i.e. Firefox is likely running).", exception.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static List<string> GetFirefoxHistory()
|
|
{
|
|
List<string> results = new List<string>();
|
|
try
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive"));
|
|
string[] dirs = Directory.GetDirectories(userFolder);
|
|
foreach (string dir in dirs)
|
|
{
|
|
string[] parts = dir.Split('\\');
|
|
string userName = parts[parts.Length - 1];
|
|
if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")))
|
|
{
|
|
string userFirefoxBasePath = String.Format("{0}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\", dir);
|
|
results = ParseFirefoxHistory(userFirefoxBasePath, userName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string userName = Environment.GetEnvironmentVariable("USERNAME");
|
|
string userFirefoxBasePath = String.Format("{0}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
results = ParseFirefoxHistory(userFirefoxBasePath, userName);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static Dictionary<string, string> GetChromeDbs()
|
|
{
|
|
Dictionary<string, string> results = new Dictionary<string, string>();
|
|
// checks if Chrome has a history database
|
|
try
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive"));
|
|
string[] dirs = Directory.GetDirectories(userFolder);
|
|
foreach (string dir in dirs)
|
|
{
|
|
string[] parts = dir.Split('\\');
|
|
string userName = parts[parts.Length - 1];
|
|
if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")))
|
|
{
|
|
string userChromeCookiesPath = String.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cookies", dir);
|
|
if (System.IO.File.Exists(userChromeCookiesPath))
|
|
results["userChromeCookiesPath"] = userChromeCookiesPath;
|
|
|
|
string userChromeLoginDataPath = String.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data", dir);
|
|
if (System.IO.File.Exists(userChromeLoginDataPath))
|
|
results["userChromeLoginDataPath"] = userChromeLoginDataPath;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string userChromeCookiesPath = String.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cookies", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
if (System.IO.File.Exists(userChromeCookiesPath))
|
|
results["userChromeCookiesPath"] = userChromeCookiesPath;
|
|
|
|
string userChromeLoginDataPath = String.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
if (System.IO.File.Exists(userChromeLoginDataPath))
|
|
results["userChromeLoginDataPath"] = userChromeLoginDataPath;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
|
|
public static List<string> ParseChromeHistory(string path, string user)
|
|
{
|
|
List<string> results = new List<string>();
|
|
|
|
// parses a Chrome history file via regex
|
|
if (System.IO.File.Exists(path))
|
|
{
|
|
Regex historyRegex = new Regex(@"(http|ftp|https|file)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?");
|
|
|
|
try
|
|
{
|
|
using (StreamReader r = new StreamReader(path))
|
|
{
|
|
string line;
|
|
while ((line = r.ReadLine()) != null)
|
|
{
|
|
Match m = historyRegex.Match(line);
|
|
if (m.Success)
|
|
{
|
|
results.Add(m.Groups[0].ToString().Trim());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (System.IO.IOException exception)
|
|
{
|
|
Console.WriteLine("\r\n [x] IO exception, history file likely in use (i.e. Browser is likely running): ", exception.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
public static List<string> ParseChromeBookmarks(string path, string user)
|
|
{
|
|
List<string> results = new List<string>();
|
|
// parses a Chrome bookmarks
|
|
if (System.IO.File.Exists(path))
|
|
{
|
|
try
|
|
{
|
|
string contents = System.IO.File.ReadAllText(path);
|
|
|
|
// reference: http://www.tomasvera.com/programming/using-javascriptserializer-to-parse-json-objects/
|
|
JavaScriptSerializer json = new JavaScriptSerializer();
|
|
Dictionary<string, object> deserialized = json.Deserialize<Dictionary<string, object>>(contents);
|
|
Dictionary<string, object> roots = (Dictionary<string, object>)deserialized["roots"];
|
|
Dictionary<string, object> bookmark_bar = (Dictionary<string, object>)roots["bookmark_bar"];
|
|
System.Collections.ArrayList children = (System.Collections.ArrayList)bookmark_bar["children"];
|
|
|
|
foreach (Dictionary<string, object> entry in children)
|
|
{
|
|
//Console.WriteLine(" Name: {0}", entry["name"].ToString().Trim());
|
|
if (entry.ContainsKey("url"))
|
|
results.Add(entry["url"].ToString().Trim());
|
|
}
|
|
}
|
|
catch (System.IO.IOException exception)
|
|
{
|
|
Console.WriteLine("\r\n [x] IO exception, Bookmarks file likely in use (i.e. Chrome is likely running).", exception.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
public static Dictionary<string, List<string>> GetChromeHistBook()
|
|
{
|
|
Dictionary<string, List<string>> results = new Dictionary<string, List<string>>()
|
|
{
|
|
{ "history", new List<string>() },
|
|
{ "bookarks", new List<string>() },
|
|
};
|
|
try
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
Console.WriteLine("\r\n\r\n=== Chrome (All Users) ===");
|
|
|
|
string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive"));
|
|
string[] dirs = Directory.GetDirectories(userFolder);
|
|
foreach (string dir in dirs)
|
|
{
|
|
string[] parts = dir.Split('\\');
|
|
string userName = parts[parts.Length - 1];
|
|
if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")))
|
|
{
|
|
string userChromeHistoryPath = String.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\History", dir);
|
|
results["history"] = ParseChromeHistory(userChromeHistoryPath, userName);
|
|
|
|
string userChromeBookmarkPath = String.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Bookmarks", dir);
|
|
results["bookmarks"] = ParseChromeBookmarks(userChromeBookmarkPath, userName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string userChromeHistoryPath = String.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\History", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
results["history"] = ParseChromeHistory(userChromeHistoryPath, System.Environment.GetEnvironmentVariable("USERNAME"));
|
|
|
|
string userChromeBookmarkPath = String.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Bookmarks", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
|
|
results["bookmarks"] = ParseChromeBookmarks(userChromeBookmarkPath, System.Environment.GetEnvironmentVariable("USERNAME"));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
|
|
public static Dictionary<string, List<string>> GetIEHistFav()
|
|
{
|
|
int lastDays = 90;
|
|
Dictionary<string, List<string>> results = new Dictionary<string, List<string>>()
|
|
{
|
|
{ "history", new List<string>() },
|
|
{ "favorites", new List<string>() },
|
|
};
|
|
|
|
DateTime startTime = System.DateTime.Now.AddDays(-lastDays);
|
|
|
|
try
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string[] SIDs = Registry.Users.GetSubKeyNames();
|
|
foreach (string SID in SIDs)
|
|
{
|
|
if (SID.StartsWith("S-1-5") && !SID.EndsWith("_Classes"))
|
|
{
|
|
Dictionary<string, object> settings = MyUtils.GetRegValues("HKU", String.Format("{0}\\SOFTWARE\\Microsoft\\Internet Explorer\\TypedURLs", SID));
|
|
if ((settings != null) && (settings.Count > 1))
|
|
{
|
|
foreach (KeyValuePair<string, object> kvp in settings)
|
|
{
|
|
byte[] timeBytes = MyUtils.GetRegValueBytes("HKU", String.Format("{0}\\SOFTWARE\\Microsoft\\Internet Explorer\\TypedURLsTime", SID), kvp.Key.ToString().Trim());
|
|
if (timeBytes != null)
|
|
{
|
|
long timeLong = (long)(BitConverter.ToInt64(timeBytes, 0));
|
|
DateTime urlTime = DateTime.FromFileTime(timeLong);
|
|
if (urlTime > startTime)
|
|
{
|
|
results["history"].Add(kvp.Value.ToString().Trim());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive"));
|
|
string[] dirs = Directory.GetDirectories(userFolder);
|
|
foreach (string dir in dirs)
|
|
{
|
|
string[] parts = dir.Split('\\');
|
|
string userName = parts[parts.Length - 1];
|
|
if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")))
|
|
{
|
|
string userIEBookmarkPath = String.Format("{0}\\Favorites\\", dir);
|
|
|
|
if (Directory.Exists(userIEBookmarkPath))
|
|
{
|
|
string[] bookmarkPaths = Directory.GetFiles(userIEBookmarkPath, "*.url", SearchOption.AllDirectories);
|
|
if (bookmarkPaths.Length != 0)
|
|
{
|
|
foreach (string bookmarkPath in bookmarkPaths)
|
|
{
|
|
using (StreamReader rdr = new StreamReader(bookmarkPath))
|
|
{
|
|
string line;
|
|
string url = "";
|
|
while ((line = rdr.ReadLine()) != null)
|
|
{
|
|
if (line.StartsWith("URL=", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
if (line.Length > 4)
|
|
url = line.Substring(4);
|
|
break;
|
|
}
|
|
}
|
|
results["history"].Add(url.ToString().Trim());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Dictionary<string, object> settings = MyUtils.GetRegValues("HKCU", "SOFTWARE\\Microsoft\\Internet Explorer\\TypedURLs");
|
|
if ((settings != null) && (settings.Count != 0))
|
|
{
|
|
foreach (KeyValuePair<string, object> kvp in settings)
|
|
{
|
|
byte[] timeBytes = MyUtils.GetRegValueBytes("HKCU", "SOFTWARE\\Microsoft\\Internet Explorer\\TypedURLsTime", kvp.Key.ToString().Trim());
|
|
if (timeBytes != null)
|
|
{
|
|
long timeLong = (long)(BitConverter.ToInt64(timeBytes, 0));
|
|
DateTime urlTime = DateTime.FromFileTime(timeLong);
|
|
if (urlTime > startTime)
|
|
{
|
|
results["history"].Add(kvp.Value.ToString().Trim());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
string userIEBookmarkPath = String.Format("{0}\\Favorites\\", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
|
|
string[] bookmarkPaths = Directory.GetFiles(userIEBookmarkPath, "*.url", SearchOption.AllDirectories);
|
|
|
|
foreach (string bookmarkPath in bookmarkPaths)
|
|
{
|
|
using (StreamReader rdr = new StreamReader(bookmarkPath))
|
|
{
|
|
string line;
|
|
string url = "";
|
|
while ((line = rdr.ReadLine()) != null)
|
|
{
|
|
if (line.StartsWith("URL=", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
if (line.Length > 4)
|
|
url = line.Substring(4);
|
|
break;
|
|
}
|
|
}
|
|
results["favorites"].Add(url.ToString().Trim());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
public static List<string> GetCurrentIETabs()
|
|
{
|
|
List<string> results = new List<string>();
|
|
// Lists currently open Internet Explorer tabs, via COM
|
|
// Notes:
|
|
// https://searchcode.com/codesearch/view/9859954/
|
|
// https://gist.github.com/yizhang82/a1268d3ea7295a8a1496e01d60ada816
|
|
|
|
try
|
|
{
|
|
// Shell.Application COM GUID
|
|
Type shell = Type.GetTypeFromCLSID(new Guid("13709620-C279-11CE-A49E-444553540000"));
|
|
|
|
// actually instantiate the Shell.Application COM object
|
|
Object shellObj = Activator.CreateInstance(shell);
|
|
|
|
// grab all the current windows
|
|
Object windows = shellObj.GetType().InvokeMember("Windows", BindingFlags.InvokeMethod, null, shellObj, null);
|
|
|
|
// grab the open tab count
|
|
Object openTabs = windows.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, windows, null);
|
|
int openTabsCount = Int32.Parse(openTabs.ToString());
|
|
|
|
for (int i = 0; i < openTabsCount; i++)
|
|
{
|
|
// grab the acutal tab
|
|
Object item = windows.GetType().InvokeMember("Item", BindingFlags.InvokeMethod, null, windows, new object[] { i });
|
|
try
|
|
{
|
|
// extract the tab properties
|
|
Object locationName = item.GetType().InvokeMember("LocationName", BindingFlags.GetProperty, null, item, null);
|
|
Object locationURL = item.GetType().InvokeMember("LocationUrl", BindingFlags.GetProperty, null, item, null);
|
|
|
|
// ensure we have a site address
|
|
if (Regex.IsMatch(locationURL.ToString(), @"(^https?://.+)|(^ftp://)"))
|
|
{
|
|
results.Add(String.Format("{0}", locationURL));
|
|
}
|
|
Marshal.ReleaseComObject(item);
|
|
item = null;
|
|
}
|
|
catch
|
|
{
|
|
//
|
|
}
|
|
}
|
|
Marshal.ReleaseComObject(windows);
|
|
windows = null;
|
|
Marshal.ReleaseComObject(shellObj);
|
|
shellObj = null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
public static class VaultCli
|
|
{
|
|
// pulled directly from @djhohnstein's SharpWeb project: https://github.com/djhohnstein/SharpWeb/blob/master/Edge/SharpEdge.cs
|
|
public enum VAULT_ELEMENT_TYPE : Int32
|
|
{
|
|
Undefined = -1,
|
|
Boolean = 0,
|
|
Short = 1,
|
|
UnsignedShort = 2,
|
|
Int = 3,
|
|
UnsignedInt = 4,
|
|
Double = 5,
|
|
Guid = 6,
|
|
String = 7,
|
|
ByteArray = 8,
|
|
TimeStamp = 9,
|
|
ProtectedArray = 10,
|
|
Attribute = 11,
|
|
Sid = 12,
|
|
Last = 13
|
|
}
|
|
|
|
public enum VAULT_SCHEMA_ELEMENT_ID : Int32
|
|
{
|
|
Illegal = 0,
|
|
Resource = 1,
|
|
Identity = 2,
|
|
Authenticator = 3,
|
|
Tag = 4,
|
|
PackageSid = 5,
|
|
AppStart = 100,
|
|
AppEnd = 10000
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
|
public struct VAULT_ITEM_WIN8
|
|
{
|
|
public Guid SchemaId;
|
|
public IntPtr pszCredentialFriendlyName;
|
|
public IntPtr pResourceElement;
|
|
public IntPtr pIdentityElement;
|
|
public IntPtr pAuthenticatorElement;
|
|
public IntPtr pPackageSid;
|
|
public UInt64 LastModified;
|
|
public UInt32 dwFlags;
|
|
public UInt32 dwPropertiesCount;
|
|
public IntPtr pPropertyElements;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
|
public struct VAULT_ITEM_WIN7
|
|
{
|
|
public Guid SchemaId;
|
|
public IntPtr pszCredentialFriendlyName;
|
|
public IntPtr pResourceElement;
|
|
public IntPtr pIdentityElement;
|
|
public IntPtr pAuthenticatorElement;
|
|
public UInt64 LastModified;
|
|
public UInt32 dwFlags;
|
|
public UInt32 dwPropertiesCount;
|
|
public IntPtr pPropertyElements;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
|
|
public struct VAULT_ITEM_ELEMENT
|
|
{
|
|
[FieldOffset(0)]
|
|
public VAULT_SCHEMA_ELEMENT_ID SchemaElementId;
|
|
[FieldOffset(8)]
|
|
public VAULT_ELEMENT_TYPE Type;
|
|
}
|
|
|
|
[DllImport("vaultcli.dll")]
|
|
public extern static Int32 VaultOpenVault(ref Guid vaultGuid, UInt32 offset, ref IntPtr vaultHandle);
|
|
|
|
[DllImport("vaultcli.dll")]
|
|
public extern static Int32 VaultCloseVault(ref IntPtr vaultHandle);
|
|
|
|
[DllImport("vaultcli.dll")]
|
|
public extern static Int32 VaultFree(ref IntPtr vaultHandle);
|
|
|
|
[DllImport("vaultcli.dll")]
|
|
public extern static Int32 VaultEnumerateVaults(Int32 offset, ref Int32 vaultCount, ref IntPtr vaultGuid);
|
|
|
|
[DllImport("vaultcli.dll")]
|
|
public extern static Int32 VaultEnumerateItems(IntPtr vaultHandle, Int32 chunkSize, ref Int32 vaultItemCount, ref IntPtr vaultItem);
|
|
|
|
[DllImport("vaultcli.dll", EntryPoint = "VaultGetItem")]
|
|
public extern static Int32 VaultGetItem_WIN8(IntPtr vaultHandle, ref Guid schemaId, IntPtr pResourceElement, IntPtr pIdentityElement, IntPtr pPackageSid, IntPtr zero, Int32 arg6, ref IntPtr passwordVaultPtr);
|
|
|
|
[DllImport("vaultcli.dll", EntryPoint = "VaultGetItem")]
|
|
public extern static Int32 VaultGetItem_WIN7(IntPtr vaultHandle, ref Guid schemaId, IntPtr pResourceElement, IntPtr pIdentityElement, IntPtr zero, Int32 arg5, ref IntPtr passwordVaultPtr);
|
|
|
|
}
|
|
|
|
|
|
public static object GetVaultElementValue(IntPtr vaultElementPtr)
|
|
{
|
|
// Helper function to extract the ItemValue field from a VAULT_ITEM_ELEMENT struct
|
|
// pulled directly from @djhohnstein's SharpWeb project: https://github.com/djhohnstein/SharpWeb/blob/master/Edge/SharpEdge.cs
|
|
object results;
|
|
object partialElement = System.Runtime.InteropServices.Marshal.PtrToStructure(vaultElementPtr, typeof(VaultCli.VAULT_ITEM_ELEMENT));
|
|
FieldInfo partialElementInfo = partialElement.GetType().GetField("Type");
|
|
var partialElementType = partialElementInfo.GetValue(partialElement);
|
|
|
|
IntPtr elementPtr = (IntPtr)(vaultElementPtr.ToInt64() + 16);
|
|
switch ((int)partialElementType)
|
|
{
|
|
case 7: // VAULT_ELEMENT_TYPE == String; These are the plaintext passwords!
|
|
IntPtr StringPtr = System.Runtime.InteropServices.Marshal.ReadIntPtr(elementPtr);
|
|
results = System.Runtime.InteropServices.Marshal.PtrToStringUni(StringPtr);
|
|
break;
|
|
case 0: // VAULT_ELEMENT_TYPE == bool
|
|
results = System.Runtime.InteropServices.Marshal.ReadByte(elementPtr);
|
|
results = (bool)results;
|
|
break;
|
|
case 1: // VAULT_ELEMENT_TYPE == Short
|
|
results = System.Runtime.InteropServices.Marshal.ReadInt16(elementPtr);
|
|
break;
|
|
case 2: // VAULT_ELEMENT_TYPE == Unsigned Short
|
|
results = System.Runtime.InteropServices.Marshal.ReadInt16(elementPtr);
|
|
break;
|
|
case 3: // VAULT_ELEMENT_TYPE == Int
|
|
results = System.Runtime.InteropServices.Marshal.ReadInt32(elementPtr);
|
|
break;
|
|
case 4: // VAULT_ELEMENT_TYPE == Unsigned Int
|
|
results = System.Runtime.InteropServices.Marshal.ReadInt32(elementPtr);
|
|
break;
|
|
case 5: // VAULT_ELEMENT_TYPE == Double
|
|
results = System.Runtime.InteropServices.Marshal.PtrToStructure(elementPtr, typeof(Double));
|
|
break;
|
|
case 6: // VAULT_ELEMENT_TYPE == GUID
|
|
results = System.Runtime.InteropServices.Marshal.PtrToStructure(elementPtr, typeof(Guid));
|
|
break;
|
|
case 12: // VAULT_ELEMENT_TYPE == Sid
|
|
IntPtr sidPtr = System.Runtime.InteropServices.Marshal.ReadIntPtr(elementPtr);
|
|
var sidObject = new System.Security.Principal.SecurityIdentifier(sidPtr);
|
|
results = sidObject.Value;
|
|
break;
|
|
default:
|
|
/* Several VAULT_ELEMENT_TYPES are currently unimplemented according to
|
|
* Lord Graeber. Thus we do not implement them. */
|
|
results = null;
|
|
break;
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static List<Dictionary<string, string>> DumpVault()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// pulled directly from @djhohnstein's SharpWeb project: https://github.com/djhohnstein/SharpWeb/blob/master/Edge/SharpEdge.cs
|
|
var OSVersion = Environment.OSVersion.Version;
|
|
var OSMajor = OSVersion.Major;
|
|
var OSMinor = OSVersion.Minor;
|
|
|
|
Type VAULT_ITEM;
|
|
|
|
if (OSMajor >= 6 && OSMinor >= 2)
|
|
{
|
|
VAULT_ITEM = typeof(VaultCli.VAULT_ITEM_WIN8);
|
|
}
|
|
else
|
|
{
|
|
VAULT_ITEM = typeof(VaultCli.VAULT_ITEM_WIN7);
|
|
}
|
|
|
|
Int32 vaultCount = 0;
|
|
IntPtr vaultGuidPtr = IntPtr.Zero;
|
|
var result = VaultCli.VaultEnumerateVaults(0, ref vaultCount, ref vaultGuidPtr);
|
|
|
|
//var result = CallVaultEnumerateVaults(VaultEnum, 0, ref vaultCount, ref vaultGuidPtr);
|
|
|
|
if ((int)result != 0)
|
|
{
|
|
Console.WriteLine(" [ERROR] Unable to enumerate vaults. Error (0x" + result.ToString() + ")");
|
|
return results;
|
|
}
|
|
|
|
// Create dictionary to translate Guids to human readable elements
|
|
IntPtr guidAddress = vaultGuidPtr;
|
|
Dictionary<Guid, string> vaultSchema = new Dictionary<Guid, string>();
|
|
vaultSchema.Add(new Guid("2F1A6504-0641-44CF-8BB5-3612D865F2E5"), "Windows Secure Note");
|
|
vaultSchema.Add(new Guid("3CCD5499-87A8-4B10-A215-608888DD3B55"), "Windows Web Password Credential");
|
|
vaultSchema.Add(new Guid("154E23D0-C644-4E6F-8CE6-5069272F999F"), "Windows Credential Picker Protector");
|
|
vaultSchema.Add(new Guid("4BF4C442-9B8A-41A0-B380-DD4A704DDB28"), "Web Credentials");
|
|
vaultSchema.Add(new Guid("77BC582B-F0A6-4E15-4E80-61736B6F3B29"), "Windows Credentials");
|
|
vaultSchema.Add(new Guid("E69D7838-91B5-4FC9-89D5-230D4D4CC2BC"), "Windows Domain Certificate Credential");
|
|
vaultSchema.Add(new Guid("3E0E35BE-1B77-43E7-B873-AED901B6275B"), "Windows Domain Password Credential");
|
|
vaultSchema.Add(new Guid("3C886FF3-2669-4AA2-A8FB-3F6759A77548"), "Windows Extended Credential");
|
|
vaultSchema.Add(new Guid("00000000-0000-0000-0000-000000000000"), null);
|
|
|
|
for (int i = 0; i < vaultCount; i++)
|
|
{
|
|
|
|
// Open vault block
|
|
object vaultGuidString = System.Runtime.InteropServices.Marshal.PtrToStructure(guidAddress, typeof(Guid));
|
|
Guid vaultGuid = new Guid(vaultGuidString.ToString());
|
|
guidAddress = (IntPtr)(guidAddress.ToInt64() + System.Runtime.InteropServices.Marshal.SizeOf(typeof(Guid)));
|
|
IntPtr vaultHandle = IntPtr.Zero;
|
|
string vaultType;
|
|
if (vaultSchema.ContainsKey(vaultGuid))
|
|
{
|
|
vaultType = vaultSchema[vaultGuid];
|
|
}
|
|
else
|
|
{
|
|
vaultType = vaultGuid.ToString();
|
|
}
|
|
result = VaultCli.VaultOpenVault(ref vaultGuid, (UInt32)0, ref vaultHandle);
|
|
if (result != 0)
|
|
{
|
|
Console.WriteLine("Unable to open the following vault: " + vaultType + ". Error: 0x" + result.ToString());
|
|
continue;
|
|
}
|
|
// Vault opened successfully! Continue.
|
|
|
|
// Fetch all items within Vault
|
|
int vaultItemCount = 0;
|
|
IntPtr vaultItemPtr = IntPtr.Zero;
|
|
result = VaultCli.VaultEnumerateItems(vaultHandle, 512, ref vaultItemCount, ref vaultItemPtr);
|
|
if (result != 0)
|
|
{
|
|
Console.WriteLine("Unable to enumerate vault items from the following vault: " + vaultType + ". Error 0x" + result.ToString());
|
|
continue;
|
|
}
|
|
var structAddress = vaultItemPtr;
|
|
if (vaultItemCount > 0)
|
|
{
|
|
// For each vault item...
|
|
for (int j = 1; j <= vaultItemCount; j++)
|
|
{
|
|
Dictionary<string, string> vault_cred = new Dictionary<string, string>() {
|
|
{ "GUID", String.Format("{0}", vaultGuid) },
|
|
{ "Type", vaultType },
|
|
{ "Resource", "" },
|
|
{ "Identity", "" },
|
|
{ "PacakgeSid", "" },
|
|
{ "Credential", "" },
|
|
{ "Last Modified", "" },
|
|
{ "Error", "" }
|
|
};
|
|
|
|
// Begin fetching vault item...
|
|
var currentItem = System.Runtime.InteropServices.Marshal.PtrToStructure(structAddress, VAULT_ITEM);
|
|
structAddress = (IntPtr)(structAddress.ToInt64() + System.Runtime.InteropServices.Marshal.SizeOf(VAULT_ITEM));
|
|
|
|
IntPtr passwordVaultItem = IntPtr.Zero;
|
|
// Field Info retrieval
|
|
FieldInfo schemaIdInfo = currentItem.GetType().GetField("SchemaId");
|
|
Guid schemaId = new Guid(schemaIdInfo.GetValue(currentItem).ToString());
|
|
FieldInfo pResourceElementInfo = currentItem.GetType().GetField("pResourceElement");
|
|
IntPtr pResourceElement = (IntPtr)pResourceElementInfo.GetValue(currentItem);
|
|
FieldInfo pIdentityElementInfo = currentItem.GetType().GetField("pIdentityElement");
|
|
IntPtr pIdentityElement = (IntPtr)pIdentityElementInfo.GetValue(currentItem);
|
|
FieldInfo dateTimeInfo = currentItem.GetType().GetField("LastModified");
|
|
UInt64 lastModified = (UInt64)dateTimeInfo.GetValue(currentItem);
|
|
|
|
IntPtr pPackageSid = IntPtr.Zero;
|
|
if (OSMajor >= 6 && OSMinor >= 2)
|
|
{
|
|
// Newer versions have package sid
|
|
FieldInfo pPackageSidInfo = currentItem.GetType().GetField("pPackageSid");
|
|
pPackageSid = (IntPtr)pPackageSidInfo.GetValue(currentItem);
|
|
result = VaultCli.VaultGetItem_WIN8(vaultHandle, ref schemaId, pResourceElement, pIdentityElement, pPackageSid, IntPtr.Zero, 0, ref passwordVaultItem);
|
|
}
|
|
else
|
|
{
|
|
result = VaultCli.VaultGetItem_WIN7(vaultHandle, ref schemaId, pResourceElement, pIdentityElement, IntPtr.Zero, 0, ref passwordVaultItem);
|
|
}
|
|
|
|
if (result != 0)
|
|
{
|
|
vault_cred["Error"] = "Occured while retrieving vault item. Error: 0x" + result.ToString();
|
|
continue;
|
|
}
|
|
object passwordItem = System.Runtime.InteropServices.Marshal.PtrToStructure(passwordVaultItem, VAULT_ITEM);
|
|
FieldInfo pAuthenticatorElementInfo = passwordItem.GetType().GetField("pAuthenticatorElement");
|
|
IntPtr pAuthenticatorElement = (IntPtr)pAuthenticatorElementInfo.GetValue(passwordItem);
|
|
// Fetch the credential from the authenticator element
|
|
object cred = GetVaultElementValue(pAuthenticatorElement);
|
|
object packageSid = null;
|
|
if (pPackageSid != IntPtr.Zero && pPackageSid != null)
|
|
{
|
|
packageSid = GetVaultElementValue(pPackageSid);
|
|
}
|
|
if (cred != null) // Indicates successful fetch
|
|
{
|
|
object resource = GetVaultElementValue(pResourceElement);
|
|
if (resource != null)
|
|
{
|
|
vault_cred["Resource"] = String.Format("{0}", resource);
|
|
}
|
|
object identity = GetVaultElementValue(pIdentityElement);
|
|
if (identity != null)
|
|
{
|
|
vault_cred["Identity"] = String.Format("{0}", identity);
|
|
}
|
|
if (packageSid != null)
|
|
{
|
|
vault_cred["PacakgeSid"] = String.Format("{0}", packageSid);
|
|
}
|
|
vault_cred["Credential"] = String.Format("{0}", cred);
|
|
vault_cred["Last Modified"] = String.Format("{0}", System.DateTime.FromFileTimeUtc((long)lastModified));
|
|
results.Add(vault_cred);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static void GetCredsCredmanager()
|
|
{
|
|
var cm = new Credential { };
|
|
cm.Load();
|
|
}
|
|
|
|
public static List<Dictionary<string, string>> GetSavedRDPConnections()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
//shows saved RDP connections, including username hints (if present)
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string[] SIDs = Registry.Users.GetSubKeyNames();
|
|
foreach (string SID in SIDs)
|
|
{
|
|
if (SID.StartsWith("S-1-5") && !SID.EndsWith("_Classes"))
|
|
{
|
|
string[] subkeys = MyUtils.GetRegSubkeys("HKU", String.Format("{0}\\Software\\Microsoft\\Terminal Server Client\\Servers", SID));
|
|
if (subkeys != null)
|
|
{
|
|
Console.WriteLine("\r\n\r\n=== Saved RDP Connection Information ({0}) ===", SID);
|
|
foreach (string host in subkeys)
|
|
{
|
|
string usernameHint = MyUtils.GetRegValue("HKCU", String.Format("Software\\Microsoft\\Terminal Server Client\\Servers\\{0}", host), "UsernameHint");
|
|
Dictionary<string, string> rdp_info = new Dictionary<string, string>() {
|
|
{ "SID", SID },
|
|
{ "Host", host },
|
|
{ "Username Hint", usernameHint },
|
|
};
|
|
results.Add(rdp_info);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string[] subkeys = MyUtils.GetRegSubkeys("HKCU", "Software\\Microsoft\\Terminal Server Client\\Servers");
|
|
if (subkeys != null)
|
|
{
|
|
foreach (string host in subkeys)
|
|
{
|
|
string usernameHint = MyUtils.GetRegValue("HKCU", String.Format("Software\\Microsoft\\Terminal Server Client\\Servers\\{0}", host), "UsernameHint");
|
|
Dictionary<string, string> rdp_info = new Dictionary<string, string>() {
|
|
{ "SID", "" },
|
|
{ "Host", host },
|
|
{ "Username Hint", usernameHint },
|
|
};
|
|
results.Add(rdp_info);
|
|
}
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static Dictionary<string, object> GetRecentRunCommands()
|
|
{
|
|
Dictionary<string, object> results = new Dictionary<string, object>();
|
|
// lists recently run commands via the RunMRU registry key
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string[] SIDs = Registry.Users.GetSubKeyNames();
|
|
foreach (string SID in SIDs)
|
|
{
|
|
if (SID.StartsWith("S-1-5") && !SID.EndsWith("_Classes"))
|
|
results = MyUtils.GetRegValues("HKU", String.Format("{0}\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU", SID));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
results = MyUtils.GetRegValues("HKCU", "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU");
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static List<Dictionary<string, string>> GetPuttySessions()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// extracts saved putty sessions and basic configs (via the registry)
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
Console.WriteLine("\r\n\r\n=== Putty Saved Session Information (All Users) ===\r\n");
|
|
|
|
string[] SIDs = Registry.Users.GetSubKeyNames();
|
|
foreach (string SID in SIDs)
|
|
{
|
|
if (SID.StartsWith("S-1-5") && !SID.EndsWith("_Classes"))
|
|
{
|
|
string[] subKeys = MyUtils.GetRegSubkeys("HKU", String.Format("{0}\\Software\\SimonTatham\\PuTTY\\Sessions\\", SID));
|
|
|
|
foreach (string sessionName in subKeys)
|
|
{
|
|
Dictionary<string, string> putty_sess = new Dictionary<string, string>()
|
|
{
|
|
{ "User SID", SID },
|
|
{ "SessionName", sessionName },
|
|
{ "HostName", "" },
|
|
{ "PortNumber", ""},
|
|
{ "UserName", "" },
|
|
{ "PublicKeyFile", "" },
|
|
{ "PortForwardings", "" },
|
|
{ "ConnectionSharing", "" },
|
|
{ "ProxyPassword", "" },
|
|
{ "ProxyUsername", "" },
|
|
};
|
|
|
|
string[] keys =
|
|
{
|
|
"HostName",
|
|
"PortNumber",
|
|
"UserName",
|
|
"PublicKeyFile",
|
|
"PortForwardings",
|
|
"ConnectionSharing",
|
|
"ProxyPassword",
|
|
"ProxyUsername",
|
|
};
|
|
|
|
foreach (string key in keys)
|
|
putty_sess[key] = MyUtils.GetRegValue("HKU", String.Format("{0}\\Software\\SimonTatham\\PuTTY\\Sessions\\{1}", SID, sessionName), key);
|
|
|
|
results.Add(putty_sess);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string[] subKeys = MyUtils.GetRegSubkeys("HKCU", "Software\\SimonTatham\\PuTTY\\Sessions\\");
|
|
foreach (string sessionName in subKeys)
|
|
{
|
|
Dictionary<string, string> putty_sess = new Dictionary<string, string>()
|
|
{
|
|
{ "SessionName", sessionName },
|
|
{ "HostName", "" },
|
|
{ "PortNumber", "" },
|
|
{ "UserName", "" },
|
|
{ "PublicKeyFile", "" },
|
|
{ "PortForwardings", "" },
|
|
{ "ConnectionSharing", "" },
|
|
{ "ProxyPassword", "" },
|
|
{ "ProxyUsername", "" },
|
|
};
|
|
|
|
string[] keys =
|
|
{
|
|
"HostName",
|
|
"PortNumber",
|
|
"UserName",
|
|
"PublicKeyFile",
|
|
"PortForwardings",
|
|
"ConnectionSharing",
|
|
"ProxyPassword",
|
|
"ProxyUsername",
|
|
};
|
|
|
|
foreach (string key in keys)
|
|
putty_sess[key] = MyUtils.GetRegValue("HKCU", String.Format("Software\\SimonTatham\\PuTTY\\Sessions\\{0}", sessionName), key);
|
|
|
|
results.Add(putty_sess);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
public static List<Dictionary<string, string>> ListPuttySSHHostKeys()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// extracts saved putty host keys (via the registry)
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
Console.WriteLine("\r\n\r\n=== Putty SSH Host Hosts (All Users) ===\r\n");
|
|
|
|
string[] SIDs = Registry.Users.GetSubKeyNames();
|
|
foreach (string SID in SIDs)
|
|
{
|
|
if (SID.StartsWith("S-1-5") && !SID.EndsWith("_Classes"))
|
|
{
|
|
Dictionary<string, object> hostKeys = MyUtils.GetRegValues("HKU", String.Format("{0}\\Software\\SimonTatham\\PuTTY\\SshHostKeys\\", SID));
|
|
if ((hostKeys != null) && (hostKeys.Count != 0))
|
|
{
|
|
Dictionary<string, string> putty_ssh = new Dictionary<string, string>();
|
|
putty_ssh["UserSID"] = SID;
|
|
foreach (KeyValuePair<string, object> kvp in hostKeys)
|
|
{
|
|
putty_ssh[kvp.Key] = ""; //Looks like only matters the key name, not the value
|
|
}
|
|
results.Add(putty_ssh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Dictionary<string, object> hostKeys = MyUtils.GetRegValues("HKCU", "Software\\SimonTatham\\PuTTY\\SshHostKeys\\");
|
|
if ((hostKeys != null) && (hostKeys.Count != 0))
|
|
{
|
|
Dictionary<string, string> putty_ssh = new Dictionary<string, string>();
|
|
foreach (KeyValuePair<string, object> kvp in hostKeys)
|
|
{
|
|
putty_ssh[kvp.Key] = ""; //Looks like only matters the key name, not the value
|
|
}
|
|
results.Add(putty_ssh);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static List<Dictionary<string, string>> ListCloudCreds()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// checks for various cloud credential files (AWS, Microsoft Azure, and Google Compute)
|
|
// adapted from https://twitter.com/cmaddalena's SharpCloud project (https://github.com/chrismaddalena/SharpCloud/)
|
|
try
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive"));
|
|
string[] dirs = Directory.GetDirectories(userFolder);
|
|
foreach (string dir in dirs)
|
|
{
|
|
string[] parts = dir.Split('\\');
|
|
string userName = parts[parts.Length - 1];
|
|
if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")))
|
|
{
|
|
string awsKeyFile = String.Format("{0}\\.aws\\credentials", dir);
|
|
if (System.IO.File.Exists(awsKeyFile))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(awsKeyFile);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(awsKeyFile);
|
|
long size = new System.IO.FileInfo(awsKeyFile).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", awsKeyFile },
|
|
{ "Description", "AWS credentials file" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
string computeCredsDb = String.Format("{0}\\AppData\\Roaming\\gcloud\\credentials.db", dir);
|
|
if (System.IO.File.Exists(computeCredsDb))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(computeCredsDb);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(computeCredsDb);
|
|
long size = new System.IO.FileInfo(computeCredsDb).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", computeCredsDb },
|
|
{ "Description", "GC Compute creds" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
string computeLegacyCreds = String.Format("{0}\\AppData\\Roaming\\gcloud\\legacy_credentials", dir);
|
|
if (System.IO.File.Exists(computeLegacyCreds))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(computeLegacyCreds);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(computeLegacyCreds);
|
|
long size = new System.IO.FileInfo(computeLegacyCreds).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", computeLegacyCreds },
|
|
{ "Description", "GC Compute creds legacy" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
string computeAccessTokensDb = String.Format("{0}\\AppData\\Roaming\\gcloud\\access_tokens.db", dir);
|
|
if (System.IO.File.Exists(computeAccessTokensDb))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(computeAccessTokensDb);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(computeAccessTokensDb);
|
|
long size = new System.IO.FileInfo(computeAccessTokensDb).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", computeAccessTokensDb },
|
|
{ "Description", "GC Compute tokens" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
string azureTokens = String.Format("{0}\\.azure\\accessTokens.json", dir);
|
|
if (System.IO.File.Exists(azureTokens))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(azureTokens);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(azureTokens);
|
|
long size = new System.IO.FileInfo(azureTokens).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", azureTokens },
|
|
{ "Description", "Azure tokens" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
string azureProfile = String.Format("{0}\\.azure\\azureProfile.json", dir);
|
|
if (System.IO.File.Exists(azureProfile))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(azureProfile);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(azureProfile);
|
|
long size = new System.IO.FileInfo(azureProfile).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", azureProfile },
|
|
{ "Description", "Azure profile" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string awsKeyFile = String.Format("{0}\\.aws\\credentials", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
if (System.IO.File.Exists(awsKeyFile))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(awsKeyFile);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(awsKeyFile);
|
|
long size = new System.IO.FileInfo(awsKeyFile).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", awsKeyFile },
|
|
{ "Description", "AWS keys file" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
string computeCredsDb = String.Format("{0}\\AppData\\Roaming\\gcloud\\credentials.db", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
if (System.IO.File.Exists(computeCredsDb))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(computeCredsDb);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(computeCredsDb);
|
|
long size = new System.IO.FileInfo(computeCredsDb).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", computeCredsDb },
|
|
{ "Description", "GC Compute creds" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
string computeLegacyCreds = String.Format("{0}\\AppData\\Roaming\\gcloud\\legacy_credentials", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
if (System.IO.File.Exists(computeLegacyCreds))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(computeLegacyCreds);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(computeLegacyCreds);
|
|
long size = new System.IO.FileInfo(computeLegacyCreds).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", computeLegacyCreds },
|
|
{ "Description", "GC Compute creds legacy" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
string computeAccessTokensDb = String.Format("{0}\\AppData\\Roaming\\gcloud\\access_tokens.db", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
if (System.IO.File.Exists(computeAccessTokensDb))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(computeAccessTokensDb);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(computeAccessTokensDb);
|
|
long size = new System.IO.FileInfo(computeAccessTokensDb).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", computeAccessTokensDb },
|
|
{ "Description", "GC Compute tokens" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
string azureTokens = String.Format("{0}\\.azure\\accessTokens.json", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
if (System.IO.File.Exists(azureTokens))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(azureTokens);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(azureTokens);
|
|
long size = new System.IO.FileInfo(azureTokens).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", azureTokens },
|
|
{ "Description", "Azure tokens" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
string azureProfile = String.Format("{0}\\.azure\\azureProfile.json", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
if (System.IO.File.Exists(azureProfile))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(azureProfile);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(azureProfile);
|
|
long size = new System.IO.FileInfo(azureProfile).Length;
|
|
results.Add(new Dictionary<string, string>() {
|
|
{ "file", azureProfile },
|
|
{ "Description", "Azure profile" },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) }
|
|
});
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
public static List<Dictionary<string, string>> GetRecentFiles()
|
|
{
|
|
// parses recent file shortcuts via COM
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
int lastDays = 7;
|
|
DateTime startTime = System.DateTime.Now.AddDays(-lastDays);
|
|
|
|
try
|
|
{
|
|
// WshShell COM object GUID
|
|
Type shell = Type.GetTypeFromCLSID(new Guid("F935DC22-1CF0-11d0-ADB9-00C04FD58A0B"));
|
|
Object shellObj = Activator.CreateInstance(shell);
|
|
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive"));
|
|
string[] dirs = Directory.GetDirectories(userFolder);
|
|
foreach (string dir in dirs)
|
|
{
|
|
string[] parts = dir.Split('\\');
|
|
string userName = parts[parts.Length - 1];
|
|
|
|
if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")))
|
|
{
|
|
string recentPath = String.Format("{0}\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\", dir);
|
|
try
|
|
{
|
|
string[] recentFiles = Directory.GetFiles(recentPath, "*.lnk", SearchOption.AllDirectories);
|
|
|
|
if (recentFiles.Length != 0)
|
|
{
|
|
Console.WriteLine(" {0} :\r\n", userName);
|
|
foreach (string recentFile in recentFiles)
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(recentFile);
|
|
|
|
if (lastAccessed > startTime)
|
|
{
|
|
// invoke the WshShell com object, creating a shortcut to then extract the TargetPath from
|
|
Object shortcut = shellObj.GetType().InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, shellObj, new object[] { recentFile });
|
|
Object TargetPath = shortcut.GetType().InvokeMember("TargetPath", BindingFlags.GetProperty, null, shortcut, new object[] { });
|
|
|
|
if (TargetPath.ToString().Trim() != "")
|
|
{
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "Target", TargetPath.ToString() },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) }
|
|
});
|
|
}
|
|
Marshal.ReleaseComObject(shortcut);
|
|
shortcut = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string recentPath = String.Format("{0}\\Microsoft\\Windows\\Recent\\", System.Environment.GetEnvironmentVariable("APPDATA"));
|
|
|
|
string[] recentFiles = Directory.GetFiles(recentPath, "*.lnk", SearchOption.AllDirectories);
|
|
|
|
foreach (string recentFile in recentFiles)
|
|
{
|
|
// old method (needed interop dll)
|
|
//WshShell shell = new WshShell();
|
|
//IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(recentFile);
|
|
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(recentFile);
|
|
|
|
if (lastAccessed > startTime)
|
|
{
|
|
// invoke the WshShell com object, creating a shortcut to then extract the TargetPath from
|
|
Object shortcut = shellObj.GetType().InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, shellObj, new object[] { recentFile });
|
|
Object TargetPath = shortcut.GetType().InvokeMember("TargetPath", BindingFlags.GetProperty, null, shortcut, new object[] { });
|
|
if (TargetPath.ToString().Trim() != "")
|
|
{
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "Target", TargetPath.ToString() },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) }
|
|
});
|
|
}
|
|
Marshal.ReleaseComObject(shortcut);
|
|
shortcut = null;
|
|
}
|
|
}
|
|
}
|
|
// release the WshShell COM object
|
|
Marshal.ReleaseComObject(shellObj);
|
|
shellObj = null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static List<Dictionary<string, string>> ListMasterKeys()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// lists any found DPAPI master keys
|
|
try
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive"));
|
|
string[] dirs = Directory.GetDirectories(userFolder);
|
|
foreach (string dir in dirs)
|
|
{
|
|
string[] parts = dir.Split('\\');
|
|
string userName = parts[parts.Length - 1];
|
|
if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")))
|
|
{
|
|
string userDPAPIBasePath = String.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", dir);
|
|
if (System.IO.Directory.Exists(userDPAPIBasePath))
|
|
{
|
|
string[] directories = Directory.GetDirectories(userDPAPIBasePath);
|
|
foreach (string directory in directories)
|
|
{
|
|
string[] files = Directory.GetFiles(directory);
|
|
|
|
foreach (string file in files)
|
|
{
|
|
if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}"))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(file);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(file);
|
|
string fileName = System.IO.Path.GetFileName(file);
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "MasterKey", file },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string userName = Environment.GetEnvironmentVariable("USERNAME");
|
|
string userDPAPIBasePath = String.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
|
|
if (System.IO.Directory.Exists(userDPAPIBasePath))
|
|
{
|
|
string[] directories = Directory.GetDirectories(userDPAPIBasePath);
|
|
foreach (string directory in directories)
|
|
{
|
|
string[] files = Directory.GetFiles(directory);
|
|
|
|
foreach (string file in files)
|
|
{
|
|
if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}"))
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(file);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(file);
|
|
string fileName = System.IO.Path.GetFileName(file);
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "MasterKey", file },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
public static List<Dictionary<string, string>> GetCredFiles()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// lists any found files in Local\Microsoft\Credentials\*
|
|
try
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive"));
|
|
string[] dirs = Directory.GetDirectories(userFolder);
|
|
|
|
foreach (string dir in dirs)
|
|
{
|
|
string[] parts = dir.Split('\\');
|
|
string userName = parts[parts.Length - 1];
|
|
if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")))
|
|
{
|
|
string userCredFilePath = String.Format("{0}\\AppData\\Local\\Microsoft\\Credentials\\", dir);
|
|
if (System.IO.Directory.Exists(userCredFilePath))
|
|
{
|
|
string[] systemFiles = Directory.GetFiles(userCredFilePath);
|
|
if ((systemFiles != null) && (systemFiles.Length != 0))
|
|
{
|
|
foreach (string file in systemFiles)
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(file);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(file);
|
|
long size = new System.IO.FileInfo(file).Length;
|
|
string fileName = System.IO.Path.GetFileName(file);
|
|
|
|
// jankily parse the bytes to extract the credential type and master key GUID
|
|
// reference- https://github.com/gentilkiwi/mimikatz/blob/3d8be22fff9f7222f9590aa007629e18300cf643/modules/kull_m_dpapi.h#L24-L54
|
|
byte[] credentialArray = File.ReadAllBytes(file);
|
|
byte[] guidMasterKeyArray = new byte[16];
|
|
Array.Copy(credentialArray, 36, guidMasterKeyArray, 0, 16);
|
|
Guid guidMasterKey = new Guid(guidMasterKeyArray);
|
|
|
|
byte[] stringLenArray = new byte[16];
|
|
Array.Copy(credentialArray, 56, stringLenArray, 0, 4);
|
|
int descLen = BitConverter.ToInt32(stringLenArray, 0);
|
|
|
|
byte[] descBytes = new byte[descLen];
|
|
Array.Copy(credentialArray, 60, descBytes, 0, descLen - 4);
|
|
|
|
string desc = Encoding.Unicode.GetString(descBytes);
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "CredFile", file },
|
|
{ "Description", desc },
|
|
{ "MasterKey", String.Format("{0}", guidMasterKey) },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
string systemFolder = String.Format("{0}\\System32\\config\\systemprofile\\AppData\\Local\\Microsoft\\Credentials", Environment.GetEnvironmentVariable("SystemRoot"));
|
|
string[] files = Directory.GetFiles(systemFolder);
|
|
if ((files != null) && (files.Length != 0))
|
|
{
|
|
foreach (string file in files)
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(file);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(file);
|
|
long size = new System.IO.FileInfo(file).Length;
|
|
string fileName = System.IO.Path.GetFileName(file);
|
|
|
|
// jankily parse the bytes to extract the credential type and master key GUID
|
|
// reference- https://github.com/gentilkiwi/mimikatz/blob/3d8be22fff9f7222f9590aa007629e18300cf643/modules/kull_m_dpapi.h#L24-L54
|
|
byte[] credentialArray = File.ReadAllBytes(file);
|
|
byte[] guidMasterKeyArray = new byte[16];
|
|
Array.Copy(credentialArray, 36, guidMasterKeyArray, 0, 16);
|
|
Guid guidMasterKey = new Guid(guidMasterKeyArray);
|
|
|
|
byte[] stringLenArray = new byte[16];
|
|
Array.Copy(credentialArray, 56, stringLenArray, 0, 4);
|
|
int descLen = BitConverter.ToInt32(stringLenArray, 0);
|
|
|
|
byte[] descBytes = new byte[descLen];
|
|
Array.Copy(credentialArray, 60, descBytes, 0, descLen - 4);
|
|
|
|
string desc = Encoding.Unicode.GetString(descBytes);
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "CredFile", file },
|
|
{ "Description", desc },
|
|
{ "MasterKey", String.Format("{0}", guidMasterKey) },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string userName = Environment.GetEnvironmentVariable("USERNAME");
|
|
string userCredFilePath = String.Format("{0}\\AppData\\Local\\Microsoft\\Credentials\\", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
|
|
if (System.IO.Directory.Exists(userCredFilePath))
|
|
{
|
|
string[] files = Directory.GetFiles(userCredFilePath);
|
|
|
|
foreach (string file in files)
|
|
{
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(file);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(file);
|
|
long size = new System.IO.FileInfo(file).Length;
|
|
string fileName = System.IO.Path.GetFileName(file);
|
|
|
|
// jankily parse the bytes to extract the credential type and master key GUID
|
|
// reference- https://github.com/gentilkiwi/mimikatz/blob/3d8be22fff9f7222f9590aa007629e18300cf643/modules/kull_m_dpapi.h#L24-L54
|
|
byte[] credentialArray = File.ReadAllBytes(file);
|
|
byte[] guidMasterKeyArray = new byte[16];
|
|
Array.Copy(credentialArray, 36, guidMasterKeyArray, 0, 16);
|
|
Guid guidMasterKey = new Guid(guidMasterKeyArray);
|
|
|
|
byte[] stringLenArray = new byte[16];
|
|
Array.Copy(credentialArray, 56, stringLenArray, 0, 4);
|
|
int descLen = BitConverter.ToInt32(stringLenArray, 0);
|
|
|
|
byte[] descBytes = new byte[descLen];
|
|
Array.Copy(credentialArray, 60, descBytes, 0, descLen - 4);
|
|
|
|
string desc = Encoding.Unicode.GetString(descBytes);
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "CredFile", file },
|
|
{ "Description", desc },
|
|
{ "MasterKey", String.Format("{0}", guidMasterKey) },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ "Size", String.Format("{0}", size) },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
public static List<Dictionary<string, string>> GetRDCManFiles()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// lists any found files in Local\Microsoft\Credentials\*
|
|
try
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive"));
|
|
string[] dirs = Directory.GetDirectories(userFolder);
|
|
|
|
foreach (string dir in dirs)
|
|
{
|
|
string[] parts = dir.Split('\\');
|
|
string userName = parts[parts.Length - 1];
|
|
if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")))
|
|
{
|
|
string userRDManFile = String.Format("{0}\\AppData\\Local\\Microsoft\\Remote Desktop Connection Manager\\RDCMan.settings", dir);
|
|
if (System.IO.File.Exists(userRDManFile))
|
|
{
|
|
XmlDocument xmlDoc = new XmlDocument();
|
|
xmlDoc.Load(userRDManFile);
|
|
|
|
// grab the recent RDG files
|
|
XmlNodeList filesToOpen = xmlDoc.GetElementsByTagName("FilesToOpen");
|
|
XmlNodeList items = filesToOpen[0].ChildNodes;
|
|
XmlNode node = items[0];
|
|
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(userRDManFile);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(userRDManFile);
|
|
Dictionary<string, string> rdg = new Dictionary<string, string>(){
|
|
{ "RDCManFile", userRDManFile },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ ".RDG Files", "" },
|
|
};
|
|
|
|
foreach (XmlNode rdgFile in items)
|
|
rdg[".RDG Files"] += rdgFile.InnerText;
|
|
|
|
results.Add(rdg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string userName = Environment.GetEnvironmentVariable("USERNAME");
|
|
string userRDManFile = String.Format("{0}\\AppData\\Local\\Microsoft\\Remote Desktop Connection Manager\\RDCMan.settings", System.Environment.GetEnvironmentVariable("USERPROFILE"));
|
|
|
|
if (System.IO.File.Exists(userRDManFile))
|
|
{
|
|
XmlDocument xmlDoc = new XmlDocument();
|
|
xmlDoc.Load(userRDManFile);
|
|
|
|
// grab the recent RDG files
|
|
XmlNodeList filesToOpen = xmlDoc.GetElementsByTagName("FilesToOpen");
|
|
XmlNodeList items = filesToOpen[0].ChildNodes;
|
|
XmlNode node = items[0];
|
|
|
|
DateTime lastAccessed = System.IO.File.GetLastAccessTime(userRDManFile);
|
|
DateTime lastModified = System.IO.File.GetLastWriteTime(userRDManFile);
|
|
Dictionary<string, string> rdg = new Dictionary<string, string>(){
|
|
{ "RDCManFile", userRDManFile },
|
|
{ "Accessed", String.Format("{0}", lastAccessed) },
|
|
{ "Modified", String.Format("{0}", lastModified) },
|
|
{ ".RDG Files", "" },
|
|
};
|
|
|
|
foreach (XmlNode rdgFile in items)
|
|
rdg[".RDG Files"] += rdgFile.InnerText;
|
|
results.Add(rdg);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
|
|
[DllImport("advapi32.dll", SetLastError = true)]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
|
|
|
|
[DllImport("secur32.dll", SetLastError = true)]
|
|
public static extern int LsaRegisterLogonProcess(LSA_STRING_IN LogonProcessName, out IntPtr LsaHandle, out ulong SecurityMode);
|
|
|
|
[DllImport("advapi32.dll")]
|
|
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
|
|
|
|
[DllImport("advapi32.dll", SetLastError = true)]
|
|
static extern bool ImpersonateLoggedOnUser(IntPtr hToken);
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
static extern bool CloseHandle(IntPtr hObject);
|
|
|
|
[DllImport("advapi32.dll", SetLastError = true)]
|
|
static extern bool RevertToSelf();
|
|
|
|
[DllImport("Secur32.dll", SetLastError = false)]
|
|
private static extern uint LsaEnumerateLogonSessions(out UInt64 LogonSessionCount, out IntPtr LogonSessionList);
|
|
|
|
[DllImport("Secur32.dll", SetLastError = false)]
|
|
private static extern uint LsaGetLogonSessionData(IntPtr luid, out IntPtr ppLogonSessionData);
|
|
|
|
[DllImport("secur32.dll", SetLastError = false)]
|
|
public static extern int LsaLookupAuthenticationPackage([In] IntPtr LsaHandle, [In] ref LSA_STRING_IN PackageName, [Out] out int AuthenticationPackage);
|
|
|
|
[DllImport("secur32.dll", SetLastError = false)]
|
|
private static extern int LsaCallAuthenticationPackage(IntPtr LsaHandle, int AuthenticationPackage, ref KERB_QUERY_TKT_CACHE_REQUEST ProtocolSubmitBuffer, int SubmitBufferLength, out IntPtr ProtocolReturnBuffer, out int ReturnBufferLength, out int ProtocolStatus);
|
|
|
|
[DllImport("secur32.dll", SetLastError = false)]
|
|
private static extern uint LsaFreeReturnBuffer(IntPtr buffer);
|
|
[DllImport("secur32.dll", SetLastError = false)]
|
|
private static extern int LsaConnectUntrusted([Out] out IntPtr LsaHandle);
|
|
|
|
[DllImport("secur32.dll", SetLastError = false)]
|
|
private static extern int LsaDeregisterLogonProcess([In] IntPtr LsaHandle);
|
|
|
|
[DllImport("secur32.dll", EntryPoint = "LsaCallAuthenticationPackage", SetLastError = false)]
|
|
private static extern int LsaCallAuthenticationPackage_KERB_RETRIEVE_TKT(IntPtr LsaHandle, int AuthenticationPackage, ref KERB_RETRIEVE_TKT_REQUEST ProtocolSubmitBuffer, int SubmitBufferLength, out IntPtr ProtocolReturnBuffer, out int ReturnBufferLength, out int ProtocolStatus);
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct LSA_STRING_IN
|
|
{
|
|
public UInt16 Length;
|
|
public UInt16 MaximumLength;
|
|
public string Buffer;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct LSA_STRING_OUT
|
|
{
|
|
public UInt16 Length;
|
|
public UInt16 MaximumLength;
|
|
public IntPtr Buffer;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
protected struct LUID
|
|
{
|
|
public uint LowPart;
|
|
public int HighPart;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct SECURITY_LOGON_SESSION_DATA
|
|
{
|
|
public UInt32 Size;
|
|
public LUID LoginID;
|
|
public LSA_STRING_OUT Username;
|
|
public LSA_STRING_OUT LoginDomain;
|
|
public LSA_STRING_OUT AuthenticationPackage;
|
|
public UInt32 LogonType;
|
|
public UInt32 Session;
|
|
public IntPtr PSiD;
|
|
public UInt64 LoginTime;
|
|
public LSA_STRING_OUT LogonServer;
|
|
public LSA_STRING_OUT DnsDomainName;
|
|
public LSA_STRING_OUT Upn;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct KERB_QUERY_TKT_CACHE_REQUEST
|
|
{
|
|
public KERB_PROTOCOL_MESSAGE_TYPE MessageType;
|
|
public LUID LogonId;
|
|
}
|
|
public enum KERB_PROTOCOL_MESSAGE_TYPE : UInt32
|
|
{
|
|
KerbDebugRequestMessage = 0,
|
|
KerbQueryTicketCacheMessage = 1,
|
|
KerbChangeMachinePasswordMessage = 2,
|
|
KerbVerifyPacMessage = 3,
|
|
KerbRetrieveTicketMessage = 4,
|
|
KerbUpdateAddressesMessage = 5,
|
|
KerbPurgeTicketCacheMessage = 6,
|
|
KerbChangePasswordMessage = 7,
|
|
KerbRetrieveEncodedTicketMessage = 8,
|
|
KerbDecryptDataMessage = 9,
|
|
KerbAddBindingCacheEntryMessage = 10,
|
|
KerbSetPasswordMessage = 11,
|
|
KerbSetPasswordExMessage = 12,
|
|
KerbVerifyCredentialsMessage = 13,
|
|
KerbQueryTicketCacheExMessage = 14,
|
|
KerbPurgeTicketCacheExMessage = 15,
|
|
KerbRefreshSmartcardCredentialsMessage = 16,
|
|
KerbAddExtraCredentialsMessage = 17,
|
|
KerbQuerySupplementalCredentialsMessage = 18,
|
|
KerbTransferCredentialsMessage = 19,
|
|
KerbQueryTicketCacheEx2Message = 20,
|
|
KerbSubmitTicketMessage = 21,
|
|
KerbAddExtraCredentialsExMessage = 22,
|
|
KerbQueryKdcProxyCacheMessage = 23,
|
|
KerbPurgeKdcProxyCacheMessage = 24,
|
|
KerbQueryTicketCacheEx3Message = 25,
|
|
KerbCleanupMachinePkinitCredsMessage = 26,
|
|
KerbAddBindingCacheEntryExMessage = 27,
|
|
KerbQueryBindingCacheMessage = 28,
|
|
KerbPurgeBindingCacheMessage = 29,
|
|
KerbQueryDomainExtendedPoliciesMessage = 30,
|
|
KerbQueryS4U2ProxyCacheMessage = 31
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct KERB_QUERY_TKT_CACHE_RESPONSE
|
|
{
|
|
public KERB_PROTOCOL_MESSAGE_TYPE MessageType;
|
|
public int CountOfTickets;
|
|
// public KERB_TICKET_CACHE_INFO[] Tickets;
|
|
public IntPtr Tickets;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct KERB_TICKET_CACHE_INFO
|
|
{
|
|
public LSA_STRING_OUT ServerName;
|
|
public LSA_STRING_OUT RealmName;
|
|
public Int64 StartTime;
|
|
public Int64 EndTime;
|
|
public Int64 RenewTime;
|
|
public Int32 EncryptionType;
|
|
public UInt32 TicketFlags;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct KERB_RETRIEVE_TKT_RESPONSE
|
|
{
|
|
public KERB_EXTERNAL_TICKET Ticket;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct KERB_CRYPTO_KEY
|
|
{
|
|
public Int32 KeyType;
|
|
public Int32 Length;
|
|
public IntPtr Value;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct KERB_EXTERNAL_TICKET
|
|
{
|
|
public IntPtr ServiceName;
|
|
public IntPtr TargetName;
|
|
public IntPtr ClientName;
|
|
public LSA_STRING_OUT DomainName;
|
|
public LSA_STRING_OUT TargetDomainName;
|
|
public LSA_STRING_OUT AltTargetDomainName;
|
|
public KERB_CRYPTO_KEY SessionKey;
|
|
public UInt32 TicketFlags;
|
|
public UInt32 Flags;
|
|
public Int64 KeyExpirationTime;
|
|
public Int64 StartTime;
|
|
public Int64 EndTime;
|
|
public Int64 RenewUntil;
|
|
public Int64 TimeSkew;
|
|
public Int32 EncodedTicketSize;
|
|
public IntPtr EncodedTicket;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct KERB_RETRIEVE_TKT_REQUEST
|
|
{
|
|
public KERB_PROTOCOL_MESSAGE_TYPE MessageType;
|
|
public LUID LogonId;
|
|
public LSA_STRING_IN TargetName;
|
|
public UInt64 TicketFlags;
|
|
public KERB_CACHE_OPTIONS CacheOptions;
|
|
public Int64 EncryptionType;
|
|
public SECURITY_HANDLE CredentialsHandle;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct SECURITY_HANDLE
|
|
{
|
|
public IntPtr LowPart;
|
|
public IntPtr HighPart;
|
|
public SECURITY_HANDLE(int dummy)
|
|
{
|
|
LowPart = HighPart = IntPtr.Zero;
|
|
}
|
|
};
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct KERB_EXTERNAL_NAME
|
|
{
|
|
public Int16 NameType;
|
|
public UInt16 NameCount;
|
|
public LSA_STRING_OUT Names;
|
|
}
|
|
[Flags]
|
|
private enum KERB_CACHE_OPTIONS : UInt64
|
|
{
|
|
KERB_RETRIEVE_TICKET_DEFAULT = 0x0,
|
|
KERB_RETRIEVE_TICKET_DONT_USE_CACHE = 0x1,
|
|
KERB_RETRIEVE_TICKET_USE_CACHE_ONLY = 0x2,
|
|
KERB_RETRIEVE_TICKET_USE_CREDHANDLE = 0x4,
|
|
KERB_RETRIEVE_TICKET_AS_KERB_CRED = 0x8,
|
|
KERB_RETRIEVE_TICKET_WITH_SEC_CRED = 0x10,
|
|
KERB_RETRIEVE_TICKET_CACHE_TICKET = 0x20,
|
|
KERB_RETRIEVE_TICKET_MAX_LIFETIME = 0x40,
|
|
}
|
|
|
|
private enum SECURITY_LOGON_TYPE : uint
|
|
{
|
|
Interactive = 2, // logging on interactively.
|
|
Network, // logging using a network.
|
|
Batch, // logon for a batch process.
|
|
Service, // logon for a service account.
|
|
Proxy, // Not supported.
|
|
Unlock, // Tattempt to unlock a workstation.
|
|
NetworkCleartext, // network logon with cleartext credentials
|
|
NewCredentials, // caller can clone its current token and specify new credentials for outbound connections
|
|
RemoteInteractive, // terminal server session that is both remote and interactive
|
|
CachedInteractive, // attempt to use the cached credentials without going out across the network
|
|
CachedRemoteInteractive,// same as RemoteInteractive, except used internally for auditing purposes
|
|
CachedUnlock // attempt to unlock a workstation
|
|
}
|
|
public enum KERB_ENCRYPTION_TYPE : UInt32
|
|
{
|
|
reserved0 = 0,
|
|
des_cbc_crc = 1,
|
|
des_cbc_md4 = 2,
|
|
des_cbc_md5 = 3,
|
|
reserved1 = 4,
|
|
des3_cbc_md5 = 5,
|
|
reserved2 = 6,
|
|
des3_cbc_sha1 = 7,
|
|
dsaWithSHA1_CmsOID = 9,
|
|
md5WithRSAEncryption_CmsOID = 10,
|
|
sha1WithRSAEncryption_CmsOID = 11,
|
|
rc2CBC_EnvOID = 12,
|
|
rsaEncryption_EnvOID = 13,
|
|
rsaES_OAEP_ENV_OID = 14,
|
|
des_ede3_cbc_Env_OID = 15,
|
|
des3_cbc_sha1_kd = 16,
|
|
aes128_cts_hmac_sha1_96 = 17,
|
|
aes256_cts_hmac_sha1_96 = 18,
|
|
aes128_cts_hmac_sha256_128 = 19,
|
|
aes256_cts_hmac_sha384_192 = 20,
|
|
rc4_hmac = 23,
|
|
rc4_hmac_exp = 24,
|
|
camellia128_cts_cmac = 25,
|
|
camellia256_cts_cmac = 26,
|
|
subkey_keymaterial = 65
|
|
}
|
|
[Flags]
|
|
public enum KERB_TICKET_FLAGS : UInt32
|
|
{
|
|
reserved = 2147483648,
|
|
forwardable = 0x40000000,
|
|
forwarded = 0x20000000,
|
|
proxiable = 0x10000000,
|
|
proxy = 0x08000000,
|
|
may_postdate = 0x04000000,
|
|
postdated = 0x02000000,
|
|
invalid = 0x01000000,
|
|
renewable = 0x00800000,
|
|
initial = 0x00400000,
|
|
pre_authent = 0x00200000,
|
|
hw_authent = 0x00100000,
|
|
ok_as_delegate = 0x00040000,
|
|
name_canonicalize = 0x00010000,
|
|
//cname_in_pa_data = 0x00040000,
|
|
enc_pa_rep = 0x00010000,
|
|
reserved1 = 0x00000001
|
|
}
|
|
public static IntPtr LsaRegisterLogonProcessHelper()
|
|
{
|
|
// helper that establishes a connection to the LSA server and verifies that the caller is a logon application
|
|
// used for Kerberos ticket enumeration
|
|
|
|
string logonProcessName = "User32LogonProcesss";
|
|
LSA_STRING_IN LSAString;
|
|
IntPtr lsaHandle = IntPtr.Zero;
|
|
UInt64 securityMode = 0;
|
|
|
|
LSAString.Length = (ushort)logonProcessName.Length;
|
|
LSAString.MaximumLength = (ushort)(logonProcessName.Length + 1);
|
|
LSAString.Buffer = logonProcessName;
|
|
|
|
int ret = LsaRegisterLogonProcess(LSAString, out lsaHandle, out securityMode);
|
|
|
|
return lsaHandle;
|
|
}
|
|
|
|
public static bool GetSystem()
|
|
{
|
|
// helper to elevate to SYSTEM for Kerberos ticket enumeration via token impersonation
|
|
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
IntPtr hToken = IntPtr.Zero;
|
|
|
|
// Open winlogon's token with TOKEN_DUPLICATE accesss so ca can make a copy of the token with DuplicateToken
|
|
Process[] processes = Process.GetProcessesByName("winlogon");
|
|
IntPtr handle = processes[0].Handle;
|
|
|
|
// TOKEN_DUPLICATE = 0x0002
|
|
bool success = OpenProcessToken(handle, 0x0002, out hToken);
|
|
if (!success)
|
|
{
|
|
//Console.WriteLine("OpenProcessToken failed!");
|
|
return false;
|
|
}
|
|
|
|
// make a copy of the NT AUTHORITY\SYSTEM token from winlogon
|
|
// 2 == SecurityImpersonation
|
|
IntPtr hDupToken = IntPtr.Zero;
|
|
success = DuplicateToken(hToken, 2, ref hDupToken);
|
|
if (!success)
|
|
{
|
|
//Console.WriteLine("DuplicateToken failed!");
|
|
return false;
|
|
}
|
|
|
|
success = ImpersonateLoggedOnUser(hDupToken);
|
|
if (!success)
|
|
{
|
|
//Console.WriteLine("ImpersonateLoggedOnUser failed!");
|
|
return false;
|
|
}
|
|
|
|
// clean up the handles we created
|
|
CloseHandle(hToken);
|
|
CloseHandle(hDupToken);
|
|
|
|
string name = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
|
|
if (name != "NT AUTHORITY\\SYSTEM")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
public static IEnumerable<string> Split(string text, int partLength)
|
|
{
|
|
if (text == null) { Console.WriteLine("[ERROR] Split() - singleLineString"); }
|
|
if (partLength < 1) { Console.WriteLine("[ERROR] Split() - 'columns' must be greater than 0."); }
|
|
|
|
var partCount = Math.Ceiling((double)text.Length / partLength);
|
|
if (partCount < 2)
|
|
{
|
|
yield return text;
|
|
}
|
|
|
|
for (int i = 0; i < partCount; i++)
|
|
{
|
|
var index = i * partLength;
|
|
var lengthLeft = Math.Min(partLength, text.Length - index);
|
|
var line = text.Substring(index, lengthLeft);
|
|
yield return line;
|
|
}
|
|
}
|
|
public static List<Dictionary<string, string>> ListKerberosTickets()
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
return ListKerberosTicketsAllUsers();
|
|
}
|
|
else
|
|
{
|
|
return ListKerberosTicketsCurrentUser();
|
|
}
|
|
}
|
|
|
|
public static List<Dictionary<string, string>> ListKerberosTicketsAllUsers()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// adapted partially from Vincent LE TOUX' work
|
|
// https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2939-L2950
|
|
// and https://www.dreamincode.net/forums/topic/135033-increment-memory-pointer-issue/
|
|
// also Jared Atkinson's work at https://github.com/Invoke-IR/ACE/blob/master/ACE-Management/PS-ACE/Scripts/ACE_Get-KerberosTicketCache.ps1
|
|
|
|
IntPtr hLsa = LsaRegisterLogonProcessHelper();
|
|
int totalTicketCount = 0;
|
|
|
|
// if the original call fails then it is likely we don't have SeTcbPrivilege
|
|
// to get SeTcbPrivilege we can Impersonate a NT AUTHORITY\SYSTEM Token
|
|
if (hLsa == IntPtr.Zero)
|
|
{
|
|
GetSystem();
|
|
// should now have the proper privileges to get a Handle to LSA
|
|
hLsa = LsaRegisterLogonProcessHelper();
|
|
// we don't need our NT AUTHORITY\SYSTEM Token anymore so we can revert to our original token
|
|
RevertToSelf();
|
|
}
|
|
|
|
try
|
|
{
|
|
// first return all the logon sessions
|
|
|
|
DateTime systime = new DateTime(1601, 1, 1, 0, 0, 0, 0); //win32 systemdate
|
|
UInt64 count;
|
|
IntPtr luidPtr = IntPtr.Zero;
|
|
IntPtr iter = luidPtr;
|
|
|
|
uint ret = LsaEnumerateLogonSessions(out count, out luidPtr); // get an array of pointers to LUIDs
|
|
|
|
for (ulong i = 0; i < count; i++)
|
|
{
|
|
IntPtr sessionData;
|
|
ret = LsaGetLogonSessionData(luidPtr, out sessionData);
|
|
SECURITY_LOGON_SESSION_DATA data = (SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(sessionData, typeof(SECURITY_LOGON_SESSION_DATA));
|
|
|
|
// if we have a valid logon
|
|
if (data.PSiD != IntPtr.Zero)
|
|
{
|
|
// user session data
|
|
string username = Marshal.PtrToStringUni(data.Username.Buffer).Trim();
|
|
System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(data.PSiD);
|
|
string domain = Marshal.PtrToStringUni(data.LoginDomain.Buffer).Trim();
|
|
string authpackage = Marshal.PtrToStringUni(data.AuthenticationPackage.Buffer).Trim();
|
|
SECURITY_LOGON_TYPE logonType = (SECURITY_LOGON_TYPE)data.LogonType;
|
|
DateTime logonTime = systime.AddTicks((long)data.LoginTime);
|
|
string logonServer = Marshal.PtrToStringUni(data.LogonServer.Buffer).Trim();
|
|
string dnsDomainName = Marshal.PtrToStringUni(data.DnsDomainName.Buffer).Trim();
|
|
string upn = Marshal.PtrToStringUni(data.Upn.Buffer).Trim();
|
|
|
|
// now we want to get the tickets for this logon ID
|
|
string name = "kerberos";
|
|
LSA_STRING_IN LSAString;
|
|
LSAString.Length = (ushort)name.Length;
|
|
LSAString.MaximumLength = (ushort)(name.Length + 1);
|
|
LSAString.Buffer = name;
|
|
|
|
IntPtr ticketPointer = IntPtr.Zero;
|
|
IntPtr ticketsPointer = IntPtr.Zero;
|
|
DateTime sysTime = new DateTime(1601, 1, 1, 0, 0, 0, 0);
|
|
int authPack;
|
|
int returnBufferLength = 0;
|
|
int protocalStatus = 0;
|
|
int retCode;
|
|
|
|
KERB_QUERY_TKT_CACHE_REQUEST tQuery = new KERB_QUERY_TKT_CACHE_REQUEST();
|
|
KERB_QUERY_TKT_CACHE_RESPONSE tickets = new KERB_QUERY_TKT_CACHE_RESPONSE();
|
|
KERB_TICKET_CACHE_INFO ticket;
|
|
|
|
// obtains the unique identifier for the kerberos authentication package.
|
|
retCode = LsaLookupAuthenticationPackage(hLsa, ref LSAString, out authPack);
|
|
|
|
// input object for querying the ticket cache for a specific logon ID
|
|
LUID userLogonID = new LUID();
|
|
userLogonID.LowPart = data.LoginID.LowPart;
|
|
userLogonID.HighPart = 0;
|
|
tQuery.LogonId = userLogonID;
|
|
tQuery.MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheMessage;
|
|
|
|
// query LSA, specifying we want the ticket cache
|
|
retCode = LsaCallAuthenticationPackage(hLsa, authPack, ref tQuery, Marshal.SizeOf(tQuery), out ticketPointer, out returnBufferLength, out protocalStatus);
|
|
|
|
/*Console.WriteLine("\r\n UserName : {0}", username);
|
|
Console.WriteLine(" Domain : {0}", domain);
|
|
Console.WriteLine(" LogonId : {0}", data.LoginID.LowPart);
|
|
Console.WriteLine(" UserSID : {0}", sid.AccountDomainSid);
|
|
Console.WriteLine(" AuthenticationPackage : {0}", authpackage);
|
|
Console.WriteLine(" LogonType : {0}", logonType);
|
|
Console.WriteLine(" LogonType : {0}", logonTime);
|
|
Console.WriteLine(" LogonServer : {0}", logonServer);
|
|
Console.WriteLine(" LogonServerDNSDomain : {0}", dnsDomainName);
|
|
Console.WriteLine(" UserPrincipalName : {0}\r\n", upn);*/
|
|
|
|
if (ticketPointer != IntPtr.Zero)
|
|
{
|
|
// parse the returned pointer into our initial KERB_QUERY_TKT_CACHE_RESPONSE structure
|
|
tickets = (KERB_QUERY_TKT_CACHE_RESPONSE)Marshal.PtrToStructure((System.IntPtr)ticketPointer, typeof(KERB_QUERY_TKT_CACHE_RESPONSE));
|
|
int count2 = tickets.CountOfTickets;
|
|
|
|
if (count2 != 0)
|
|
{
|
|
Console.WriteLine(" [*] Enumerated {0} ticket(s):\r\n", count2);
|
|
totalTicketCount += count2;
|
|
// get the size of the structures we're iterating over
|
|
Int32 dataSize = Marshal.SizeOf(typeof(KERB_TICKET_CACHE_INFO));
|
|
|
|
for (int j = 0; j < count2; j++)
|
|
{
|
|
// iterate through the structures
|
|
IntPtr currTicketPtr = (IntPtr)(long)((ticketPointer.ToInt64() + (int)(8 + j * dataSize)));
|
|
|
|
// parse the new ptr to the appropriate structure
|
|
ticket = (KERB_TICKET_CACHE_INFO)Marshal.PtrToStructure(currTicketPtr, typeof(KERB_TICKET_CACHE_INFO));
|
|
|
|
// extract our fields
|
|
string serverName = Marshal.PtrToStringUni(ticket.ServerName.Buffer, ticket.ServerName.Length / 2);
|
|
string realmName = Marshal.PtrToStringUni(ticket.RealmName.Buffer, ticket.RealmName.Length / 2);
|
|
DateTime startTime = DateTime.FromFileTime(ticket.StartTime);
|
|
DateTime endTime = DateTime.FromFileTime(ticket.EndTime);
|
|
DateTime renewTime = DateTime.FromFileTime(ticket.RenewTime);
|
|
string encryptionType = ((KERB_ENCRYPTION_TYPE)ticket.EncryptionType).ToString();
|
|
string ticketFlags = ((KERB_TICKET_FLAGS)ticket.TicketFlags).ToString();
|
|
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "UserPrincipalName", upn },
|
|
{ "serverName", serverName },
|
|
{ "RealmName", realmName },
|
|
{ "StartTime", String.Format("{0}", startTime) },
|
|
{ "EndTime", String.Format("{0}", endTime) },
|
|
{ "RenewTime", String.Format("{0}", renewTime) },
|
|
{ "EncryptionType", encryptionType },
|
|
{ "TicketFlags", ticketFlags },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// move the pointer forward
|
|
luidPtr = (IntPtr)((long)luidPtr.ToInt64() + Marshal.SizeOf(typeof(LUID)));
|
|
LsaFreeReturnBuffer(sessionData);
|
|
}
|
|
LsaFreeReturnBuffer(luidPtr);
|
|
|
|
// disconnect from LSA
|
|
LsaDeregisterLogonProcess(hLsa);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
return results;
|
|
}
|
|
public static List<Dictionary<string, string>> ListKerberosTicketsCurrentUser()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// adapted partially from Vincent LE TOUX' work
|
|
// https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2939-L2950
|
|
// and https://www.dreamincode.net/forums/topic/135033-increment-memory-pointer-issue/
|
|
// also Jared Atkinson's work at https://github.com/Invoke-IR/ACE/blob/master/ACE-Management/PS-ACE/Scripts/ACE_Get-KerberosTicketCache.ps1
|
|
|
|
try
|
|
{
|
|
string name = "kerberos";
|
|
LSA_STRING_IN LSAString;
|
|
LSAString.Length = (ushort)name.Length;
|
|
LSAString.MaximumLength = (ushort)(name.Length + 1);
|
|
LSAString.Buffer = name;
|
|
|
|
IntPtr ticketPointer = IntPtr.Zero;
|
|
IntPtr ticketsPointer = IntPtr.Zero;
|
|
DateTime sysTime = new DateTime(1601, 1, 1, 0, 0, 0, 0);
|
|
int authPack;
|
|
int returnBufferLength = 0;
|
|
int protocalStatus = 0;
|
|
IntPtr lsaHandle;
|
|
int retCode;
|
|
|
|
// If we want to look at tickets from a session other than our own
|
|
// then we need to use LsaRegisterLogonProcess instead of LsaConnectUntrusted
|
|
retCode = LsaConnectUntrusted(out lsaHandle);
|
|
|
|
KERB_QUERY_TKT_CACHE_REQUEST tQuery = new KERB_QUERY_TKT_CACHE_REQUEST();
|
|
KERB_QUERY_TKT_CACHE_RESPONSE tickets = new KERB_QUERY_TKT_CACHE_RESPONSE();
|
|
KERB_TICKET_CACHE_INFO ticket;
|
|
|
|
// obtains the unique identifier for the kerberos authentication package.
|
|
retCode = LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out authPack);
|
|
|
|
// input object for querying the ticket cache (https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/ns-ntsecapi-_kerb_query_tkt_cache_request)
|
|
tQuery.LogonId = new LUID();
|
|
tQuery.MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheMessage;
|
|
|
|
// query LSA, specifying we want the ticket cache
|
|
retCode = LsaCallAuthenticationPackage(lsaHandle, authPack, ref tQuery, Marshal.SizeOf(tQuery), out ticketPointer, out returnBufferLength, out protocalStatus);
|
|
|
|
// parse the returned pointer into our initial KERB_QUERY_TKT_CACHE_RESPONSE structure
|
|
tickets = (KERB_QUERY_TKT_CACHE_RESPONSE)Marshal.PtrToStructure((System.IntPtr)ticketPointer, typeof(KERB_QUERY_TKT_CACHE_RESPONSE));
|
|
int count = tickets.CountOfTickets;
|
|
|
|
// get the size of the structures we're iterating over
|
|
Int32 dataSize = Marshal.SizeOf(typeof(KERB_TICKET_CACHE_INFO));
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
// iterate through the structures
|
|
IntPtr currTicketPtr = (IntPtr)(long)((ticketPointer.ToInt64() + (int)(8 + i * dataSize)));
|
|
|
|
// parse the new ptr to the appropriate structure
|
|
ticket = (KERB_TICKET_CACHE_INFO)Marshal.PtrToStructure(currTicketPtr, typeof(KERB_TICKET_CACHE_INFO));
|
|
|
|
// extract our fields
|
|
string serverName = Marshal.PtrToStringUni(ticket.ServerName.Buffer, ticket.ServerName.Length / 2);
|
|
string realmName = Marshal.PtrToStringUni(ticket.RealmName.Buffer, ticket.RealmName.Length / 2);
|
|
DateTime startTime = DateTime.FromFileTime(ticket.StartTime);
|
|
DateTime endTime = DateTime.FromFileTime(ticket.EndTime);
|
|
DateTime renewTime = DateTime.FromFileTime(ticket.RenewTime);
|
|
string encryptionType = ((KERB_ENCRYPTION_TYPE)ticket.EncryptionType).ToString();
|
|
string ticketFlags = ((KERB_TICKET_FLAGS)ticket.TicketFlags).ToString();
|
|
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "serverName", serverName },
|
|
{ "RealmName", realmName },
|
|
{ "StartTime", String.Format("{0}", startTime) },
|
|
{ "EndTime", String.Format("{0}", endTime) },
|
|
{ "RenewTime", String.Format("{0}", renewTime) },
|
|
{ "EncryptionType", encryptionType },
|
|
{ "TicketFlags", ticketFlags },
|
|
});
|
|
}
|
|
|
|
// disconnect from LSA
|
|
LsaDeregisterLogonProcess(lsaHandle);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static List<Dictionary<string, string>> GetKerberosTGTData()
|
|
{
|
|
if (MyUtils.IsHighIntegrity())
|
|
{
|
|
return ListKerberosTGTDataAllUsers();
|
|
}
|
|
else
|
|
{
|
|
return ListKerberosTGTDataCurrentUser();
|
|
}
|
|
}
|
|
public static List<Dictionary<string, string>> ListKerberosTGTDataAllUsers()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// adapted partially from Vincent LE TOUX' work
|
|
// https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2939-L2950
|
|
// and https://www.dreamincode.net/forums/topic/135033-increment-memory-pointer-issue/
|
|
// also Jared Atkinson's work at https://github.com/Invoke-IR/ACE/blob/master/ACE-Management/PS-ACE/Scripts/ACE_Get-KerberosTicketCache.ps1
|
|
|
|
IntPtr hLsa = LsaRegisterLogonProcessHelper();
|
|
int totalTicketCount = 0;
|
|
|
|
// if the original call fails then it is likely we don't have SeTcbPrivilege
|
|
// to get SeTcbPrivilege we can Impersonate a NT AUTHORITY\SYSTEM Token
|
|
if (hLsa == IntPtr.Zero)
|
|
{
|
|
GetSystem();
|
|
// should now have the proper privileges to get a Handle to LSA
|
|
hLsa = LsaRegisterLogonProcessHelper();
|
|
// we don't need our NT AUTHORITY\SYSTEM Token anymore so we can revert to our original token
|
|
RevertToSelf();
|
|
}
|
|
|
|
try
|
|
{
|
|
// first return all the logon sessions
|
|
|
|
DateTime systime = new DateTime(1601, 1, 1, 0, 0, 0, 0); //win32 systemdate
|
|
UInt64 count;
|
|
IntPtr luidPtr = IntPtr.Zero;
|
|
IntPtr iter = luidPtr;
|
|
|
|
uint ret = LsaEnumerateLogonSessions(out count, out luidPtr); // get an array of pointers to LUIDs
|
|
|
|
for (ulong i = 0; i < count; i++)
|
|
{
|
|
IntPtr sessionData;
|
|
ret = LsaGetLogonSessionData(luidPtr, out sessionData);
|
|
SECURITY_LOGON_SESSION_DATA data = (SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(sessionData, typeof(SECURITY_LOGON_SESSION_DATA));
|
|
|
|
// if we have a valid logon
|
|
if (data.PSiD != IntPtr.Zero)
|
|
{
|
|
// user session data
|
|
string username = Marshal.PtrToStringUni(data.Username.Buffer).Trim();
|
|
System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(data.PSiD);
|
|
string domain = Marshal.PtrToStringUni(data.LoginDomain.Buffer).Trim();
|
|
string authpackage = Marshal.PtrToStringUni(data.AuthenticationPackage.Buffer).Trim();
|
|
SECURITY_LOGON_TYPE logonType = (SECURITY_LOGON_TYPE)data.LogonType;
|
|
DateTime logonTime = systime.AddTicks((long)data.LoginTime);
|
|
string logonServer = Marshal.PtrToStringUni(data.LogonServer.Buffer).Trim();
|
|
string dnsDomainName = Marshal.PtrToStringUni(data.DnsDomainName.Buffer).Trim();
|
|
string upn = Marshal.PtrToStringUni(data.Upn.Buffer).Trim();
|
|
|
|
// now we want to get the tickets for this logon ID
|
|
string name = "kerberos";
|
|
LSA_STRING_IN LSAString;
|
|
LSAString.Length = (ushort)name.Length;
|
|
LSAString.MaximumLength = (ushort)(name.Length + 1);
|
|
LSAString.Buffer = name;
|
|
|
|
IntPtr responsePointer = IntPtr.Zero;
|
|
int authPack;
|
|
int returnBufferLength = 0;
|
|
int protocalStatus = 0;
|
|
int retCode;
|
|
|
|
KERB_RETRIEVE_TKT_REQUEST tQuery = new KERB_RETRIEVE_TKT_REQUEST();
|
|
KERB_RETRIEVE_TKT_RESPONSE response = new KERB_RETRIEVE_TKT_RESPONSE();
|
|
|
|
// obtains the unique identifier for the kerberos authentication package.
|
|
retCode = LsaLookupAuthenticationPackage(hLsa, ref LSAString, out authPack);
|
|
|
|
// input object for querying the TGT for a specific logon ID (https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/ns-ntsecapi-_kerb_retrieve_tkt_request)
|
|
LUID userLogonID = new LUID();
|
|
userLogonID.LowPart = data.LoginID.LowPart;
|
|
userLogonID.HighPart = 0;
|
|
tQuery.LogonId = userLogonID;
|
|
tQuery.MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveTicketMessage;
|
|
// indicate we want kerb creds yo'
|
|
tQuery.CacheOptions = KERB_CACHE_OPTIONS.KERB_RETRIEVE_TICKET_AS_KERB_CRED;
|
|
|
|
// query LSA, specifying we want the the TGT data
|
|
retCode = LsaCallAuthenticationPackage_KERB_RETRIEVE_TKT(hLsa, authPack, ref tQuery, Marshal.SizeOf(tQuery), out responsePointer, out returnBufferLength, out protocalStatus);
|
|
|
|
if ((retCode) == 0 && (responsePointer != IntPtr.Zero))
|
|
{
|
|
/*Console.WriteLine("\r\n UserName : {0}", username);
|
|
Console.WriteLine(" Domain : {0}", domain);
|
|
Console.WriteLine(" LogonId : {0}", data.LoginID.LowPart);
|
|
Console.WriteLine(" UserSID : {0}", sid.AccountDomainSid);
|
|
Console.WriteLine(" AuthenticationPackage : {0}", authpackage);
|
|
Console.WriteLine(" LogonType : {0}", logonType);
|
|
Console.WriteLine(" LogonType : {0}", logonTime);
|
|
Console.WriteLine(" LogonServer : {0}", logonServer);
|
|
Console.WriteLine(" LogonServerDNSDomain : {0}", dnsDomainName);
|
|
Console.WriteLine(" UserPrincipalName : {0}", upn);*/
|
|
|
|
// parse the returned pointer into our initial KERB_RETRIEVE_TKT_RESPONSE structure
|
|
response = (KERB_RETRIEVE_TKT_RESPONSE)Marshal.PtrToStructure((System.IntPtr)responsePointer, typeof(KERB_RETRIEVE_TKT_RESPONSE));
|
|
|
|
KERB_EXTERNAL_NAME serviceNameStruct = (KERB_EXTERNAL_NAME)Marshal.PtrToStructure(response.Ticket.ServiceName, typeof(KERB_EXTERNAL_NAME));
|
|
string serviceName = Marshal.PtrToStringUni(serviceNameStruct.Names.Buffer, serviceNameStruct.Names.Length / 2).Trim();
|
|
|
|
string targetName = "";
|
|
if (response.Ticket.TargetName != IntPtr.Zero)
|
|
{
|
|
KERB_EXTERNAL_NAME targetNameStruct = (KERB_EXTERNAL_NAME)Marshal.PtrToStructure(response.Ticket.TargetName, typeof(KERB_EXTERNAL_NAME));
|
|
targetName = Marshal.PtrToStringUni(targetNameStruct.Names.Buffer, targetNameStruct.Names.Length / 2).Trim();
|
|
}
|
|
|
|
KERB_EXTERNAL_NAME clientNameStruct = (KERB_EXTERNAL_NAME)Marshal.PtrToStructure(response.Ticket.ClientName, typeof(KERB_EXTERNAL_NAME));
|
|
string clientName = Marshal.PtrToStringUni(clientNameStruct.Names.Buffer, clientNameStruct.Names.Length / 2).Trim();
|
|
|
|
string domainName = Marshal.PtrToStringUni(response.Ticket.DomainName.Buffer, response.Ticket.DomainName.Length / 2).Trim();
|
|
string targetDomainName = Marshal.PtrToStringUni(response.Ticket.TargetDomainName.Buffer, response.Ticket.TargetDomainName.Length / 2).Trim();
|
|
string altTargetDomainName = Marshal.PtrToStringUni(response.Ticket.AltTargetDomainName.Buffer, response.Ticket.AltTargetDomainName.Length / 2).Trim();
|
|
|
|
// extract the session key
|
|
KERB_ENCRYPTION_TYPE sessionKeyType = (KERB_ENCRYPTION_TYPE)response.Ticket.SessionKey.KeyType;
|
|
Int32 sessionKeyLength = response.Ticket.SessionKey.Length;
|
|
byte[] sessionKey = new byte[sessionKeyLength];
|
|
Marshal.Copy(response.Ticket.SessionKey.Value, sessionKey, 0, sessionKeyLength);
|
|
string base64SessionKey = Convert.ToBase64String(sessionKey);
|
|
|
|
DateTime keyExpirationTime = DateTime.FromFileTime(response.Ticket.KeyExpirationTime);
|
|
DateTime startTime = DateTime.FromFileTime(response.Ticket.StartTime);
|
|
DateTime endTime = DateTime.FromFileTime(response.Ticket.EndTime);
|
|
DateTime renewUntil = DateTime.FromFileTime(response.Ticket.RenewUntil);
|
|
Int64 timeSkew = response.Ticket.TimeSkew;
|
|
Int32 encodedTicketSize = response.Ticket.EncodedTicketSize;
|
|
|
|
string ticketFlags = ((KERB_TICKET_FLAGS)response.Ticket.TicketFlags).ToString();
|
|
|
|
// extract the TGT and base64 encode it
|
|
byte[] encodedTicket = new byte[encodedTicketSize];
|
|
Marshal.Copy(response.Ticket.EncodedTicket, encodedTicket, 0, encodedTicketSize);
|
|
string base64TGT = Convert.ToBase64String(encodedTicket);
|
|
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "UserPrincipalName", upn },
|
|
{ "ServiceName", serviceName },
|
|
{ "TargetName", targetName },
|
|
{ "ClientName", clientName },
|
|
{ "DomainName", domainName },
|
|
{ "TargetDomainName", targetDomainName },
|
|
{ "SessionKeyType", String.Format("{0}", sessionKeyType) },
|
|
{ "Base64SessionKey", base64SessionKey },
|
|
{ "KeyExpirationTime", String.Format("{0}", keyExpirationTime) },
|
|
{ "TicketFlags", ticketFlags },
|
|
{ "StartTime", String.Format("{0}", startTime) },
|
|
{ "EndTime", String.Format("{0}", endTime) },
|
|
{ "RenewUntil", String.Format("{0}", renewUntil) },
|
|
{ "TimeSkew", String.Format("{0}", timeSkew) },
|
|
{ "EncodedTicketSize", String.Format("{0}", encodedTicketSize) },
|
|
{ "Base64EncodedTicket", base64TGT },
|
|
});
|
|
totalTicketCount++;
|
|
}
|
|
}
|
|
luidPtr = (IntPtr)((long)luidPtr.ToInt64() + Marshal.SizeOf(typeof(LUID)));
|
|
//move the pointer forward
|
|
LsaFreeReturnBuffer(sessionData);
|
|
//free the SECURITY_LOGON_SESSION_DATA memory in the struct
|
|
}
|
|
LsaFreeReturnBuffer(luidPtr); //free the array of LUIDs
|
|
|
|
// disconnect from LSA
|
|
LsaDeregisterLogonProcess(hLsa);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
return results;
|
|
}
|
|
public static List<Dictionary<string, string>> ListKerberosTGTDataCurrentUser()
|
|
{
|
|
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
|
|
// adapted partially from Vincent LE TOUX' work
|
|
// https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2939-L2950
|
|
// and https://www.dreamincode.net/forums/topic/135033-increment-memory-pointer-issue/
|
|
// also Jared Atkinson's work at https://github.com/Invoke-IR/ACE/blob/master/ACE-Management/PS-ACE/Scripts/ACE_Get-KerberosTicketCache.ps1
|
|
|
|
try
|
|
{
|
|
string name = "kerberos";
|
|
LSA_STRING_IN LSAString;
|
|
LSAString.Length = (ushort)name.Length;
|
|
LSAString.MaximumLength = (ushort)(name.Length + 1);
|
|
LSAString.Buffer = name;
|
|
|
|
IntPtr responsePointer = IntPtr.Zero;
|
|
int authPack;
|
|
int returnBufferLength = 0;
|
|
int protocalStatus = 0;
|
|
IntPtr lsaHandle;
|
|
int retCode;
|
|
|
|
// If we want to look at tickets from a session other than our own
|
|
// then we need to use LsaRegisterLogonProcess instead of LsaConnectUntrusted
|
|
retCode = LsaConnectUntrusted(out lsaHandle);
|
|
|
|
KERB_RETRIEVE_TKT_REQUEST tQuery = new KERB_RETRIEVE_TKT_REQUEST();
|
|
KERB_RETRIEVE_TKT_RESPONSE response = new KERB_RETRIEVE_TKT_RESPONSE();
|
|
|
|
// obtains the unique identifier for the kerberos authentication package.
|
|
retCode = LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out authPack);
|
|
|
|
// input object for querying the TGT (https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/ns-ntsecapi-_kerb_retrieve_tkt_request)
|
|
tQuery.LogonId = new LUID();
|
|
tQuery.MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveTicketMessage;
|
|
// indicate we want kerb creds yo'
|
|
//tQuery.CacheOptions = KERB_CACHE_OPTIONS.KERB_RETRIEVE_TICKET_AS_KERB_CRED;
|
|
|
|
// query LSA, specifying we want the the TGT data
|
|
retCode = LsaCallAuthenticationPackage_KERB_RETRIEVE_TKT(lsaHandle, authPack, ref tQuery, Marshal.SizeOf(tQuery), out responsePointer, out returnBufferLength, out protocalStatus);
|
|
|
|
// parse the returned pointer into our initial KERB_RETRIEVE_TKT_RESPONSE structure
|
|
response = (KERB_RETRIEVE_TKT_RESPONSE)Marshal.PtrToStructure((System.IntPtr)responsePointer, typeof(KERB_RETRIEVE_TKT_RESPONSE));
|
|
|
|
KERB_EXTERNAL_NAME serviceNameStruct = (KERB_EXTERNAL_NAME)Marshal.PtrToStructure(response.Ticket.ServiceName, typeof(KERB_EXTERNAL_NAME));
|
|
string serviceName = Marshal.PtrToStringUni(serviceNameStruct.Names.Buffer, serviceNameStruct.Names.Length / 2).Trim();
|
|
|
|
string targetName = "";
|
|
if (response.Ticket.TargetName != IntPtr.Zero)
|
|
{
|
|
KERB_EXTERNAL_NAME targetNameStruct = (KERB_EXTERNAL_NAME)Marshal.PtrToStructure(response.Ticket.TargetName, typeof(KERB_EXTERNAL_NAME));
|
|
targetName = Marshal.PtrToStringUni(targetNameStruct.Names.Buffer, targetNameStruct.Names.Length / 2).Trim();
|
|
}
|
|
|
|
KERB_EXTERNAL_NAME clientNameStruct = (KERB_EXTERNAL_NAME)Marshal.PtrToStructure(response.Ticket.ClientName, typeof(KERB_EXTERNAL_NAME));
|
|
string clientName = Marshal.PtrToStringUni(clientNameStruct.Names.Buffer, clientNameStruct.Names.Length / 2).Trim();
|
|
|
|
string domainName = Marshal.PtrToStringUni(response.Ticket.DomainName.Buffer, response.Ticket.DomainName.Length / 2).Trim();
|
|
string targetDomainName = Marshal.PtrToStringUni(response.Ticket.TargetDomainName.Buffer, response.Ticket.TargetDomainName.Length / 2).Trim();
|
|
string altTargetDomainName = Marshal.PtrToStringUni(response.Ticket.AltTargetDomainName.Buffer, response.Ticket.AltTargetDomainName.Length / 2).Trim();
|
|
|
|
// extract the session key
|
|
KERB_ENCRYPTION_TYPE sessionKeyType = (KERB_ENCRYPTION_TYPE)response.Ticket.SessionKey.KeyType;
|
|
Int32 sessionKeyLength = response.Ticket.SessionKey.Length;
|
|
byte[] sessionKey = new byte[sessionKeyLength];
|
|
Marshal.Copy(response.Ticket.SessionKey.Value, sessionKey, 0, sessionKeyLength);
|
|
string base64SessionKey = Convert.ToBase64String(sessionKey);
|
|
|
|
DateTime keyExpirationTime = DateTime.FromFileTime(response.Ticket.KeyExpirationTime);
|
|
DateTime startTime = DateTime.FromFileTime(response.Ticket.StartTime);
|
|
DateTime endTime = DateTime.FromFileTime(response.Ticket.EndTime);
|
|
DateTime renewUntil = DateTime.FromFileTime(response.Ticket.RenewUntil);
|
|
Int64 timeSkew = response.Ticket.TimeSkew;
|
|
Int32 encodedTicketSize = response.Ticket.EncodedTicketSize;
|
|
|
|
string ticketFlags = ((KERB_TICKET_FLAGS)response.Ticket.TicketFlags).ToString();
|
|
|
|
// extract the TGT and base64 encode it
|
|
byte[] encodedTicket = new byte[encodedTicketSize];
|
|
Marshal.Copy(response.Ticket.EncodedTicket, encodedTicket, 0, encodedTicketSize);
|
|
string base64TGT = Convert.ToBase64String(encodedTicket);
|
|
|
|
results.Add(new Dictionary<string, string>()
|
|
{
|
|
{ "ServiceName", serviceName },
|
|
{ "TargetName", targetName },
|
|
{ "ClientName", clientName },
|
|
{ "DomainName", domainName },
|
|
{ "TargetDomainName", targetDomainName },
|
|
{ "SessionKeyType", String.Format("{0}", sessionKeyType) },
|
|
{ "Base64SessionKey", base64SessionKey },
|
|
{ "KeyExpirationTime", String.Format("{0}", keyExpirationTime) },
|
|
{ "TicketFlags", ticketFlags },
|
|
{ "StartTime", String.Format("{0}", startTime) },
|
|
{ "EndTime", String.Format("{0}", endTime) },
|
|
{ "RenewUntil", String.Format("{0}", renewUntil) },
|
|
{ "TimeSkew", String.Format("{0}", timeSkew) },
|
|
{ "EncodedTicketSize", String.Format("{0}", encodedTicketSize) },
|
|
{ "Base64EncodedTicket", base64TGT },
|
|
});
|
|
|
|
// disconnect from LSA
|
|
LsaDeregisterLogonProcess(lsaHandle);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
|
|
}
|
|
return results;
|
|
}
|
|
}
|
|
}
|