13
13
using Newtonsoft . Json . Serialization ;
14
14
using Serilog ;
15
15
using AttackSurfaceAnalyzer . ObjectTypes ;
16
+ using System . Text . RegularExpressions ;
17
+ using System . Text ;
16
18
17
19
namespace AttackSurfaceAnalyzer . Collectors . Certificates
18
20
{
@@ -42,7 +44,7 @@ public void Truncate(string runid)
42
44
43
45
public override bool CanRunOnPlatform ( )
44
46
{
45
- return RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ;
47
+ return RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) || RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) || RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ;
46
48
}
47
49
48
50
public void Write ( StoreLocation storeLocation , StoreName storeName , X509Certificate2 obj )
@@ -64,7 +66,7 @@ public void Write(StoreLocation storeLocation, StoreName storeName, X509Certific
64
66
}
65
67
else
66
68
{
67
- cmd . Parameters . AddWithValue ( "@pkcs12" , obj . Export ( X509ContentType . Pkcs12 ) ) ;
69
+ cmd . Parameters . AddWithValue ( "@pkcs12" , obj . Export ( X509ContentType . Pfx ) ) ;
68
70
}
69
71
70
72
cmd . Parameters . AddWithValue ( "@row_key" , CryptoHelpers . CreateHash ( runId + recordCounter ) ) ;
@@ -76,7 +78,6 @@ public void Write(StoreLocation storeLocation, StoreName storeName, X509Certific
76
78
CertificateHashString = obj . GetCertHashString ( ) ,
77
79
Subject = obj . Subject
78
80
} ;
79
-
80
81
cmd . Parameters . AddWithValue ( "@serialized" , JsonConvert . SerializeObject ( cert ) ) ;
81
82
cmd . ExecuteNonQuery ( ) ;
82
83
}
@@ -89,6 +90,11 @@ public void Write(StoreLocation storeLocation, StoreName storeName, X509Certific
89
90
Log . Warning ( e . Message ) ;
90
91
//This catches duplicate certificates
91
92
}
93
+ catch ( Exception e )
94
+ {
95
+ Log . Warning ( e . GetType ( ) . ToString ( ) ) ;
96
+ Log . Warning ( e . StackTrace ) ;
97
+ }
92
98
}
93
99
94
100
public override void Execute ( )
@@ -101,30 +107,88 @@ public override void Execute()
101
107
Start ( ) ;
102
108
Truncate ( runId ) ;
103
109
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 ) )
105
112
{
106
- foreach ( StoreName storeName in ( StoreName [ ] ) Enum . GetValues ( typeof ( StoreName ) ) )
113
+ foreach ( StoreLocation storeLocation in ( StoreLocation [ ] ) Enum . GetValues ( typeof ( StoreLocation ) ) )
107
114
{
108
- try
115
+ foreach ( StoreName storeName in ( StoreName [ ] ) Enum . GetValues ( typeof ( StoreName ) ) )
109
116
{
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 ) ;
112
121
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 )
114
129
{
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 ) ;
116
134
}
117
- store . Close ( ) ;
118
135
}
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 )
120
156
{
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
+
125
160
}
126
161
}
127
162
}
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
+
128
192
DatabaseManager . Commit ( ) ;
129
193
Stop ( ) ;
130
194
}
0 commit comments