<?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>Windows PrivEsc: Singular Mistakes Costing You Hours (And What Actually Gets You SYSTEM)</title>
      <dc:creator>Niklas</dc:creator>
      <pubDate>Sun, 03 May 2026 14:41:45 +0000</pubDate>
      <link>https://forem.com/ahzek/windows-privesc-singular-mistakes-costing-you-hours-and-what-actually-gets-you-system-3dmo</link>
      <guid>https://forem.com/ahzek/windows-privesc-singular-mistakes-costing-you-hours-and-what-actually-gets-you-system-3dmo</guid>
      <description>&lt;p&gt;Maybe you know that feeling of getting completely lost in the endless rabbit hole an extremely indepth HTB course can throw you in. Even if not, you know that whatever you consume, the &lt;strong&gt;info-to-funfact&lt;/strong&gt; ratio has to be atleast 80/20 for you to be worth it.&lt;/p&gt;

&lt;p&gt;Working through the HackTheBox Windows Privilege Escalation module, &lt;br&gt;
I kept hitting the same problem: too much content, no clear signal on what actually matters &lt;strong&gt;when you're only starting out&lt;/strong&gt;. So here's the distilled version.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Privilege &amp;amp; Group Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Privilege / Group&lt;/th&gt;
&lt;th&gt;What it gets you&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SeImpersonatePrivilege&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SYSTEM via token impersonation&lt;/td&gt;
&lt;td&gt;Service accounts have it by default — MSSQL, IIS, anything running as a service&lt;/td&gt;
&lt;td&gt;PrintSpoofer, SigmaPotato&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SeAssignPrimaryTokenPrivilege&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SYSTEM via token impersonation&lt;/td&gt;
&lt;td&gt;Same accounts, same story&lt;/td&gt;
&lt;td&gt;Potato family&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SeDebugPrivilege&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every credential cached in memory&lt;/td&gt;
&lt;td&gt;Read/write any process including LSASS&lt;/td&gt;
&lt;td&gt;mimikatz + procdump&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SeTakeOwnershipPrivilege&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any file on the system&lt;/td&gt;
&lt;td&gt;Claim ownership of TrustedInstaller-protected files&lt;/td&gt;
&lt;td&gt;takeown + icacls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SeBackupPrivilege&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any file regardless of ACL&lt;/td&gt;
&lt;td&gt;Designed for backup software, abusable for NTDS.dit&lt;/td&gt;
&lt;td&gt;robocopy /B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Backup Operators&lt;/code&gt; (group)&lt;/td&gt;
&lt;td&gt;Same as SeBackupPrivilege&lt;/td&gt;
&lt;td&gt;Group grants the privilege automatically&lt;/td&gt;
&lt;td&gt;robocopy /B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Event Log Readers&lt;/code&gt; (group)&lt;/td&gt;
&lt;td&gt;Plaintext creds from process logs&lt;/td&gt;
&lt;td&gt;Event ID 4688 logs full command lines — passwords typed inline stay there&lt;/td&gt;
&lt;td&gt;wevtutil&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;DnsAdmins&lt;/code&gt; (group)&lt;/td&gt;
&lt;td&gt;SYSTEM via DNS service&lt;/td&gt;
&lt;td&gt;Load a malicious DLL on DNS restart — DNS runs as SYSTEM&lt;/td&gt;
&lt;td&gt;msfvenom + dnscmd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Server Operators&lt;/code&gt; (group)&lt;/td&gt;
&lt;td&gt;SYSTEM via service hijack&lt;/td&gt;
&lt;td&gt;Reconfigure any LocalSystem service binary path&lt;/td&gt;
&lt;td&gt;sc.exe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UAC bypass&lt;/td&gt;
&lt;td&gt;Full admin token without prompt&lt;/td&gt;
&lt;td&gt;DLL hijack via writable PATH + auto-elevating Microsoft binary&lt;/td&gt;
&lt;td&gt;msfvenom + rundll32&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Run &lt;code&gt;whoami /all&lt;/code&gt;, not just &lt;code&gt;whoami /priv&lt;/code&gt;.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Group membership grants privileges just as effectively as direct assignment, &lt;br&gt;
and it's checked far less often.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  One fun little gotcha that cost me the most time
&lt;/h2&gt;

