@@ -84,6 +84,44 @@ impl fmt::Display for PIE {
84
84
}
85
85
}
86
86
87
+ /// Fortification status: `Full`, `Partial`, `None` or `Undecidable`
88
+ #[ derive( Debug , Deserialize , Serialize ) ]
89
+ pub enum Fortify {
90
+ Full ,
91
+ Partial ,
92
+ None ,
93
+ Undecidable ,
94
+ }
95
+
96
+ impl fmt:: Display for Fortify {
97
+ #[ cfg( not( feature = "color" ) ) ]
98
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
99
+ write ! (
100
+ f,
101
+ "{:<11}" ,
102
+ match self {
103
+ Self :: Full => "Full" ,
104
+ Self :: Partial => "Partial" ,
105
+ Self :: None => "None" ,
106
+ Self :: Undecidable => "Undecidable" ,
107
+ }
108
+ )
109
+ }
110
+ #[ cfg( feature = "color" ) ]
111
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
112
+ write ! (
113
+ f,
114
+ "{:<11}" ,
115
+ match self {
116
+ Self :: Full => "Full" . green( ) ,
117
+ Self :: Partial => "Partial" . bright_green( ) ,
118
+ Self :: None => "None" . red( ) ,
119
+ Self :: Undecidable => "Undecidable" . yellow( ) ,
120
+ }
121
+ )
122
+ }
123
+ }
124
+
87
125
/// Checksec result struct for ELF32/64 binaries
88
126
///
89
127
/// **Example**
@@ -111,10 +149,11 @@ pub struct CheckSecResults {
111
149
/// Clang SafeStack (*CFLAGS=*`-fsanitize=safe-stack`)
112
150
pub clang_safestack : bool ,
113
151
/// Fortify (*CFLAGS=*`-D_FORTIFY_SOURCE`)
114
- pub fortify : bool ,
152
+ pub fortify : Fortify ,
115
153
/// Fortified functions
116
154
pub fortified : u32 ,
117
- //fortifiable: Option<Vec<OsString>>,
155
+ /// Fortifiable functions
156
+ pub fortifiable : u32 ,
118
157
/// No Execute
119
158
pub nx : bool ,
120
159
/// Position Inpendent Executable (*CFLAGS=*`-pie -fPIE`)
@@ -129,12 +168,20 @@ pub struct CheckSecResults {
129
168
impl CheckSecResults {
130
169
#[ must_use]
131
170
pub fn parse ( elf : & Elf ) -> Self {
171
+ let ( fortified, fortifiable) = elf. has_fortified ( ) ;
172
+ let fortify = match ( fortified, fortifiable) {
173
+ ( 1 .., 0 ) => Fortify :: Full ,
174
+ ( 1 .., 1 ..) => Fortify :: Partial ,
175
+ ( 0 , 1 ..) => Fortify :: None ,
176
+ ( 0 , 0 ) => Fortify :: Undecidable ,
177
+ } ;
132
178
Self {
133
179
canary : elf. has_canary ( ) ,
134
180
clang_cfi : elf. has_clang_cfi ( ) ,
135
181
clang_safestack : elf. has_clang_safestack ( ) ,
136
- fortify : elf. has_fortify ( ) ,
137
- fortified : elf. has_fortified ( ) ,
182
+ fortify,
183
+ fortified,
184
+ fortifiable,
138
185
nx : elf. has_nx ( ) ,
139
186
pie : elf. has_pie ( ) ,
140
187
relro : elf. has_relro ( ) ,
@@ -151,12 +198,13 @@ impl fmt::Display for CheckSecResults {
151
198
write ! (
152
199
f,
153
200
"Canary: {} CFI: {} SafeStack: {} Fortify: {} Fortified: {:2} \
154
- NX: {} PIE: {} Relro: {} RPATH: {} RUNPATH: {}",
201
+ Fortifiable: {:2} NX: {} PIE: {} Relro: {} RPATH: {} RUNPATH: {}",
155
202
self . canary,
156
203
self . clang_cfi,
157
204
self . clang_safestack,
158
205
self . fortify,
159
206
self . fortified,
207
+ self . fortifiable,
160
208
self . nx,
161
209
self . pie,
162
210
self . relro,
@@ -169,17 +217,19 @@ impl fmt::Display for CheckSecResults {
169
217
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
170
218
write ! (
171
219
f,
172
- "{} {} {} {} {} {} {} {} {} {:2} {} {} {} {} {} {} {} {} {} {}" ,
220
+ "{} {} {} {} {} {} {} {} {} {:2} {} {:2} {} { } {} {} {} {} {} {} {} {}" ,
173
221
"Canary:" . bold( ) ,
174
222
colorize_bool!( self . canary) ,
175
223
"CFI:" . bold( ) ,
176
224
colorize_bool!( self . clang_cfi) ,
177
225
"SafeStack:" . bold( ) ,
178
226
colorize_bool!( self . clang_safestack) ,
179
227
"Fortify:" . bold( ) ,
180
- colorize_bool! ( self . fortify) ,
228
+ self . fortify,
181
229
"Fortified:" . bold( ) ,
182
230
self . fortified,
231
+ "Fortifiable:" . bold( ) ,
232
+ self . fortifiable,
183
233
"NX:" . bold( ) ,
184
234
colorize_bool!( self . nx) ,
185
235
"PIE:" . bold( ) ,
@@ -221,10 +271,8 @@ pub trait Properties {
221
271
fn has_clang_safestack ( & self ) -> bool ;
222
272
/// check for symbols ending in `_chk` from dynstrtab
223
273
fn has_fortify ( & self ) -> bool ;
224
- /// counts symbols ending in `_chk` from dynstrtab
225
- fn has_fortified ( & self ) -> u32 ;
226
- // requires running platform to be Linux
227
- //fn has_fortifiable(&self) -> u32;
274
+ /// counts fortified and fortifiable symbols from dynstrtab
275
+ fn has_fortified ( & self ) -> ( u32 , u32 ) ;
228
276
/// check `p_flags` of the `PT_GNU_STACK` ELF header
229
277
fn has_nx ( & self ) -> bool ;
230
278
/// check `d_val` of `DT_FLAGS`/`DT_FLAGS_1` of the `PT_DYN ELF` header
@@ -242,6 +290,94 @@ pub trait Properties {
242
290
fn get_dynstr_by_tag ( & self , tag : u64 ) -> Option < String > ;
243
291
}
244
292
293
+ // readelf -s -W /lib/x86_64-linux-gnu/libc.so.6 | grep _chk
294
+ const FORTIFIABLE_FUNCTIONS : [ & str ; 79 ] = [
295
+ "asprintf" ,
296
+ "confstr" ,
297
+ "dprintf" ,
298
+ "explicit_bzero" ,
299
+ "fdelt" ,
300
+ "fgets" ,
301
+ "fgets_unlocked" ,
302
+ "fgetws" ,
303
+ "fgetws_unlocked" ,
304
+ "fprintf" ,
305
+ "fread" ,
306
+ "fread_unlocked" ,
307
+ "fwprintf" ,
308
+ "getcwd" ,
309
+ "getdomainname" ,
310
+ "getgroups" ,
311
+ "gethostname" ,
312
+ "getlogin_r" ,
313
+ "gets" ,
314
+ "getwd" ,
315
+ "longjmp" ,
316
+ "mbsnrtowcs" ,
317
+ "mbsrtowcs" ,
318
+ "mbstowcs" ,
319
+ "memcpy" ,
320
+ "memmove" ,
321
+ "mempcpy" ,
322
+ "memset" ,
323
+ "obstack_printf" ,
324
+ "obstack_vprintf" ,
325
+ "poll" ,
326
+ "ppoll" ,
327
+ "pread64" ,
328
+ "pread" ,
329
+ "printf" ,
330
+ "ptsname_r" ,
331
+ "read" ,
332
+ "readlinkat" ,
333
+ "readlink" ,
334
+ "realpath" ,
335
+ "recv" ,
336
+ "recvfrom" ,
337
+ "snprintf" ,
338
+ "sprintf" ,
339
+ "stpcpy" ,
340
+ "stpncpy" ,
341
+ "strcat" ,
342
+ "strcpy" ,
343
+ "strncat" ,
344
+ "strncpy" ,
345
+ "swprintf" ,
346
+ "syslog" ,
347
+ "ttyname_r" ,
348
+ "vasprintf" ,
349
+ "vdprintf" ,
350
+ "vfprintf" ,
351
+ "vfwprintf" ,
352
+ "vprintf" ,
353
+ "vsnprintf" ,
354
+ "vsprintf" ,
355
+ "vswprintf" ,
356
+ "vsyslog" ,
357
+ "vwprintf" ,
358
+ "wcpcpy" ,
359
+ "wcpncpy" ,
360
+ "wcrtomb" ,
361
+ "wcscat" ,
362
+ "wcscpy" ,
363
+ "wcsncat" ,
364
+ "wcsncpy" ,
365
+ "wcsnrtombs" ,
366
+ "wcsrtombs" ,
367
+ "wcstombs" ,
368
+ "wctomb" ,
369
+ "wmemcpy" ,
370
+ "wmemmove" ,
371
+ "wmempcpy" ,
372
+ "wmemset" ,
373
+ "wprintf" ,
374
+ ] ;
375
+ /*
376
+ * TODO: static assert that FORTIFIABLE_FUNCTIONS is sorted
377
+ * unstable library feature 'is_sorted':
378
+ * const _: () = assert!(FORTIFIABLE_FUNCTIONS.is_sorted(), "must be sorted for binary search");
379
+ */
380
+
245
381
impl Properties for Elf < ' _ > {
246
382
fn has_canary ( & self ) -> bool {
247
383
for sym in & self . dynsyms {
@@ -286,28 +422,44 @@ impl Properties for Elf<'_> {
286
422
}
287
423
fn has_fortify ( & self ) -> bool {
288
424
for sym in & self . dynsyms {
425
+ if !sym. is_function ( ) {
426
+ continue ;
427
+ }
289
428
if let Some ( name) = self . dynstrtab . get_at ( sym. st_name ) {
290
- if name. ends_with ( "_chk" ) {
429
+ if name. starts_with ( "__" ) && name . ends_with ( "_chk" ) {
291
430
return true ;
292
431
}
293
432
}
294
433
}
295
434
false
296
435
}
297
- fn has_fortified ( & self ) -> u32 {
436
+ fn has_fortified ( & self ) -> ( u32 , u32 ) {
298
437
let mut fortified_count: u32 = 0 ;
438
+ let mut fortifiable_count: u32 = 0 ;
299
439
for sym in & self . dynsyms {
440
+ if !sym. is_function ( ) {
441
+ continue ;
442
+ }
300
443
if let Some ( name) = self . dynstrtab . get_at ( sym. st_name ) {
301
- if name. ends_with ( "_chk" ) {
444
+ if name. starts_with ( "__" ) && name . ends_with ( "_chk" ) {
302
445
fortified_count += 1 ;
446
+ } else if FORTIFIABLE_FUNCTIONS . binary_search ( & name) . is_ok ( ) {
447
+ fortifiable_count += 1 ;
303
448
}
304
449
}
305
450
}
306
- fortified_count
451
+ ( fortified_count, fortifiable_count )
307
452
}
308
453
/*
309
454
// requires running platform to be Linux
310
- fn has_forifiable(&self) -> Option<Vec<OsString>> {
455
+ fn has_fortifiable(&self) -> Vec<String> {
456
+ self.dynsyms
457
+ .iter()
458
+ .filter(goblin::elf::Sym::is_function)
459
+ .filter_map(|sym| self.dynstrtab.get_at(sym.st_name))
460
+ .filter(|func| FORTIFIABLE_FUNCTIONS.binary_search(func).is_ok())
461
+ .map(std::string::ToString::to_string)
462
+ .collect()
311
463
}
312
464
*/
313
465
fn has_nx ( & self ) -> bool {
0 commit comments