<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rich</title>
    <description>The latest articles on DEV Community by Rich (@richjj98).</description>
    <link>https://dev.to/richjj98</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4203602%2F43bb4269-a388-404b-bad0-80827b8bf041.jpg</url>
      <title>DEV Community: Rich</title>
      <link>https://dev.to/richjj98</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/richjj98"/>
    <language>en</language>
    <item>
      <title>firmware-auditor: Zero-Emulation Static Security Analysis for Firmware Rootfs in CI/CD</title>
      <dc:creator>Rich</dc:creator>
      <pubDate>Sat, 24 Oct 2026 04:01:12 +0000</pubDate>
      <link>https://dev.to/richjj98/firmware-auditor-zero-emulation-static-security-analysis-for-firmware-rootfs-in-cicd-2g6d</link>
      <guid>https://dev.to/richjj98/firmware-auditor-zero-emulation-static-security-analysis-for-firmware-rootfs-in-cicd-2g6d</guid>
      <description>&lt;p&gt;Security assessments for IoT devices and embedded Linux systems have long relied on two main approaches: manual static inspection or full dynamic emulation (QEMU, Unicorn, Firmadyne, FirmAE).&lt;/p&gt;

&lt;p&gt;Dynamic analysis is invaluable for verifying vulnerabilities, but plugging emulated environments into automated CI/CD pipelines often leads to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;High execution overhead&lt;/strong&gt; and slow feedback loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture incompatibilities&lt;/strong&gt;: custom NVRAM, vendor peripherals and proprietary SoC hardware are exactly where generic emulators tend to break.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flaky automation&lt;/strong&gt; that stalls pull requests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To bridge this gap I built &lt;strong&gt;&lt;a href="https://github.com/RichJJ98/firmware-auditor" rel="noopener noreferrer"&gt;firmware-auditor&lt;/a&gt;&lt;/strong&gt;: a fast, zero-emulation static analysis framework for unpacked root filesystem images.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Idea: Reproducible Static Posture
&lt;/h2&gt;

&lt;p&gt;When you have an unpacked SquashFS, ext4 or UBIFS rootfs, many security posture indicators can be checked entirely offline, without executing a single target binary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ELF binary hardening:&lt;/strong&gt; are binaries built with modern mitigations, or exposed to classic exploitation techniques?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filesystem hygiene and secret exposure:&lt;/strong&gt; did the build bake private SSH keys, a readable &lt;code&gt;/etc/shadow&lt;/code&gt; or world-writable scripts into production?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actionable DevSecOps gates:&lt;/strong&gt; can the pipeline fail (&lt;code&gt;exit 1&lt;/code&gt;) on critical issues and emit standardized, machine-readable results?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is what a run looks like. This is a &lt;strong&gt;test rootfs with intentionally planted flaws&lt;/strong&gt;, not a real firmware image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rootfs     : test_rootfs
duration   : 0.007s (workers=1)
severity   : &amp;gt;= info
analyzers  : binary_hardening 1.0.0 ok (0 findings, 0.004s)
             filesystem_hygiene 1.0.0 ok (5 findings, 0.002s)

SUMMARY BY SEVERITY
CRITICAL   5 ####################
HIGH       0
MEDIUM     0
LOW        0
TOTAL      5