&lt;p&gt;In PowerShell, &lt;code&gt;sc&lt;/code&gt; is an alias for &lt;code&gt;Set-Content&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Running &lt;code&gt;sc stop dns&lt;/code&gt; creates a file called &lt;em&gt;stop&lt;/em&gt; containing the text &lt;em&gt;dns&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
Always use &lt;code&gt;sc.exe&lt;/code&gt; explicitly when talking to the Service Control Manager.&lt;/p&gt;




&lt;p&gt;Full walkthrough with real shell output, failed attempts, and the reasoning &lt;br&gt;
behind every step:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://niklas-heringer.com/skills-lab/windows-privilege-escalation-02-privesc-paths/" rel="noopener noreferrer"&gt;Windows PrivEsc 02 on niklas-heringer.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>hacking</category>
      <category>tutorial</category>
      <category>career</category>
    </item>
    <item>
      <title>Windows PrivEsc 01: Initial Enumeration (The Part That Actually Matters)</title>
      <dc:creator>Niklas</dc:creator>
      <pubDate>Sun, 26 Apr 2026 08:39:57 +0000</pubDate>
      <link>https://forem.com/ahzek/windows-privesc-01-initial-enumeration-the-part-that-actually-matters-8mb</link>
      <guid>https://forem.com/ahzek/windows-privesc-01-initial-enumeration-the-part-that-actually-matters-8mb</guid>
      <description>&lt;p&gt;If you've ever popped a box on HackTheBox, TryHackMe, or OffSec Proving Grounds, you know the drill. Initial access between Linux and Windows isn't that different. Scan, fuzz, find a CVE ("Heey there's an &lt;code&gt;exploit.py&lt;/code&gt;"), get a shell. Not that much different between the OS.&lt;/p&gt;

&lt;p&gt;It gets &lt;em&gt;interesting&lt;/em&gt; with privesc.&lt;/p&gt;

&lt;p&gt;On Linux you've got your SUID bits, writable cron jobs, sudo -l... it's almost cozy. Windows? Windows has services, tokens, ACLs, AppLocker, registry keys, integrity levels, and about fifteen ways a misconfigured service account will hand you SYSTEM if you know where to look.&lt;/p&gt;

&lt;p&gt;This post is Part 01 of my Windows PrivEsc series, amidst my series on Active Directory haha. Before we dive into the juicy stuff, here's the initial enumeration baseline you need to build every single time you land a shell.&lt;/p&gt;




&lt;h2&gt;
  
  
  Know Where You Are
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WmiObject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Win32_OperatingSystem&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;whoami&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/user&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;whoami&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/priv&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;whoami&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/groups&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;whoami /priv&lt;/code&gt; is nice. Spot &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt;? That's basically game over via PrintSpoofer or Juicy Potato. &lt;code&gt;SeBackupPrivilege&lt;/code&gt;? You can read SAM and NTDS.dit. Even &lt;code&gt;Disabled&lt;/code&gt; state doesn't save you; these can be enabled in the same process with a few API calls.&lt;/p&gt;

&lt;p&gt;Want to properly memorize the important stuff? My blog post got interactive quizzes for that → &lt;a href="https://niklas-heringer.com/penetration-testing/windows-privilege-escalation-01/" rel="noopener noreferrer"&gt;niklas-heringer.com&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Network Recon From Inside
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ipconfig /all     # dual-homed? new network segment?
arp -a            # who has this machine talked to recently?
route print       # where can traffic go?
netstat -ano      # what's listening? especially on 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anything bound to &lt;code&gt;127.0.0.1&lt;/code&gt; in &lt;code&gt;netstat&lt;/code&gt; is invisible from outside, but once you have a shell, it's right there. A SQL Server or local web app running as SYSTEM on loopback with no hardening is a classic setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  Check Your Defenses
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-MpComputerStatus&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c"&gt;# Defender: is RealTimeProtection actually on?&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Get-AppLockerPolicy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Effective&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ExpandProperty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RuleCollections&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AppLocker blocks &lt;code&gt;cmd.exe&lt;/code&gt; for everyone? Fine, &lt;code&gt;powershell.exe&lt;/code&gt; might not be? Or only a specific file?. Read the rules, find the gaps.&lt;/p&gt;




&lt;h2&gt;
  
  
  Process &amp;amp; Service Hunting
&lt;/h2&gt;



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

&lt;/div&gt;



&lt;p&gt;Ignore the &lt;code&gt;svchost.exe&lt;/code&gt; army. Look for: Tomcat, FileZilla, SQL Server, third-party VPN services. Old Tomcat with default creds (&lt;code&gt;tomcat:tomcat&lt;/code&gt;) = deploy a WAR = code execution. Old SQL Server = &lt;code&gt;xp_cmdshell&lt;/code&gt; = SYSTEM.&lt;/p&gt;




