<?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>Forem: Niklas</title>
    <description>The latest articles on Forem by Niklas (@ahzek).</description>
    <link>https://forem.com/ahzek</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3853002%2F95054850-e735-4ac4-b825-00cfd792bd4e.png</url>
      <title>Forem: Niklas</title>
      <link>https://forem.com/ahzek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ahzek"/>
    <language>en</language>
    <item>
      <title>AD Pentesting 01: Getting your head right</title>
      <dc:creator>Niklas</dc:creator>
      <pubDate>Mon, 13 Apr 2026 11:53:39 +0000</pubDate>
      <link>https://forem.com/ahzek/ad-pentesting-01-getting-your-head-right-2n23</link>
      <guid>https://forem.com/ahzek/ad-pentesting-01-getting-your-head-right-2n23</guid>
      <description>&lt;p&gt;I'm currently going through &lt;a href="https://github.com/Orange-Cyberdefense/GOAD" rel="noopener noreferrer"&gt;GOAD&lt;/a&gt; with a friend, and before writing up any of the actual attack paths we traverse, i wanted to put down the foundation that makes everything else makes sense.&lt;/p&gt;

&lt;p&gt;This post covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interactive learning quizzes so you &lt;strong&gt;really remember&lt;/strong&gt; the foundations&lt;/li&gt;
&lt;li&gt;Domains, trees, forests — and why the forest is the boundary 
that actually matters&lt;/li&gt;
&lt;li&gt;The Domain Controller: what it does, why it should do nothing 
else, and why it's our primary target&lt;/li&gt;
&lt;li&gt;Kerberos SSO and why a single domain account gives you far more 
visibility than people expect&lt;/li&gt;
&lt;li&gt;PDC/BDC history → multi-master replication → the PDC Emulator 
and why it keeps coming up&lt;/li&gt;
&lt;li&gt;RODCs and why they exist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No exploits yet. Just the stuff that needs to be load-bearing &lt;br&gt;
before anything offensive makes sense.&lt;/p&gt;

&lt;p&gt;Full post (with interactive knowledge checks) on my blog →&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://niklas-heringer.com/penetration-testing/active-directory-pentesting-part-01/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.ghost.io%2Fc%2Fa1%2F92%2Fa19235e1-df52-43a1-9f25-1151fdb92c82%2Fcontent%2Fimages%2F2026%2F04%2Fad_01.png" height="800" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://niklas-heringer.com/penetration-testing/active-directory-pentesting-part-01/" rel="noopener noreferrer" class="c-link"&gt;
            Active Directory Pentesting: Part 01
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Before we get offensive, we get grounded. This post covers the AD fundamentals every pentester needs locked in: domains, trees, forests, the Domain Controller as crown jewel, Kerberos SSO, multi-master replication, and why even a low-privileged domain account is worth more than it looks.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.ghost.io%2Fc%2Fa1%2F92%2Fa19235e1-df52-43a1-9f25-1151fdb92c82%2Fcontent%2Fimages%2Fsize%2Fw256h256%2F2025%2F11%2FDesign-ohne-Titel.png" width="256" height="256"&gt;
          niklas-heringer.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>forensics</category>
      <category>security</category>
      <category>linux</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I built a forensics documentation tool because my university course drove me crazy</title>
      <dc:creator>Niklas</dc:creator>
      <pubDate>Mon, 06 Apr 2026 13:17:56 +0000</pubDate>
      <link>https://forem.com/ahzek/i-built-a-forensics-documentation-tool-because-my-university-course-drove-me-crazy-4lhg</link>
      <guid>https://forem.com/ahzek/i-built-a-forensics-documentation-tool-because-my-university-course-drove-me-crazy-4lhg</guid>
      <description>&lt;p&gt;I'm not a professional forensics investigator, just a security student who had a university course on digital forensics last summer and got &lt;em&gt;increasingly&lt;/em&gt; frustrated with one specific part of it: not the investigation, but the documentation.&lt;/p&gt;