FINDINGS BY RULE (4 rule(s), 5 finding(s))
SEVERITY RULE         COUNT LOCATION SAMPLE     TITLE
CRITICAL FWK-PERM-001     1 etc                 World-writable directory
CRITICAL FWK-KEY-001      1 etc/id_rsa          Private key material in the firmware image
CRITICAL FWK-PERM-002     2 etc/id_rsa; shadow  World-writable file in a sensitive tree
CRITICAL FWK-PERM-004     1 etc/shadow          Password hash database readable by any user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The framework is written in Python 3.10+ with a modular design built around isolation and reproducibility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;firmware_auditor/
├── analyzers/
│   ├── base.py                 # Base analyzer &amp;amp; Finding contract
│   ├── binary_hardening.py     # ELF checks (NX, PIE, Canary, RELRO)
│   └── filesystem_hygiene.py   # Secrets, permissions, credentials
├── core/
│   ├── pipeline.py             # Concurrent runner, fault isolation
│   └── reporter.py             # Console, JSON and SARIF v2.1.0
└── tests/                      # pytest suite (Ubuntu &amp;amp; Windows)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each analyzer runs behind a fault-isolation boundary with a timeout guard, so one broken analyzer or a malformed binary doesn't take down the whole scan.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. ELF Hardening Inspections
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;pyelftools&lt;/code&gt;, the engine parses ELF headers directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NX / DEP:&lt;/strong&gt; checks the &lt;code&gt;PT_GNU_STACK&lt;/code&gt; flags for an executable stack. A &lt;em&gt;missing&lt;/em&gt; &lt;code&gt;PT_GNU_STACK&lt;/code&gt; header is also treated as an executable stack, since that is the default on several architectures common in firmware (ARM, MIPS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PIE / ASLR:&lt;/strong&gt; &lt;code&gt;ET_DYN&lt;/code&gt; alone is not enough, because shared libraries are &lt;code&gt;ET_DYN&lt;/code&gt; too. The engine separates PIE executables from &lt;code&gt;.so&lt;/code&gt; files by checking for &lt;code&gt;PT_INTERP&lt;/code&gt; and the &lt;code&gt;DF_1_PIE&lt;/code&gt; flag in &lt;code&gt;DT_FLAGS_1&lt;/code&gt;. &lt;code&gt;ET_EXEC&lt;/code&gt; means a fixed load address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stack canaries:&lt;/strong&gt; looks for &lt;code&gt;__stack_chk_fail&lt;/code&gt; in &lt;code&gt;.dynsym&lt;/code&gt; and &lt;code&gt;.symtab&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RELRO:&lt;/strong&gt; checks &lt;code&gt;PT_GNU_RELRO&lt;/code&gt; for Partial RELRO, and for &lt;strong&gt;Full&lt;/strong&gt; RELRO also requires immediate binding (&lt;code&gt;DT_BIND_NOW&lt;/code&gt;, &lt;code&gt;DF_BIND_NOW&lt;/code&gt; in &lt;code&gt;DT_FLAGS&lt;/code&gt;, or &lt;code&gt;DF_1_NOW&lt;/code&gt; in &lt;code&gt;DT_FLAGS_1&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Filesystem Hygiene
&lt;/h2&gt;

&lt;p&gt;Instead of running arbitrary regex over the entire filesystem, the module targets specific security attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks permission masks (&lt;code&gt;st_mode&lt;/code&gt;) on sensitive Unix paths (&lt;code&gt;/etc&lt;/code&gt;, &lt;code&gt;/bin&lt;/code&gt;, &lt;code&gt;/sbin&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Detects unshadowed or empty password hashes in authentication files.&lt;/li&gt;
&lt;li&gt;Identifies raw private key material &lt;strong&gt;without leaking the secret&lt;/strong&gt; into CI logs or reports.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; permission checks need an extraction that preserves Unix modes (&lt;code&gt;unsquashfs&lt;/code&gt;, &lt;code&gt;tar&lt;/code&gt;, WSL or Linux). A rootfs copied onto NTFS loses this information, and permission findings will not be reliable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Native SARIF Support for GitHub Code Scanning
&lt;/h2&gt;

&lt;p&gt;firmware-auditor outputs &lt;strong&gt;SARIF v2.1.0&lt;/strong&gt; instead of a proprietary report format, so findings can show up directly under &lt;strong&gt;Security &amp;gt; Code scanning&lt;/strong&gt; on GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firmware-auditor /path/to/extracted/rootfs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--format&lt;/span&gt; sarif &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; report.sarif &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--fail-on&lt;/span&gt; high
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GitHub Actions example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;firmware-audit&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;security-events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;audit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.12"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install firmware-auditor&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git clone https://github.com/RichJJ98/firmware-auditor&lt;/span&gt;
          &lt;span class="s"&gt;pip install ./firmware-auditor&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Extract rootfs (preserving permissions)&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y squashfs-tools&lt;/span&gt;
          &lt;span class="s"&gt;sudo unsquashfs -f -d rootfs firmware.squashfs&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run audit&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;firmware-auditor rootfs --format sarif --output report.sarif --fail-on high&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload SARIF&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github/codeql-action/upload-sarif@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;sarif_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;report.sarif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;if: always()&lt;/code&gt; on the upload step matters: with &lt;code&gt;--fail-on high&lt;/code&gt; the audit step exits non-zero, and without it the findings would never reach the Code scanning tab.&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitations (What It Does Not Cover)
&lt;/h2&gt;

&lt;p&gt;Static analysis has blind spots, and it is better to say so upfront:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not a replacement for dynamic analysis.&lt;/strong&gt; It won't confirm exploitability or find runtime and logic vulnerabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No CVE matching&lt;/strong&gt; of bundled library versions (yet).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Possible false negatives on canaries:&lt;/strong&gt; stripped or statically linked binaries may not expose &lt;code&gt;__stack_chk_fail&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Needs an already unpacked rootfs.&lt;/strong&gt; Extraction (binwalk, unsquashfs, etc.) is a separate step, and encrypted or proprietary containers are out of scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permission checks depend on extraction fidelity&lt;/strong&gt; (see the note above).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Open Source &amp;amp; Next Steps
&lt;/h2&gt;

&lt;p&gt;firmware-auditor is released under the &lt;strong&gt;MIT License&lt;/strong&gt;, and the test suite runs in CI on both Linux and Windows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/RichJJ98/firmware-auditor" rel="noopener noreferrer"&gt;https://github.com/RichJJ98/firmware-auditor&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work with embedded devices, IoT security or DevSecOps pipelines, clone it, try it on your own firmware images and contribute new rule modules. Feedback, issues and pull requests are very welcome.&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>devsecops</category>
      <category>iot</category>
    </item>
  </channel>
</rss>
