-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Grant Murphy
committed
Nov 22, 2012
1 parent
de03578
commit 479d09a
Showing
4 changed files
with
261 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,12 @@ | ||
checksec | ||
======== | ||
|
||
Elf checksec in Go. | ||
This is a port of the checksec.sh script from coredump.cx | ||
to Go. It ain't pretty but works ok. I wrote this because | ||
readelf doesn't seem to like receiving binary files via | ||
stdin. Without this it would mean that I have to install | ||
every single package of Fedora for example to extract the | ||
binary hardening information. (Use with script + *.iso | ||
or reposync). | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
package main | ||
|
||
//#include "os_spec.h" | ||
import "C" | ||
|
||
import ( | ||
"bytes" | ||
"debug/elf" | ||
"encoding/binary" | ||
"fmt" | ||
"io" | ||
"os" | ||
) | ||
|
||
const ( | ||
IS_GOOD = "(good)" | ||
IS_OK = "(alright)" | ||
IS_BAD = "(bad)" | ||
DISABLED = "Disabled" | ||
ENABLED = "Enabled" | ||
PARTIAL = "Partial" | ||
NX = "NX" | ||
CANARY = "CANARY" | ||
RELRO = "RELRO" | ||
RPATH = "RPATH" | ||
RUNPATH = "RUNPATH" | ||
PIE = "PIE" | ||
SEP = "," | ||
STACK_CHK = "__stack_chk_fail" | ||
) | ||
|
||
func dyn(s *elf.Section, tag elf.DynTag) *elf.Dyn64 { | ||
|
||
var entry elf.Dyn64 | ||
sectionReader := io.NewSectionReader(s, 0, int64(s.Size)) | ||
for { | ||
|
||
err := binary.Read(sectionReader, binary.LittleEndian, &entry) | ||
if err != nil { | ||
if err != io.EOF { | ||
fmt.Println(err) | ||
} | ||
break | ||
} | ||
|
||
if elf.DynTag(entry.Tag) == tag { | ||
return &entry | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func nx(progs []*elf.Prog) string { | ||
|
||
rv := ENABLED | ||
for _, prog := range progs { | ||
|
||
if int64(prog.Type) == int64(C.GNU_STACK) { | ||
if prog.Flags&elf.PF_X == elf.PF_X { | ||
rv = DISABLED | ||
} | ||
} | ||
} | ||
return rv | ||
} | ||
|
||
func canary(symbols []elf.Symbol) string { | ||
|
||
rv := DISABLED | ||
for _, sym := range symbols { | ||
|
||
if bytes.HasPrefix([]byte(sym.Name), []byte(STACK_CHK)) { | ||
rv = ENABLED | ||
break | ||
} | ||
} | ||
return rv | ||
} | ||
|
||
func relro(progs []*elf.Prog, dynamic *elf.Section) string { | ||
|
||
haveRelro := false | ||
haveBindNow := false | ||
|
||
for _, prog := range progs { | ||
if int64(prog.Type) == int64(C.GNU_RELRO) { | ||
haveRelro = true | ||
break | ||
} | ||
} | ||
|
||
haveBindNow = (dyn(dynamic, elf.DT_BIND_NOW) != nil) | ||
|
||
if haveBindNow && haveRelro { | ||
return ENABLED | ||
} | ||
|
||
if haveRelro { | ||
return PARTIAL | ||
} | ||
|
||
return DISABLED | ||
|
||
} | ||
|
||
func printResult(expected, actual, name string) { | ||
|
||
fmt.Printf("%s=%s", name, actual) | ||
if expected != actual { | ||
|
||
if actual == PARTIAL { | ||
fmt.Print(IS_OK) | ||
|
||
} else { | ||
fmt.Print(IS_BAD) | ||
} | ||
|
||
} else { | ||
fmt.Print(IS_GOOD) | ||
} | ||
} | ||
|
||
func checksec(file *elf.File) { | ||
|
||
var status string | ||
|
||
dynamic := file.Section(".dynamic") | ||
symbols, _ := file.Symbols() | ||
|
||
// NX Enabled | ||
status = nx(file.Progs) | ||
printResult(ENABLED, status, NX) | ||
fmt.Print(SEP) | ||
|
||
// Stack protection enabled | ||
status = canary(symbols) | ||
printResult(ENABLED, status, CANARY) | ||
fmt.Print(SEP) | ||
|
||
// RELRO | ||
status = relro(file.Progs, dynamic) | ||
printResult(ENABLED, status, RELRO) | ||
fmt.Print(SEP) | ||
|
||
// PIE | ||
status = DISABLED | ||
if file.Type == elf.ET_DYN { | ||
status = ENABLED | ||
} | ||
printResult(ENABLED, status, PIE) | ||
fmt.Print(SEP) | ||
|
||
// RPATH | ||
status = DISABLED | ||
if rpath := dyn(dynamic, elf.DT_RPATH); rpath != nil { | ||
status = ENABLED | ||
} | ||
printResult(DISABLED, status, RPATH) | ||
fmt.Print(SEP) | ||
|
||
// RUNPATH | ||
status = DISABLED | ||
if runpath := dyn(dynamic, elf.DT_RUNPATH); runpath != nil { | ||
status = ENABLED | ||
} | ||
printResult(DISABLED, status, RUNPATH) | ||
fmt.Println() | ||
} | ||
|
||
func main() { | ||
|
||
// cat <bin> | ./a.out | ||
if len(os.Args) == 1 { | ||
|
||
var buf bytes.Buffer | ||
var kb [1024]byte | ||
for { | ||
|
||
nbytes, err := os.Stdin.Read(kb[:]) | ||
if nbytes > 0 { | ||
buf.Write(kb[0:nbytes]) | ||
} | ||
if err == io.EOF { | ||
break | ||
} | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
data := buf.Bytes() | ||
if len(data) > 0 { | ||
file, e := elf.NewFile(bytes.NewReader(data)) | ||
if e != nil { | ||
fmt.Println(e) | ||
os.Exit(1) | ||
} | ||
checksec(file) | ||
} | ||
|
||
// FILE [FILE]* | ||
} else { | ||
|
||
for _, arg := range os.Args[1:] { | ||
|
||
file, e := elf.Open(arg) | ||
if e != nil { | ||
fmt.Println(e) | ||
os.Exit(1) | ||
} | ||
checksec(file) | ||
file.Close() | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* So you probably need this for things to work */ | ||
#ifndef os_spec_h | ||
#define os_spec_h | ||
|
||
#include <sys/types.h> | ||
#include <elf.h> | ||
|
||
int64_t GNU_RELRO = PT_GNU_RELRO; | ||
int64_t GNU_STACK = PT_GNU_STACK; | ||
|
||
#endif /* os_spec_h */ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#!/bin/bash | ||
# Produces a list of all setuid executables installed on a | ||
# distribution and checks if they have been built using PIE. | ||
|
||
for file in "$@"; do | ||
for data in `rpm -qp --queryformat '[%{NAME} %{FILEMODES:perms} %{FILENAMES}\n]' $file | grep -E "^.* -..x..x..x " | awk '{ print sprintf("%s:%s", $1, $3)}'`; do | ||
|
||
split=`echo $data | sed -e 's/\:/ /'` | ||
set -- $split | ||
echo $2 | grep -E "/lib.*" >> /dev/null | ||
is_lib=$? | ||
echo $2 | grep -E "/usr/share.*" >> /dev/null | ||
is_shared=$? | ||
|
||
if test $is_shared -eq 1 && test $is_lib -eq 1 && test -x $2 && test -f $2; then | ||
rv=`rpm2cpio $file | cpio --to-stdout -iv .$2 2>/dev/null | ./checksec` | ||
if test $? -eq 0; then | ||
echo $(basename $file),$2,$rv | ||
fi | ||
fi | ||
done | ||
done |