&lt;p&gt;Every tool, every command, every hash, manually noted. Timestamps written essentially by hand. Chain of custody as an afterthought. My colleagues felt the same way. So we built something to fix it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/mev0lent/forensic-log-tracker" rel="noopener noreferrer"&gt;forensic-log-tracker&lt;/a&gt;&lt;/strong&gt; wraps your forensic commands, whatever you can do in a shell, and automatically produces timestamped, SHA256-hashed, GPG-signed investigation logs. &lt;br&gt;
One command at the end generates a complete case report in Markdown. &lt;br&gt;
It also provides explanations, as report readers are hardly ever experts, so for your commands you get structures like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---

### [+] Command: `sha256sum working_copy.img`
- Timestamp: `2026-04-06T09-08-28-524115+00-00`
- GPG-signature: [+] Valid
- SHA256: `92cebec98bfd99f06db56bd758d5977b62abc27513805ca24a72cdb7ed0f5756`

#### Output:
[STDOUT]
08f8672e957e4f7f08ac9a7f2797c34bdffe51d35a7e04f60c1be256a82cc0ff  working_copy.img

[STDERR]

#### Context:
### [+] Legal Context for `sha256sum working_copy.img`

**Analyst:** Niklas Heringer
**Timestamp:** 2026-04-06T12:50:04.899436+00:00

The `sha256sum` command calculates a SHA-256 cryptographic hash of a file.

---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explanations and configs can be adjusted to your needs in YAML files that come along with your install.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'd love if you checked it out and gave me feedback. One thing might be a PDF report option?&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;forensic-log-tracker

flt setup &lt;span class="c"&gt;# to setup a GPG key for the projects&lt;/span&gt;

flt new-case MY_CASE_NAME &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Investigating suspicious image"&lt;/span&gt;
flt run &lt;span class="s2"&gt;"dd if=evidence.img of=working_copy.img bs=4M conv=noerror,sync"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; MY_CASE_NAME 
flt run &lt;span class="s2"&gt;"foremost -i working_copy.img -o output/ -v"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; MY_CASE_NAME 
flt report &lt;span class="nt"&gt;-c&lt;/span&gt; mycase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/mev0lent/forensic-log-tracker" rel="noopener noreferrer"&gt;All it can do can be found in the README :D&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;To provide a bit of context, I wrote a beginner forensics guide around it, &lt;code&gt;dd&lt;/code&gt;, Foremost, Scalpel, &lt;code&gt;strings&lt;/code&gt;, the works. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There's a pre-built practice image to download and work through, and three interactive quizzes embedded in the post.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://niklas-heringer.com/digital-forensics/digital-forensics-for-beginners/" rel="noopener noreferrer"&gt;Full guide with interactive exercises&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you use a forensic tool that's not in the &lt;code&gt;explanations.yaml&lt;/code&gt; yet, &lt;strong&gt;PRs are very welcome.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⭐ &lt;a href="https://github.com/mev0lent/forensic-log-tracker" rel="noopener noreferrer"&gt;github.com/mev0lent/forensic-log-tracker&lt;/a&gt;&lt;/p&gt;

</description>
      <category>forensics</category>
      <category>security</category>
      <category>linux</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Architecting an Ultra-Minimal Linux VM with Buildroot | Part 1: Build, Break, Fix</title>
      <dc:creator>Niklas</dc:creator>
      <pubDate>Wed, 01 Apr 2026 14:37:16 +0000</pubDate>
      <link>https://forem.com/ahzek/architecting-an-ultra-minimal-linux-vm-with-buildroot-part-1-build-break-fix-28pa</link>
      <guid>https://forem.com/ahzek/architecting-an-ultra-minimal-linux-vm-with-buildroot-part-1-build-break-fix-28pa</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This is Part 1 of a multi-part series. We go from zero to a working VM that passes every audit check. Part 2 will cover packaging it into a bootable ISO and squeezing under the 20MB target.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My new semester just started, and one of the first challenges from my OS module was this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Build the smallest bootable Linux VM you can. It must only pass the tests in this bash-testing-script, nothing more. Personally, I'd love to see &amp;lt;20MB."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds simple. It's not. Everything has gotten bigger. Software that would've been lean 15 years ago is now bloated by default. But let's try to ace it anyway.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Audit Script: What We're Actually Building For
