1#!/usr/local/bin/perl 2# Report the hardening characterists of a set of binaries. 3# Copyright (C) 2009-2013 Kees Cook <kees@debian.org> 4# License: GPLv2 or newer 5use strict; 6use warnings; 7use Getopt::Long qw(:config no_ignore_case bundling); 8use Pod::Usage; 9use IPC::Open3; 10use Symbol qw(gensym); 11use Term::ANSIColor; 12 13my $skip_pie = 0; 14my $skip_stackprotector = 0; 15my $skip_fortify = 0; 16my $skip_relro = 0; 17my $skip_bindnow = 0; 18my $report_functions = 0; 19my $find_libc_functions = 0; 20my $color = 0; 21my $lintian = 0; 22my $verbose = 0; 23my $debug = 0; 24my $quiet = 0; 25my $help = 0; 26my $man = 0; 27 28GetOptions( 29 "nopie|p+" => \$skip_pie, 30 "nostackprotector|s+" => \$skip_stackprotector, 31 "nofortify|f+" => \$skip_fortify, 32 "norelro|r+" => \$skip_relro, 33 "nobindnow|b+" => \$skip_bindnow, 34 "report-functions|R!" => \$report_functions, 35 "find-libc-functions|F!" => \$find_libc_functions, 36 "color|c!" => \$color, 37 "lintian|l!" => \$lintian, 38 "verbose|v!" => \$verbose, 39 "debug!" => \$debug, 40 "quiet|q!" => \$quiet, 41 "help|h|?" => \$help, 42 "man|H" => \$man, 43 ) or pod2usage(2); 44pod2usage(1) if $help; 45pod2usage(-exitstatus => 0, -verbose => 2, -noperldoc => 1) if $man; 46 47my $overall = 0; 48my $rc = 0; 49my $report = ""; 50my @tags; 51my %libc; 52 53# Report a good test. 54sub good { 55 my ($name, $msg_color, $msg) = @_; 56 $msg_color = colored($msg_color, 'green') if $color; 57 if (defined $msg) { 58 $msg_color .= $msg; 59 } 60 good_msg("$name: $msg_color"); 61} 62sub good_msg($) { 63 my ($msg) = @_; 64 if ($quiet == 0) { 65 $report .= "\n$msg"; 66 } 67} 68 69sub unknown { 70 my ($name, $msg) = @_; 71 $msg = colored($msg, 'yellow') if $color; 72 good_msg("$name: $msg"); 73} 74 75# Report a failed test, possibly ignoring it. 76sub bad($$$$$) { 77 my ($name, $file, $long_name, $msg, $ignore) = @_; 78 79 $msg = colored($msg, 'red') if $color; 80 81 $msg = "$long_name: " . $msg; 82 if ($ignore) { 83 $msg .= " (ignored)"; 84 } 85 else { 86 $rc = 1; 87 if ($lintian) { 88 push(@tags, "$name:$file"); 89 } 90 } 91 $report .= "\n$msg"; 92} 93 94# Safely run list-based command line and return stdout. 95sub output(@) { 96 my (@cmd) = @_; 97 my ($pid, $stdout, $stderr); 98 if ($debug) { 99 print join(" ", @cmd),"\n"; 100 } 101 $stdout = gensym; 102 $stderr = gensym; 103 $pid = open3(gensym, $stdout, $stderr, @cmd); 104 my $collect = ""; 105 while ( <$stdout> ) { 106 $collect .= $_; 107 } 108 waitpid($pid, 0); 109 my $rc = $?; 110 if ($rc != 0) { 111 while ( <$stderr> ) { 112 print STDERR; 113 } 114 return ""; 115 } 116 return $collect; 117} 118 119# Find the libc used in this executable, if any. 120sub find_libc($) { 121 my ($file) = @_; 122 my $ldd = output("ldd", $file); 123 $ldd =~ /^\s*libc\.so\.\S+\s+\S+\s+(\S+)/m; 124 return $1 || ""; 125} 126 127sub find_functions($$) { 128 my ($file, $undefined) = @_; 129 my (%funcs); 130 131 # Catch "NOTYPE" for object archives. 132 my $func_regex = " (I?FUNC|NOTYPE) "; 133 134 my $relocs = output("readelf", "-sW", $file); 135 for my $line (split("\n", $relocs)) { 136 next if ($line !~ /$func_regex/); 137 next if ($undefined && $line !~ /$func_regex.* UND /); 138 139 $line =~ s/ \([0-9]+\)$//; 140 $line =~ s/.* //; 141 $line =~ s/@.*//; 142 $funcs{$line} = 1; 143 } 144 145 return \%funcs; 146} 147 148 149$ENV{'LANG'} = "C"; 150 151if ($find_libc_functions) { 152 pod2usage(1) if (!defined($ARGV[0])); 153 my $libc_path = find_libc($ARGV[0]); 154 155 my $funcs = find_functions($libc_path, 0); 156 for my $func (sort(keys(%{$funcs}))) { 157 if ($func =~ /^__(\S+)_chk$/) { 158 print " '$1' => 1,\n"; 159 } 160 } 161 exit(0); 162} 163#die "List of libc functions not defined!" if (scalar(keys %libc) < 1); 164 165my $name; 166foreach my $file (@ARGV) { 167 $rc = 0; 168 my $elf = 1; 169 170 $report = "$file:"; 171 @tags = (); 172 173 # Get program headers. 174 my $PROG_REPORT=output("readelf", "-lW", $file); 175 if (length($PROG_REPORT) == 0) { 176 $overall = 1; 177 next; 178 } 179 180 # Get ELF headers. 181 my $DYN_REPORT=output("readelf", "-dW", $file); 182 183 # Get list of all symbols needing external resolution. 184 my $functions = find_functions($file, 1); 185 186 # PIE 187 # First, verify this is an executable, not a library. This seems to be 188 # best seen by checking for the PHDR program header. 189 $name = " Position Independent Executable"; 190 $PROG_REPORT =~ /^Elf file type is (\S+)/m; 191 my $elftype = $1 || ""; 192 if ($elftype eq "DYN") { 193 if ($PROG_REPORT =~ /^ *\bPHDR\b/m) { 194 # Executable, DYN ELF type. 195 good($name, "yes"); 196 } 197 else { 198 # Shared library, DYN ELF type. 199 good($name, "no, regular shared library (ignored)"); 200 } 201 } 202 elsif ($elftype eq "EXEC") { 203 # Executable, EXEC ELF type. 204 bad("no-pie", $file, $name, 205 "no, normal executable!", $skip_pie); 206 } 207 else { 208 $elf = 0; 209 # Is this an ar file with objects? 210 open(AR, "<$file"); 211 my $header = <AR>; 212 close(AR); 213 if ($header eq "!<arch>\n") { 214 good($name, "no, object archive (ignored)"); 215 } 216 else { 217 # ELF type is neither DYN nor EXEC. 218 bad("unknown-elf", $file, $name, 219 "not a known ELF type!? ($elftype)", 0); 220 } 221 } 222 223 # Stack-protected 224 $name = " Stack protected"; 225 if (defined($functions->{'__stack_chk_fail'}) || 226 (!$elf && defined($functions->{'__stack_chk_fail_local'}))) { 227 good($name, "yes") 228 } 229 else { 230 bad("no-stackprotector", $file, $name, 231 "no, not found!", $skip_stackprotector); 232 } 233 234 # Fortified Source 235 $name = " Fortify Source functions"; 236 my @unprotected; 237 my @protected; 238 for my $name (keys(%libc)) { 239 if (defined($functions->{$name})) { 240 push(@unprotected, $name); 241 } 242 if (defined($functions->{"__${name}_chk"})) { 243 push(@protected, $name); 244 } 245 } 246 if ($#protected > -1) { 247 if ($#unprotected == -1) { 248 # Certain. 249 good($name, "yes"); 250 } 251 else { 252 # Vague, due to possible compile-time optimization, 253 # multiple linkages, etc. Assume "yes" for now. 254 good($name, "yes", " (some protected functions found)"); 255 } 256 } 257 else { 258 if ($#unprotected == -1) { 259 unknown($name, "unknown, no protectable libc functions used"); 260 } 261 else { 262 # Vague, since it's possible to have the compile-time 263 # optimizations do away with them, or be unverifiable 264 # at runtime. Assume "no" for now. 265 bad("no-fortify-functions", $file, $name, 266 "no, only unprotected functions found!", $skip_fortify); 267 } 268 } 269 if ($verbose) { 270 for my $name (@unprotected) { 271 good_msg("\tunprotected: $name"); 272 } 273 for my $name (@protected) { 274 good_msg("\tprotected: $name"); 275 } 276 } 277 278 # Format 279 # Unfortunately, I haven't thought of a way to test for this after 280 # compilation. What it really needs is a lintian-like check that 281 # reviews the build logs and looks for the warnings, or that the 282 # argument is changed to use -Werror=format-security to stop the build. 283 284 # RELRO 285 $name = " Read-only relocations"; 286 if ($PROG_REPORT =~ /^ *\bGNU_RELRO\b/m) { 287 good($name, "yes"); 288 } else { 289 if ($elf) { 290 bad("no-relro", $file, $name, "no, not found!", $skip_relro); 291 } else { 292 good($name, "no", ", non-ELF (ignored)"); 293 } 294 } 295 296 # BIND_NOW 297 # This marking keeps changing: 298 # 0x0000000000000018 (BIND_NOW) 299 # 0x000000006ffffffb (FLAGS) Flags: BIND_NOW 300 # 0x000000006ffffffb (FLAGS_1) Flags: NOW 301 302 $name = " Immediate binding"; 303 if ($DYN_REPORT =~ /^\s*\S+\s+\(BIND_NOW\)/m || 304 $DYN_REPORT =~ /^\s*\S+\s+\(FLAGS\).*\bBIND_NOW\b/m || 305 $DYN_REPORT =~ /^\s*\S+\s+\(FLAGS_1\).*\bNOW\b/m) { 306 good($name, "yes"); 307 } else { 308 if ($elf) { 309 bad("no-bindnow", $file, $name, "no, not found!", $skip_bindnow); 310 } else { 311 good($name, "no", ", non-ELF (ignored)"); 312 } 313 } 314 315 if (!$lintian && (!$quiet || $rc != 0)) { 316 print $report,"\n"; 317 } 318 319 if ($report_functions) { 320 for my $name (keys(%{$functions})) { 321 print $name,"\n"; 322 } 323 } 324 325 if (!$lintian && $rc) { 326 $overall = $rc; 327 } 328 329 if ($lintian) { 330 for my $tag (@tags) { 331 print $tag, "\n"; 332 } 333 } 334} 335 336exit($overall); 337 338__END__ 339 340=pod 341 342=head1 NAME 343 344hardening-check - check binaries for security hardening features 345 346=head1 SYNOPSIS 347 348hardening-check [options] [ELF ...] 349 350Examine a given set of ELF binaries and check for several security hardening 351features, failing if they are not all found. 352 353=head1 DESCRIPTION 354 355This utility checks a given list of ELF binaries for several security 356hardening features that can be compiled into an executable. These 357features are: 358 359=over 8 360 361=item B<Position Independent Executable> 362 363This indicates that the executable was built in such a way (PIE) that 364the "text" section of the program can be relocated in memory. To take 365full advantage of this feature, the executing kernel must support text 366Address Space Layout Randomization (ASLR). 367 368=item B<Stack Protected> 369 370This indicates that there is evidence that the ELF was compiled with the 371L<gcc(1)> option B<-fstack-protector> (e.g. uses B<__stack_chk_fail>). The 372program will be resistant to having its stack overflowed. 373 374When an executable was built without any character arrays being allocated 375on the stack, this check will lead to false alarms (since there is no 376use of B<__stack_chk_fail>), even though it was compiled with the correct 377options. 378 379=item B<Fortify Source functions> 380 381This indicates that the executable was compiled with 382B<-D_FORTIFY_SOURCE=2> and B<-O1> or higher. This causes certain unsafe 383glibc functions with their safer counterparts (e.g. B<strncpy> instead 384of B<strcpy>), or replaces calls that are verifiable at runtime with the 385runtime-check version (e.g. B<__memcpy_chk> insteade of B<memcpy>). 386 387When an executable was built such that the fortified versions of the glibc 388functions are not useful (e.g. use is verified as safe at compile time, or 389use cannot be verified at runtime), this check will lead to false alarms. 390In an effort to mitigate this, the check will pass if any fortified function 391is found, and will fail if only unfortified functions are found. Uncheckable 392conditions also pass (e.g. no functions that could be fortified are found, or 393not linked against glibc). This function is currently Not supported on FreeBSD. 394 395=item B<Read-only relocations> 396 397This indicates that the executable was build with B<-Wl,-z,relro> to 398have ELF markings (RELRO) that ask the runtime linker to mark any 399regions of the relocation table as "read-only" if they were resolved 400before execution begins. This reduces the possible areas of memory in 401a program that can be used by an attacker that performs a successful 402memory corruption exploit. 403 404=item B<Immediate binding> 405 406This indicates that the executable was built with B<-Wl,-z,now> to have 407ELF markings (BIND_NOW) that ask the runtime linker to resolve all 408relocations before starting program execution. When combined with RELRO 409above, this further reduces the regions of memory available to memory 410corruption attacks. 411 412=back 413 414=head1 OPTIONS 415 416=over 8 417 418=item B<--nopie>, B<-p> 419 420No not require that the checked binaries be built as PIE. 421 422=item B<--nostackprotector>, B<-s> 423 424No not require that the checked binaries be built with the stack protector. 425 426=item B<--nofortify>, B<-f> 427 428No not require that the checked binaries be built with Fority Source. 429 430=item B<--norelro>, B<-r> 431 432No not require that the checked binaries be built with RELRO. 433 434=item B<--nobindnow>, B<-b> 435 436No not require that the checked binaries be built with BIND_NOW. 437 438=item B<--quiet>, B<-q> 439 440Only report failures. 441 442=item B<--verbose>, B<-v> 443 444Report verbosely on failures. 445 446=item B<--report-functions>, B<-R> 447 448After the report, display all external functions needed by the ELF. 449 450=item B<--find-libc-functions>, B<-F> 451 452Instead of the regular report, locate the libc for the first ELF on the 453command line and report all the known "fortified" functions exported by 454libc. Not supported on FreeBSD now. 455 456=item B<--color>, B<-c> 457 458Enable colorized status output. 459 460=item B<--lintian>, B<-l> 461 462Switch reporting to lintian-check-parsable output. 463 464=item B<--debug> 465 466Report some debugging during processing. 467 468=item B<--help>, B<-h>, B<-?> 469 470Print a brief help message and exit. 471 472=item B<--man>, B<-H> 473 474Print the manual page and exit. 475 476=back 477 478=head1 RETURN VALUE 479 480When all checked binaries have all checkable hardening features detected, 481this program will finish with an exit code of 0. If any check fails, the 482exit code with be 1. Individual checks can be disabled via command line 483options. 484 485=head1 AUTHOR 486 487Kees Cook <kees@debian.org> 488 489=head1 COPYRIGHT AND LICENSE 490 491Copyright 2009-2013 Kees Cook <kees@debian.org>. 492 493This program is free software; you can redistribute it and/or modify it 494under the terms of the GNU General Public License as published by the 495Free Software Foundation; version 2 or later. 496 497=head1 SEE ALSO 498 499L<gcc(1)>, L<hardening-wrapper(1)> 500 501=cut 502