├── .editorconfig ├── .git-hooks ├── post-checkout ├── post-commit ├── post-merge ├── pre-commit └── pre-push ├── .github ├── ISSUE_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── codeql.yml │ └── push.yml ├── .gitignore ├── .goreleaser.yml ├── .pre-commit-config.yaml ├── ChangeLog ├── Dockerfile ├── Dockerfile.ubuntu ├── LICENSE ├── Makefile ├── README.md ├── checksec.bash ├── cmd ├── dir.go ├── file.go ├── fortifyFile.go ├── fortifyProc.go ├── kernel.go ├── proc.go ├── procAll.go ├── procLibs.go └── root.go ├── docs ├── _config.yml └── index.md ├── extras ├── man │ └── checksec.1 └── rpm │ └── checksec.spec ├── go.mod ├── go.sum ├── hack └── enable-git-hooks.sh ├── kernel_configs ├── Dockerfile ├── build_kernel_configs.sh ├── configs │ ├── config-3.1.1 │ ├── config-3.10.1 │ ├── config-3.11.1 │ ├── config-3.12.1 │ ├── config-3.13.1 │ ├── config-3.14.1 │ ├── config-3.15.1 │ ├── config-3.16.1 │ ├── config-3.17.1 │ ├── config-3.18.1 │ ├── config-3.2.1 │ ├── config-3.3.1 │ ├── config-3.4.1 │ ├── config-3.5.1 │ ├── config-3.6.1 │ ├── config-3.7.1 │ ├── config-3.8.1 │ ├── config-3.9.1 │ ├── config-4.0.1 │ ├── config-4.1.1 │ ├── config-4.10.1 │ ├── config-4.11.1 │ ├── config-4.12.1 │ ├── config-4.13.1 │ ├── config-4.14.1 │ ├── config-4.15.1 │ ├── config-4.16.1 │ ├── config-4.17.1 │ ├── config-4.18.1 │ ├── config-4.19.1 │ ├── config-4.2.1 │ ├── config-4.3.1 │ ├── config-4.4.1 │ ├── config-4.5.1 │ ├── config-4.6.1 │ ├── config-4.7.1 │ ├── config-4.8.1 │ ├── config-4.9.1 │ ├── config-5.1.1 │ ├── config-5.10.1 │ ├── config-5.11.1 │ ├── config-5.12.1 │ ├── config-5.13.1 │ ├── config-5.14.1 │ ├── config-5.15.1 │ ├── config-5.2.1 │ ├── config-5.3.1 │ ├── config-5.4.1 │ ├── config-5.5.1 │ ├── config-5.6.1 │ ├── config-5.7.1 │ ├── config-5.8.1 │ ├── config-5.9.1 │ ├── config-6.1.1 │ ├── config-6.2.1 │ ├── config-6.3.1 │ ├── config-6.4.1 │ ├── config-6.5.1 │ ├── config-6.6.1 │ ├── config-6.7.1 │ └── config-6.8.1 └── docker-compose.yml ├── main.go ├── pkg ├── checksec │ ├── canary.go │ ├── fortify.go │ ├── kernel.go │ ├── nx.go │ ├── pie.go │ ├── relro.go │ ├── rpath.go │ ├── runpath.go │ ├── symbols.go │ └── sysctl.go └── utils │ ├── checks.go │ ├── filePrinter.go │ ├── files.go │ ├── fortifyPrinter.go │ ├── kernelPrinter.go │ └── utils.go └── tests ├── binaries ├── build_binaries.sh ├── fszero.c ├── nolibc.asm ├── nolibc32.asm └── test.c ├── docker-build.sh ├── hardening-checks.sh ├── json-checks.sh ├── kernel.config ├── test-checksec.sh └── xml-checks.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 2 11 | trim_trailing_whitespace = true 12 | -------------------------------------------------------------------------------- /.git-hooks/post-checkout: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # The current repo root 3 | CURRENT_REPO=$(git rev-parse --show-toplevel) 4 | if [[ -f $CURRENT_REPO/.gitlfs ]]; then 5 | command -v git-lfs > /dev/null 2>&1 || { 6 | echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-commit.\n" 7 | exit 2 8 | } 9 | git lfs post-checkout "$@" 10 | fi 11 | -------------------------------------------------------------------------------- /.git-hooks/post-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # The current repo root 3 | CURRENT_REPO=$(git rev-parse --show-toplevel) 4 | if [[ -f ${CURRENT_REPO}/.gitlfs ]]; then 5 | command -v git-lfs > /dev/null 2>&1 || { 6 | echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-commit.\n" 7 | exit 2 8 | } 9 | git lfs post-commit "$@" 10 | fi 11 | 12 | "${CURRENT_REPO}"/hack/enable-git-hooks.sh 13 | -------------------------------------------------------------------------------- /.git-hooks/post-merge: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # The current repo root 3 | CURRENT_REPO=$(git rev-parse --show-toplevel) 4 | if [[ -f $CURRENT_REPO/.gitlfs ]]; then 5 | command -v git-lfs > /dev/null 2>&1 || { 6 | echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-commit.\n" 7 | exit 2 8 | } 9 | git lfs post-merge "$@" 10 | fi 11 | -------------------------------------------------------------------------------- /.git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git commit" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | # The git hooks repo 11 | DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)" 12 | REPO_ROOT=$(cd "${DIR}" && git rev-parse --show-toplevel) 13 | # The current repo root, in case the repo is different from the hooks (this allows using a single pre-commit across multiple repos) 14 | CURRENT_REPO=$(git rev-parse --show-toplevel) 15 | 16 | # Redirect output to stderr. 17 | exec 1>&2 18 | 19 | # check to ensure all tools exist 20 | tools=('pre-commit' 'checkov' 'shfmt') 21 | for tool in "${tools[@]}"; do 22 | if ! command -v "${tool}" > /dev/null 2>&1; then 23 | cat << EOF 24 | Error: ${tool} not found 25 | Please install via brew or package manager 26 | 'brew install ${tool}' 27 | or 28 | install required tools 29 | ${tools[*]} 30 | EOF 31 | exit 2 32 | fi 33 | done 34 | 35 | # run pre-commit checks 36 | pre-commit hook-impl --config="${REPO_ROOT}"/.pre-commit-config.yaml --hook-type=pre-commit --hook-dir "${CURRENT_REPO}" -- "$@" 37 | -------------------------------------------------------------------------------- /.git-hooks/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # The current repo root 3 | CURRENT_REPO=$(git rev-parse --show-toplevel) 4 | if [[ -f $CURRENT_REPO/.gitlfs ]]; then 5 | command -v git-lfs > /dev/null 2>&1 || { 6 | echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-commit.\n" 7 | exit 2 8 | } 9 | git lfs pre-push "$@" 10 | fi 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Issue tracker 2 | If any of these values are not included, the issue will be closed and not worked 3 | 4 | ## Issue 5 | 6 | 7 | ## Debug Report 8 | include the output of `checksec --debug_report` 9 | 10 | ## Command run to produce the error 11 | 12 | 13 | 14 | 15 | ## OS version and Kernel version 16 | 17 | 18 | 19 | ## Debug output 20 | 21 | Run the same command as above to reproduce the error but include the --debug flag 22 | 23 | e.x `checksec --debug -f /usr/bin/ls` 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | # Enable version updates for Docker 13 | - package-ecosystem: "docker" 14 | # Look for a `Dockerfile` in the `root` directory 15 | directory: "/" 16 | # Check for updates once a week 17 | schedule: 18 | interval: "weekly" 19 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | branches: [ "main" ] 19 | schedule: 20 | - cron: '26 17 * * 4' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | permissions: 32 | # required for all workflows 33 | security-events: write 34 | 35 | # required to fetch internal or private CodeQL packs 36 | packages: read 37 | 38 | # only required for workflows in private repositories 39 | actions: read 40 | contents: read 41 | 42 | strategy: 43 | fail-fast: false 44 | matrix: 45 | include: 46 | - language: go 47 | build-mode: autobuild 48 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 49 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 50 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 51 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 52 | steps: 53 | - name: Checkout repository 54 | uses: actions/checkout@v4 55 | 56 | # Initializes the CodeQL tools for scanning. 57 | - name: Initialize CodeQL 58 | uses: github/codeql-action/init@v3 59 | with: 60 | languages: ${{ matrix.language }} 61 | build-mode: ${{ matrix.build-mode }} 62 | # If you wish to specify custom queries, you can do so here or in a config file. 63 | # By default, queries listed here will override any specified in a config file. 64 | # Prefix the list here with "+" to use these queries and those in the config file. 65 | 66 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 67 | # queries: security-extended,security-and-quality 68 | 69 | # If the analyze step fails for one of the languages you are analyzing with 70 | # "We were unable to automatically build your code", modify the matrix above 71 | # to set the build mode to "manual" for that language. Then modify this step 72 | # to build your code. 73 | # ℹ️ Command-line programs to run using the OS shell. 74 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 75 | - if: matrix.build-mode == 'manual' 76 | shell: bash 77 | run: | 78 | echo 'If you are using a "manual" build mode for one or more of the' \ 79 | 'languages you are analyzing, replace this with the commands to build' \ 80 | 'your code, for example:' 81 | echo ' make bootstrap' 82 | echo ' make release' 83 | exit 1 84 | 85 | - name: Perform CodeQL Analysis 86 | uses: github/codeql-action/analyze@v3 87 | with: 88 | category: "/language:${{matrix.language}}" 89 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: checksec-release 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: docker build 12 | run: docker build -t slimm609/checksec:latest . 13 | - name: Login to DockerHub 14 | uses: docker/login-action@v1 15 | with: 16 | username: ${{ secrets.DOCKER_USERNAME }} 17 | password: ${{ secrets.DOCKER_PASSWORD }} 18 | - name: Push to Docker Hub 19 | uses: docker/build-push-action@v2 20 | with: 21 | push: true 22 | tags: slimm609/checksec:latest 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | sign-checksec.sh 2 | checksec.crt 3 | checksec.key 4 | tests/binaries/output/ 5 | dist/ 6 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | project_name: checksec 3 | 4 | release: 5 | prerelease: auto 6 | extra_files: 7 | - glob: dist/*.sig 8 | - glob: dist/*.pub 9 | - glob: dist/*.yaml 10 | - glob: dist/*.txt 11 | 12 | env: 13 | - DOCKER_CLI_EXPERIMENTAL=enabled 14 | - CGO_ENABLED=0 15 | 16 | builds: 17 | - id: core 18 | binary: checksec 19 | main: ./main.go 20 | buildmode: "pie" 21 | ldflags: 22 | - -s -w 23 | - -X "main.version={{ .Version }}" 24 | - -X "main.commit={{ .Commit }}" 25 | - -X "main.date={{ .Date }}" 26 | - -extldflags "-Wl,-z,relro,-z,now,-z,noexecstack" 27 | goos: 28 | - linux 29 | - darwin 30 | goarch: 31 | - amd64 32 | - arm64 33 | 34 | sboms: 35 | - id: checksec 36 | documents: 37 | - "${artifact}-{{ .Os }}-{{ .Arch }}.spdx.sbom.json" 38 | cmd: syft 39 | args: ["${artifact}", "--output", "cyclonedx-json=${document}"] 40 | artifacts: binary 41 | 42 | checksum: 43 | name_template: "{{ .ProjectName }}_checksums.sha512" 44 | algorithm: sha512 45 | 46 | report_sizes: true 47 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: local 3 | hooks: 4 | - id: enable_hooks 5 | name: enable git hooks 6 | entry: bash ./hack/enable-git-hooks.sh 7 | language: system 8 | pass_filenames: false 9 | - repo: https://github.com/pre-commit/pre-commit-hooks 10 | rev: v4.5.0 11 | hooks: 12 | - id: end-of-file-fixer 13 | - id: trailing-whitespace 14 | - id: check-merge-conflict 15 | - id: debug-statements 16 | - id: forbid-new-submodules 17 | - id: mixed-line-ending 18 | - repo: https://github.com/fauust/pre-commit-shell 19 | rev: v1.1 20 | hooks: 21 | - id: shfmt 22 | args: ["-sr", "-i", "2", "-ci", "-w"] 23 | - id: shellcheck 24 | files: ^checksec$ 25 | - repo: https://github.com/Lucas-C/pre-commit-hooks 26 | rev: v1.5.5 27 | hooks: 28 | - id: forbid-crlf 29 | - id: remove-crlf 30 | - repo: https://github.com/sirosen/fix-smartquotes 31 | rev: 0.2.0 32 | hooks: 33 | - id: fix-smartquotes 34 | - repo: https://github.com/dnephin/pre-commit-golang 35 | rev: v0.5.1 36 | hooks: 37 | - id: go-fmt 38 | - id: go-vet 39 | # - id: go-lint 40 | - id: go-mod-tidy 41 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | Rev-2024042601 Brian Davis 2 | * Update to 2.7.1 3 | * checksec FORTIFY detection (#236) 4 | Thanks @teoberi 5 | * Fix duplicate entries in fortify count 6 | * update tests 7 | Rev-2024042101 Brian Davis 8 | * Update to 2.7.0 9 | * libc cleanup 10 | * Added github pages 11 | * Check kernel for YAMA 12 | Thanks @cgzones 13 | * Fix Nx checks 14 | * Move github primary branch from master to main 15 | * Update gitattributes 16 | Thanks @calebTree 17 | * Allow setting a libc file 18 | Thanks @sreschke80 19 | * Add tests for listfile 20 | Thanks @ysmaoui 21 | * Fix trailing comma with listfile 22 | Thanks @ysmaoui 23 | * Several shellcheck fixes 24 | Thanks @ysmaoui 25 | * If no header is present, return N/A for Relro and nx 26 | Thanks @petervas 27 | * Remove superfluous -W flags 28 | Thanks @petervas 29 | * Fix broken Nx checks 30 | Thanks @petervas 31 | * change default libc search path from / to /lib 32 | Thanks @petervas 33 | * Cleanup grep 34 | Thanks @petervas 35 | * Rewrite man page in mdoc 36 | Thanks @Artoria2e5 37 | * Remove s_readelf 38 | Thanks @petervas 39 | * Fix stack protection and full relro check 40 | Thanks @petervas 41 | * Add tests suite for hardening checks 42 | Thanks @petervas 43 | * Speed up FS_comparison by 10x 44 | Thanks @Artoria2e5 45 | * Make shellcheck only run on final script 46 | Thanks @Artoria2e5 47 | * Fix debug exit check 48 | Thanks @katexochen 49 | * Use Dynamic Section if there is no symbols table 50 | Thanks @ffontaine 51 | * Check fortify vs fortified 52 | Thanks @azat 53 | Rev-2022052701 Brian Davis 54 | * update to 2.6.0 55 | * fix missing sysctl on fedora 56 | Thanks @spdfnet 57 | * remove extra parenthesis 58 | Thanks @koobs 59 | * add missing libc on LoongArch-64 60 | Thanks @xiaoxiaoafeifei 61 | Rev-2021101001 Brian Davis 62 | * update to 2.5.0 63 | * split checksec into multiple files for easier maintenance and debugging 64 | * remove space between options and only support `=` until refactor can happen 65 | * Add pre-commit-checks 66 | * update License.txt to include BSD license 67 | Thanks @mr-segfault 68 | * Move to new Arch Linux docker images 69 | Thanks @Maryse47 70 | * Add photon support for tests 71 | * Check journalctl -k for NX protection 72 | Thanks @Tatsh 73 | * improve debug formatting 74 | Thanks @bmwiedemann 75 | * Fix shellcheck warnings and style issues 76 | Thanks @a1346054 77 | * Make --dir option follow symlinks 78 | Rev-2020081501 Brian Davis 79 | * checksec.sh: Updated to 2.4.0 80 | * checksec.sh: checksec_automator.sh add check x-pie-executable 81 | Thanks @ja-pa 82 | * checksec.sh: Support for list file modifier 83 | Thanks @dsuarezv 84 | * checksec.sh: Update license 85 | Thanks @mr-segfault 86 | Rev-2020052701 Brian Davis 87 | * checksec.sh: Updated to 2.2.0 88 | * checksec.sh: fix several small issues 89 | Thanks @cgzones 90 | * checksec.sh: add selfrando checks 91 | Thanks @Estella 92 | * checksec.sh: fix json validation 93 | * checksec.sh: added github actions validation tests 94 | * checksec.sh: fix stack protector functions 95 | Thanks @cgzones 96 | * checksec.sh: improve core dump checks 97 | Thanks @cgzones 98 | * checksec.sh: Run readelf in wide mode 99 | Thanks @cgzones 100 | Rev-2019061301 Brian Davis 101 | * checksec.sh: Updated to 2.0.0 - Breaking changes in options, no longer support short options 102 | * checksec.sh: Rewrite checksec to use getopts and move to all functions 103 | * checksec.sh: add MUSL support 104 | Thanks g3ngr33n 105 | * checksec.sh: fixed coredumpcheck 106 | Rev-2019061301 Brian Davis 107 | * checksec.sh: adds Clang CFI and SafeStack checks 108 | Thanks dobin 109 | * checksec.sh: Proc-all proccheck() json fix 110 | Thanks etke 111 | * checksec.sh: Fix --proc-all json output 112 | Thanks etke 113 | * checksec.sh: Switch --proc to use pgrep and fix json output 114 | Thanks etke 115 | * checksec.sh: Fix --proc-libs json output 116 | Thanks etke 117 | * checksec.sh: Fixed some calls to readelf missing stderr redirection to /dev/null 118 | Thanks areisbr 119 | * checksec.sh: fixed several issues around json and xml formatting 120 | * checksec.sh: fixed fortify source catching false positives 121 | Rev-2019011901 Brian Davis 122 | * checksec.sh: Updated to 1.11.1 123 | * checksec.sh: resolved issues with readelf 124 | * checksec.sh: Added docker images for testing 125 | * checksec.sh: Added armhf and aarch64 libc locations 126 | Thanks Avamander 127 | * checksec.sh: Replace FS_COUNT with fgrep 128 | Thanks Iraugusto 129 | * checksec.sh: Fixed symbols count in csv 130 | Thanks Iraugusto 131 | * checksec.sh: Fixed RW-RPATH and RW-RUNPATH 132 | Thanks Iraugusto 133 | * checksec.sh: Added stack canaries generated by intel compiler 134 | Thanks Xavier Brouckaert 135 | * checksec.sh: Mute stat errors for non-existent directories 136 | Thanks Iraugusto 137 | * checksec.sh: Removed invalid json structures and duplicate kernel checks 138 | * checksec.sh: fixed spaces in -d option 139 | * checksec.sh: Added stack-protector-string check 140 | Thanks scottellis 141 | * checksec.sh: Add arm64 specific kernel checks 142 | Thanks scottellis 143 | * checksec.sh: Add REFCOUNT_FULL to kernel tests 144 | Thanks scottellis 145 | * checksec.sh: Remove OSX support 146 | Rev-2018012401 Brian Davis 147 | * checksec.sh: Updated to 1.9.0 148 | * checksec.sh: made all kernel checks dependant on kernel version 149 | * checksec.sh: moved man page to section 1 150 | * checksec.sh: fixed debug flag 151 | * checksec.sh: resolved issue with -d 152 | * checksec.sh: fixed stack protector on 4.18+ kernels 153 | Thanks cheese 154 | * checksec.sh: fixed runpath name in output 155 | Thanks philipturnbull 156 | * checksec.sh: updated readme for offline testing 157 | Thanks matthew-l-weber 158 | Rev-2018012401 Brian Davis 159 | * checksec.sh: Updated to 1.8.0 160 | * checksec.sh: resolved issue with eu-readelf debug 161 | * checksec.sh: shellcheck cleanup 162 | Rev-2017080801 Brian Davis 163 | * checksec.sh: Cleaned up if statements for proper bash expressions 164 | Rev-2016102701 Brian Davis 165 | * checksec.sh: updated to 1.7.5 166 | * checksec.sh: added OSX support 167 | Thanks Ben Actis 168 | * checksec.sh: added space and underscore support 169 | Thanks brianmwaters 170 | * checksec.sh: cleaned up code formatting 171 | Rev-2016022002 Brian Davis 172 | * checksec.sh: updated to 1.7.4 173 | * checksec.sh: fixed man page 174 | * checksec.sh: added pkg_release option to disable updates for packaged releases 175 | * checksec.sh: cleanup up proc-libs 176 | Rev-2016021501 Brian Davis 177 | * checksec.sh: merged in zsh completion 178 | Thanks Vaeth 179 | * checksec.sh: added man page for checksec 180 | * checksec.sh: updated readme to reflect output in place of format option 181 | Rev-2016021501 Brian Davis 182 | * checksec.sh: updated to 1.7.3 183 | * checksec.sh: added xml and json validation tests 184 | * checksec.sh: fixed xml and json errors from validation tests 185 | * checksec.sh: expanded grsecurity checks and cleaned up formatting 186 | Rev-2016010502 Brian Davis 187 | * checksec.sh: Added some extra debug output and started cleanup. 188 | Rev-2016010501 Brian Davis 189 | * checksec.sh: Fixed sysctl path issue #20 190 | Thanks hartwork 191 | Rev-2015122201 Brian Davis 192 | * checksec.sh: Merged in json fixes. 193 | Thanks jpouellet 194 | Rev-2015122101 Brian Davis 195 | * checksec.sh: Merged in passing in command line kernel config, x86 fix and optional tools. 196 | Thanks philippedeswert 197 | * checksec.sh: split off mandatory tool from optional tools. 198 | * checksec.sh: Updated to 1.7.1 199 | * checksec.sh: Added Seccomp tests from olivierlemoal. 200 | Rev-2015102001 Brian Davis 201 | * checksec.sh: Set static LC_ALL to resolve LANG errors. Resolves Ticket #13 202 | * checksec.sh: Merged in additional kernel options and arch specific options. Ticket #14 203 | Thanks philippedeswert 204 | * checksec.sh: Updated to 1.7.0 to support revision releases. 205 | * checksec.sh: put in checks to not display checks that are for different architectures. 206 | Rev-2015091505 Brian Davis 207 | * checksec.sh: added additional debug output for troubleshooting purposes 208 | Rev-2015091401 Brian Davis 209 | * checksec.sh: added debug option for troubleshooting purposes 210 | Rev-2015091301 Brian Davis 211 | * checksec.sh: merged in changes for fedora/epel compliance 212 | Thanks Besser82 213 | * checksec.sh: updated check binaries on run 214 | Thanks Roberto Martelloni 215 | Rev-2015060201 Brian Davis 216 | * checksec.sh: merged in fortified/fortify-able stats on --file output changed 217 | Thanks Roberto Martelloni 218 | Rev-2015011201 Brian Davis 219 | * checksec.sh: moved checksec.sh to checksec 220 | Rev-2014021802 Brian Davis 221 | * checksec.sh: merged in RODATA and STRICT_USER_COPY changes 222 | Thanks N8Fear 223 | Rev-2014021801 Brian Davis 224 | * checksec.sh: merged in JIT and MODHARDEN changes 225 | Thanks N8Fear 226 | Rev-2014021605 Brian Davis 227 | * checksec.sh: Changed --update to verify signature of updates. 228 | * checksec.sig: file added 229 | Rev-2014021601 Brian Davis 230 | * checksec.sh: Removed deprecated Kern Heap section 231 | Thanks Unspawn 232 | 2014-02-14 Brian Davis 233 | * checksec.sh: Updated to version 1.6 234 | * checksec.sh: Implemented rev numbers and --update option 235 | * checksec.sh: Added SELinux checks as additional checks for kernel security. 236 | * checksec.sh: Added update option to pull the latest release 237 | * checksec.sh: Added fortify_source to proc-all output. 238 | * checksec.sh: Added Json, strict XML and updated Grsecurity section. 239 | * checksec.sh: Carried over Robin David's changes with XML and CSV. 240 | 2013-10-06 Robin David 241 | * add machine-readable outputs like CSV and XML 242 | 2011-11-17 Tobias Klein 243 | * 1.5 244 | * New checks for rpath and runpath elements in the dynamic sections. 245 | Thanks to Ollie Whitehouse. 246 | * Other bugfixes and improvements 247 | - checksec.sh now takes account of the KBUILD_OUTPUT 248 | environment variable when checking the Linux kernel 249 | protection mechanisms (--kernel). 250 | Thanks to Martin Vaeth for the hint. 251 | - Some minor changes and clean-ups. Thanks to Brian Davis. 252 | - Ubuntu 11.10 support for --fortify-file and --fortify-proc. 253 | 2011-01-14 Tobias Klein 254 | * 1.4 255 | 256 | * Support for FORTIFY_SOURCE (--fortify-file, --fortify-proc) 257 | 258 | * Lots of other bugfixes and improvements 259 | - Check if the readelf command is available 260 | - readelf support for 64-bit ELF files 261 | - Check if the requested files and directories do exist 262 | - '--dir' is now case-sensitive and correctly deals with 263 | trailing slashes 264 | - Check user permissions 265 | - Etc. 266 | 2010-06-15 Tobias Klein 267 | * 1.3.1 268 | 269 | * New BSD License 270 | (http://www.opensource.org/licenses/bsd-license.php) 271 | 2010-05-04 Tobias Klein 272 | * 1.3 273 | * Additional checks for a number of Linux kernel 274 | protection mechanisms. 275 | Thanks to Jon Oberheide (jon.oberheide.org). 276 | 2010-01-02 Tobias Klein 277 | * 1.2 278 | * Additional PaX (http://pax.grsecurity.net/) checks. 279 | Thanks to Brad Spengler (grsecurity.net) for the PaX 280 | support. 281 | * Some minor fixes (coloring adjusted, 'pidof' replacement) 282 | 2009-12-27 Tobias Klein 283 | * 1.1 284 | * New '--proc-libs' option. This option instructs 285 | checksec.sh to test the loaded libraries of a process. 286 | * Additional information on ASLR results (--proc, 287 | -proc-all, --proc-libs) 288 | Thanks to Anthony G. Basile of the Tin Hat project 289 | for the hint. 290 | * Additional CPU NX check (--proc, --proc-all, --proc-libs) 291 | 2009-01-28 Tobias Klein 292 | * 1.0 293 | * Initial release 294 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | COPY checksec /bin/ 4 | 5 | RUN apt-get update && apt-get -y -q upgrade && DEBIAN_FRONTEND=noninteractive apt-get -y -q install \ 6 | bc bison flex build-essential ccache git file \ 7 | libncurses-dev libssl-dev u-boot-tools wget \ 8 | xz-utils vim xfce4 libxml2-utils python3 python3-pip jq \ 9 | gcc clang && apt-get clean \ 10 | pip3 install --upgrade pip && pip3 install setuptools --break-system-packages && \ 11 | pip3 install demjson3 --break-system-packages && \ 12 | chmod +x /bin/checksec 13 | -------------------------------------------------------------------------------- /Dockerfile.ubuntu: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | # install the build script and run it. 4 | # this is to give more flexiblity to the build process 5 | COPY tests/docker-build.sh /tmp 6 | RUN /tmp/docker-build.sh 7 | 8 | COPY . /root 9 | WORKDIR /root 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The BSD License (http://www.opensource.org/licenses/bsd-license.php) 2 | specifies the terms and conditions of use for checksec.sh: 3 | Copyright (c) 2014-2022, Brian Davis 4 | Copyright (c) 2013, Robin David 5 | Copyright (c) 2009-2011, Tobias Klein 6 | All rights reserved. 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | * Neither the name of Tobias Klein nor the name of trapkit.de may be 17 | used to endorse or promote products derived from this software 18 | without specific prior written permission. 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 26 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 27 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 29 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | DAMAGE. 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL = bash 2 | VERSION ?= 3.0.2 3 | 4 | .PHONY: test 5 | test: 6 | ./tests/test-checksec.sh 7 | 8 | .PHONY: build-image 9 | build-image: 10 | docker build -t slimm609/checksec . 11 | 12 | .PHONY: build 13 | build: 14 | goreleaser build --snapshot --clean 15 | 16 | .PHONY: release 17 | release: 18 | git tag $(VERSION) -m "release of $(VERSION)" 19 | goreleaser release --clean 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | checksec 2 | ======== 3 | Checksec checks the properties of executables (like PIE, RELRO, Canaries, ASLR, Fortify Source). 4 | It has been originally written by Tobias Klein and the original source is available here: http://www.trapkit.de/tools/checksec.html. Over time this has expanded in feature set and has now moved from Bash to Golang. 5 | 6 | Version: 3.0.1 7 | 8 | Updates 9 | ------- 10 | Checksec was originally released with 1.0 in early 2009 and has been used for validating binary checks of Linux systems for over a decade. Over time as more checks were supported and Linux distributions have changed, this has brought more dependencies into checksec. Adding more and more dependenies to be able to check the security flags of files, it not an ideal solution for systems with minor dependencies including embedded systems, distroless containers, and cross platform checks. 11 | - Feature partial between the bash version and the golang version will be mostly supported. 12 | - Adding support for yaml output 13 | - Removing support for CSV 14 | - JSON and XML will still both be supported 15 | - Much faster results. When checking 694 files in a directory 16 | - bash: real 0m10.348s 17 | - golang: real 0m0.691s 18 | 19 | 20 | For OSX 21 | ------- 22 | Checksec can scan linux files from OSX however, some checks may be limited due to OS dependencies on resources like glibc. 23 | 24 | 25 | Examples 26 | -------- 27 | 28 | **normal (or --format=cli)** 29 | 30 | $checksec file /bin/ls 31 | RELRO Stack Canary NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable Name 32 | Partial RELRO Canary Found NX enabled PIE Enabled No RPATH No RUNPATH No Symbols No 0 14 /bin/ls 33 | 34 | **yaml** 35 | 36 | $ checksec file /bin/ls --output yaml 37 | - checks: 38 | canary: Canary Found 39 | fortified: "0" 40 | fortify_source: "No" 41 | fortifyable: "14" 42 | nx: NX enabled 43 | pie: PIE Enabled 44 | relro: Partial RELRO 45 | rpath: No RPATH 46 | runpath: No RUNPATH 47 | symbols: No Symbols 48 | name: /bin/ls 49 | 50 | **xml** 51 | 52 | $ checksec file /bin/ls --output xml 53 | 54 | /bin/ls 55 | 56 | Canary Found 57 | 0 58 | 14 59 | No 60 | NX enabled 61 | PIE Enabled 62 | Partial RELRO 63 | No RPATH 64 | No RUNPATH 65 | No Symbols 66 | 67 | 68 | 69 | **json** 70 | 71 | $ checksec file /bin/ls --output json 72 | [ 73 | { 74 | "checks": { 75 | "canary": "Canary Found", 76 | "fortified": "0", 77 | "fortify_source": "No", 78 | "fortifyable": "14", 79 | "nx": "NX enabled", 80 | "pie": "PIE Enabled", 81 | "relro": "Partial RELRO", 82 | "rpath": "No RPATH", 83 | "runpath": "No RUNPATH", 84 | "symbols": "No Symbols" 85 | }, 86 | "name": "/bin/ls" 87 | } 88 | ] 89 | 90 | **Fortify test in cli** 91 | 92 | $ checksec fortifyProc 1 93 | 94 | _____ _ _ ______ _____ _ __ _____ ______ _____ 95 | / ____| | | | ____/ ____| |/ // ____| ____/ ____| 96 | | | | |__| | |__ | | | ' /| (___ | |__ | | 97 | | | | __ | __|| | | < \___ \| __|| | 98 | | |____| | | | |___| |____| . \ ____) | |___| |____ 99 | \_____|_| |_|______\_____|_|\_\_____/|______\_____| 100 | 101 | * FORTIFY_SOURCE support available (libc): Yes 102 | * Binary compiled with FORTIFY_SOURCE support: No 103 | 104 | ------ EXECUTABLE-FILE ------- | -------- LIBC -------- 105 | Fortifiable library functions | Checked function names 106 | Coming Soon 107 | 108 | SUMMARY 109 | * Number of checked functions in libc : 18 110 | * Total number of library functions in the executable: 2011 111 | * Number of Fortifiable functions in the executable : 12 112 | * Number of checked functions in the executable : 0 113 | * Number of unchecked functions in the executable : 12 114 | 115 | 116 | **Kernel test in Cli** 117 | 118 | $ checksec kernel 119 | 120 | _____ _ _ ______ _____ _ __ _____ ______ _____ 121 | / ____| | | | ____/ ____| |/ // ____| ____/ ____| 122 | | | | |__| | |__ | | | ' /| (___ | |__ | | 123 | | | | __ | __|| | | < \___ \| __|| | 124 | | |____| | | | |___| |____| . \ ____) | |___| |____ 125 | \_____|_| |_|______\_____|_|\_\_____/|______\_____| 126 | 127 | Kernel configs only print what is supported by the specific kernel/kernel config 128 | Description Value Check Type Config Key 129 | Virtually-mapped kernel stack Disabled Kernel Config CONFIG_VMAP_STACK 130 | Harden str/mem functions Disabled Kernel Config CONFIG_FORTIFY_SOURCE 131 | Restrict Kernel RWX Enabled Kernel Config CONFIG_STRICT_KERNEL_RWX 132 | Restrict /dev/mem access Enabled Kernel Config CONFIG_STRICT_DEVMEM 133 | SELinux Kernel Flag Disabled Kernel Config CONFIG_SECURITY_SELINUX 134 | Emulate privileged access never Disabled Kernel Config CONFIG_ARM64_SW_TTBR0_PAN 135 | Restrict I/O access to /dev/mem Disabled Kernel Config CONFIG_IO_STRICT_DEVMEM 136 | Kernel Heap Randomization Disabled Kernel Config CONFIG_COMPAT_BRK 137 | Stack Protector Strong Disabled Kernel Config CONFIG_STACKPROTECTOR_STRONG 138 | Hardened Usercopy Disabled Kernel Config CONFIG_HARDENED_USERCOPY 139 | Restrict Module RWX Enabled Kernel Config CONFIG_STRICT_MODULE_RWX 140 | Address space layout randomization Disabled Kernel Config CONFIG_RANDOMIZE_BASE 141 | Randomize address of kernel image Disabled Kernel Config CONFIG_RANDOMIZE_BASE 142 | Stack Protector Disabled Kernel Config CONFIG_STACKPROTECTOR 143 | Unmap kernel in userspace (KAISER) Enabled Kernel Config CONFIG_UNMAP_KERNEL_AT_EL0 144 | SLAB freelist randomization Disabled Kernel Config CONFIG_SLAB_FREELIST_RANDOM 145 | SELinux Enabled Disabled SELinux SELinux 146 | Protected symlinks Enabled Sysctl fs.protected_symlinks 147 | Protected hardlinks Enabled Sysctl fs.protected_hardlinks 148 | Ipv4 reverse path filtering Disabled Sysctl net.ipv4.conf.all.rp_filter 149 | YAMA Unknown Sysctl kernel.yama.ptrace_scope 150 | Exec Shield Unknown Sysctl kernel.exec-shield 151 | Unprivileged BPF Disabled Disabled Sysctl kernel.unprivileged_bpf_disabled 152 | Vanilla Kernel ASLR Enabled Sysctl kernel.randomize_va_space 153 | Dmesg Restrictions Enabled Sysctl kernel.dmesg_restrict 154 | Kernel Pointer Restrictions Disabled Sysctl kernel.kptr_restrict 155 | Protected fifos Disabled Sysctl fs.protected_fifos 156 | Protected regular Disabled Sysctl fs.protected_regular 157 | Performance events by normal users Enabled Sysctl kernel.perf_event_paranoid 158 | Disable Autoload TTY Line Disciplines Disabled Sysctl dev.tty.ldisc_autoload 159 | Disable Legacy TIOCSTI Disabled Sysctl dev.tty.legacy_tiocsti 160 | 161 | 162 | **Kernel Test in XML** 163 | 164 | $ checksec kernel --output xml 165 | 166 | CONFIG_IO_STRICT_DEVMEM 167 | Restrict I/O access to /dev/mem 168 | Disabled 169 | Kernel Config 170 | 171 | 172 | CONFIG_STRICT_MODULE_RWX 173 | Restrict Module RWX 174 | Enabled 175 | Kernel Config 176 | 177 | 178 | CONFIG_SECURITY_SELINUX 179 | SELinux Kernel Flag 180 | Disabled 181 | Kernel Config 182 | 183 | 184 | **Kernel Test in Json** 185 | 186 | $ checksec kernel --output json 187 | [ 188 | { 189 | "desc": "Hardened Usercopy", 190 | "name": "CONFIG_HARDENED_USERCOPY", 191 | "type": "Kernel Config", 192 | "value": "Disabled" 193 | }, 194 | { 195 | "desc": "Harden str/mem functions", 196 | "name": "CONFIG_FORTIFY_SOURCE", 197 | "type": "Kernel Config", 198 | "value": "Disabled" 199 | }, 200 | { 201 | "desc": "Restrict Kernel RWX", 202 | "name": "CONFIG_STRICT_KERNEL_RWX", 203 | "type": "Kernel Config", 204 | "value": "Enabled" 205 | }, 206 | { 207 | "desc": "Virtually-mapped kernel stack", 208 | "name": "CONFIG_VMAP_STACK", 209 | "type": "Kernel Config", 210 | "value": "Disabled" 211 | }, 212 | { 213 | "desc": "SELinux Kernel Flag", 214 | "name": "CONFIG_SECURITY_SELINUX", 215 | "type": "Kernel Config", 216 | "value": "Disabled" 217 | } 218 | ] 219 | 220 | Using with Cross-compiled Systems 221 | --------------------------------------- 222 | The checksec tool can be used against cross-compiled target file-systems offline. Key limitations to note: 223 | * Kernel tests - require you to execute the script on the running system you'd like to check as they directly access kernel resources to identify system configuration/state. You can specify the config file for the kernel after the -k option. 224 | 225 | * File check - the offline testing works for all the checks but the Fortify feature. By default, Fortify, uses the running system's libraries vs those in the offline file-system. There are ways to workaround this (chroot) but at the moment, the ideal configuration would have this script executing on the running system when checking the files. An other option is to specify where the cross-compiled libc is located through the -libc option. 226 | 227 | The checksec tool's normal use case is for runtime checking of the systems configuration. If the system is an embedded target, the native binutils tools like readelf may not be present. This would restrict which parts of the script will work. 228 | 229 | Even with those limitations, the amount of valuable information this script provides, still makes it a valuable tool for checking offline file-systems. 230 | -------------------------------------------------------------------------------- /cmd/dir.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/slimm609/checksec/pkg/utils" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // dirCmd represents the dir command 10 | var dirCmd = &cobra.Command{ 11 | Use: "dir", 12 | Short: "check all files in a directory", 13 | Args: cobra.ExactArgs(1), 14 | Run: func(cmd *cobra.Command, args []string) { 15 | dir := args[0] 16 | recursive, _ := cmd.Flags().GetBool("recursive") 17 | utils.CheckDirExists(dir) 18 | var Elements []interface{} 19 | var ElementColors []interface{} 20 | for _, file := range utils.GetAllFilesFromDir(dir, recursive) { 21 | data, color := utils.RunFileChecks(file, libc) 22 | Elements = append(Elements, data...) 23 | ElementColors = append(ElementColors, color...) 24 | } 25 | utils.FilePrinter(outputFormat, Elements, ElementColors, noBanner, noHeader) 26 | 27 | }, 28 | } 29 | 30 | func init() { 31 | rootCmd.AddCommand(dirCmd) 32 | dirCmd.Flags().BoolP("recursive", "r", false, "Enable recursive through the directories") 33 | } 34 | -------------------------------------------------------------------------------- /cmd/file.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/slimm609/checksec/pkg/utils" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // fileCmd represents the file command 10 | var fileCmd = &cobra.Command{ 11 | Use: "file", 12 | Short: "Check a single binary file", 13 | Args: cobra.ExactArgs(1), 14 | Run: func(cmd *cobra.Command, args []string) { 15 | file := args[0] 16 | 17 | utils.CheckElfExists(file) 18 | data, color := utils.RunFileChecks(file, libc) 19 | utils.FilePrinter(outputFormat, data, color, noBanner, noHeader) 20 | }, 21 | } 22 | 23 | func init() { 24 | rootCmd.AddCommand(fileCmd) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/fortifyFile.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/slimm609/checksec/pkg/checksec" 8 | "github.com/slimm609/checksec/pkg/utils" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | // fortifyFileCmd represents the fortifyFile command 14 | var fortifyFileCmd = &cobra.Command{ 15 | Use: "fortifyFile", 16 | Short: "Check Fortify for binary file", 17 | Run: func(cmd *cobra.Command, args []string) { 18 | if len(args) != 1 { 19 | fmt.Printf("Error: no filename provided") 20 | os.Exit(1) 21 | } 22 | file := args[0] 23 | 24 | utils.CheckElfExists(file) 25 | binary := utils.GetBinary(file) 26 | fortify := checksec.Fortify(file, binary, libc) 27 | output := []interface{}{ 28 | map[string]interface{}{ 29 | "name": file, 30 | "checks": map[string]interface{}{ 31 | "fortify_source": fortify.Output, 32 | "fortified": fortify.Fortified, 33 | "fortifyable": fortify.Fortifiable, 34 | "noFortify": fortify.NoFortify, 35 | "libcSupport": fortify.LibcSupport, 36 | "numLibcFunc": fortify.NumLibcFunc, 37 | "numFileFunc": fortify.NumFileFunc, 38 | }, 39 | }, 40 | } 41 | color := []interface{}{ 42 | map[string]interface{}{ 43 | "name": file, 44 | "checks": map[string]interface{}{ 45 | "fortified": fortify.Fortified, 46 | "fortifiedColor": "unset", 47 | "noFortify": fortify.NoFortify, 48 | "fortifyable": fortify.Fortifiable, 49 | "fortifyableColor": "unset", 50 | "fortify_source": fortify.Output, 51 | "fortify_sourceColor": fortify.Color, 52 | "libcSupport": fortify.LibcSupport, 53 | "libcSupportColor": fortify.LibcSupportColor, 54 | "numLibcFunc": fortify.NumLibcFunc, 55 | "numFileFunc": fortify.NumFileFunc, 56 | }, 57 | }, 58 | } 59 | utils.FortifyPrinter(outputFormat, output, color, noBanner, noHeader) 60 | }, 61 | } 62 | 63 | func init() { 64 | rootCmd.AddCommand(fortifyFileCmd) 65 | } 66 | -------------------------------------------------------------------------------- /cmd/fortifyProc.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/slimm609/checksec/pkg/checksec" 9 | "github.com/slimm609/checksec/pkg/utils" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // fortifyProcCmd represents the fortifyProc command 15 | var fortifyProcCmd = &cobra.Command{ 16 | Use: "fortifyProc", 17 | Short: "Check Fortify for running process", 18 | Run: func(cmd *cobra.Command, args []string) { 19 | if len(args) != 1 { 20 | fmt.Printf("Error: no process id provided") 21 | os.Exit(1) 22 | } 23 | proc := args[0] 24 | 25 | file, err := os.Readlink(filepath.Join("/proc", proc, "exe")) 26 | if err != nil { 27 | fmt.Printf("Error: Pid %s not found", proc) 28 | os.Exit(1) 29 | } 30 | 31 | utils.CheckElfExists(file) 32 | binary := utils.GetBinary(file) 33 | fortify := checksec.Fortify(file, binary, libc) 34 | output := []interface{}{ 35 | map[string]interface{}{ 36 | "name": file, 37 | "checks": map[string]interface{}{ 38 | "fortify_source": fortify.Output, 39 | "fortified": fortify.Fortified, 40 | "fortifyable": fortify.Fortifiable, 41 | "noFortify": fortify.NoFortify, 42 | "libcSupport": fortify.LibcSupport, 43 | "numLibcFunc": fortify.NumLibcFunc, 44 | "numFileFunc": fortify.NumFileFunc, 45 | }, 46 | }, 47 | } 48 | color := []interface{}{ 49 | map[string]interface{}{ 50 | "name": file, 51 | "checks": map[string]interface{}{ 52 | "fortified": fortify.Fortified, 53 | "fortifiedColor": "unset", 54 | "noFortify": fortify.NoFortify, 55 | "fortifyable": fortify.Fortifiable, 56 | "fortifyableColor": "unset", 57 | "fortify_source": fortify.Output, 58 | "fortify_sourceColor": fortify.Color, 59 | "libcSupport": fortify.LibcSupport, 60 | "libcSupportColor": fortify.LibcSupportColor, 61 | "numLibcFunc": fortify.NumLibcFunc, 62 | "numFileFunc": fortify.NumFileFunc, 63 | }, 64 | }, 65 | } 66 | utils.FortifyPrinter(outputFormat, output, color, noBanner, noHeader) 67 | }, 68 | } 69 | 70 | func init() { 71 | rootCmd.AddCommand(fortifyProcCmd) 72 | } 73 | -------------------------------------------------------------------------------- /cmd/kernel.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/slimm609/checksec/pkg/utils" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | // kernelCmd represents the kernel command 13 | var kernelCmd = &cobra.Command{ 14 | Use: "kernel", 15 | Short: "Check kernel security flags", 16 | Run: func(cmd *cobra.Command, args []string) { 17 | var configFile string 18 | if len(args) > 0 { 19 | configFile = args[0] 20 | } else if _, err := os.Stat("/proc/config.gz"); err == nil { 21 | configFile = "/proc/config.gz" 22 | } else { 23 | osReleaseFile := "/proc/sys/kernel/osrelease" 24 | _, err := os.Stat(osReleaseFile) 25 | if err != nil { 26 | fmt.Println("Error: could not find kernel config") 27 | os.Exit(1) 28 | } 29 | osReleaseVersion, err := os.ReadFile(osReleaseFile) 30 | if err != nil { 31 | fmt.Println("Error: could not find kernel config") 32 | os.Exit(1) 33 | } 34 | content := strings.ReplaceAll(string(osReleaseVersion), "\n", "") 35 | configFile = fmt.Sprintf("%s-%s", "/boot/config", content) 36 | _, err = os.Stat(configFile) 37 | if err != nil { 38 | fmt.Println("Error: could not find kernel config") 39 | os.Exit(1) 40 | } 41 | } 42 | 43 | utils.CheckFileExists(configFile) 44 | 45 | kernel, kernelColors := utils.ParseKernel(configFile) 46 | utils.KernelPrinter(outputFormat, kernel, kernelColors, noBanner, noHeader) 47 | }, 48 | } 49 | 50 | func init() { 51 | rootCmd.AddCommand(kernelCmd) 52 | } 53 | -------------------------------------------------------------------------------- /cmd/proc.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/slimm609/checksec/pkg/utils" 8 | 9 | "path/filepath" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // procCmd represents the proc command 15 | var procCmd = &cobra.Command{ 16 | Use: "proc", 17 | Short: "Check a file of a running process", 18 | Args: cobra.ExactArgs(1), 19 | Run: func(cmd *cobra.Command, args []string) { 20 | proc := args[0] 21 | 22 | file, err := os.Readlink(filepath.Join("/proc", proc, "exe")) 23 | if err != nil { 24 | fmt.Printf("Error: Pid %s not found", proc) 25 | os.Exit(1) 26 | } 27 | 28 | utils.CheckElfExists(file) 29 | data, color := utils.RunFileChecks(file, libc) 30 | utils.FilePrinter(outputFormat, data, color, noBanner, noHeader) 31 | }, 32 | } 33 | 34 | func init() { 35 | rootCmd.AddCommand(procCmd) 36 | } 37 | -------------------------------------------------------------------------------- /cmd/procAll.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/slimm609/checksec/pkg/utils" 10 | 11 | "github.com/shirou/gopsutil/v3/process" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | // procAllCmd represents the procAll command 16 | var procAllCmd = &cobra.Command{ 17 | Use: "procAll", 18 | Short: "Check all running processes", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | var Elements []interface{} 22 | var ElementColors []interface{} 23 | processes, _ := process.Processes() 24 | for _, process := range processes { 25 | proc := process.Pid 26 | // skip checksec 27 | if proc == int32(os.Getpid()) { 28 | continue 29 | } 30 | filePath := filepath.Join("/proc", fmt.Sprint(proc), "exe") 31 | file, err := os.Readlink(filePath) 32 | file = strings.Split(file, " ")[0] 33 | if err != nil { 34 | continue 35 | } 36 | data, color := utils.RunFileChecks(file, libc) 37 | Elements = append(Elements, data...) 38 | ElementColors = append(ElementColors, color...) 39 | } 40 | utils.FilePrinter(outputFormat, Elements, ElementColors, noBanner, noHeader) 41 | }, 42 | } 43 | 44 | func init() { 45 | rootCmd.AddCommand(procAllCmd) 46 | } 47 | -------------------------------------------------------------------------------- /cmd/procLibs.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // procLibsCmd represents the procLibs command 10 | var procLibsCmd = &cobra.Command{ 11 | Use: "procLibs", 12 | Short: "check process libraries", 13 | // hide until refactored 14 | Hidden: true, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | fmt.Println("procLibs called") 17 | }, 18 | } 19 | 20 | func init() { 21 | rootCmd.AddCommand(procLibsCmd) 22 | } 23 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var ( 11 | libc string 12 | outputFormat string 13 | noBanner bool 14 | noHeader bool 15 | ) 16 | 17 | // rootCmd represents the base command when called without any subcommands 18 | var rootCmd = &cobra.Command{ 19 | Use: "checksec", 20 | Short: "A binary scanning security tool", 21 | Long: `A tool used to quickly survey mitigation technologies in use by processes on a Linux system.`, 22 | } 23 | 24 | func SetVersionInfo(version, commit, date string) { 25 | rootCmd.Version = fmt.Sprintf("%s (Built on %s from Git SHA %s)", version, date, commit) 26 | } 27 | 28 | // Execute adds all child commands to the root command and sets flags appropriately. 29 | // This is called by main.main(). It only needs to happen once to the rootCmd. 30 | func Execute() { 31 | rootCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "table", "Output format (table, xml, json or yaml)") 32 | rootCmd.PersistentFlags().StringVarP(&libc, "libc", "l", "", "Set libc location (useful for FORTIFY check on offline embedded file-system)") 33 | rootCmd.PersistentFlags().BoolVarP(&noBanner, "no-banner", "", false, "disable the banner") 34 | rootCmd.PersistentFlags().BoolVarP(&noHeader, "no-headers", "", false, "disable the headers") 35 | err := rootCmd.Execute() 36 | if err != nil { 37 | os.Exit(1) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: pages-themes/hacker@v0.2.0 2 | 3 | title: Checksec 4 | description: A tool used to quickly survey mitigation technologies in use by processes on a Linux system. 5 | 6 | repository: slimm609/checksec.sh 7 | 8 | plugins: 9 | - jekyll-remote-theme 10 | - jekyll-github-metadata 11 | - jekyll-mentions 12 | - jekyll-redirect-from 13 | - jekyll-sitemap 14 | - jemoji 15 | 16 | show_downloads: true 17 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | 2 | Checksec is a bash shell script that is used to check the properties of executables (like PIE, RELRO, Canaries, ASLR, Fortify Source) and the linux kernel. 3 | It has been originally written by Tobias Klein in 2011 and the original source is available here: [http://www.trapkit.de/tools/checksec.html](http://www.trapkit.de/tools/checksec.html) 4 | 5 | 6 | Manually verify checksec 7 | 8 | ``` 9 | openssl dgst -sha256 -verify checksec.pub -signature checksec.sig checksec 10 | ``` 11 | 12 | Examples 13 | -------- 14 | 15 | #### **normal (or --format=cli)** 16 | ```bash 17 | $ checksec --file=/bin/ls 18 | RELRO STACK CANARY NX PIE RPATH RUNPATH FILE 19 | Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH /bin/ls 20 | ``` 21 | 22 | #### **csv** 23 | ```bash 24 | $ checksec --output=csv --file=/bin/ls 25 | Partial RELRO,Canary found,NX enabled,No PIE,No RPATH,No RUNPATH,/bin/ls 26 | ``` 27 | 28 | #### **xml** 29 | ```bash 30 | $ checksec --output=xml --file=/bin/ls 31 | 32 | 33 | ``` 34 | 35 | #### **json** 36 | ```bash 37 | $ checksec --output=json --file=/bin/ls 38 | { "file": { "relro":"partial","canary":"yes","nx":"yes","pie":"no","rpath":"no","runpath":"no","filename":"/bin/ls" } } 39 | ``` 40 | 41 | #### **Fortify test in cli** 42 | ```bash 43 | $ checksec --fortify-proc=1 44 | * Process name (PID) : init (1) 45 | * FORTIFY_SOURCE support available (libc) : Yes 46 | * Binary compiled with FORTIFY_SOURCE support: Yes 47 | 48 | ------ EXECUTABLE-FILE ------- . -------- LIBC -------- 49 | FORTIFY-able library functions | Checked function names 50 | ------------------------------------------------------- 51 | fdelt_chk | __fdelt_chk 52 | read | __read_chk 53 | syslog_chk | __syslog_chk 54 | fprintf_chk | __fprintf_chk 55 | vsnprintf_chk | __vsnprintf_chk 56 | fgets | __fgets_chk 57 | strncpy | __strncpy_chk 58 | snprintf_chk | __snprintf_chk 59 | memset | __memset_chk 60 | strncat_chk | __strncat_chk 61 | memcpy | __memcpy_chk 62 | fread | __fread_chk 63 | sprintf_chk | __sprintf_chk 64 | 65 | SUMMARY: 66 | 67 | * Number of checked functions in libc : 78 68 | * Total number of library functions in the executable: 116 69 | * Number of FORTIFY-able functions in the executable : 13 70 | * Number of checked functions in the executable : 7 71 | * Number of unchecked functions in the executable : 6 72 | ``` 73 | 74 | Note on Fortify results. There is not currently a known way to determine if the binary was compiled with FORTIFY_SOURCE level 1, 2, or 3 in a reliable manner. 75 | 76 | Some binaries include some details about how it was compiled. For example, VIM on ubuntu is compiled with `-D_FORTIFY_SOURCE=1`. This can be identified with strings on the binary. Most binaries do not include this data, but some do. 77 | 78 | ``` 79 | $ strings vim | grep FORTIFY 80 | gcc -c -I. -Iproto -DHAVE_CONFIG_H -Wdate-time -g -O2 -ffile-prefix-map=/build/vim-CSyBG7/vim-8.2.3995=. -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 81 | ``` 82 | 83 | #### **Kernel test in Cli** 84 | ```bash 85 | $ checksec --kernel 86 | * Kernel protection information: 87 | 88 | Description - List the status of kernel protection mechanisms. Rather than 89 | inspect kernel mechanisms that may aid in the prevention of exploitation of 90 | userspace processes, this option lists the status of kernel configuration 91 | options that harden the kernel itself against attack. 92 | 93 | Kernel config: /proc/config.gz 94 | 95 | GCC stack protector support: Enabled 96 | Strict user copy checks: Disabled 97 | Enforce read-only kernel data: Disabled 98 | Restrict /dev/mem access: Enabled 99 | Restrict /dev/kmem access: Enabled 100 | 101 | * Kernel Heap Hardening: No KERNHEAP 102 | 103 | The KERNHEAP hardening patchset is available here: 104 | https://www.subreption.com/kernheap/ 105 | ``` 106 | 107 | 108 | #### **Kernel Test in XML** 109 | ```bash 110 | $ checksec --output=xml --kernel 111 | 112 | 113 | 114 | 115 | ``` 116 | 117 | #### **Kernel Test in Json** 118 | ```bash 119 | $ checksec --output=json --kernel 120 | { "kernel": { "KernelConfig":"/boot/config-3.11-2-amd64","gcc_stack_protector":"yes","strict_user_copy_check":"no","ro_kernel_data":"yes","restrict_dev_mem_access":"yes","restrict_dev_kmem_access":"no" },{ "kernheap_config":"no" } } 121 | ``` 122 | 123 | ### Using with Cross-compiled Systems 124 | The checksec tool can be used against cross-compiled target file-systems offline. Key limitations to note: 125 | 126 | * Kernel tests - require you to execute the script on the running system you'd like to check as they directly access kernel resources to identify system configuration/state. You can specify the config file for the kernel after the -k option. 127 | 128 | * File check - the offline testing works for all the checks but the Fortify feature. Fortify, uses the running system's libraries vs those in the offline file-system. There are ways to workaround this (chroot) but at the moment, the ideal configuration would have this script executing on the running system when checking the files. 129 | 130 | The checksec tool's normal use case is for runtime checking of the systems configuration. If the system is an embedded target, the native binutils tools like readelf may not be present. This would restrict which parts of the script will work. 131 | 132 | Even with those limitations, information this script provides still makes it a valuable tool for checking offline file-systems. 133 | 134 | ### OSX and BSD based systems Support 135 | Most of the tools do not work on mach-O binaries, the OSX kernel or BSD kernels. It may work on certain BSD based systems but is not officially supported. 136 | -------------------------------------------------------------------------------- /extras/man/checksec.1: -------------------------------------------------------------------------------- 1 | .\" Process this file with 2 | .\" groff -mdoc -Tascii foo.1 3 | .\" 4 | .Dd March 2023 5 | .Dt checksec 1 6 | .Os 7 | .Sh NAME 8 | .Nm checksec 9 | .Nd check executables and kernel properties 10 | .Sh SYNOPSIS 11 | .Nm checksec 12 | .Op Fl -options 13 | .Op Ar file 14 | .Sh DESCRIPTION 15 | .Nm 16 | is a bash script used to check the properties of executables 17 | (like PIE, RELRO, PaX, Canaries, ASLR, Fortify Source), library calls (Fortify Source), 18 | and kernel security options (like GRSecurity and SELinux). 19 | .Sh Options 20 | Options specifying input and action: 21 | .Bl -tag -width Ds 22 | .It Fl \-file Ns = Ns Ar filename 23 | Checks individual files for security features compiled into the executable 24 | .It Fl \-dir Ns = Ns Ar directory 25 | Recursively checks all executable files in the directory for security features compiled into the executables 26 | .It Fl \-listfile Ns = Ns Ar listfile 27 | Check all files specified in a newline-separeted text file for security features compiled into the executable 28 | .It Fl \-proc Ns = Ns Ar pid 29 | Checks the security features of a running process by name 30 | .It Fl \-proc-all 31 | Checks the security features of all running processes 32 | .It Fl \-proc-libs 33 | Checks the security features of the all libraries of a running process ID 34 | .It Fl \-kernel Ns Op = Ns Ar config 35 | Checks the security features of the running kernel or a specified kernel config 36 | .It Fl \-fortify-file Ns = Ns Ar filename 37 | Checks for the use of fortifiable and fortified library functions in a file 38 | .It Fl \-fortify-proc Ns = Ns Ar pid 39 | Checks for the use of fortifiable and fortified library functions in a running process 40 | .El 41 | 42 | Options modifying behavior: 43 | .Bl -tag -width Ds 44 | .It Fl \-debug 45 | Enable debug-level output. 46 | .It Fl \-extended 47 | Check for additional security features (e.g. Clang CFI, SafeStack) 48 | .It Fl \-libcfile Ns = Ns Ar path 49 | Specify the libc file path or a search path 50 | .It Fl \-output Ns = Ns Ar (cli|csv|xml|json) No Ns , or Fl \-format Ns = Ns Ar (cli|csv|xml|json) 51 | Output the results in different formats for ingestion to other applications. 52 | .It Fl \-trace 53 | Enable bash tracing (set 54 | .Fl x No Ns ). 55 | .El 56 | 57 | Miscellaneous options: 58 | .Bl -tag -width Ds 59 | .It Fl \-debug_report 60 | Generate a system report and exit. 61 | .It Fl h No or Fl \-help 62 | Displays the help text and exit 63 | .It Fl \-update No or Fl \-upgrade 64 | Checks source for a signed update and updates the application if available and exit 65 | .It Fl \-version 66 | Shows the current version of the running software and exit 67 | .El 68 | \".Sh EXAMPLES 69 | \" TODO 70 | .Sh DIAGNOSTICS 71 | The following diagnostics may be issued on stderr: 72 | .Bl -tag -width Ds 73 | .It Permission Denied. 74 | For most of the checks you must be root. 75 | .El 76 | .Sh SEE ALSO 77 | .Xr hardening-check 1 78 | .Ns , 79 | .Xr feature_test_macros 7 80 | .Ns , 81 | .Xr gcc 1 82 | .Ns , 83 | .Xr ld 1 84 | .Sh HISTORY 85 | .Nm 86 | was originally written by 87 | .An Tobias Klein . 88 | This version is expanded and maintained by 89 | .An Brian Davis Aq Mt slimm609@gmail.com 90 | -------------------------------------------------------------------------------- /extras/rpm/checksec.spec: -------------------------------------------------------------------------------- 1 | Summary: Tool to check system for binary-hardening 2 | Name: checksec 3 | Version: 1.7.4 4 | Release: 1 5 | License: BSD 6 | Group: Development/Tools 7 | Source0: https://raw.githubusercontent.com/slimm609/checksec.sh/master/%{name} 8 | Source1: https://raw.githubusercontent.com/slimm609/checksec.sh/master/ChangeLog 9 | URL: https://github.com/slimm609/checksec.sh 10 | Requires: binutils 11 | BuildArch: noarch 12 | BuildRoot: %{tmpdir}/%{name}-%{version}-root-%(id -u -n) 13 | 14 | %description 15 | Modern Linux distributions offer some mitigation techniques to make it 16 | harder to exploit software vulnerabilities reliably. Mitigations such 17 | as RELRO, NoExecute (NX), Stack Canaries, Address Space Layout 18 | Randomization (ASLR) and Position Independent Executables (PIE) have 19 | made reliably exploiting any vulnerabilities that do exist far more 20 | challenging. 21 | 22 | The checksec script is designed to test what *standard* Linux OS and 23 | PaX security features are being used. 24 | 25 | As of version 1.3 the script also lists the status of various Linux 26 | kernel protection mechanisms. 27 | 28 | checksec can check binary-files and running processes for hardening 29 | features. 30 | 31 | %prep 32 | cp -p %{SOURCE1} ChangeLog 33 | 34 | %install 35 | rm -rf $RPM_BUILD_ROOT 36 | install -d $RPM_BUILD_ROOT%{_bindir} 37 | install -p %{SOURCE0} $RPM_BUILD_ROOT%{_bindir}/%{name} 38 | 39 | %clean 40 | rm -rf $RPM_BUILD_ROOT 41 | 42 | %files 43 | %defattr(644,root,root,755) 44 | %doc ChangeLog 45 | %attr(755,root,root) %{_bindir}/%{name} 46 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/slimm609/checksec 2 | 3 | go 1.23.1 4 | 5 | require ( 6 | github.com/fatih/color v1.18.0 7 | github.com/lorenzosaino/go-sysctl v0.3.1 8 | github.com/opencontainers/selinux v1.12.0 9 | github.com/shirou/gopsutil/v3 v3.24.5 10 | github.com/spf13/cobra v1.9.1 11 | github.com/u-root/u-root v0.14.0 12 | sigs.k8s.io/yaml v1.4.0 13 | ) 14 | 15 | require ( 16 | github.com/BurntSushi/toml v1.1.0 // indirect 17 | github.com/go-ole/go-ole v1.2.6 // indirect 18 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 19 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect 20 | github.com/mattn/go-colorable v0.1.13 // indirect 21 | github.com/mattn/go-isatty v0.0.20 // indirect 22 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect 23 | github.com/shoenig/go-m1cpu v0.1.6 // indirect 24 | github.com/spf13/pflag v1.0.6 // indirect 25 | github.com/tklauser/go-sysconf v0.3.12 // indirect 26 | github.com/tklauser/numcpus v0.6.1 // indirect 27 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 28 | golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d // indirect 29 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect 30 | golang.org/x/mod v0.15.0 // indirect 31 | golang.org/x/sys v0.25.0 // indirect 32 | golang.org/x/tools v0.18.0 // indirect 33 | honnef.co/go/tools v0.3.2 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= 2 | github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 7 | github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 8 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 9 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 10 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 11 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 12 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 13 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 14 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 15 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 16 | github.com/lorenzosaino/go-sysctl v0.3.1 h1:3phX80tdITw2fJjZlwbXQnDWs4S30beNcMbw0cn0HtY= 17 | github.com/lorenzosaino/go-sysctl v0.3.1/go.mod h1:5grcsBRpspKknNS1qzt1eIeRDLrhpKZAtz8Fcuvs1Rc= 18 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 19 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 20 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 21 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 22 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 23 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 24 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 25 | github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8= 26 | github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U= 27 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 28 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 29 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 30 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 31 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 32 | github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= 33 | github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= 34 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= 35 | github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 36 | github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= 37 | github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= 38 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 39 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 40 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 41 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 42 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 43 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 44 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 45 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 46 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 47 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 48 | github.com/u-root/u-root v0.14.0 h1:Ka4T10EEML7dQ5XDvO9c3MBN8z4nuSnGjcd1jmU2ivg= 49 | github.com/u-root/u-root v0.14.0/go.mod h1:hAyZorapJe4qzbLWlAkmSVCJGbfoU9Pu4jpJ1WMluqE= 50 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= 51 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 52 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 53 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 54 | golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d h1:+W8Qf4iJtMGKkyAygcKohjxTk4JPsL9DpzApJ22m5Ic= 55 | golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= 56 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= 57 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 58 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 59 | golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= 60 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 61 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 62 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 63 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 64 | golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= 65 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 66 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 67 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 68 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 69 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 70 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 71 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 72 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 73 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 74 | golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= 75 | golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 76 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 77 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 78 | golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= 79 | golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= 80 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 81 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 82 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 83 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 84 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 85 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 86 | honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34= 87 | honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= 88 | sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= 89 | sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= 90 | -------------------------------------------------------------------------------- /hack/enable-git-hooks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # enable git commit hooks 4 | 5 | LOCAL_REPO="$(git rev-parse --show-toplevel)" 6 | if [[ -d ${LOCAL_REPO}/.git-hooks/ ]]; then 7 | git config core.hooksPath "${LOCAL_REPO}/.git-hooks/" > /dev/null 8 | fi 9 | -------------------------------------------------------------------------------- /kernel_configs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:bionic 2 | 3 | # Install dependencies 4 | RUN apt-get update \ 5 | && apt-get -y -q upgrade \ 6 | && apt-get -y -q install \ 7 | bc bison flex build-essential \ 8 | ccache git libncurses-dev libssl-dev \ 9 | u-boot-tools wget xz-utils \ 10 | && apt-get clean 11 | 12 | COPY build_kernel_configs.sh /root 13 | -------------------------------------------------------------------------------- /kernel_configs/build_kernel_configs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Build all yes kernel config for testing different versions 3 | set -eou pipefail 4 | 5 | build_config() { 6 | folder=${1} 7 | major=${2} 8 | minor=${3} 9 | revision=${4:-1} 10 | cd /root 11 | if [[ ! -s /root/configs/config-${major}.${minor}.${revision} ]]; then 12 | wget --no-check-certificate "https://mirrors.edge.kernel.org/pub/linux/kernel/v${folder}/linux-${major}.${minor}.${revision}.tar.xz" 13 | tar Jxvf "linux-${major}.${minor}.${revision}.tar.xz" 14 | cd "linux-${major}.${minor}.${revision}" 15 | make allyesconfig 16 | cp .config "/root/configs/config-${major}.${minor}.${revision}" 17 | cd /root 18 | rm -rf "linux-${major}.${minor}.${revision}.tar.xz" "linux-${major}.${minor}.${revision}" 19 | fi 20 | } 21 | 22 | #build configs for 4.x up to 4.19 23 | for i in {0..19}; do 24 | build_config 4.x 4 "$i" 25 | done 26 | 27 | #build configs for 5.x up to 5.15 28 | for i in {1..15}; do 29 | build_config 5.x 5 "$i" 30 | done 31 | 32 | #build configs for 6.x up to 6.8 33 | for i in {1..8}; do 34 | build_config 6.x 6 "$i" 35 | done 36 | -------------------------------------------------------------------------------- /kernel_configs/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | services: 4 | 5 | config_builder: 6 | build: 7 | context: ./ 8 | dockerfile: Dockerfile 9 | image: checksec/config_builder 10 | volumes: 11 | - "./configs:/root/configs" 12 | command: ["/root/build_kernel_configs.sh"] 13 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/slimm609/checksec/cmd" 4 | 5 | var ( 6 | version = "dev" 7 | commit = "none" 8 | date = "unknown" 9 | ) 10 | 11 | func main() { 12 | cmd.SetVersionInfo(version, commit, date) 13 | cmd.Execute() 14 | } 15 | -------------------------------------------------------------------------------- /pkg/checksec/canary.go: -------------------------------------------------------------------------------- 1 | package checksec 2 | 3 | import ( 4 | "bytes" 5 | "debug/elf" 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | // canary struct 11 | type canary struct { 12 | Output string 13 | Color string 14 | } 15 | 16 | // StackChk to check for stack_chk_fail value 17 | const StackChk = "__stack_chk_fail" 18 | 19 | // Canary - Check for canary bits 20 | func Canary(name string) *canary { 21 | // To get the dynamic values 22 | // Open the ELF binary file 23 | f, err := os.Open(name) 24 | if err != nil { 25 | fmt.Println("Error:", err) 26 | os.Exit(1) 27 | } 28 | defer f.Close() 29 | file, err := elf.NewFile(f) 30 | if err != nil { 31 | fmt.Println("Error parsing ELF file:", err) 32 | os.Exit(1) 33 | } 34 | res := canary{} 35 | if symbols, err := file.Symbols(); err == nil { 36 | for _, symbol := range symbols { 37 | if bytes.HasPrefix([]byte(symbol.Name), []byte(StackChk)) { 38 | res.Output = "Canary Found" 39 | res.Color = "green" 40 | return &res 41 | } 42 | } 43 | } 44 | 45 | if importedSymbols, err := file.ImportedSymbols(); err == nil { 46 | for _, imp := range importedSymbols { 47 | if bytes.HasPrefix([]byte(imp.Name), []byte(StackChk)) { 48 | res.Output = "Canary Found" 49 | res.Color = "green" 50 | return &res 51 | } 52 | } 53 | } 54 | 55 | if dynamicFunctions, err := FunctionsFromSymbolTable(f); err == nil { 56 | for _, symbol := range dynamicFunctions { 57 | if bytes.HasPrefix([]byte(symbol.Name), []byte(StackChk)) { 58 | res.Output = "Canary Found" 59 | res.Color = "green" 60 | return &res 61 | } 62 | } 63 | } 64 | 65 | res.Output = "No Canary Found" 66 | res.Color = "red" 67 | return &res 68 | } 69 | -------------------------------------------------------------------------------- /pkg/checksec/fortify.go: -------------------------------------------------------------------------------- 1 | package checksec 2 | 3 | import ( 4 | "debug/elf" 5 | "fmt" 6 | "os" 7 | "sort" 8 | "strconv" 9 | "strings" 10 | 11 | uroot "github.com/u-root/u-root/pkg/ldd" 12 | ) 13 | 14 | type fortify struct { 15 | Output string 16 | Color string 17 | Fortifiable string 18 | Fortified string 19 | NoFortify string 20 | LibcSupport string 21 | LibcSupportColor string 22 | NumLibcFunc string 23 | NumFileFunc string 24 | } 25 | 26 | func Fortify(name string, binary *elf.File, ldd string) *fortify { 27 | res := fortify{} 28 | var chkFuncLibs []string 29 | var funcLibs []string 30 | var fileFunc []string 31 | // limit to only checks that can actually be foritifed 32 | // https://github.com/gcc-mirror/gcc/blob/master/gcc/builtins.def#L1112 33 | supportedFuncs := []string{"__memcpy_chk", "__memmove_chk", "__mempcpy_chk", "__memset_chk", "__stpcpy_chk", "__stpncpy_chk", "__strcat_chk", "__strcpy_chk", "__strncat_chk", "__strncpy_chk", "__snprintf_chk", "__sprintf_chk", "__vsnprintf_chk", "__vsprintf_chk", "__fprintf_chk", "__printf_chk", "__vfprintf_chk", "__vprintf_chk"} 34 | sort.Strings(supportedFuncs) 35 | checked := 0 36 | total := 0 37 | 38 | if ldd == "" { 39 | ldd = getLdd(name) 40 | } 41 | 42 | if ldd == "none" || ldd == "unk" { 43 | res.Output = "N/A" 44 | res.Color = "unset" 45 | res.Fortified = "0" 46 | res.Fortifiable = "0" 47 | res.LibcSupport = "N/A" 48 | return &res 49 | } 50 | 51 | libcfile, err := os.Open(ldd) 52 | if err != nil { 53 | fmt.Println("Error:", err) 54 | os.Exit(1) 55 | } 56 | defer libcfile.Close() 57 | libc, err := elf.NewFile(libcfile) 58 | 59 | libcDynSymbols, err := libc.DynamicSymbols() 60 | if err != nil { 61 | libcDynSymbols, err = FunctionsFromSymbolTable(libcfile); 62 | if err != nil { 63 | fmt.Println("Error:", err) 64 | os.Exit(1) 65 | } 66 | } 67 | 68 | // Iterate through dynamic symbols and print their information 69 | for _, sym := range libcDynSymbols { 70 | if strings.HasPrefix(sym.Name, "__") && strings.HasSuffix(sym.Name, "_chk") { 71 | if isInSlice(sym.Name, supportedFuncs) { 72 | chkFuncLibs = append(chkFuncLibs, strings.Trim(sym.Name, "__")) 73 | funcLibs = append(funcLibs, strings.Trim(strings.Trim(sym.Name, "__"), "_chk")) 74 | } 75 | } 76 | } 77 | 78 | if len(chkFuncLibs) > 0 { 79 | res.LibcSupport = "Yes" 80 | res.LibcSupportColor = "green" 81 | res.NumLibcFunc = strconv.Itoa(len(chkFuncLibs)) 82 | } else { 83 | res.LibcSupport = "No" 84 | res.LibcSupportColor = "red" 85 | res.NumLibcFunc = "0" 86 | } 87 | 88 | f, err := os.Open(name) 89 | if err != nil { 90 | fmt.Println("Error:", err) 91 | os.Exit(1) 92 | } 93 | defer f.Close() 94 | 95 | file, err := elf.NewFile(f) 96 | if err != nil { 97 | fmt.Println("Error parsing ELF file:", err) 98 | os.Exit(1) 99 | } 100 | 101 | dynSymbols, err := file.DynamicSymbols() 102 | if err != nil { 103 | dynSymbols, err = FunctionsFromSymbolTable(f) 104 | if err != nil { 105 | fmt.Println("Error:", err) 106 | os.Exit(1) 107 | } 108 | } 109 | 110 | // Iterate through dynamic symbols and print their information 111 | for _, sym := range dynSymbols { 112 | fileFunc = append(fileFunc, strings.Trim(sym.Name, "__")) 113 | } 114 | 115 | sort.Strings(chkFuncLibs) 116 | sort.Strings(funcLibs) 117 | sort.Strings(fileFunc) 118 | 119 | for _, item := range chkFuncLibs { 120 | if isInSlice(item, fileFunc) { 121 | checked++ 122 | } 123 | } 124 | 125 | total = checked 126 | for _, item := range funcLibs { 127 | if isInSlice(item, fileFunc) { 128 | total++ 129 | } 130 | } 131 | 132 | if checked > 0 { 133 | res.Output = "Yes" 134 | res.Color = "green" 135 | res.Fortified = strconv.Itoa(checked) 136 | res.Fortifiable = strconv.Itoa(total) 137 | res.NoFortify = strconv.Itoa(total - checked) 138 | res.NumFileFunc = strconv.Itoa(len(dynSymbols)) 139 | return &res 140 | } else { 141 | res.Output = "No" 142 | res.Color = "red" 143 | res.Fortified = strconv.Itoa(checked) 144 | res.Fortifiable = strconv.Itoa(total) 145 | res.NoFortify = strconv.Itoa(total - checked) 146 | res.NumFileFunc = strconv.Itoa(len(dynSymbols)) 147 | return &res 148 | } 149 | 150 | } 151 | 152 | func getLdd(filename string) string { 153 | dynamic := false 154 | file, err := elf.Open(filename) 155 | if err != nil { 156 | fmt.Println("Error:", err) 157 | os.Exit(1) 158 | } 159 | defer file.Close() 160 | for _, prog := range file.Progs { 161 | if prog.Type == elf.PT_DYNAMIC { 162 | dynamic = true 163 | } 164 | } 165 | 166 | files, _ := uroot.FList(filename) 167 | if dynamic && len(files) == 0 { 168 | fmt.Println("Warning: Dynamic Binary found but missing libc. Fortify results will be skipped") 169 | return "unk" 170 | } 171 | 172 | for _, libc := range files { 173 | if strings.Contains(libc, "libc.") { 174 | return libc 175 | } 176 | } 177 | return "none" 178 | } 179 | 180 | func isInSlice(item string, slice []string) bool { 181 | for _, s := range slice { 182 | if s == item { 183 | return true 184 | } 185 | } 186 | return false 187 | } 188 | -------------------------------------------------------------------------------- /pkg/checksec/kernel.go: -------------------------------------------------------------------------------- 1 | package checksec 2 | 3 | import ( 4 | "bufio" 5 | "compress/gzip" 6 | "fmt" 7 | "io" 8 | "log" 9 | "os" 10 | "strings" 11 | 12 | "github.com/opencontainers/selinux/go-selinux" 13 | ) 14 | 15 | func KernelConfig(name string) ([]interface{}, []interface{}) { 16 | var Results []interface{} 17 | var ColorResults []interface{} 18 | var Secolor string 19 | var SelinuxStatus string 20 | var Seres []interface{} 21 | var Secolors []interface{} 22 | 23 | kernelChecks := []map[string]interface{}{ 24 | {"name": "CONFIG_COMPAT_BRK", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Kernel Heap Randomization"}}, 25 | {"name": "CONFIG_STACKPROTECTOR", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Stack Protector"}}, 26 | {"name": "CONFIG_STACKPROTECTOR_STRONG", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Stack Protector Strong"}}, 27 | {"name": "CONFIG_CC_STACKPROTECTOR", "values": map[string]string{"arch": "all", "expect": "y", "desc": "GCC Stack Protector"}}, 28 | {"name": "CONFIG_CC_STACKPROTECTOR_REGULAR", "values": map[string]string{"arch": "all", "expect": "y", "desc": "GCC Stack Protector Regular"}}, 29 | {"name": "CONFIG_CC_STACKPROTECTOR_AUTO", "values": map[string]string{"arch": "all", "expect": "y", "desc": "GCC Stack Protector Auto"}}, 30 | {"name": "CONFIG_CC_STACKPROTECTOR_STRONG", "values": map[string]string{"arch": "all", "expect": "y", "desc": "GCC Stack Protector Strong"}}, 31 | {"name": "CONFIG_GCC_PLUGIN_STRUCTLEAK", "values": map[string]string{"arch": "all", "expect": "y", "desc": "GCC structleak plugin"}}, 32 | {"name": "CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL", "values": map[string]string{"arch": "all", "expect": "y", "desc": "GCC structleak by ref plugin"}}, 33 | {"name": "CONFIG_SLAB_FREELIST_RANDOM", "values": map[string]string{"arch": "all", "expect": "y", "desc": "SLAB freelist randomization"}}, 34 | {"name": "CPU_SW_DOMAIN_PAN", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Use CPU domains"}}, 35 | {"name": "CONFIG_VMAP_STACK", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Virtually-mapped kernel stack"}}, 36 | {"name": "CONFIG_STRICT_DEVMEM", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Restrict /dev/mem access"}}, 37 | {"name": "CONFIG_STRICT_KERNEL_RWX", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Restrict Kernel RWX"}}, 38 | {"name": "CONFIG_STRICT_MODULE_RWX", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Restrict Module RWX"}}, 39 | {"name": "CONFIG_IO_STRICT_DEVMEM", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Restrict I/O access to /dev/mem"}}, 40 | {"name": "CONFIG_REFCOUNT_FULL", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Full reference count validation"}}, 41 | {"name": "CONFIG_HARDENED_USERCOPY", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Hardened Usercopy"}}, 42 | {"name": "CONFIG_FORTIFY_SOURCE", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Harden str/mem functions"}}, 43 | {"name": "CONFIG_DEVKMEM", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Restrict /dev/kmem access"}}, 44 | {"name": "CONFIG_DEBUG_STRICT_USER_COPY_CHECKS", "values": map[string]string{"arch": "amd", "expect": "y", "desc": "Strict user copy checks"}}, 45 | {"name": "CONFIG_ARM_KERNMEM_PERMS", "values": map[string]string{"arch": "arm", "expect": "y", "desc": "Restrict kernel memory permissions"}}, 46 | {"name": "CONFIG_DEBUG_ALIGN_RODATA", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Make rodata strictly non-excutable"}}, 47 | {"name": "CONFIG_UNMAP_KERNEL_AT_EL0", "values": map[string]string{"arch": "arm64", "expect": "y", "desc": "Unmap kernel in userspace (KAISER)"}}, 48 | {"name": "CONFIG_HARDEN_BRANCH_PREDICTOR", "values": map[string]string{"arch": "arm64", "expect": "y", "desc": "Harden branch predictor"}}, 49 | {"name": "CONFIG_HARDEN_EL2_VECTORS", "values": map[string]string{"arch": "arm64", "expect": "y", "desc": "Harden EL2 vector mapping"}}, 50 | {"name": "CONFIG_ARM64_SSBD", "values": map[string]string{"arch": "arm64", "expect": "y", "desc": "Speculative store bypass disable"}}, 51 | {"name": "CONFIG_ARM64_SW_TTBR0_PAN", "values": map[string]string{"arch": "arm64", "expect": "y", "desc": "Emulate privileged access never"}}, 52 | {"name": "CONFIG_RANDOMIZE_BASE", "values": map[string]string{"arch": "arm64", "expect": "y", "desc": "Randomize address of kernel image"}}, 53 | {"name": "CONFIG_RANDOMIZE_MODULE_REGION_FULL", "values": map[string]string{"arch": "arm64", "expect": "y", "desc": "Randomize module region over 4GB"}}, 54 | {"name": "CONFIG_DEBUG_WX", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Report dangerous memory permissions"}}, 55 | {"name": "CONFIG_SYN_COOKIES", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Syn Flood protection"}}, 56 | {"name": "CONFIG_LIST_HARDENED", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Check integrity of linked list manipulation"}}, 57 | {"name": "CONFIG_DEBUG_NOTIFIERS", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Debug notifier call chains"}}, 58 | {"name": "CONFIG_DEBUG_LIST", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Debug linked list manipulation"}}, 59 | {"name": "CONFIG_DEBUG_SG", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Debug SG table operations"}}, 60 | {"name": "CONFIG_DEBUG_VIRTUAL", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Debug VM translations"}}, 61 | {"name": "CONFIG_SCHED_STACK_END_CHECK", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Detect stack corruption on calls to schedule"}}, 62 | {"name": "CONFIG_SECCOMP", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Safely execute untrusted bytecode"}}, 63 | {"name": "CONFIG_SECCOMP_FILTER", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Secure computing for BPF"}}, 64 | {"name": "CONFIG_LDISC_AUTOLOAD", "values": map[string]string{"arch": "all", "expect": "is not set", "desc": "Automatically load TTY Line Disciplines"}}, 65 | {"name": "CONFIG_SECURITY", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Enable different security models"}}, 66 | {"name": "CONFIG_SECURITY_YAMA", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Security Yama support"}}, 67 | {"name": "CONFIG_SECURITY_LANDLOCK", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Security Landlock support"}}, 68 | {"name": "CONFIG_SECURITY_SELINUX", "values": map[string]string{"arch": "all", "expect": "y", "desc": "SELinux Kernel Flag"}}, 69 | {"name": "CONFIG_SECURITY_SELINUX_BOOTPARAM", "values": map[string]string{"arch": "all", "expect": "is not set", "desc": "Allow disabling selinux at boot"}}, 70 | {"name": "CONFIG_SECURITY_SELINUX_DEVELOP", "values": map[string]string{"arch": "all", "expect": "is not set", "desc": "SELinux Development Support"}}, 71 | {"name": "CONFIG_SECURITY_SELINUX_DEBUG", "values": map[string]string{"arch": "all", "expect": "is not set", "desc": "SELinux Debug Support"}}, 72 | {"name": "CONFIG_SECURITY_LOCKDOWN_LSM", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Enable the lockdown LSM"}}, 73 | {"name": "CONFIG_SECURITY_LOCKDOWN_LSM_EARLY", "values": map[string]string{"arch": "all", "expect": "y", "desc": "Enable the lockdown LSM early in boot"}}, 74 | {"name": "CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY", "values": map[string]string{"arch": "all", "expect": "y", "desc": "kernel runs in confidentiality mode"}}, 75 | {"name": "CONFIG_HARDENED_USERCOPY_FALLBACK", "values": map[string]string{"arch": "all", "expect": "is not set", "desc": "Allow usercopy whitelist violations to fallback to object size"}}, 76 | {"name": "CONFIG_HARDENED_USERCOPY_PAGESPAN", "values": map[string]string{"arch": "all", "expect": "is not set", "desc": "Refuse to copy allocations that span multiple pages"}}, 77 | } 78 | 79 | data, err := parseKernelConfig(name) 80 | if err != nil { 81 | fmt.Println("Error:", err) 82 | os.Exit(1) 83 | } 84 | for configKey, configVal := range data { 85 | for _, k := range kernelChecks { 86 | var res []interface{} 87 | var colors []interface{} 88 | var output string 89 | var color string 90 | if k["name"] == configKey { 91 | values := k["values"].(map[string]string) 92 | if values["expect"] == configVal { 93 | output = "Enabled" 94 | color = "green" 95 | } else { 96 | output = "Disabled" 97 | color = "red" 98 | } 99 | res = []interface{}{ 100 | map[string]interface{}{ 101 | "name": k["name"], 102 | "value": output, 103 | "desc": values["desc"], 104 | "type": "Kernel Config", 105 | }, 106 | } 107 | colors = []interface{}{ 108 | map[string]interface{}{ 109 | "name": k["name"], 110 | "value": output, 111 | "color": color, 112 | "desc": values["desc"], 113 | "type": "Kernel Config", 114 | }, 115 | } 116 | } 117 | Results = append(Results, res...) 118 | ColorResults = append(ColorResults, colors...) 119 | 120 | } 121 | } 122 | 123 | sestatus := selinux.GetEnabled() 124 | if sestatus { 125 | Secolor = "green" 126 | SelinuxStatus = "Enabled" 127 | } else { 128 | Secolor = "red" 129 | SelinuxStatus = "Disabled" 130 | } 131 | 132 | Seres = []interface{}{ 133 | map[string]interface{}{ 134 | "name": "SELinux", 135 | "value": SelinuxStatus, 136 | "desc": "SELinux Enabled", 137 | "type": "SELinux", 138 | }, 139 | } 140 | Secolors = []interface{}{ 141 | map[string]interface{}{ 142 | "name": "SELinux", 143 | "value": SelinuxStatus, 144 | "color": Secolor, 145 | "desc": "SELinux Enabled", 146 | "type": "SELinux", 147 | }, 148 | } 149 | Results = append(Results, Seres...) 150 | ColorResults = append(ColorResults, Secolors...) 151 | 152 | return Results, ColorResults 153 | } 154 | 155 | func parseKernelConfig(filename string) (map[string]string, error) { 156 | stat, err := os.Stat(filename) 157 | var bytes []byte 158 | if err != nil { 159 | return nil, err 160 | } 161 | if !stat.Mode().IsRegular() { 162 | return nil, fmt.Errorf("Not a file: %s", filename) 163 | } 164 | if strings.HasSuffix(filename, ".gz") { 165 | file, err := os.Open(filename) 166 | if err != nil { 167 | log.Fatal(err) 168 | } 169 | defer file.Close() 170 | reader, err := gzip.NewReader(file) 171 | if err != nil { 172 | log.Fatal(err) 173 | } 174 | defer reader.Close() 175 | 176 | bytes, err = io.ReadAll(reader) 177 | if err != nil { 178 | log.Fatal(err) 179 | } 180 | } else { 181 | bytes, err = os.ReadFile(filename) 182 | if err != nil { 183 | return nil, err 184 | } 185 | } 186 | 187 | options := make(map[string]string) 188 | 189 | scanner := bufio.NewScanner(strings.NewReader(string(bytes))) 190 | for scanner.Scan() { 191 | line := scanner.Text() 192 | if strings.HasPrefix(line, "CONFIG_") { 193 | split := strings.Split(line, "=") 194 | options[split[0]] = strings.TrimPrefix(line, fmt.Sprintf("%s=", split[0])) 195 | } else if strings.HasPrefix(line, "# CONFIG_") && strings.HasSuffix(scanner.Text(), "is not set") { 196 | opt := strings.TrimPrefix(line, "# ") 197 | opt = strings.TrimSuffix(opt, " is not set") 198 | options[opt] = "is not set" 199 | } 200 | } 201 | 202 | return options, nil 203 | } 204 | -------------------------------------------------------------------------------- /pkg/checksec/nx.go: -------------------------------------------------------------------------------- 1 | package checksec 2 | 3 | import ( 4 | "debug/elf" 5 | ) 6 | 7 | type nx struct { 8 | Output string 9 | Color string 10 | } 11 | 12 | func NX(name string, binary *elf.File) *nx { 13 | res := nx{} 14 | if len(binary.Progs) == 0 { 15 | res.Color = "italic" 16 | res.Output = "N/A" 17 | return &res 18 | } 19 | for _, p := range binary.Progs { 20 | if p.Type == elf.PT_GNU_STACK && p.Flags&elf.PF_X == 0 { 21 | res.Color = "green" 22 | res.Output = "NX enabled" 23 | return &res 24 | } 25 | } 26 | 27 | res.Color = "red" 28 | res.Output = "NX disabled" 29 | return &res 30 | } 31 | -------------------------------------------------------------------------------- /pkg/checksec/pie.go: -------------------------------------------------------------------------------- 1 | package checksec 2 | 3 | import ( 4 | "debug/elf" 5 | ) 6 | 7 | type pie struct { 8 | Output string 9 | Color string 10 | } 11 | 12 | func PIE(name string, binary *elf.File) *pie { 13 | res := pie{} 14 | switch binary.Type { 15 | case elf.ET_DYN: 16 | res.Color = "green" 17 | res.Output = "PIE Enabled" 18 | case elf.ET_REL: 19 | res.Color = "yellow" 20 | res.Output = "REL" 21 | default: 22 | res.Color = "red" 23 | res.Output = "PIE Disabled" 24 | } 25 | 26 | return &res 27 | } 28 | -------------------------------------------------------------------------------- /pkg/checksec/relro.go: -------------------------------------------------------------------------------- 1 | package checksec 2 | 3 | import ( 4 | "debug/elf" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type relro struct { 10 | Output string 11 | Color string 12 | } 13 | 14 | func RELRO(name string) *relro { 15 | res := relro{} 16 | relroHeader := false 17 | bindNow := false 18 | 19 | // To get the dynamic values 20 | // Open the ELF binary file 21 | file, err := elf.Open(name) 22 | if err != nil { 23 | fmt.Println("Error:", err) 24 | os.Exit(1) 25 | } 26 | defer file.Close() 27 | 28 | if len(file.Progs) == 0 { 29 | res.Color = "italic" 30 | res.Output = "N/A" 31 | return &res 32 | } 33 | 34 | // check both bind and bind_flag. 35 | // if DT_BIND_NOW == 0, then it is set 36 | // if DT_FLAGS == 8, then DF_BIND_NOW is set 37 | // this is depending on the compiler version used. 38 | bind, _ := file.DynValue(elf.DT_BIND_NOW) 39 | if (len(bind) == 0) { 40 | bind, _ = DynValueFromPTDynamic(file, elf.DT_BIND_NOW) 41 | } 42 | bind_flag, _ := file.DynValue(elf.DT_FLAGS) 43 | if (len(bind_flag) == 0) { 44 | bind_flag, _ = DynValueFromPTDynamic(file, elf.DT_FLAGS) 45 | } 46 | 47 | if (len(bind) > 0 && bind[0] == 0) || (len(bind_flag) > 0 && bind_flag[0] == 8) { 48 | bindNow = true 49 | } 50 | 51 | for _, prog := range file.Progs { 52 | if prog.Type == elf.PT_GNU_RELRO { 53 | relroHeader = true 54 | } 55 | } 56 | 57 | if bindNow == true { 58 | res.Color = "green" 59 | res.Output = "Full RELRO" 60 | return &res 61 | } else if relroHeader == true { 62 | res.Color = "yellow" 63 | res.Output = "Partial RELRO" 64 | return &res 65 | } else { 66 | res.Color = "red" 67 | res.Output = "No RELRO" 68 | return &res 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/checksec/rpath.go: -------------------------------------------------------------------------------- 1 | package checksec 2 | 3 | import ( 4 | "debug/elf" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type rpath struct { 10 | Output string 11 | Color string 12 | } 13 | 14 | func RPATH(name string) *rpath { 15 | res := rpath{} 16 | file, err := elf.Open(name) 17 | if err != nil { 18 | fmt.Println("Error:", err) 19 | os.Exit(1) 20 | } 21 | defer file.Close() 22 | 23 | rpath, _ := file.DynValue(15) 24 | if len(rpath) == 0 { 25 | res.Output = "No RPATH" 26 | res.Color = "green" 27 | } else { 28 | res.Output = "RPATH" 29 | res.Color = "red" 30 | } 31 | return &res 32 | } 33 | -------------------------------------------------------------------------------- /pkg/checksec/runpath.go: -------------------------------------------------------------------------------- 1 | package checksec 2 | 3 | import ( 4 | "debug/elf" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type runpath struct { 10 | Output string 11 | Color string 12 | } 13 | 14 | // Detect runpath in binary 15 | func RUNPATH(name string) *runpath { 16 | res := runpath{} 17 | file, err := elf.Open(name) 18 | if err != nil { 19 | fmt.Println("Error:", err) 20 | os.Exit(1) 21 | } 22 | defer file.Close() 23 | 24 | runpath, _ := file.DynValue(29) 25 | if len(runpath) == 0 { 26 | res.Output = "No RUNPATH" 27 | res.Color = "green" 28 | } else { 29 | res.Output = "RUNPATH" 30 | res.Color = "red" 31 | } 32 | return &res 33 | } 34 | -------------------------------------------------------------------------------- /pkg/checksec/symbols.go: -------------------------------------------------------------------------------- 1 | package checksec 2 | 3 | import ( 4 | "bytes" 5 | "debug/elf" 6 | "encoding/binary" 7 | "fmt" 8 | "os" 9 | ) 10 | 11 | type symbols struct { 12 | Output string 13 | Color string 14 | } 15 | 16 | // SYMBOLS detects usage of elf symbols 17 | func SYMBOLS(name string) *symbols { 18 | res := symbols{} 19 | file, err := elf.Open(name) 20 | if err != nil { 21 | fmt.Println("Error:", err) 22 | os.Exit(1) 23 | } 24 | defer file.Close() 25 | 26 | symbols, _ := file.Symbols() 27 | if len(symbols) == 0 { 28 | res.Output = "No Symbols" 29 | res.Color = "green" 30 | } else { 31 | res.Output = fmt.Sprintf("%d symbols", len(symbols)) 32 | res.Color = "red" 33 | } 34 | return &res 35 | } 36 | 37 | func DynValueFromPTDynamic(file *elf.File, tag elf.DynTag) ([]uint64, error) { 38 | var res []uint64 39 | 40 | for _, prog := range file.Progs { 41 | if prog.Type == elf.PT_DYNAMIC { 42 | data := make([]byte, prog.Filesz) 43 | _, err := prog.ReadAt(data, 0) 44 | if err != nil { 45 | fmt.Println("Error reading dynamic section:", err) 46 | return res, err 47 | } 48 | 49 | if file.Class == elf.ELFCLASS64 { 50 | for i := 0; i < len(data); i += 16 { // Each entry is typically 16 bytes 51 | if i+8 > len(data) { 52 | break 53 | } 54 | if elf.DynTag(binary.LittleEndian.Uint64(data[i : i+8])) == tag { 55 | value := binary.LittleEndian.Uint64(data[i+8 : i+16]) 56 | return append(res, value), err 57 | } 58 | } 59 | } else { 60 | for i := 0; i < len(data); i += 8 { // Each entry is typically 8 bytes 61 | if i+4 > len(data) { 62 | break 63 | } 64 | if elf.DynTag(binary.LittleEndian.Uint32(data[i : i+4])) == tag { 65 | value := uint64(binary.LittleEndian.Uint32(data[i+4 : i+8])) 66 | return append(res, value), err 67 | } 68 | } 69 | } 70 | } 71 | } 72 | return res, nil 73 | } 74 | 75 | func FunctionsFromSymbolTable(file *os.File) ([]elf.Symbol, error) { 76 | // Iterate over the dynamic section to handle stripped binaries with no sections 77 | var functions []elf.Symbol 78 | 79 | f, err := elf.NewFile(file) 80 | if err != nil { 81 | fmt.Println("Error parsing ELF file:", err) 82 | return functions, err 83 | } 84 | 85 | symTabOffset, _ := DynValueFromPTDynamic(f, elf.DT_SYMTAB) 86 | strTabOffset, _ := DynValueFromPTDynamic(f, elf.DT_STRTAB) 87 | strTabSize, _ := DynValueFromPTDynamic(f, elf.DT_STRSZ) 88 | 89 | if symTabOffset == nil || strTabSize == nil || strTabOffset == nil { 90 | return functions, err 91 | } 92 | 93 | // Read the symbol table 94 | symData := make([]byte, symTabOffset[0]) 95 | _, err = file.ReadAt(symData, int64(symTabOffset[0])) 96 | if err != nil { 97 | fmt.Println("Error reading symbol table:", err) 98 | return functions, err 99 | } 100 | 101 | // Read the string table 102 | strData := make([]byte, strTabSize[0]) 103 | _, err = file.ReadAt(strData, int64(strTabOffset[0])) 104 | if err != nil { 105 | fmt.Println("Error reading string table:", err) 106 | return functions, err 107 | } 108 | 109 | // Determine the size of the symbol entry based on architecture 110 | var symSize int 111 | var is64Bit bool 112 | if f.Class == elf.ELFCLASS32 { 113 | symSize = binary.Size(elf.Sym32{}) 114 | is64Bit = false 115 | } else { 116 | symSize = binary.Size(elf.Sym64{}) 117 | is64Bit = true 118 | } 119 | 120 | // Iterate over the symbol table to extract function names 121 | for i := 0; i < len(symData); i += symSize { 122 | if i+symSize > len(symData) { 123 | break 124 | } 125 | if is64Bit { 126 | sym := elf.Sym64{} 127 | err := binary.Read(bytes.NewReader(symData[i:i+symSize]), binary.LittleEndian, &sym) 128 | if err != nil { 129 | fmt.Println("Error reading symbol:", err) 130 | continue 131 | } 132 | 133 | // Check if the symbol is a function 134 | if elf.STT_FUNC == elf.SymType(sym.Info&0x0F) { 135 | // Ensure the index is within bounds 136 | if int(sym.Name) < len(strData) { 137 | funcName := strData[sym.Name:] 138 | // Find the end of the string 139 | endIndex := 0 140 | for endIndex = 0; endIndex < len(funcName); endIndex++ { 141 | if funcName[endIndex] == 0 { 142 | break 143 | } 144 | } 145 | function := elf.Symbol{Name: string(funcName[:endIndex])} 146 | functions = append(functions, function) 147 | } 148 | } 149 | } else { 150 | sym := elf.Sym32{} 151 | err := binary.Read(bytes.NewReader(symData[i:i+symSize]), binary.LittleEndian, &sym) 152 | if err != nil { 153 | fmt.Println("Error reading symbol:", err) 154 | continue 155 | } 156 | 157 | // Check if the symbol is a function 158 | if elf.STT_FUNC == elf.SymType(sym.Info&0x0F) { 159 | // Ensure the index is within bounds 160 | if int(sym.Name) < len(strData) { 161 | funcName := strData[sym.Name:] 162 | // Find the end of the string 163 | endIndex := 0 164 | for endIndex = 0; endIndex < len(funcName); endIndex++ { 165 | if funcName[endIndex] == 0 { 166 | break 167 | } 168 | } 169 | function := elf.Symbol{Name: string(funcName[:endIndex])} 170 | functions = append(functions, function) 171 | } 172 | } 173 | } 174 | } 175 | return functions, err 176 | } 177 | -------------------------------------------------------------------------------- /pkg/checksec/sysctl.go: -------------------------------------------------------------------------------- 1 | package checksec 2 | 3 | import ( 4 | "runtime" 5 | 6 | "github.com/lorenzosaino/go-sysctl" 7 | ) 8 | 9 | func SysctlCheck() ([]interface{}, []interface{}) { 10 | var Results []interface{} 11 | var ColorResults []interface{} 12 | 13 | sysctlChecks := []map[string]interface{}{ 14 | {"name": "fs.protected_symlinks", "desc": "Protected symlinks", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Enabled", "color": "green"}}}, 15 | {"name": "fs.protected_hardlinks", "desc": "Protected hardlinks", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Enabled", "color": "green"}}}, 16 | {"name": "net.ipv4.conf.all.rp_filter", "desc": "Ipv4 reverse path filtering", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Enabled", "color": "green"}}}, 17 | {"name": "kernel.yama.ptrace_scope", "desc": "YAMA", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Enabled", "color": "green"}}}, 18 | {"name": "kernel.exec-shield", "desc": "Exec Shield", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Enabled", "color": "green"}}}, 19 | {"name": "kernel.unprivileged_bpf_disabled", "desc": "Unprivileged BPF Disabled", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Enabled", "color": "green"}}}, 20 | {"name": "kernel.randomize_va_space", "desc": "Vanilla Kernel ASLR", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Partial", "color": "yellow"}, "2": {"res": "Enabled", "color": "green"}}}, 21 | {"name": "kernel.dmesg_restrict", "desc": "Dmesg Restrictions", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Enabled", "color": "green"}}}, 22 | {"name": "kernel.kptr_restrict", "desc": "Kernel Pointer Restrictions", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Partial", "color": "yellow"}, "2": {"res": "Enabled", "color": "green"}}}, 23 | {"name": "fs.protected_fifos", "desc": "Protected fifos", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Partial", "color": "yellow"}, "2": {"res": "Enabled", "color": "green"}}}, 24 | {"name": "fs.protected_regular", "desc": "Protected regular", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Partial", "color": "yellow"}, "2": {"res": "Enabled", "color": "green"}}}, 25 | {"name": "kernel.perf_event_paranoid", "desc": "Performance events by normal users", "values": map[string]map[string]string{"-1": {"res": "Disabled", "color": "red"}, "0": {"res": "Disabled", "color": "red"}, "1": {"res": "Partial", "color": "yellow"}, "2": {"res": "Enabled", "color": "green"}}}, 26 | {"name": "dev.tty.ldisc_autoload", "desc": "Disable Autoload TTY Line Disciplines", "values": map[string]map[string]string{"1": {"res": "Disabled", "color": "red"}, "0": {"res": "Enabled", "color": "green"}}}, 27 | {"name": "dev.tty.legacy_tiocsti", "desc": "Disable Legacy TIOCSTI (breaks screen readers)", "values": map[string]map[string]string{"1": {"res": "Disabled", "color": "red"}, "0": {"res": "Enabled", "color": "green"}}}, 28 | {"name": "kernel.perf_event_paranoid", "desc": "Block non-uid-0 profiling", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Partial", "color": "yellow"}, "2": {"res": "Enabled", "color": "green"}, "3": {"res": "Enabled", "color": "green"}}}, 29 | {"name": "kernel.kexec_load_disabled", "desc": "Turn off kexec", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Enabled", "color": "green"}}}, 30 | {"name": "net.core.bpf_jit_harden", "desc": "Turn on BPF JIT hardening", "values": map[string]map[string]string{"0": {"res": "Disabled", "color": "red"}, "1": {"res": "Partial", "color": "yellow"}, "2": {"res": "Enabled", "color": "green"}}}, 31 | {"name": "vm.unprivileged_userfaultfd", "desc": "Disable userfaultfd usage", "values": map[string]map[string]string{"1": {"res": "Disabled", "color": "red"}, "0": {"res": "Enabled", "color": "green"}}}, 32 | {"name": "fs.suid_dumpable", "desc": "Ensure suid binaries can't be dumped", "values": map[string]map[string]string{"2": {"res": "Disabled", "color": "red"}, "1": {"res": "Partial", "color": "yellow"}, "0": {"res": "Enabled", "color": "green"}}}, 33 | } 34 | 35 | for _, s := range sysctlChecks { 36 | var res []interface{} 37 | var colors []interface{} 38 | var output string 39 | var color string 40 | check, _ := sysctl.Get(s["name"].(string)) 41 | 42 | values := s["values"].(map[string]map[string]string) 43 | 44 | if len(check) == 0 { 45 | if runtime.GOOS == "linux" { 46 | output = "Unknown" 47 | } else { 48 | output = "N/A" 49 | } 50 | color = "italic" 51 | } else { 52 | output = values[check]["res"] 53 | color = values[check]["color"] 54 | } 55 | res = []interface{}{ 56 | map[string]interface{}{ 57 | "name": s["name"], 58 | "value": output, 59 | "desc": s["desc"], 60 | "type": "Sysctl", 61 | }, 62 | } 63 | colors = []interface{}{ 64 | map[string]interface{}{ 65 | "name": s["name"], 66 | "value": output, 67 | "color": color, 68 | "desc": s["desc"], 69 | "type": "Sysctl", 70 | }, 71 | } 72 | Results = append(Results, res...) 73 | ColorResults = append(ColorResults, colors...) 74 | } 75 | 76 | return Results, ColorResults 77 | } 78 | -------------------------------------------------------------------------------- /pkg/utils/checks.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/slimm609/checksec/pkg/checksec" 7 | ) 8 | 9 | // RunFileChecks - Run the file checks 10 | func RunFileChecks(filename string, libc string) ([]interface{}, []interface{}) { 11 | 12 | binary := GetBinary(filename) 13 | relro := checksec.RELRO(filename) 14 | canary := checksec.Canary(filename) 15 | nx := checksec.NX(filename, binary) 16 | pie := checksec.PIE(filename, binary) 17 | rpath := checksec.RPATH(filename) 18 | runpath := checksec.RUNPATH(filename) 19 | symbols := checksec.SYMBOLS(filename) 20 | fortify := checksec.Fortify(filename, binary, libc) 21 | 22 | data := []interface{}{ 23 | map[string]interface{}{ 24 | "name": filename, 25 | "checks": map[string]interface{}{ 26 | "relro": relro.Output, 27 | "canary": canary.Output, 28 | "nx": nx.Output, 29 | "pie": pie.Output, 30 | "rpath": rpath.Output, 31 | "runpath": runpath.Output, 32 | "symbols": symbols.Output, 33 | "fortify_source": fortify.Output, 34 | "fortified": fortify.Fortified, 35 | "fortifyable": fortify.Fortifiable, 36 | }, 37 | }, 38 | } 39 | 40 | color := []interface{}{ 41 | map[string]interface{}{ 42 | "name": filename, 43 | "checks": map[string]interface{}{ 44 | "canary": canary.Output, 45 | "canaryColor": canary.Color, 46 | "fortified": fortify.Fortified, 47 | "fortifiedColor": "unset", 48 | "fortifyable": fortify.Fortifiable, 49 | "fortifyableColor": "unset", 50 | "fortify_source": fortify.Output, 51 | "fortify_sourceColor": fortify.Color, 52 | "nx": nx.Output, 53 | "nxColor": nx.Color, 54 | "pie": pie.Output, 55 | "pieColor": pie.Color, 56 | "relro": relro.Output, 57 | "relroColor": relro.Color, 58 | "rpath": rpath.Output, 59 | "rpathColor": rpath.Color, 60 | "runpath": runpath.Output, 61 | "runpathColor": runpath.Color, 62 | "symbols": symbols.Output, 63 | "symbolsColor": symbols.Color, 64 | }, 65 | }, 66 | } 67 | 68 | return data, color 69 | } 70 | 71 | // ParseKernel - Parses the kernel config and runs the checks 72 | func ParseKernel(filename string) (any, any) { 73 | 74 | kernelCheckResults, kernelCheckResultsColors := checksec.KernelConfig(filename) 75 | sysctlCheckResults, sysctlCheckResultsColors := checksec.SysctlCheck() 76 | 77 | data := reflect.AppendSlice(reflect.ValueOf(kernelCheckResults), reflect.ValueOf(sysctlCheckResults)).Interface() 78 | dataColors := reflect.AppendSlice(reflect.ValueOf(kernelCheckResultsColors), reflect.ValueOf(sysctlCheckResultsColors)).Interface() 79 | 80 | return data, dataColors 81 | 82 | } 83 | -------------------------------------------------------------------------------- /pkg/utils/filePrinter.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "encoding/xml" 6 | "fmt" 7 | "log" 8 | 9 | "sigs.k8s.io/yaml" 10 | ) 11 | 12 | type SecurityCheck struct { 13 | Name string `json:"name"` 14 | Checks struct { 15 | Canary string `json:"canary"` 16 | Fortified string `json:"fortified"` 17 | FortifyAble string `json:"fortifyable"` 18 | FortifySource string `json:"fortify_source"` 19 | NX string `json:"nx"` 20 | PIE string `json:"pie"` 21 | Relro string `json:"relro"` 22 | RPath string `json:"rpath"` 23 | RunPath string `json:"runpath"` 24 | Symbols string `json:"symbols"` 25 | } `json:"checks"` 26 | } 27 | 28 | type SecurityCheckColor struct { 29 | Name string `json:"name"` 30 | Checks struct { 31 | Canary string `json:"canary"` 32 | CanaryColor string `json:"canaryColor"` 33 | Fortified string `json:"fortified"` 34 | FortifyAble string `json:"fortifyable"` 35 | FortifySource string `json:"fortify_source"` 36 | FortifySourceColor string `json:"fortify_sourceColor"` 37 | NX string `json:"nx"` 38 | NXColor string `json:"nxColor"` 39 | PIE string `json:"pie"` 40 | PIEColor string `json:"pieColor"` 41 | Relro string `json:"relro"` 42 | RelroColor string `json:"relroColor"` 43 | RPath string `json:"rpath"` 44 | RPathColor string `json:"rpathColor"` 45 | RunPath string `json:"runpath"` 46 | RunPathColor string `json:"runpathColor"` 47 | Symbols string `json:"symbols"` 48 | SymbolsColor string `json:"symbolsColor"` 49 | } `json:"checks"` 50 | } 51 | 52 | func FilePrinter(outputFormat string, data interface{}, colors interface{}, noBanner bool, noHeader bool) { 53 | 54 | formatted, err := json.MarshalIndent(data, "", " ") 55 | if err != nil { 56 | fmt.Printf("err: %v\n", err) 57 | } 58 | formattedcolor, err := json.MarshalIndent(colors, "", " ") 59 | if err != nil { 60 | fmt.Printf("err: %v\n", err) 61 | } 62 | var securityChecks []SecurityCheck 63 | // Unmarshal JSON data 64 | if err := json.Unmarshal([]byte(formatted), &securityChecks); err != nil { 65 | fmt.Println("Error:", err) 66 | return 67 | } 68 | 69 | if outputFormat == "yaml" { 70 | yamlResponse, err := yaml.JSONToYAML(formatted) 71 | if err != nil { 72 | fmt.Printf("err: %v\n", err) 73 | } 74 | fmt.Println(string(yamlResponse)) 75 | } else if outputFormat == "json" { 76 | fmt.Println(string(formatted)) 77 | } else if outputFormat == "xml" { 78 | xmlData, err := xml.MarshalIndent(securityChecks, "", " ") 79 | if err != nil { 80 | log.Fatal(err) 81 | } 82 | fmt.Println(string(xmlData)) 83 | } else { 84 | PrintLogo(noBanner) 85 | var securityChecksColors []SecurityCheckColor 86 | 87 | // Unmarshal JSON data 88 | if err = json.Unmarshal([]byte(formattedcolor), &securityChecksColors); err != nil { 89 | fmt.Println("Error:", err) 90 | return 91 | } 92 | if !noHeader { 93 | fmt.Printf("%-24s%-26s%-22s%-24s%-19s%-21s%-24s%-19s%-20s%-25s%-40s\n", 94 | colorPrinter("RELRO", "unset"), 95 | colorPrinter("Stack Canary", "unset"), 96 | colorPrinter("NX", "unset"), 97 | colorPrinter("PIE", "unset"), 98 | colorPrinter("RPATH", "unset"), 99 | colorPrinter("RUNPATH", "unset"), 100 | colorPrinter("Symbols", "unset"), 101 | colorPrinter("FORTIFY", "unset"), 102 | colorPrinter("Fortified", "unset"), 103 | colorPrinter("Fortifiable", "unset"), 104 | colorPrinter("Name", "unset"), 105 | ) 106 | } 107 | for _, check := range securityChecksColors { 108 | fmt.Printf("%-25s%-27s%-23s%-25s%-20s%-22s%-25s%-20s%-20s%-25s%-40s\n", 109 | colorPrinter(check.Checks.Relro, check.Checks.RelroColor), 110 | colorPrinter(check.Checks.Canary, check.Checks.CanaryColor), 111 | colorPrinter(check.Checks.NX, check.Checks.NXColor), 112 | colorPrinter(check.Checks.PIE, check.Checks.PIEColor), 113 | colorPrinter(check.Checks.RPath, check.Checks.RPathColor), 114 | colorPrinter(check.Checks.RunPath, check.Checks.RunPathColor), 115 | colorPrinter(check.Checks.Symbols, check.Checks.SymbolsColor), 116 | colorPrinter(check.Checks.FortifySource, check.Checks.FortifySourceColor), 117 | colorPrinter(check.Checks.Fortified, "unset"), 118 | colorPrinter(check.Checks.FortifyAble, "unset"), 119 | colorPrinter(check.Name, "unset"), 120 | ) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /pkg/utils/files.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "debug/elf" 5 | "fmt" 6 | "io/fs" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | // CheckElfExists - Check if file exists and is an Elf file 13 | func CheckElfExists(fileName string) bool { 14 | if !CheckFileExists(fileName) { 15 | fmt.Println("File not found:", fileName) 16 | os.Exit(1) 17 | } 18 | if !CheckIfElf(fileName) { 19 | fmt.Println("File is not an ELF file:", fileName) 20 | os.Exit(1) 21 | } 22 | 23 | return true 24 | } 25 | 26 | // CheckIfElf - Check if the file is an Elf file 27 | func CheckIfElf(fileName string) bool { 28 | _, err := elf.Open(fileName) 29 | if err != nil { 30 | return false 31 | } 32 | 33 | return true 34 | } 35 | 36 | // CheckDirExists - Check if the directory exists 37 | func CheckDirExists(dirName string) bool { 38 | dirInfo, err := os.Stat(dirName) 39 | if err != nil { 40 | if os.IsNotExist(err) { 41 | fmt.Println("Directory not found:", dirName) 42 | os.Exit(1) 43 | } else { 44 | fmt.Println("An error occurred:", err) 45 | os.Exit(1) 46 | } 47 | } 48 | 49 | if !dirInfo.IsDir() { 50 | fmt.Printf("%s is not a Directory", dirName) 51 | os.Exit(1) 52 | } 53 | 54 | return true 55 | } 56 | 57 | // CheckFileExists - check if the file exists 58 | func CheckFileExists(fileName string) bool { 59 | _, err := os.Stat(fileName) 60 | if err != nil { 61 | if os.IsNotExist(err) { 62 | fmt.Println("File not found:", fileName) 63 | os.Exit(1) 64 | } else { 65 | fmt.Println("An error occurred:", err) 66 | os.Exit(1) 67 | } 68 | } 69 | 70 | return true 71 | } 72 | 73 | // GetAllFilesFromDir - get the list of all elf files from a directory (or recursively) 74 | func GetAllFilesFromDir(dirName string, recursive bool) []string { 75 | var results []string 76 | var fileList []string 77 | 78 | if recursive { 79 | filepath.WalkDir(dirName, func(path string, file fs.DirEntry, err error) error { 80 | if err != nil { 81 | return fs.SkipDir 82 | } 83 | if !file.IsDir() && CheckIfElf(path) && file.Type().IsRegular() { 84 | results = append(results, path) 85 | } 86 | 87 | return nil 88 | }) 89 | } else { 90 | fileList, _ = filepath.Glob(fmt.Sprintf("%s/*", dirName)) 91 | for _, j := range fileList { 92 | dirInfo, _ := os.Stat(j) 93 | if dirInfo == nil { 94 | continue 95 | } 96 | if j != "." && !dirInfo.IsDir() && CheckIfElf(j) { 97 | results = append(results, j) 98 | } 99 | } 100 | 101 | } 102 | 103 | if len(results) == 0 { 104 | log.Fatalf("Error: No binary files found in %s", dirName) 105 | } 106 | return results 107 | } 108 | 109 | // GetBinary - Return the binary details 110 | func GetBinary(fileName string) *elf.File { 111 | 112 | binary, err := elf.Open(fileName) 113 | if err != nil { 114 | fmt.Fprintln(os.Stderr, err) 115 | os.Exit(1) 116 | } 117 | defer func(f *elf.File) { 118 | err := f.Close() 119 | if err != nil { 120 | log.Fatal(err) 121 | } 122 | }(binary) 123 | 124 | return binary 125 | } 126 | -------------------------------------------------------------------------------- /pkg/utils/fortifyPrinter.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "encoding/xml" 6 | "fmt" 7 | "log" 8 | 9 | "sigs.k8s.io/yaml" 10 | ) 11 | 12 | // FortifyCheck struct for non-colored data, 13 | // keeping a dedicated struct allows conversion without removing the colors 14 | type FortifyCheck struct { 15 | Name string `json:"name"` 16 | Checks struct { 17 | Fortified string `json:"fortified"` 18 | FortifyAble string `json:"fortifyable"` 19 | FortifySource string `json:"fortify_source"` 20 | NoFortify string `json:"noFortify"` 21 | LibcSupport string `json:"libcSupport"` 22 | NumLibcFunc string `json:"numLibcFunc"` 23 | NumFileFunc string `json:"numFileFunc"` 24 | } `json:"checks"` 25 | } 26 | 27 | // FortifyCheckColor struct for colored data 28 | type FortifyCheckColor struct { 29 | Name string `json:"name"` 30 | Checks struct { 31 | Fortified string `json:"fortified"` 32 | FortifyAble string `json:"fortifyable"` 33 | FortifySource string `json:"fortify_source"` 34 | FortifySourceColor string `json:"fortify_sourceColor"` 35 | NoFortify string `json:"noFortify"` 36 | LibcSupport string `json:"libcSupport"` 37 | LibcSupportColor string `json:"libcSupportColor"` 38 | NumLibcFunc string `json:"numLibcFunc"` 39 | NumFileFunc string `json:"numFileFunc"` 40 | } `json:"checks"` 41 | } 42 | 43 | // FortifyPrinter - Print the output from FortifyFile function 44 | func FortifyPrinter(outputFormat string, data interface{}, colors interface{}, noBanner bool, noHeader bool) { 45 | 46 | formatted, err := json.MarshalIndent(data, "", " ") 47 | if err != nil { 48 | fmt.Printf("err: %v\n", err) 49 | } 50 | formattedcolor, err := json.MarshalIndent(colors, "", " ") 51 | if err != nil { 52 | fmt.Printf("err: %v\n", err) 53 | } 54 | var fortifyChecks []FortifyCheck 55 | // Unmarshal JSON data 56 | if err := json.Unmarshal([]byte(formatted), &fortifyChecks); err != nil { 57 | fmt.Println("Error:", err) 58 | return 59 | } 60 | 61 | if outputFormat == "yaml" { 62 | yamlResponse, err := yaml.JSONToYAML(formatted) 63 | if err != nil { 64 | fmt.Printf("err: %v\n", err) 65 | } 66 | fmt.Println(string(yamlResponse)) 67 | } else if outputFormat == "json" { 68 | fmt.Println(string(formatted)) 69 | } else if outputFormat == "xml" { 70 | xmlData, err := xml.MarshalIndent(fortifyChecks, "", " ") 71 | if err != nil { 72 | log.Fatal(err) 73 | } 74 | fmt.Println(string(xmlData)) 75 | } else { 76 | PrintLogo(noBanner) 77 | var fortifyChecksColors []FortifyCheckColor 78 | 79 | // Unmarshal JSON data 80 | if err = json.Unmarshal([]byte(formattedcolor), &fortifyChecksColors); err != nil { 81 | fmt.Println("Error:", err) 82 | return 83 | } 84 | 85 | for _, check := range fortifyChecksColors { 86 | fmt.Printf("* FORTIFY_SOURCE support available (libc): %s\n", colorPrinter(check.Checks.LibcSupport, check.Checks.LibcSupportColor)) 87 | fmt.Printf("* Binary compiled with FORTIFY_SOURCE support: %s\n\n", colorPrinter(check.Checks.FortifySource, check.Checks.FortifySourceColor)) 88 | fmt.Println("------ EXECUTABLE-FILE ------- | -------- LIBC --------") 89 | fmt.Println("Fortifiable library functions | Checked function names") 90 | // TODO: add function breakdown 91 | fmt.Println("Coming Soon") 92 | fmt.Printf("\n%s\n", colorPrinter("SUMMARY", "green")) 93 | fmt.Printf("* Number of checked functions in libc : %s\n", colorPrinter(check.Checks.NumLibcFunc, "unset")) 94 | fmt.Printf("* Total number of library functions in the executable: %s\n", colorPrinter(check.Checks.NumFileFunc, "unset")) 95 | fmt.Printf("* Number of Fortifiable functions in the executable : %s\n", colorPrinter(check.Checks.FortifyAble, "unset")) 96 | fmt.Printf("* Number of checked functions in the executable : %s\n", colorPrinter(check.Checks.Fortified, "green")) 97 | fmt.Printf("* Number of unchecked functions in the executable : %s\n", colorPrinter(check.Checks.NoFortify, "red")) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /pkg/utils/kernelPrinter.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "encoding/xml" 6 | "fmt" 7 | "log" 8 | 9 | "sigs.k8s.io/yaml" 10 | ) 11 | 12 | type KernelCheckColor struct { 13 | Name string `json:"name"` 14 | Description string `json:"desc"` 15 | Color string `json:"color"` 16 | Value string `json:"value"` 17 | CheckType string `json:"type"` 18 | } 19 | 20 | type KernelCheck struct { 21 | Name string `json:"name"` 22 | Description string `json:"desc"` 23 | Value string `json:"value"` 24 | CheckType string `json:"type"` 25 | } 26 | 27 | func KernelPrinter(outputFormat string, kernel any, kernelColors any, noBanner bool, noHeader bool) { 28 | 29 | formattedKernel, err := json.MarshalIndent(kernel, "", " ") 30 | if err != nil { 31 | fmt.Printf("err: %v\n", err) 32 | } 33 | 34 | formattedKernelColors, err := json.MarshalIndent(kernelColors, "", " ") 35 | if err != nil { 36 | fmt.Printf("err: %v\n", err) 37 | } 38 | 39 | var KernelCheck []KernelCheck 40 | // Unmarshal JSON data 41 | if err := json.Unmarshal([]byte(formattedKernel), &KernelCheck); err != nil { 42 | fmt.Println("Error:", err) 43 | return 44 | } 45 | 46 | var KernelCheckColor []KernelCheckColor 47 | // Unmarshal JSON data 48 | if err := json.Unmarshal([]byte(formattedKernelColors), &KernelCheckColor); err != nil { 49 | fmt.Println("Error:", err) 50 | return 51 | } 52 | 53 | if outputFormat == "yaml" { 54 | yamlResponse, err := yaml.JSONToYAML(formattedKernel) 55 | if err != nil { 56 | fmt.Printf("err: %v\n", err) 57 | } 58 | fmt.Println(string(yamlResponse)) 59 | } else if outputFormat == "json" { 60 | fmt.Println(string(formattedKernel)) 61 | } else if outputFormat == "xml" { 62 | xmlData, err := xml.MarshalIndent(KernelCheck, "", " ") 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | fmt.Println(string(xmlData)) 67 | } else { 68 | PrintLogo(noBanner) 69 | if !noHeader { 70 | fmt.Println("Kernel configs only print what is supported by the specific kernel/kernel config") 71 | fmt.Printf("%-70s%-25s%-30s%-30s\n", 72 | colorPrinter("Description", "unset"), 73 | colorPrinter("Value", "unset"), 74 | colorPrinter("Check Type", "unset"), 75 | colorPrinter("Config Key", "unset"), 76 | ) 77 | } 78 | for _, check := range KernelCheckColor { 79 | fmt.Printf("%-70s%-26s%-30s%-30s\n", 80 | colorPrinter(check.Description, "unset"), 81 | colorPrinter(check.Value, check.Color), 82 | colorPrinter(check.CheckType, "unset"), 83 | colorPrinter(check.Name, "unset"), 84 | ) 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/fatih/color" 5 | ) 6 | 7 | func PrintLogo(noBanner bool) { 8 | green := color.New(color.FgHiGreen, color.Bold) 9 | asciiLogo := ` 10 | _____ _ _ ______ _____ _ __ _____ ______ _____ 11 | / ____| | | | ____/ ____| |/ // ____| ____/ ____| 12 | | | | |__| | |__ | | | ' /| (___ | |__ | | 13 | | | | __ | __|| | | < \___ \| __|| | 14 | | |____| | | | |___| |____| . \ ____) | |___| |____ 15 | \_____|_| |_|______\_____|_|\_\_____/|______\_____| 16 | ` 17 | if !noBanner { 18 | green.Println(asciiLogo) 19 | } 20 | } 21 | 22 | func colorPrinter(result string, resultColor string) string { 23 | unset := color.New(color.Reset).SprintFunc() 24 | italic := color.New(color.Italic).SprintfFunc() 25 | red := color.New(color.FgRed).SprintFunc() 26 | green := color.New(color.FgGreen).SprintFunc() 27 | yellow := color.New(color.FgYellow).SprintFunc() 28 | blue := color.New(color.FgBlue).SprintFunc() 29 | 30 | if resultColor == "green" { 31 | return green(result) 32 | } else if resultColor == "red" { 33 | return red(result) 34 | } else if resultColor == "yellow" { 35 | return yellow(result) 36 | } else if resultColor == "blue" { 37 | return blue(result) 38 | } else if resultColor == "italic" { 39 | return italic(result) 40 | } else { 41 | return unset(result) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/binaries/build_binaries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | export PATH=$PATH:/zig/ 5 | mkdir -p output 6 | 7 | # All hardening features on (except for CFI and SafeStack) 8 | gcc -o output/all test.c -w -D_FORTIFY_SOURCE=3 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s 9 | # Partial RELRO 10 | gcc -o output/partial test.c -w -D_FORTIFY_SOURCE=1 -fstack-protector-strong -fpie -O2 -z relro -z lazy -z noexecstack -s 11 | # RPATH 12 | gcc -o output/rpath test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s -Wl,-rpath,./ -Wl,--disable-new-dtags 13 | # RUNPATH 14 | gcc -o output/runpath test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s -Wl,-rpath,./ -Wl,--enable-new-dtags 15 | # no hardening features 16 | gcc -o output/none test.c -w -D_FORTIFY_SOURCE=0 -fno-stack-protector -no-pie -O2 -z norelro -z lazy -z execstack 17 | # REL (PIE) 18 | gcc -c test.c -o output/rel.o 19 | # DSO (PIE) 20 | gcc -shared -fPIC -o output/dso.so test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -O2 -z relro -z now -z noexecstack -s 21 | # CFI and SafeStack 22 | clang -o output/cfi test.c -w -flto -fsanitize=cfi -fvisibility=default 23 | clang -o output/sstack test.c -w -fsanitize=safe-stack 24 | # clang instead of gcc 25 | clang -o output/all_cl test.c -w -D_FORTIFY_SOURCE=3 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s 26 | clang -o output/partial_cl test.c -w -D_FORTIFY_SOURCE=1 -fstack-protector-strong -fpie -O2 -z relro -z lazy -z noexecstack -s 27 | clang -o output/rpath_cl test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s -Wl,-rpath,./ -Wl,--disable-new-dtags 28 | clang -o output/runpath_cl test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s -Wl,-rpath,./ -Wl,--enable-new-dtags 29 | clang -o output/none_cl test.c -w -D_FORTIFY_SOURCE=0 -fno-stack-protector -no-pie -O2 -z norelro -z lazy -z execstack 30 | clang -c test.c -o output/rel_cl.o 31 | clang -shared -fPIC -o output/dso_cl.so test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -O2 -z relro -z now -z noexecstack -s 32 | 33 | # 32-bit use zig for cross compile 34 | # zig cc --target=x86-linux-gnu 35 | gcc -m32 -o output/all32 test.c -w -D_FORTIFY_SOURCE=3 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s 36 | gcc -m32 -o output/partial32 test.c -w -D_FORTIFY_SOURCE=1 -fstack-protector-strong -fpie -O2 -z relro -z lazy -z noexecstack -s 37 | gcc -m32 -o output/rpath32 test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s -Wl,-rpath,./ -Wl,--disable-new-dtags 38 | gcc -m32 -o output/runpath32 test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s -Wl,-rpath,./ -Wl,--enable-new-dtags 39 | gcc -m32 -o output/none32 test.c -w -D_FORTIFY_SOURCE=0 -fno-stack-protector -no-pie -O2 -z norelro -z lazy -z execstack 40 | gcc -m32 -c test.c -o output/rel32.o 41 | gcc -m32 -shared -fPIC -o output/dso32.so test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -O2 -z relro -z now -z noexecstack -s 42 | 43 | clang -m32 -o output/cfi32 test.c -w -flto -fsanitize=cfi -fvisibility=default 44 | clang -m32 -o output/sstack32 test.c -w -fsanitize=safe-stack 45 | clang -m32 -o output/all_cl32 test.c -w -D_FORTIFY_SOURCE=3 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s 46 | clang -m32 -o output/partial_cl32 test.c -w -D_FORTIFY_SOURCE=1 -fstack-protector-strong -fpie -O2 -z relro -z lazy -z noexecstack -s 47 | clang -m32 -o output/rpath_cl32 test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s -Wl,-rpath,./ -Wl,--disable-new-dtags 48 | clang -m32 -o output/runpath_cl32 test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fpie -O2 -z relro -z now -z noexecstack -pie -s -Wl,-rpath,./ -Wl,--enable-new-dtags 49 | clang -m32 -o output/none_cl32 test.c -w -D_FORTIFY_SOURCE=0 -fno-stack-protector -no-pie -O2 -z norelro -z lazy -z execstack 50 | clang -m32 -c test.c -o output/rel_cl32.o 51 | clang -m32 -shared -fPIC -o output/dso_cl32.so test.c -w -D_FORTIFY_SOURCE=2 -fstack-protector-strong -O2 -z relro -z now -z noexecstack -s 52 | 53 | # Fortify source (NASM assembler installation is required) 54 | nasm -f elf64 -o nolibc.o nolibc.asm 55 | nasm -f elf32 -o nolibc32.o nolibc32.asm 56 | gcc -o output/nolibc nolibc.o -w -nostdlib -no-pie -s 57 | clang -o output/nolibc_cl nolibc.o -w -nostdlib -no-pie -s 58 | gcc -m32 -o output/nolibc32 nolibc32.o -w -nostdlib -no-pie -s 59 | clang -m32 -o output/nolibc_cl32 nolibc32.o -w -nostdlib -no-pie -s 60 | 61 | gcc -o output/fszero fszero.c -w -D_FORTIFY_SOURCE=0 -O2 -s 62 | clang -o output/fszero_cl fszero.c -w -D_FORTIFY_SOURCE=0 -O2 -s 63 | gcc -m32 -o output/fszero32 fszero.c -w -D_FORTIFY_SOURCE=0 -O2 -s 64 | clang -m32 -o output/fszero_cl32 fszero.c -w -D_FORTIFY_SOURCE=0 -O2 -s 65 | -------------------------------------------------------------------------------- /tests/binaries/fszero.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | printf("Hello World\n"); 7 | sleep(2); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /tests/binaries/nolibc.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | msg db 'Hello, World!', 0ah ;note the newline (Line Feed-LF) at the end (hex:0ah; decimal:10) 3 | len equ $ - msg ;calculate the length of the message 4 | delay dq 2, 100000000 ;define delay with Timespec structure members tv_sec, tv_nsec (qwords, 64-bit integer values) 5 | 6 | section .text 7 | global _start ;must be declared for linker (ld) 8 | 9 | _start: ;tells linker entry point 10 | mov rax, 1 ;system call for write (sys_write 1) 11 | mov rdi, 1 ;file descriptor (1 is stdout) 12 | mov rsi, msg ;address of string to output 13 | mov rdx, len ;message length 14 | syscall ;invoke operating system to do the write 15 | 16 | mov rax, 35 ;system call for nanosleep (sys_nanosleep 35) 17 | mov rdi, delay ;load the pointer to our delay 18 | mov rsi, 0 ;exit code 0 19 | syscall ;invoke operating system to do the delay 20 | 21 | mov rax, 60 ;system call for exit (sys_exit 60) 22 | xor rdi, rdi ;exit code 0 23 | syscall ;invoke operating system to exit 24 | -------------------------------------------------------------------------------- /tests/binaries/nolibc32.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | msg db "Hello, world!", 0xa ;note the newline (Line Feed-LF) at the end (hex:0ah; decimal:10) 3 | len equ $ - msg ;calculate the length of the message 4 | delay dd 2, 100000000 ;define delay with Timespec structure members tv_sec, tv_nsec (dwords, 32-bit integer values) 5 | 6 | section .text 7 | global _start ;must be declared for linker (ld) 8 | 9 | _start: ;tells linker entry point 10 | mov eax,4 ;system call for write (sys_write 4) 11 | mov ebx,1 ;file descriptor (1 is stdout) 12 | mov ecx,msg ;address of string to output 13 | mov edx,len ;message length 14 | int 0x80 ;invoke operating system to do the write 15 | 16 | mov eax, 162 ;system call for nanosleep (sys_nanosleep 162) 17 | mov ebx, delay ;load the pointer to our delay 18 | mov ecx, 0 ;exit code 0 19 | int 0x80 ;invoke operating system to do the delay 20 | 21 | mov eax,1 ;system call for exit (sys_exit 1) 22 | xor ebx, ebx ;exit code 0 23 | int 0x80 ;invoke operating system to exit 24 | -------------------------------------------------------------------------------- /tests/binaries/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int false__stack_chk_fail(int a) { return a; } 6 | 7 | int main(int argc, char** argv) { 8 | char buf[16]; 9 | int (*op)(int) = false__stack_chk_fail; 10 | 11 | if (argc>1) 12 | strcpy(buf,argv[1]); 13 | else 14 | strcpy(buf,"test"); 15 | 16 | printf("%s,%d\n", buf, op(42)); 17 | 18 | sleep(2); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /tests/docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # build the container with a shell script so that it is bash 3 | # Dockerfile syntax is sh not bash so installing gcc-multilib in an 4 | # if condition becomes more difficult. 5 | 6 | set -eou pipefail 7 | set -x 8 | 9 | apt-get update 10 | apt-get -y -q upgrade 11 | 12 | DEBIAN_FRONTEND=noninteractive apt-get -y -q install \ 13 | bc bison flex build-essential git file \ 14 | libncurses-dev libssl-dev u-boot-tools wget \ 15 | xz-utils vim libxml2-utils python3 python3-pip jq \ 16 | gcc clang nasm binutils 17 | 18 | if [[ "$(uname -m)" == "x86_64" ]]; then 19 | apt-get -y -q install gcc-multilib 20 | fi 21 | 22 | apt-get clean 23 | 24 | pip3 install setuptools demjson3 --break-system-packages 25 | -------------------------------------------------------------------------------- /tests/hardening-checks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR=$( 4 | cd "$(dirname "$0")" 5 | pwd 6 | ) 7 | PARENT=$( 8 | cd "$(dirname "$0")/.." 9 | pwd 10 | ) 11 | 12 | ( 13 | cd "${DIR}/binaries/" 14 | ./build_binaries.sh 15 | ) 16 | 17 | for bin in all all32 all_cl all_cl32 \ 18 | cfi cfi32 sstack sstack32 \ 19 | dso.so dso32.so dso_cl.so dso_cl32.so \ 20 | none none32 none_cl none_cl32 \ 21 | partial partial32 partial_cl partial_cl32 \ 22 | rel.o rel32.o rel_cl.o rel_cl32.o \ 23 | rpath rpath32 rpath_cl rpath_cl32 \ 24 | runpath runpath32 runpath_cl runpath_cl32 \ 25 | nolibc nolibc_cl nolibc32 nolibc_cl32 \ 26 | fszero fszero_cl fszero32 fszero_cl32; do 27 | if [[ ! -f "${DIR}/binaries/output/${bin}" ]]; then 28 | echo "Could not find test file output/${bin}. Run build_binaries.sh in the binaries folder to generate it." 29 | exit 255 30 | fi 31 | done 32 | 33 | #============================================ 34 | # file checks 35 | #============================================ 36 | 37 | echo "Starting RELRO check" 38 | # Full RELRO 39 | for bin in all all32 all_cl all_cl32; do 40 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f1) != "Full RELRO" ]]; then 41 | echo "Full RELRO validation failed on \"${bin}\"" 42 | exit 1 43 | fi 44 | done 45 | # Partial RELRO 46 | for bin in partial partial32 partial_cl partial_cl32; do 47 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f1) != "Partial RELRO" ]]; then 48 | echo "Partial RELRO validation failed on \"${bin}\"" 49 | exit 1 50 | fi 51 | done 52 | # No RELRO 53 | for bin in none none32 none_cl none_cl32; do 54 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f1) != "No RELRO" ]]; then 55 | echo "No RELRO validation failed on \"${bin}\"" 56 | exit 1 57 | fi 58 | done 59 | # N/A 60 | for bin in rel.o rel32.o rel_cl.o rel_cl32.o; do 61 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f1) != "N/A" ]]; then 62 | echo "N/A RELRO validation failed on \"${bin}\"" 63 | exit 1 64 | fi 65 | done 66 | echo "RELRO validation tests passed" 67 | 68 | #============================================ 69 | 70 | echo "Starting Stack Canary check" 71 | # Canary found 72 | for bin in all all32 all_cl all_cl32; do 73 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f2) != "Canary found" ]]; then 74 | echo "Stack Canary validation failed on \"${bin}\"" 75 | exit 1 76 | fi 77 | done 78 | # No Canary found 79 | for bin in none none32 none_cl none_cl32; do 80 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f2) != "No Canary found" ]]; then 81 | echo "No Stack Canary validation failed on \"${bin}\"" 82 | exit 1 83 | fi 84 | done 85 | echo "Stack Canary validation tests passed" 86 | 87 | #============================================ 88 | 89 | echo "Starting NX check" 90 | # NX enabled 91 | for bin in all all32 all_cl all_cl32; do 92 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f3) != "NX enabled" ]]; then 93 | echo "NX enabled validation failed on \"${bin}\"" 94 | exit 1 95 | fi 96 | done 97 | # NX disabled 98 | for bin in none none32 none_cl none_cl32; do 99 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f3) != "NX disabled" ]]; then 100 | echo "NX disabled validation failed on \"${bin}\"" 101 | exit 1 102 | fi 103 | done 104 | # N/A 105 | for bin in rel.o rel32.o rel_cl.o rel_cl32.o; do 106 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f3) != "N/A" ]]; then 107 | echo "N/A NX validation failed on \"${bin}\"" 108 | exit 1 109 | fi 110 | done 111 | echo "NX validation tests passed" 112 | 113 | #============================================ 114 | 115 | echo "Starting PIE check" 116 | # PIE enabled 117 | for bin in all all32 all_cl all_cl32; do 118 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f4) != "PIE enabled" ]]; then 119 | echo "PIE enabled validation failed on \"${bin}\"" 120 | exit 1 121 | fi 122 | done 123 | # No PIE 124 | for bin in none none32 none_cl none_cl32; do 125 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f4) != "No PIE" ]]; then 126 | echo "No PIE validation failed on \"${bin}\"" 127 | exit 1 128 | fi 129 | done 130 | # DSO 131 | for bin in dso.so dso32.so dso_cl.so dso_cl32.so; do 132 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f4) != "DSO" ]]; then 133 | echo "PIE DSO validation failed on \"${bin}\"" 134 | exit 1 135 | fi 136 | done 137 | # REL 138 | for bin in rel.o rel32.o rel_cl.o rel_cl32.o; do 139 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f4) != "REL" ]]; then 140 | echo "PIE REL validation failed on \"${bin}\"" 141 | exit 1 142 | fi 143 | done 144 | echo "PIE validation tests passed" 145 | 146 | #============================================ 147 | 148 | echo "Starting CFI check" 149 | # with CFI 150 | for bin in cfi cfi32; do 151 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --extended --format=csv | cut -d, -f5) != "with CFI" ]]; then 152 | echo "CFI validation failed on \"${bin}\"" 153 | exit 1 154 | fi 155 | done 156 | # without CFI 157 | for bin in none_cl none_cl32; do 158 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --extended --format=csv | cut -d, -f5) != "without CFI" ]]; then 159 | echo "No CFI validation failed on \"${bin}\"" 160 | exit 1 161 | fi 162 | done 163 | echo "CFI validation tests passed" 164 | 165 | #============================================ 166 | 167 | echo "Starting SafeStack check" 168 | # with SafeStack 169 | for bin in sstack sstack32; do 170 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --extended --format=csv | cut -d, -f6) != "with SafeStack" ]]; then 171 | echo "SafeStack validation failed on \"${bin}\"" 172 | exit 1 173 | fi 174 | done 175 | # without SafeStack 176 | for bin in none_cl none_cl32; do 177 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --extended --format=csv | cut -d, -f6) != "without SafeStack" ]]; then 178 | echo "No SafeStack validation failed on \"${bin}\"" 179 | exit 1 180 | fi 181 | done 182 | echo "SafeStack validation tests passed" 183 | 184 | #============================================ 185 | 186 | echo "Starting RUNPATH check" 187 | # No RPATH 188 | for bin in all all32 all_cl all_cl32; do 189 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f5) != "No RPATH" ]]; then 190 | echo "No RPATH validation failed on \"${bin}\"" 191 | exit 1 192 | fi 193 | done 194 | # RPATH 195 | for bin in rpath rpath32 rpath_cl rpath_cl32; do 196 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f5) != "RPATH" ]]; then 197 | echo "RPATH validation failed on \"${bin}\"" 198 | exit 1 199 | fi 200 | done 201 | # N/A 202 | for bin in rel.o rel32.o rel_cl.o rel_cl32.o; do 203 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f5) != "N/A" ]]; then 204 | echo "N/A RPATH validation failed on \"${bin}\"" 205 | exit 1 206 | fi 207 | done 208 | echo "RPATH validation tests passed" 209 | 210 | #============================================ 211 | 212 | echo "Starting RUNPATH check" 213 | # No RUNPATH 214 | for bin in all all32 all_cl all_cl32; do 215 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f6) != "No RUNPATH" ]]; then 216 | echo "No RUNPATH validation failed on \"${bin}\"" 217 | exit 1 218 | fi 219 | done 220 | # RUNPATH 221 | for bin in runpath runpath32 runpath_cl runpath_cl32; do 222 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f6) != "RUNPATH" ]]; then 223 | echo "RUNPATH validation failed on \"${bin}\"" 224 | exit 1 225 | fi 226 | done 227 | # N/A 228 | for bin in rel.o rel32.o rel_cl.o rel_cl32.o; do 229 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f6) != "N/A" ]]; then 230 | echo "N/A RUNPATH validation failed on \"${bin}\"" 231 | exit 1 232 | fi 233 | done 234 | echo "RUNPATH validation tests passed" 235 | 236 | #============================================ 237 | 238 | echo "Starting Symbols check" 239 | # No Symbols 240 | for bin in all all32 all_cl all_cl32; do 241 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f7) != "No Symbols" ]]; then 242 | echo "No Symbols validation failed on \"${bin}\"" 243 | exit 1 244 | fi 245 | done 246 | # Symbols 247 | for bin in none none32 none_cl none_cl32; do 248 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f7) != "Symbols" ]]; then 249 | echo "Symbols validation failed on \"${bin}\"" 250 | exit 1 251 | fi 252 | done 253 | echo "Symbols validation tests passed" 254 | 255 | #============================================ 256 | 257 | echo "Starting Fortify check" 258 | # Yes 259 | for bin in all all32 all_cl all_cl32; do 260 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f8) != "Yes" ]]; then 261 | echo "Fortify validation failed on \"${bin}\"" 262 | exit 1 263 | fi 264 | done 265 | # No 266 | for bin in none none32 none_cl none_cl32; do 267 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f8) != "No" ]]; then 268 | echo "No Fortify validation failed on \"${bin}\"" 269 | exit 1 270 | fi 271 | done 272 | # N/A 273 | for bin in nolibc nolibc_cl nolibc32 nolibc_cl32 fszero fszero_cl fszero32 fszero_cl32; do 274 | if [[ $("${PARENT}"/checksec --file="${DIR}/binaries/output/${bin}" --format=csv | cut -d, -f8) != "N/A" ]]; then 275 | echo "No Fortify validation failed on \"${bin}\": $("${PARENT}"/checksec --file="${DIR}/binaries/${bin}" --format=csv | cut -d, -f8)" 276 | exit 1 277 | fi 278 | done 279 | echo "Fortify validation tests passed" 280 | 281 | #============================================ 282 | # process checks 283 | #============================================ 284 | 285 | echo "Starting RELRO process check" 286 | # Full RELRO 287 | for bin in all all32 all_cl all_cl32; do 288 | "${DIR}"/binaries/output/${bin} > /dev/null & 289 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f3) != "Full RELRO" ]]; then 290 | echo "Full RELRO process validation failed on \"${bin}\"" 291 | exit 1 292 | fi 293 | done 294 | # Partial RELRO 295 | for bin in partial partial32 partial_cl partial_cl32; do 296 | "${DIR}"/binaries/output/${bin} > /dev/null & 297 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f3) != "Partial RELRO" ]]; then 298 | echo "Partial RELRO process validation failed on \"${bin}\"" 299 | exit 1 300 | fi 301 | done 302 | # No RELRO 303 | for bin in none none32 none_cl none_cl32; do 304 | "${DIR}"/binaries/output/${bin} > /dev/null & 305 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f3) != "No RELRO" ]]; then 306 | echo "No RELRO process validation failed on \"${bin}\"" 307 | exit 1 308 | fi 309 | done 310 | sleep 2 311 | echo "RELRO process validation tests passed" 312 | 313 | #============================================ 314 | 315 | echo "Starting Stack Canary process check" 316 | # Canary found 317 | for bin in all all32 all_cl all_cl32; do 318 | "${DIR}"/binaries/output/${bin} > /dev/null & 319 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f4) != "Canary found" ]]; then 320 | echo "Stack Canary process validation failed on \"${bin}\"" 321 | exit 1 322 | fi 323 | done 324 | # No Canary found 325 | for bin in none none32 none_cl none_cl32; do 326 | "${DIR}"/binaries/output/${bin} > /dev/null & 327 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f4) != "No Canary found" ]]; then 328 | echo "No Stack Canary process validation failed on \"${bin}\"" 329 | exit 1 330 | fi 331 | done 332 | sleep 2 333 | echo "Stack Canary process validation tests passed" 334 | 335 | #============================================ 336 | 337 | echo "Starting CFI process check" 338 | # with CFI 339 | for bin in cfi cfi32; do 340 | "${DIR}"/binaries/output/${bin} > /dev/null & 341 | if [[ $("${PARENT}"/checksec --proc=${bin} --extended --format=csv | cut -d, -f5) != "with CFI" ]]; then 342 | echo "CFI process validation failed on \"${bin}\"" 343 | exit 1 344 | fi 345 | done 346 | # without CFI 347 | for bin in none_cl none_cl32; do 348 | "${DIR}"/binaries/output/${bin} > /dev/null & 349 | if [[ $("${PARENT}"/checksec --proc=${bin} --extended --format=csv | cut -d, -f5) != "without CFI" ]]; then 350 | echo "No CFI process validation failed on \"${bin}\"" 351 | exit 1 352 | fi 353 | done 354 | sleep 2 355 | echo "CFI process validation tests passed" 356 | 357 | #============================================ 358 | 359 | echo "Starting SafeStack process check" 360 | # with SafeStack (omit 32-bit SafeStack because the binary does not work) 361 | bin=sstack 362 | "${DIR}"/binaries/output/${bin} > /dev/null & 363 | if [[ $("${PARENT}"/checksec --proc=${bin} --extended --format=csv | cut -d, -f6) != "with SafeStack" ]]; then 364 | echo "SafeStack process validation failed on \"${bin}\"" 365 | exit 1 366 | fi 367 | # without SafeStack 368 | for bin in none_cl none_cl32; do 369 | "${DIR}"/binaries/output/${bin} > /dev/null & 370 | if [[ $("${PARENT}"/checksec --proc=${bin} --extended --format=csv | cut -d, -f6) != "without SafeStack" ]]; then 371 | echo "No SafeStack process validation failed on \"${bin}\"" 372 | exit 1 373 | fi 374 | done 375 | sleep 2 376 | echo "SafeStack process validation tests passed" 377 | 378 | #============================================ 379 | 380 | echo "Starting NX process check" 381 | # NX enabled 382 | for bin in all all32 all_cl all_cl32; do 383 | "${DIR}"/binaries/output/${bin} > /dev/null & 384 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f6) != "NX enabled" ]]; then 385 | echo "NX enabled process validation failed on \"${bin}\"" 386 | exit 1 387 | fi 388 | done 389 | # NX disabled 390 | for bin in none none32 none_cl none_cl32; do 391 | "${DIR}"/binaries/output/${bin} > /dev/null & 392 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f6) != "NX disabled" ]]; then 393 | echo "NX disabled process validation failed on \"${bin}\"" 394 | exit 1 395 | fi 396 | done 397 | sleep 2 398 | echo "NX process validation tests passed" 399 | 400 | #============================================ 401 | 402 | echo "Starting PIE process check" 403 | # PIE enabled 404 | for bin in all all32 all_cl all_cl32; do 405 | "${DIR}"/binaries/output/${bin} > /dev/null & 406 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f7) != "PIE enabled" ]]; then 407 | echo "PIE enabled process validation failed on \"${bin}\"" 408 | exit 1 409 | fi 410 | done 411 | # No PIE 412 | for bin in none none32 none_cl none_cl32; do 413 | "${DIR}"/binaries/output/${bin} > /dev/null & 414 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f7) != "No PIE" ]]; then 415 | echo "No PIE process validation failed on \"${bin}\"" 416 | exit 1 417 | fi 418 | done 419 | sleep 2 420 | echo "PIE process validation tests passed" 421 | 422 | #============================================ 423 | 424 | echo "Starting Fortify process check" 425 | # Yes 426 | for bin in all all32 all_cl all_cl32; do 427 | "${DIR}"/binaries/output/${bin} > /dev/null & 428 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f8) != "Yes" ]]; then 429 | echo "Fortify process validation failed on \"${bin}\"" 430 | exit 1 431 | fi 432 | done 433 | # No 434 | for bin in none none32 none_cl none_cl32; do 435 | "${DIR}"/binaries/output/${bin} > /dev/null & 436 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f8) != "No" ]]; then 437 | echo "No Fortify process validation failed on \"${bin}\"" 438 | exit 1 439 | fi 440 | done 441 | # N/A 442 | for bin in nolibc nolibc_cl nolibc32 nolibc_cl32 fszero fszero_cl fszero32 fszero_cl32; do 443 | "${DIR}"/binaries/output/${bin} > /dev/null & 444 | if [[ $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f8) != "N/A" ]]; then 445 | echo "No Fortify process validation failed on \"${bin}\": $("${PARENT}"/checksec --proc=${bin} --format=csv | cut -d, -f8)" 446 | exit 1 447 | fi 448 | done 449 | echo "Fortify process validation tests passed" 450 | echo "Done." 451 | echo "All hardening validation tests passed" 452 | -------------------------------------------------------------------------------- /tests/json-checks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ou pipefail 3 | if [[ -f /bin/bash ]]; then 4 | test_file="/bin/bash" 5 | elif [[ -f /bin/sh ]]; then 6 | test_file="/bin/sh" 7 | elif [[ -f /bin/ls ]]; then 8 | test_file="/bin/ls" 9 | else 10 | echo "could not find valid file to test" 11 | exit 255 12 | fi 13 | 14 | DIR=$( 15 | cd "$(dirname "$0")" 16 | pwd 17 | ) 18 | PARENT=$( 19 | cd "$(dirname "$0")/.." 20 | pwd 21 | ) 22 | 23 | jsonlint=$(command -v jsonlint || command -v jsonlint-py) 24 | #check json for proc-all 25 | echo "starting proc-all check - json" 26 | "${PARENT}"/checksec --format=json --proc-all > "${DIR}/output.json" 27 | "${jsonlint}" --allow duplicate-keys "${DIR}/output.json" > /dev/null 28 | RET=$? 29 | jq . < "${DIR}/output.json" &> /dev/null 30 | JQRET=$? 31 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 32 | cat "${DIR}/output.json" 33 | echo "proc-all json validation failed" 34 | exit 1 35 | fi 36 | 37 | #check json for proc-all 38 | echo "starting extended proc-all check - json" 39 | "${PARENT}/checksec" --format=json --proc-all --extended > "${DIR}/output.json" 40 | "${jsonlint}" --allow duplicate-keys "${DIR}/output.json" > /dev/null 41 | RET=$? 42 | jq . < "${DIR}/output.json" &> /dev/null 43 | JQRET=$? 44 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 45 | cat "${DIR}/output.json" 46 | echo "proc-all json validation failed" 47 | exit 1 48 | fi 49 | 50 | #check json for kernel 51 | echo "starting kernel check - json" 52 | "${PARENT}/checksec" --format=json --kernel > "${DIR}/output.json" 53 | "${jsonlint}" "${DIR}/output.json" > /dev/null 54 | RET=$? 55 | jq . < "${DIR}/output.json" &> /dev/null 56 | JQRET=$? 57 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 58 | cat "${DIR}/output.json" 59 | echo "kernel json validation failed" 60 | exit 1 61 | fi 62 | 63 | echo "starting custom kernel check for file kernel.config - json" 64 | "${PARENT}/checksec" --format=json --kernel=kernel.config > "${DIR}/output.json" 65 | "${jsonlint}" "${DIR}/output.json" > /dev/null 66 | RET=$? 67 | jq . < "${DIR}/output.json" &> /dev/null 68 | JQRET=$? 69 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 70 | cat "${DIR}/output.json" 71 | echo "custom kernel json validation failed" 72 | exit 1 73 | fi 74 | 75 | while read -r file; do 76 | #check json against custom kernel config to trigger all checks 77 | echo "starting custom kernel check for file ${file} - json" 78 | "${PARENT}/checksec" --format=json --kernel="${file}" > "${DIR}/output.json" 79 | "${jsonlint}" "${DIR}/output.json" > /dev/null 80 | RET=$? 81 | jq . < "${DIR}/output.json" &> /dev/null 82 | JQRET=$? 83 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 84 | cat "${DIR}/output.json" 85 | echo "custom kernel json validation failed" 86 | exit 1 87 | fi 88 | done < <(find "${PARENT}"/kernel_configs/configs/ -type f -iname "config-*") 89 | 90 | #check json for file 91 | echo "starting file check - json" 92 | "${PARENT}/checksec" --format=json --file="${test_file}" > "${DIR}/output.json" 93 | "${jsonlint}" "${DIR}/output.json" > /dev/null 94 | RET=$? 95 | jq . < "${DIR}/output.json" &> /dev/null 96 | JQRET=$? 97 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 98 | cat "${DIR}/output.json" 99 | echo "file json validation failed" 100 | exit 1 101 | fi 102 | 103 | #check json for listfile 104 | echo "starting listfile check - json" 105 | "${PARENT}/checksec" --format=json --listfile=<(printf "%b" "${test_file}\n${test_file}") > "${DIR}/output.json" 106 | "${jsonlint}" "${DIR}/output.json" > /dev/null 107 | RET=$? 108 | jq . < "${DIR}/output.json" &> /dev/null 109 | JQRET=$? 110 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 111 | cat "${DIR}/output.json" 112 | echo "listfile json validation failed" 113 | exit 1 114 | fi 115 | 116 | #check json for file extended 117 | echo "starting extended file check - json" 118 | "${PARENT}/checksec" --format=json --extended --file="${test_file}" > "${DIR}/output.json" 119 | "${jsonlint}" "${DIR}/output.json" > /dev/null 120 | RET=$? 121 | jq . < "${DIR}/output.json" &> /dev/null 122 | JQRET=$? 123 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 124 | cat "${DIR}/output.json" 125 | echo "file json validation failed" 126 | exit 1 127 | fi 128 | 129 | #check json for fortify file 130 | echo "starting fortify-file check - json" 131 | "${PARENT}/checksec" --format=json --fortify-file="${test_file}" > "${DIR}/output.json" 132 | "${jsonlint}" --allow duplicate-keys "${DIR}/output.json" > /dev/null 133 | RET=$? 134 | jq . < "${DIR}/output.json" &> /dev/null 135 | JQRET=$? 136 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 137 | cat "${DIR}/output.json" 138 | echo "fortify-file json validation failed" 139 | exit 1 140 | fi 141 | 142 | #check json for fortify file 143 | echo "starting extended fortify-file check - json" 144 | "${PARENT}/checksec" --format=json --fortify-file="${test_file}" --extended > "${DIR}/output.json" 145 | "${jsonlint}" --allow duplicate-keys "${DIR}/output.json" > /dev/null 146 | RET=$? 147 | jq . < "${DIR}/output.json" &> /dev/null 148 | JQRET=$? 149 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 150 | cat "${DIR}/output.json" 151 | echo "fortify-file json validation failed" 152 | exit 1 153 | fi 154 | 155 | #check json for fortify proc 156 | echo "starting fortify-proc check - json" 157 | "${PARENT}/checksec" --format=json --fortify-proc=1 > "${DIR}/output.json" 158 | "${jsonlint}" --allow duplicate-keys "${DIR}/output.json" > /dev/null 159 | RET=$? 160 | jq . < "${DIR}/output.json" &> /dev/null 161 | JQRET=$? 162 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 163 | cat "${DIR}/output.json" 164 | echo "fortify-file json validation failed" 165 | exit 1 166 | fi 167 | 168 | #check json for fortify proc 169 | echo "starting extended fortify-proc check - json" 170 | "${PARENT}/checksec" --format=json --fortify-proc=1 --extended > "${DIR}/output.json" 171 | "${jsonlint}" --allow duplicate-keys "${DIR}/output.json" > /dev/null 172 | RET=$? 173 | jq . < "${DIR}/output.json" &> /dev/null 174 | JQRET=$? 175 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 176 | cat "${DIR}/output.json" 177 | echo "fortify-file json validation failed" 178 | exit 1 179 | fi 180 | 181 | #check json for dir 182 | echo "starting dir check - json" 183 | "${PARENT}"/checksec --format=json --dir=/sbin > "${DIR}/output.json" 184 | "${jsonlint}" "${DIR}/output.json" > /dev/null 185 | RET=$? 186 | jq . < "${DIR}/output.json" &> /dev/null 187 | JQRET=$? 188 | if [[ ${RET} != 0 ]] || [[ ${JQRET} != 0 ]]; then 189 | cat "${DIR}/output.json" 190 | echo "dir json validation failed" 191 | exit 1 192 | fi 193 | 194 | echo "All json validation tests passed jsonlint" 195 | rm -f "${DIR}/output.json" 196 | -------------------------------------------------------------------------------- /tests/test-checksec.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # run a quick test of checksec to ensure normal operations. 3 | DIR=$( 4 | cd "$(dirname "$0")" 5 | pwd 6 | ) 7 | 8 | "${DIR}"/xml-checks.sh || exit 2 9 | "${DIR}"/json-checks.sh || exit 2 10 | "${DIR}"/hardening-checks.sh || exit 2 11 | -------------------------------------------------------------------------------- /tests/xml-checks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ou pipefail 3 | if [[ -f /bin/bash ]]; then 4 | test_file="/bin/bash" 5 | elif [[ -f /bin/sh ]]; then 6 | test_file="/bin/sh" 7 | elif [[ -f /bin/ls ]]; then 8 | test_file="/bin/ls" 9 | else 10 | echo "could not find valid file to test" 11 | exit 255 12 | fi 13 | 14 | DIR=$( 15 | cd "$(dirname "$0")" 16 | pwd 17 | ) 18 | PARENT=$( 19 | cd "$(dirname "$0")/.." 20 | pwd 21 | ) 22 | 23 | #check xml for proc-all 24 | echo "starting proc-all check - xml" 25 | "${PARENT}"/checksec --format=xml --proc-all > "${DIR}/output.xml" 26 | xmllint --noout "${DIR}/output.xml" 27 | RET=$? 28 | if [ ${RET} != 0 ]; then 29 | cat "${DIR}/output.xml" 30 | echo "proc-all xml validation failed" 31 | exit 1 32 | fi 33 | 34 | #check xml for proc-all 35 | echo "starting extended proc-all check - xml" 36 | "${PARENT}"/checksec --format=xml --proc-all --extended > "${DIR}/output.xml" 37 | xmllint --noout "${DIR}/output.xml" 38 | RET=$? 39 | if [ ${RET} != 0 ]; then 40 | cat "${DIR}/output.xml" 41 | echo "proc-all xml validation failed" 42 | exit 1 43 | fi 44 | 45 | #check xml for kernel 46 | echo "starting kernel check - xml" 47 | "${PARENT}"/checksec --format=xml --kernel > "${DIR}/output.xml" 48 | xmllint --noout "${DIR}/output.xml" 49 | RET=$? 50 | if [ ${RET} != 0 ]; then 51 | cat "${DIR}/output.xml" 52 | echo "kernel xml validation failed" 53 | exit 1 54 | fi 55 | 56 | echo "starting custom kernel check for file kernel.config - json" 57 | "${PARENT}"/checksec --format=xml --kernel=kernel.config > "${DIR}"/output.xml 58 | xmllint --noout "${DIR}/output.xml" 59 | RET=$? 60 | if [ ${RET} != 0 ]; then 61 | cat "${DIR}/output.xml" 62 | echo "custom kernel json validation failed" 63 | exit 1 64 | fi 65 | 66 | while read -r file; do 67 | #check xml against custom kernel config to trigger all checks 68 | echo "starting custom kernel check for file ${file} - xml" 69 | "${PARENT}"/checksec --format=xml --kernel="${file}" > "${DIR}/output.xml" 70 | xmllint --noout "${DIR}/output.xml" 71 | RET=$? 72 | if [ ${RET} != 0 ]; then 73 | cat "${DIR}/output.xml" 74 | echo "custom kernel xml validation failed" 75 | exit 1 76 | fi 77 | done < <(find "${PARENT}"/kernel_configs/configs/ -type f -iname "config-*") 78 | 79 | #check xml for file 80 | echo "starting file check - xml" 81 | "${PARENT}"/checksec --format=xml --file="${test_file}" > "${DIR}/output.xml" 82 | xmllint --noout "${DIR}/output.xml" 83 | RET=$? 84 | if [ ${RET} != 0 ]; then 85 | cat "${DIR}/output.xml" 86 | echo "file xml validation failed" 87 | exit 1 88 | fi 89 | 90 | #check xml for file 91 | echo "starting extended file check - xml" 92 | "${PARENT}"/checksec --format=xml --file="${test_file}" --extended > "${DIR}/output.xml" 93 | xmllint --noout "${DIR}/output.xml" 94 | RET=$? 95 | if [ ${RET} != 0 ]; then 96 | cat "${DIR}/output.xml" 97 | echo "file xml validation failed" 98 | exit 1 99 | fi 100 | 101 | #check xml for fortify file 102 | echo "starting fortify-file check - xml" 103 | "${PARENT}"/checksec --format=xml --fortify-file="${test_file}" > "${DIR}/output.xml" 104 | xmllint --noout "${DIR}/output.xml" 105 | RET=$? 106 | if [ ${RET} != 0 ]; then 107 | cat "${DIR}/output.xml" 108 | echo "fortify-file xml validation failed" 109 | exit 1 110 | fi 111 | 112 | #check xml for fortify file 113 | echo "starting extended fortify-file check - xml" 114 | "${PARENT}"/checksec --format=xml --fortify-file="${test_file}" --extended > "${DIR}/output.xml" 115 | xmllint --noout "${DIR}/output.xml" 116 | RET=$? 117 | if [ ${RET} != 0 ]; then 118 | cat "${DIR}/output.xml" 119 | echo "fortify-file xml validation failed" 120 | exit 1 121 | fi 122 | 123 | #check xml for fortify proc 124 | echo "starting fortify-proc check - xml" 125 | "${PARENT}"/checksec --format=xml --fortify-proc=1 > "${DIR}/output.xml" 126 | xmllint --noout "${DIR}/output.xml" 127 | RET=$? 128 | if [ ${RET} != 0 ]; then 129 | cat "${DIR}/output.xml" 130 | echo "fortify-proc xml validation failed" 131 | exit 1 132 | fi 133 | 134 | #check xml for fortify proc 135 | echo "starting extended fortify-proc check - xml" 136 | "${PARENT}"/checksec --format=xml --fortify-proc=1 --extended > "${DIR}/output.xml" 137 | xmllint --noout "${DIR}/output.xml" 138 | RET=$? 139 | if [ ${RET} != 0 ]; then 140 | cat "${DIR}/output.xml" 141 | echo "fortify-proc xml validation failed" 142 | exit 1 143 | fi 144 | 145 | #check xml for dir 146 | echo "starting dir check - xml" 147 | "${PARENT}"/checksec --format=xml --dir=/sbin > "${DIR}/output.xml" 148 | xmllint --noout "${DIR}/output.xml" 149 | RET=$? 150 | if [ ${RET} != 0 ]; then 151 | cat "${DIR}/output.xml" 152 | echo "dir xml validation failed" 153 | exit 1 154 | fi 155 | 156 | echo "All XML validation tests passed xmllint" 157 | rm -f "${DIR}/output.xml" 158 | --------------------------------------------------------------------------------