&lt;/h2&gt;

&lt;p&gt;Before touching a config file, we need to know what success looks like. The professor gave us &lt;code&gt;audit.sh&lt;/code&gt;. If it doesn't return all-OK, the VM fails; &lt;em&gt;no matter how small it is&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here's what the script checks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shell &amp;amp; basic commands&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; sh &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ok &lt;span class="s2"&gt;"POSIX-Shell"&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;cmd &lt;span class="k"&gt;in &lt;/span&gt;&lt;span class="nb"&gt;cp mv cat rm ls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ok &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt; present"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Privilege &amp;amp; process inspection&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ok &lt;span class="s2"&gt;"sudo present"&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;ps &lt;span class="nt"&gt;-e&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s1"&gt;'PID'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;ok &lt;span class="s2"&gt;"ps works correctly"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Networking&lt;/strong&gt; — needs a valid RFC1918 address, the &lt;code&gt;ip&lt;/code&gt; command, and a working &lt;code&gt;lo&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connectivity tools&lt;/strong&gt; — OpenSSH (both client and &lt;code&gt;sshd&lt;/code&gt; running), &lt;code&gt;arp-scan&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt; with HTTPS, and &lt;code&gt;nc&lt;/code&gt; for raw TCP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;pgrep &lt;span class="nt"&gt;-x&lt;/span&gt; sshd &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; pgrep &lt;span class="nt"&gt;-x&lt;/span&gt; dropbear &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;ok &lt;span class="s2"&gt;"sshd/dropbear running"&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; arp-scan &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ok &lt;span class="s2"&gt;"arp-scan present"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Data handling&lt;/strong&gt; — &lt;code&gt;tar&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, HTTP banner grabbing via netcat.&lt;/p&gt;

&lt;p&gt;The challenge: packages like &lt;code&gt;openssh&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, and &lt;code&gt;arp-scan&lt;/code&gt; aren't tiny. Fitting everything under 20MB is going to be a fight.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Full post published on &lt;a href="https://niklas-heringer.com/skills-lab/ultra-minimal-linux-vm-with-buildroot-part-1/" rel="noopener noreferrer"&gt;niklas-heringer.com&lt;/a&gt;. I write about offensive security, embedded Linux, and building things from scratch. If you want, feel free to follow me there or on &lt;a href="https://github.com/mev0lent" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Buildroot?
&lt;/h2&gt;

&lt;p&gt;A friend in my OS class pointed me toward &lt;a href="https://buildroot.org" rel="noopener noreferrer"&gt;Buildroot&lt;/a&gt;, a set of Makefiles and patches that automates building a complete Linux system from scratch. You specify exactly which packages you want, it builds a cross-compilation toolchain, the kernel, and the root filesystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why not just use Alpine?&lt;/strong&gt; You could. But Buildroot gives exact control over what ends up in the image. If &lt;code&gt;audit.sh&lt;/code&gt; doesn't check for it, it doesn't ship.&lt;/p&gt;

&lt;h3&gt;
  
  
  The four layers
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;Our output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;Bridge between software and hardware&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bzImage&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root Filesystem&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/bin&lt;/code&gt;, &lt;code&gt;/etc&lt;/code&gt;, &lt;code&gt;/usr&lt;/code&gt; — everything the running system uses&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rootfs.ext2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BusyBox&lt;/td&gt;
&lt;td&gt;Single ~1MB binary replacing hundreds of GNU tools&lt;/td&gt;
&lt;td&gt;built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buildroot&lt;/td&gt;
&lt;td&gt;Automates compiling all of the above&lt;/td&gt;
&lt;td&gt;the whole pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  A note on the cross-compiler
&lt;/h3&gt;

