Code Monkey home page Code Monkey logo

checksec.rs's People

Contributors

0xricksanchez avatar cgzones avatar etke avatar marcograss avatar tnballo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

checksec.rs's Issues

Table or more readable output

It would be nice to have a more readable output for scanning directories/multiple binaries.

Some ideas:

  • If a given mitigation is not enabled in all binaries, list it only in a summary that all binaries lack those mitigations
  • Display output in a table, so that column headers would be mitigations and rows would be values
  • Consider adding a prettified json output

Rpath RW

The TODO section of the ReadMe mentions Rpath RW, probably taken from the output of checksec.sh.
From an ELF perspective RPATH and RUNPATH have no RW(read-write) attribute.
checksec.sh checks whether the respective directory is writable, which is not that interesting, since it is fine for /usr/lib/foo to be writable by root.
One could maybe divide R(UN)PATH into some sections:

  • relative path (dangerous)
  • absolute path or $ORIGIN, and the directory is "secure" (i.e. itself and all its parents are owned by root:root and not writable by others)
  • absolute path or $ORIGIN, and the directory is not "secure"

clang CFI check fails

Observation

It seems that the clang CFI check is wrongly implemented, as it only iterates over the ELF .dynsym section and looks for the ".cfi" string. However, based on my dummy binaries the hardened *.cfi functions are located in the .symtab section.

Testcase

readelf -s cfi_icall                                                                                                                      bin -> primary !

Symbol table '.dynsym' contains 8 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterT[...]
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [...]@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [...]@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@GLIBC_2.2.5 (2)
     6: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMC[...]
     7: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND [...]@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 53 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS abi-note.c
     2: 0000000000000358    32 OBJECT  LOCAL  DEFAULT    3 __abi_tag
     3: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS init.c
     4: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
     5: 0000000000001080     0 FUNC    LOCAL  DEFAULT   14 deregister_tm_clones
     6: 00000000000010b0     0 FUNC    LOCAL  DEFAULT   14 register_tm_clones
     7: 00000000000010f0     0 FUNC    LOCAL  DEFAULT   14 __do_global_dtors_aux
     8: 0000000000004058     1 OBJECT  LOCAL  DEFAULT   25 completed.0
     9: 0000000000003de0     0 OBJECT  LOCAL  DEFAULT   20 __do_global_dtor[...]
    10: 0000000000001140     0 FUNC    LOCAL  DEFAULT   14 frame_dummy
    11: 0000000000003dd8     0 OBJECT  LOCAL  DEFAULT   19 __frame_dummy_in[...]
    12: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS
    13: 0000000000001438    24 FUNC    LOCAL  DEFAULT   14 bad_int_arg
    14: 0000000000001310    57 FUNC    LOCAL  DEFAULT   14 bad_int_arg.cfi
    15: 0000000000004038    32 OBJECT  LOCAL  DEFAULT   24 f
    16: 0000000000001350    65 FUNC    LOCAL  DEFAULT   14 float_arg
    17: 0000000000001430    24 FUNC    LOCAL  DEFAULT   14 int_arg
    18: 00000000000012e0    43 FUNC    LOCAL  DEFAULT   14 int_arg.cfi
    19: 0000000000001440    24 FUNC    LOCAL  DEFAULT   14 not_entry_point
    20: 00000000000013a0   137 FUNC    LOCAL  DEFAULT   14 not_entry_point.cfi
   [...]

Expected behavior

$ checksec -f cfi_icall                                                                                                                   
ELF64: | Canary: false CFI: **true** SafeStack: false Fortify: false Fortified:  0 NX: true  PIE: Full Relro: Partial RPATH: None RUNPATH: None | File: cfi_icall

Actual behavior

$ checksec -f cfi_icall                                                                                                                   
ELF64: | Canary: false CFI: **false** SafeStack: false Fortify: false Fortified:  0 NX: true  PIE: Full Relro: Partial RPATH: None RUNPATH: None | File: cfi_icall

Fix

Just replace the iterator over the dynsyms with an iterator over the .syms

Issues with has_arc() and has_canary() Functions Not Detecting Symbols in Mach-O Files

