Skip to content

Commit e00e178

Browse files
authored
Adds Certificate Collector Support for Mac and Linux (#123)
* Certificate Collector working on linux * Reimplemented certificate collector for osx * Updated comments
1 parent cf5cc7a commit e00e178

File tree

2 files changed

+80
-17
lines changed

2 files changed

+80
-17
lines changed

Lib/Collectors/BaseCollector.cs

-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ public void Stop()
4747
t.Seconds,
4848
t.Milliseconds);
4949
Log.Information("{0} {1} in {2}",Strings.Get("Completed"), this.GetType().Name, answer);
50-
Log.Debug(t.ToString());
5150
var EndEvent = new Dictionary<string, string>();
5251
EndEvent.Add("Scanner", this.GetType().Name);
5352
EndEvent.Add("Duration", watch.ElapsedMilliseconds.ToString());

Lib/Collectors/Certificates/CertificateCollector.cs

+80-16
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
using Newtonsoft.Json.Serialization;
1414
using Serilog;
1515
using AttackSurfaceAnalyzer.ObjectTypes;
16+
using System.Text.RegularExpressions;
17+
using System.Text;
1618

1719
namespace AttackSurfaceAnalyzer.Collectors.Certificates
1820
{
@@ -42,7 +44,7 @@ public void Truncate(string runid)
4244

4345
public override bool CanRunOnPlatform()
4446
{
45-
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
47+
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
4648
}
4749

4850
public void Write(StoreLocation storeLocation, StoreName storeName, X509Certificate2 obj)
@@ -64,7 +66,7 @@ public void Write(StoreLocation storeLocation, StoreName storeName, X509Certific
6466
}
6567
else
6668
{
67-
cmd.Parameters.AddWithValue("@pkcs12", obj.Export(X509ContentType.Pkcs12));
69+
cmd.Parameters.AddWithValue("@pkcs12", obj.Export(X509ContentType.Pfx));
6870
}
6971

7072
cmd.Parameters.AddWithValue("@row_key", CryptoHelpers.CreateHash(runId + recordCounter));
@@ -76,7 +78,6 @@ public void Write(StoreLocation storeLocation, StoreName storeName, X509Certific
7678
CertificateHashString = obj.GetCertHashString(),
7779
Subject = obj.Subject
7880
};
79-
8081
cmd.Parameters.AddWithValue("@serialized", JsonConvert.SerializeObject(cert));
8182
cmd.ExecuteNonQuery();
8283
}
@@ -89,6 +90,11 @@ public void Write(StoreLocation storeLocation, StoreName storeName, X509Certific
8990
Log.Warning(e.Message);
9091
//This catches duplicate certificates
9192
}
93+
catch (Exception e)
94+
{
95+
Log.Warning(e.GetType().ToString());
96+
Log.Warning(e.StackTrace);
97+
}
9298
}
9399

94100
public override void Execute()
@@ -101,30 +107,88 @@ public override void Execute()
101107
Start();
102108
Truncate(runId);
103109

104-
foreach (StoreLocation storeLocation in (StoreLocation[])Enum.GetValues(typeof(StoreLocation)))
110+
// On Windows we can use the .NET API to iterate through all the stores.
111+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
105112
{
106-
foreach (StoreName storeName in (StoreName[])Enum.GetValues(typeof(StoreName)))
113+
foreach (StoreLocation storeLocation in (StoreLocation[])Enum.GetValues(typeof(StoreLocation)))
107114
{
108-
try
115+
foreach (StoreName storeName in (StoreName[])Enum.GetValues(typeof(StoreName)))
109116
{
110-
X509Store store = new X509Store(storeName, storeLocation);
111-
store.Open(OpenFlags.ReadOnly);
117+
try
118+
{
119+
X509Store store = new X509Store(storeName, storeLocation);
120+
store.Open(OpenFlags.ReadOnly);
112121

113-
foreach (X509Certificate2 certificate in store.Certificates)
122+
foreach (X509Certificate2 certificate in store.Certificates)
123+
{
124+
Write(storeLocation, storeName, certificate);
125+
}
126+
store.Close();
127+
}
128+
catch (Exception e)
114129
{
115-
Write(storeLocation, storeName, certificate);
130+
Log.Debug(e.StackTrace);
131+
Log.Debug(e.GetType().ToString());
132+
Log.Debug(e.Message);
133+
Telemetry.TrackTrace(Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Error, e);
116134
}
117-
store.Close();
118135
}
119-
catch (Exception e)
136+
}
137+
}
138+
// On linux we check the central trusted root store (a folder), which has symlinks to actual cert locations scattered across the db
139+
// We list all the certificates and then create a new X509Certificate2 object for each by filename.
140+
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
141+
{
142+
var runner = new ExternalCommandRunner();
143+
144+
var result = runner.RunExternalCommand("ls", new string[] { "/etc/ssl/certs", "-A" });
145+
Log.Debug("{0}", result);
146+
147+
foreach (var _line in result.Split('\n'))
148+
{
149+
Log.Debug("{0}",_line);
150+
try
151+
{
152+
X509Certificate2 cert = new X509Certificate2("/etc/ssl/certs/" + _line);
153+
Write(StoreLocation.LocalMachine, StoreName.Root, cert);
154+
}
155+
catch(Exception e)
120156
{
121-
Log.Debug(e.StackTrace);
122-
Log.Debug(e.GetType().ToString());
123-
Log.Debug(e.Message);
124-
Telemetry.TrackTrace(Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Error, e);
157+
Log.Debug("{0} {1} Issue creating certificate based on /etc/ssl/certs/{2}", e.GetType().ToString(), e.Message, _line);
158+
Log.Debug("{0}", e.StackTrace);
159+
125160
}
126161
}
127162
}
163+
// On macos we use the keychain and export the certificates as .pem.
164+
// However, on macos Certificate2 doesn't support loading from a pem,
165+
// so first we need pkcs12s instead, we convert using openssl, which requires we set a password
166+
// we import the pkcs12 with all our certs, delete the temp files and then iterate over it the certs
167+
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
168+
{
169+
var runner = new ExternalCommandRunner();
170+
171+
var result = runner.RunExternalCommand("security", new string[] { "find-certificate", "-ap", "/System/Library/Keychains/SystemRootCertificates.keychain" });
172+
string tmpPath = Path.Combine(Directory.GetCurrentDirectory(), "tmpcert.pem");
173+
string pkPath = Path.Combine(Directory.GetCurrentDirectory(), "tmpcert.pk12");
174+
175+
File.WriteAllText(tmpPath, result);
176+
_ = runner.RunExternalCommand("openssl", new string[] { "pkcs12", "-export", "-nokeys" , "-out", pkPath, "-passout pass:pass", "-in", tmpPath });
177+
178+
X509Certificate2Collection xcert = new X509Certificate2Collection();
179+
xcert.Import(pkPath,"pass",X509KeyStorageFlags.DefaultKeySet);
180+
181+
File.Delete(tmpPath);
182+
File.Delete(pkPath);
183+
184+
var X509Certificate2Enumerator = xcert.GetEnumerator();
185+
186+
while (X509Certificate2Enumerator.MoveNext())
187+
{
188+
Write(StoreLocation.LocalMachine, StoreName.Root, X509Certificate2Enumerator.Current);
189+
}
190+
}
191+
128192
DatabaseManager.Commit();
129193
Stop();
130194
}

0 commit comments

Comments
 (0)