&lt;p&gt;Even though our build machine and target VM are both x86_64, Buildroot builds its own isolated toolchain first. This guarantees reproducible, minimal output with controlled compiler flags. It's also why the first build takes 30–60 minutes, it's &lt;em&gt;literally compiling a compiler&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Buildroot
├── builds cross-compiler (host-gcc, ~20 min)
├── compiles kernel → bzImage
├── compiles packages (openssh, curl, arp-scan ...)
└── assembles rootfs → rootfs.ext2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Setting Up the Build Environment
&lt;/h2&gt;

&lt;p&gt;We use a Debian Bookworm Docker container. It gives a known-good environment that won't interfere with the host (I had a Kali VM lying around).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker.io
systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; docker

&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--dns&lt;/span&gt; 8.8.8.8 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; minlinux debian:bookworm bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two flags worth noting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--privileged&lt;/code&gt;: needed later when we mount the rootfs image as a loop device&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--dns 8.8.8.8&lt;/code&gt;: without this, &lt;code&gt;apt&lt;/code&gt; inside the container silently fails to resolve hostnames&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything from here runs &lt;strong&gt;inside the container&lt;/strong&gt; unless stated otherwise.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Build Script: Step by Step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Install build dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get update
apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    wget make gcc g++ unzip bc &lt;span class="se"&gt;\&lt;/span&gt;
    libncurses-dev rsync cpio xz-utils &lt;span class="se"&gt;\&lt;/span&gt;
    bzip2 file perl patch python3 git qemu-system-x86
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;libncurses-dev&lt;/code&gt; is for &lt;code&gt;menuconfig&lt;/code&gt;. &lt;code&gt;git&lt;/code&gt; is required even without cloning anything, some Buildroot package scripts call it internally.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Download Buildroot
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BUILDROOT_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2026.02"&lt;/span&gt;
wget &lt;span class="s2"&gt;"https://buildroot.org/downloads/buildroot-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BUILDROOT_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.xz"&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xf&lt;/span&gt; buildroot-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BUILDROOT_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.xz &lt;span class="nt"&gt;-C&lt;/span&gt; /opt/buildroot &lt;span class="nt"&gt;--strip-components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Load the base config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /opt/buildroot
make qemu_x86_64_defconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;qemu_x86_64_defconfig&lt;/code&gt; gives a working 64-bit QEMU baseline. We use it as a starting point to layer on top of.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Apply our configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .config &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'

# --- System ---
BR2_TARGET_GENERIC_HOSTNAME="minlinux"
BR2_TARGET_GENERIC_ROOT_PASSWD="aeb"
BR2_SYSTEM_DHCP="eth0"

# --- Minimize size ---
BR2_STRIP_strip=y

# --- Filesystem ---
BR2_TARGET_ROOTFS_EXT2=y
BR2_TARGET_ROOTFS_EXT2_SIZE="64M"
BR2_TARGET_ROOTFS_CPIO=y
BR2_TARGET_ROOTFS_CPIO_GZIP=y

# --- SSH ---
BR2_PACKAGE_DROPBEAR=n
BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_OPENSSH_SERVER=y
BR2_PACKAGE_OPENSSH_CLIENT=y
BR2_PACKAGE_OPENSSH_KEY_UTILS=y

# --- Network ---
BR2_PACKAGE_IPROUTE2=y
BR2_PACKAGE_IPUTILS=y
BR2_PACKAGE_ARP_SCAN=y

# --- curl + TLS ---
BR2_PACKAGE_LIBCURL=y
BR2_PACKAGE_LIBCURL_CURL=y
BR2_PACKAGE_CA_CERTIFICATES=y