Environment

  • Rust version: rustc 1.73.0 (cc66ad468 2023-10-03)
  • Goblin version: goblin = "0.5.2"
  • Operating System and version: Apple M2 Pro, macOS Sonoma 14.1

Description

I've encountered a potential issue with the has_arc() and has_canary() functions in the macho.rs file. These functions are intended to check for the presence of specific keywords in the imports of a Mach-O binary: _objc_release for has_arc(), which should return true if ARC is being used, and either ___stack_chk_fail or ___stack_chk_guard for has_canary(), which should return true if stack protection is enabled.

Expected Behavior

When provided with a Mach-O binary that has ARC and stack canaries properly applied, the has_arc() function is expected to return true if the _objc_release symbol is present

$ nm ~/Desktop/HelloWorld | grep _objc_release
                 U _objc_release

and has_canary() should return true if stack protection symbols are present.

$  nm ~/Desktop/hello_canary
                 U ___stack_chk_fail
                 U ___stack_chk_guard
0000000100000000 T __mh_execute_header
0000000100003f2c T _main
                 U _read

Actual Behavior

Despite supplying a Mach-O file that I've verified to have ARC enabled, the has_arc() function returned false. Upon further inspection, it appears that the list of imports retrieved within the function is empty. This same behavior is observed with the has_canary() function, suggesting that both functions may consistently return false regardless of the actual contents of the Mach-O binary.

$ ./checksec -f ~/Desktop/hello_canary
MachO64: | 
ARC: false 
Canary: false 
...
 | File: /Users/cpuu/Desktop/hello_canary

Investigation

To further investigate the issue, I added additional print statements to the has_arc() function to trace the flow and check where it might be failing. Below is the modified version of the has_arc() function:

fn has_arc(&self) -> bool {
    println!("Starting has_arc check...");

    match self.imports() {
        Ok(imports) => {
            println!("Successfully retrieved imports. Number of imports: {}", imports.len());
            if imports.is_empty() {
                println!("No imports found. Exiting check.");
            }

            for import in &imports {
                println!("Import name: {}", import.name);
                if import.name == "_objc_release" {
                    println!("Found '_objc_release' import. ARC is used. Exiting check.");
                    return true;
                }
            }

            println!("'_objc_release' import not found. ARC is not used.");
            false
        },
        Err(e) => {
            println!("Error while getting imports: {:?}", e);
            false
        }
    }
}

When running the above code with a Mach-O binary that is known to have ARC enabled, the output was as follows:

Starting has_arc check...
Successfully retrieved imports. Number of imports: 0
No imports found. Exiting check.
'_objc_release' import not found. ARC is not used.

This output indicates that the imports() call is successful, but it retrieves an empty list of imports, leading to the function incorrectly reporting that ARC is not used. Given that I have verified the binary has ARC enabled through other means, this result seems to be inaccurate.

Additional Information

I have verified the presence of ARC and stack canaries in my Mach-O binary using both otool and nm command-line tools, which show the expected symbols. This leads me to believe there might be a discrepancy in how the macho.rs file handles the parsing or detection of these symbols.

I am wondering if there could be an underlying issue with how imports are being retrieved or if there's an assumption made by the checker that doesn't hold true for all Mach-O binaries.

Any insights or suggestions on this matter would be greatly appreciated.

Stack canaries in Swift vs. Objective-C

checksec.rs seems to be the only checksec tool that also checks Mach-O files. I stumbled upon this tool due to this check. However, the stack canaries check looks up symbols related to Objective-C code, which is also suggested in guides such as the Mobile Security Testing Guide. According to what I read on the Internet, Swift code has a per default built-in stack protection. For Swift libraries, the stack canary is shown to be disabled. It is misleading, but I don't know how to implement a better check. Maybe the tool could call them ObjC Stack canaries to be a bit more specific. Maybe the tool could add a heuristic check, if the library is Swift-based, Objective-C-based or mixed. Maybe it is more clear than.

Include more test binaries

It would be convenient for development to have more binaries to test in test/binaries, especially some PE and MachO ones.

Installation problem

error: attributes are not yet allowed on if expressions

--> /home/User/.cargo/registry/src/github.com-1ecc6299db9ec823/sysinfo-0.14.5/src/utils.rs:27:5
|
27 | #[allow(clippy::identity_conversion)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