&lt;h2&gt;
  
  
  Users &amp;amp; Groups
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;net user
net localgroup administrators
whoami /groups
net accounts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Lockout threshold: Never&lt;/code&gt; + &lt;code&gt;Minimum password length: 0&lt;/code&gt; in &lt;code&gt;net accounts&lt;/code&gt;? Spray freely. Look for &lt;code&gt;bob&lt;/code&gt; and &lt;code&gt;bob_adm&lt;/code&gt; side by side: credential reuse gift. Non-standard groups sometimes exist purely to grant access to something sensitive and nobody maintains the membership.&lt;/p&gt;




&lt;h2&gt;
  
  
  Patch Level
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-HotFix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ft&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AutoSize&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;systeminfo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four hotfixes total, last one from 2021? Feed those KB numbers into &lt;a href="https://github.com/bitsadmin/wesng" rel="noopener noreferrer"&gt;WES-NG&lt;/a&gt; and watch it map them to CVEs for you.&lt;/p&gt;




&lt;p&gt;This is just the recon layer. Next post goes into process enumeration, service misconfigs, and where things start to get exploitable.&lt;/p&gt;

&lt;p&gt;Full walkthrough with command output and reasoning on my blog → &lt;a href="https://niklas-heringer.com/penetration-testing/windows-privilege-escalation-01/" rel="noopener noreferrer"&gt;niklas-heringer.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>pentesting</category>
      <category>windows</category>
      <category>beginners</category>
    </item>
    <item>
      <title>AD pentesting part 2: C2, pivoting &amp; password spraying</title>
      <dc:creator>Niklas</dc:creator>
      <pubDate>Sun, 19 Apr 2026 05:37:20 +0000</pubDate>
      <link>https://forem.com/ahzek/ad-pentesting-part-2-c2-pivoting-password-spraying-4p66</link>
      <guid>https://forem.com/ahzek/ad-pentesting-part-2-c2-pivoting-password-spraying-4p66</guid>
      <description>&lt;p&gt;Before you can enumerate or spray anything in an AD environment, your tooling needs to work. This is the setup layer most tutorials skip.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C2 frameworks replace raw shells&lt;/strong&gt;&lt;br&gt;
A raw reverse shell dies the moment your terminal closes. A C2 implant like Sliver has reconnect logic built in, encrypts all traffic, and queues tasks asynchronously. You leave instructions, you read results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://niklas-heringer.com/penetration-testing/active-directory-pentesting-part-02/" rel="noopener noreferrer"&gt;Get an interactive overview with quizzes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use beacons, not sessions&lt;/strong&gt;&lt;br&gt;
Sessions maintain a persistent connection, &lt;em&gt;exactly&lt;/em&gt; what EDR looks for. Beacons sleep between check-ins and add random jitter to their intervals. No consistent pattern, no persistent socket, far less visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;proxychains only intercepts TCP&lt;/strong&gt;&lt;br&gt;
UDP traffic bypasses the proxy entirely. Force Kerberos and DNS over TCP using tool-specific flags. ICMP also bypasses it, so use &lt;code&gt;nmap -sT&lt;/code&gt; for host discovery instead of ping. Statically linked binaries bypass it too — use &lt;code&gt;tun2socks&lt;/code&gt; for those, it routes at the OS level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enumerate usernames before spraying passwords&lt;/strong&gt;&lt;br&gt;
Kerbrute validates usernames by reading Kerberos AS-REQ responses. No authentication attempt means no lockout risk. Build your valid user list first, then choose one candidate password and spray slowly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Password spraying targets many accounts, not one&lt;/strong&gt;&lt;br&gt;
One password across hundreds of users keeps you under the per-account lockout threshold. Know the domain's lockout policy before you start. A locked account is loud. A slow spray is silent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full post:&lt;/strong&gt; &lt;a href="https://niklas-heringer.com/penetration-testing/active-directory-pentesting-part-02/" rel="noopener noreferrer"&gt;https://niklas-heringer.com/penetration-testing/active-directory-pentesting-part-02/&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All attacks performed against private lab environments with explicit permission. Lab used: &lt;a href="https://github.com/Orange-Cyberdefense/GOAD" rel="noopener noreferrer"&gt;Game of Active Directory&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>activedirectory</category>
      <category>pentesting</category>
      <category>networking</category>
    </item>
    <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>