# --- sudo ---
BR2_PACKAGE_SUDO=y

# --- pgrep ---
BR2_PACKAGE_PROCPS_NG=y

# --- netcat-openbsd ---
BR2_PACKAGE_NETCAT_OPENBSD=y

# --- BusyBox: sh, ls, cp, mv, cat, rm, ps, tar, awk, ping ---
BR2_PACKAGE_BUSYBOX=y
&lt;/span&gt;&lt;span class="no"&gt;
EOF

&lt;/span&gt;make olddefconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;make olddefconfig&lt;/code&gt; resolves the full dependency tree. Any option that requires another package gets pulled in automatically, but it can also silently override your settings. More on that in a minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: The actual build
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FORCE_UNSAFE_CONFIGURE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
make &lt;span class="nt"&gt;-j&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;tee&lt;/span&gt; /tmp/build.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;FORCE_UNSAFE_CONFIGURE=1&lt;/code&gt;: bypasses the check that refuses to run as root (we're in Docker, we're always root)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-j$(nproc)&lt;/code&gt;: parallel compile jobs; if you have &amp;lt;16GB RAM, drop to &lt;code&gt;-j4&lt;/code&gt; to avoid OOM kills&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tee /tmp/build.log&lt;/code&gt;: saves output so you can scroll back if the build crashes at minute 45&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wait 30–60 minutes. When done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bzImage        ~6.4M
rootfs.ext2    ~60M
rootfs.cpio.gz ~11M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Three Gotchas That Cost Me Hours
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Dropbear trap
&lt;/h3&gt;

&lt;p&gt;My first build used Dropbear instead of OpenSSH. It's a fraction of the size and does SSH perfectly well. Then the audit script hit me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; pgrep &lt;span class="nt"&gt;-x&lt;/span&gt; sshd &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;warn &lt;span class="s2"&gt;"sshd inactive"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pgrep -x&lt;/code&gt; matches exact process names. Dropbear runs as &lt;code&gt;dropbear&lt;/code&gt; in the process table, not &lt;code&gt;sshd&lt;/code&gt;. No symlink or alias changes what the kernel reports in &lt;code&gt;/proc&lt;/code&gt;. The audit would fail every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Read the audit script before choosing packages. The system follows the test, never the other way around.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. HTTPS ≠ HTTP + TLS library
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;curl&lt;/code&gt; was compiled, TLS support was there, but HTTPS failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://www.google.com/ &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1  &lt;span class="c"&gt;# FAIL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Root cause: no CA certificates. Without &lt;code&gt;BR2_PACKAGE_CA_CERTIFICATES=y&lt;/code&gt;, curl has no way to verify the certificate chain for any HTTPS connection. One line in the config, hours of confusion.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. No single netcat does everything
&lt;/h3&gt;

&lt;p&gt;The audit uses &lt;code&gt;nc&lt;/code&gt; in two incompatible ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Raw TCP: uses -q (quit after EOF delay)&lt;/span&gt;
nc &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 12345 &lt;span class="nt"&gt;-q&lt;/span&gt; 1 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;

&lt;span class="c"&gt;# Port scan: uses -z (zero-I/O mode)&lt;/span&gt;
nc &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="nt"&gt;-w1&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLIENT_IP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PORT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;BusyBox nc has &lt;code&gt;-q&lt;/code&gt; but not &lt;code&gt;-z&lt;/code&gt;. netcat-openbsd has &lt;code&gt;-z&lt;/code&gt; but not &lt;code&gt;-q&lt;/code&gt;. No single implementation satisfies both. Solution: install &lt;code&gt;netcat-openbsd&lt;/code&gt;. It handles &lt;code&gt;-z&lt;/code&gt; correctly and silently ignores &lt;code&gt;-q&lt;/code&gt; rather than crashing, and the raw TCP test still passes because the pipe closes stdin naturally anyway.&lt;/p&gt;