error: could not compile sysinfo.
warning: build failed, waiting for other jobs to finish...
error: build failed

Same error above is thrown when I tried to build/install using cargo build --release and cargo install checksec

I am using cargo v1.41.0, Ubuntu 18.04 x86_64.

Bump `goblin` version to v0.5 and release on crates.io?

Hey! Would it be possible to bump checksec's goblin dependency to the latest version (v0.5) and release on crates.io?

Currently get an error when attempting to use goblin v0.5 and checksec v0.0.8 (with goblin v0.2) simultaneously:

error[E0308]: mismatched types
   --> src/cli/cli.rs:197:76
    |
197 |                         CustomElfCheckSecResults(ElfCheckSecResults::parse(&elf))
    |                                                                            ^^^^ expected struct `goblin::elf::Elf`, found a different struct `goblin::elf::Elf`
    |
    = note: expected reference `&goblin::elf::Elf<'_>` (struct `goblin::elf::Elf`)
               found reference `&goblin::elf::Elf<'_>` (struct `goblin::elf::Elf`)
    = note: perhaps two different versions of crate `goblin` are being used?

Thank you for your work on this crate.

State of project ?

Hi, I would like to know if this project is discontinued or just not maintained anymore ? Maybe that should be mentioned in the README.md

Support color output as a run-time option instead of compile-time configuration?

This issue is for an extremely specific quirk that isn’t really a problem in practice, but bringing it to your attention on the off chance other library API users ran into the same thing.

Currently use this crate as a dependency in a CLI tool, the tool accepts a --no-color flag to turn off colored output. Original rationale was to support UNIX piping, assuming ANSI escape sequences would cause issues. But it turns out the colored crate uses the atty crate to automatically detect when a file descriptor isn’t a terminal and disable coloring. This means checksec has no problem at all with UNIX piping if colored output is enabled at compile-time.

Regardless, I’m slightly annoyed that I can’t get checksec to toggle color at runtime based on a parameter. It’s technically possible if I re-implement all formatting rules for every field member using the “new type pattern” (which I already half do here to get multi-line output), but that’s a bit painful.

Do you think it’s worth supporting coloring as a run-time option instead of compile-time configuration?

checksec.rs unable to parse regular ELF file

Downloaded and compiled latest checksec.rs

$ pwd
/root/CS/checksec.rs
$ 
$ target/debug/checksec --version
checksec 0.0.8
$ 

Compiled a sample C binary

$ 
$ cat ~/CS/hello.c 
#include<stdio.h>

int main() {
    printf("Hello World\n");
    return 0;
}
$ 
$ gcc ~/CS/hello.c -o ~/CS/hello
$ 
$ file ~/CS/hello
/root/CS/hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=3fa10253da53dcc98a69539b2486e4ed1c620171, not stripped
$ 
$ ~/CS/hello 
Hello World
$ 

Tried using check.rs against this binary, got the following parsing error

$ 
$ target/debug/checksec --file ~/CS/hello
Can not parse binary file /root/CS/hello: Malformed entity: Section 122 size (4195304) + addr (96) is out of bounds. Overflowed: false
$

More details about the ELF file below, for context I am using a RHEL8 machine for this example

$ 
$ readelf -h ~/CS/hello
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x4004a0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          15512 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         30
  Section header string table index: 29
$ 

GCC version used

gcc (GCC) 8.4.1 20200928

Strangely everything works fine for existing system binaries but not for freshly compiled binaries,
Which makes me wonder whats the case default gcc compilation of a simple binary (not special flags given)

$ ./target/debug/checksec -f /usr/bin/ls
ELF64: | Canary: true  CFI: false SafeStack: false Fortify: true  Fortified:  5 NX: true  PIE: Full Relro: Partial RPATH: None RUNPATH: None | File: /usr/bin/ls
$ 
$ ./target/debug/checksec -f /usr/bin/ps
ELF64: | Canary: true  CFI: false SafeStack: false Fortify: true  Fortified:  5 NX: true  PIE: Full Relro: Partial RPATH: None RUNPATH: None | File: /usr/bin/ps
$ 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.