&lt;h2&gt;
  
  
  First Boot: 4 Failures
&lt;/h2&gt;

&lt;p&gt;After 45 minutes of building, boot the VM, run the audit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[OK]  POSIX-Shell
[OK]  cp, mv, cat, rm, ls
[OK]  ps
[OK]  lo interface
[OK]  ssh client
[NOK] sshd couldn't be started     ← pgrep missing
[OK]  SSH key setup
[OK]  Ping / DNS
[OK]  arp-scan
[NOK] nc missing                   ← never built
[OK]  curl / HTTP
[NOK] HTTPS failed                 ← no CA certs
[NOK] HTTP banner not found        ← nc cascade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;make olddefconfig&lt;/code&gt; had silently dropped &lt;code&gt;BR2_PACKAGE_PROCPS_NG&lt;/code&gt; and &lt;code&gt;BR2_PACKAGE_NETCAT_OPENBSD&lt;/code&gt;. The defconfig baseline had its own opinions, and dependency resolution overwrote ours without a warning.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix: No Full Rebuild
&lt;/h2&gt;

&lt;p&gt;The toolchain, kernel, and everything that built correctly is already cached. We only need the missing packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /opt/buildroot
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FORCE_UNSAFE_CONFIGURE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1

make procps-ng-rebuild
make netcat-openbsd-rebuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;~30 seconds each. Verify the binaries landed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find output/target &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"pgrep"&lt;/span&gt;  &lt;span class="c"&gt;# → output/target/bin/pgrep ✓&lt;/span&gt;
find output/target &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"nc"&lt;/span&gt;     &lt;span class="c"&gt;# → output/target/usr/bin/nc ✓&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then rebuild just the filesystem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make rootfs-ext2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;30 seconds. Boot. Run the audit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Second Boot: All Green
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ POSIX Shell
✓ ls, cp, mv, cat, rm
✓ ps
✓ lo interface
✓ ssh / key setup
✓ Ping 8.8.8.8 / DNS
Network: 10.0.2.15/24
-- Live Hosts (ARP) --
10.0.2.2  52:55:0a:00:02:02
10.0.2.3  52:55:0a:00:02:03
✓ All tests complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only sections not passing are the SSH Client Analysis and OS Fingerprinting; those require connecting &lt;em&gt;via SSH from an external machine&lt;/em&gt;. When you SSH in through port-forwarded 2222, the script detects your client IP and scans you back. That's working as designed. The OS Fingerprinting section is a &lt;code&gt;TODO&lt;/code&gt; the professor intentionally left for each team.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the audit script before choosing packages.&lt;/strong&gt; 15 minutes upfront saves hours of debugging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;make olddefconfig&lt;/code&gt; can silently drop your config options.&lt;/strong&gt; Always verify what ended up in the image, not what you put in the config.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS requires CA certificates, not just TLS support.&lt;/strong&gt; &lt;code&gt;BR2_PACKAGE_CA_CERTIFICATES=y&lt;/code&gt; is not optional.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No single netcat handles all flag combinations.&lt;/strong&gt; netcat-openbsd is the pragmatic choice here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental rebuilds are fast.&lt;/strong&gt; &lt;code&gt;make &amp;lt;package&amp;gt;-rebuild&lt;/code&gt; + &lt;code&gt;make rootfs-ext2&lt;/code&gt; takes seconds. No need to start from scratch.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In Part 2 I'll tackle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Packaging into a bootable ISO&lt;/li&gt;
&lt;li&gt;Squeezing under 20MB (&lt;code&gt;rootfs.cpio.gz&lt;/code&gt; is 11MB + &lt;code&gt;bzImage&lt;/code&gt; at 6.4MB = ~17.4MB before ISO overhead.. it's going to be tight)&lt;/li&gt;
&lt;li&gt;Implementing the OS Fingerprinting TODO&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>buildroot</category>
      <category>embedded</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
