<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.hoffnet.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.hoffnet.dev/" rel="alternate" type="text/html" /><updated>2026-05-09T07:44:01-06:00</updated><id>https://blog.hoffnet.dev/feed.xml</id><title type="html">hoffnet</title><subtitle>A blog for stuff I currently find interesting.</subtitle><author><name>Zane</name><email></email></author><entry><title type="html">Dumping Router Firmware From A Netgear N600</title><link href="https://blog.hoffnet.dev/hardware-hacking/2025/01/20/dumping-router-firmware-netgear-n600/" rel="alternate" type="text/html" title="Dumping Router Firmware From A Netgear N600" /><published>2025-01-20T09:00:00-07:00</published><updated>2025-01-20T09:00:00-07:00</updated><id>https://blog.hoffnet.dev/hardware-hacking/2025/01/20/dumping-router-firmware-netgear-n600</id><content type="html" xml:base="https://blog.hoffnet.dev/hardware-hacking/2025/01/20/dumping-router-firmware-netgear-n600/"><![CDATA[<p>After watching <a href="https://www.youtube.com/@mattbrwn">Matt Brown</a> tear apart hardware and read firmware from flash chips, I had the thought “how hard could that really be?”</p>

<p>I already had a background in software reverse engineering and had even pulled apart SOHO router firmware before, but I had not spent much time working directly with the hardware. This post is the first part of that learning process: buying cheap routers, removing flash chips, dumping firmware, unpacking the filesystem, and starting to reverse engineer the web management service.</p>

<h2 id="finding-cheap-targets">Finding Cheap Targets</h2>

<p>I wanted devices I would not feel bad about destroying. Fortunately, I have several thrift stores nearby, and old networking gear shows up there pretty often.</p>

<p>On the first trip I picked up a few devices for under $20 total:</p>

<ul>
  <li>CenturyLink Zyxel C3000Z</li>
  <li>TP-Link Archer C7</li>
  <li>Netgear N600</li>
</ul>

<p><img src="/assets/images/router-firmware/thrift-router-haul.png" alt="Thrift-store router haul" /></p>

<p>I already owned a soldering station, so the next missing piece was a programmer that could read SPI flash. I ended up buying an XGecu T48 universal programmer. While waiting for it to arrive, I practiced removing chips from scrap boards and destroyed a few in the process.</p>

<p><img src="/assets/images/router-firmware/desoldering-practice.png" alt="Practicing chip removal" /></p>

<p>Removing a flash chip is simple in theory, but it is easy to lift pads, overheat the package, or do any number of silly things that destroy the device.</p>

<h2 id="choosing-the-netgear-n600">Choosing The Netgear N600</h2>

<p>The first device on the chopping block will be the Netgear N600, because that’s the one I am writing this post about.</p>

<p>The firmware version that was on this device from the thrift store was <code class="language-plaintext highlighter-rouge">V1.0.0.20_1.0.28</code>. Great, no one knew what security was 10 years ago so this might have some good bugs!</p>

<p><img src="/assets/images/router-firmware/netgear-n600-board.png" alt="Netgear N600 board view" /></p>

<p><img src="/assets/images/router-firmware/netgear-n600-open.png" alt="Netgear N600 enclosure and board" /></p>

<h2 id="reading-the-flash-chip">Reading The Flash Chip</h2>

<p>The board used an SOIC-8 flash chip:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MX25L6406E
</code></pre></div></div>

<p>In theory, this chip can sometimes be read in-circuit with a clip. I could not get a stable read that way, so I removed the chip and read it directly with the programmer.</p>

<p><img src="/assets/images/router-firmware/soic8-flash-removed.png" alt="SOIC-8 flash chip removed from the board" /></p>

<p>The good news was after dumping the flash, I was able to solder the chip back onto the board and the router still booted.</p>

<p><img src="/assets/images/router-firmware/flash-chip-resoldered.png" alt="Flash chip soldered back onto the board" /></p>

<h2 id="first-look-at-the-firmware">First Look At The Firmware</h2>

<p>With the firmware dumped, the first low-effort check was <code class="language-plaintext highlighter-rouge">strings</code>. This is not deep analysis, but it is a fast way to see whether the image contains obvious configuration values, board identifiers, paths, or service names.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>4$P*
&lt;$P*
&lt;$P*
fJ5d
&lt;%XA
)5$@
        4%@
ZSIB
FLSHh
Xboardtype=0x0550
...
0:maxp5gla0=0x3C
0:subdevid=0xbdc
apmode_dns1=
wl_antdiv=-1
wl_radio_pwrsave_level=0
wlg_defaKey=0
wla_temp_secu_type=None
wlan_acl_dev23=
0:maxp5gla1=0x3C
0:ag0=0x2
</code></pre></div></div>

<p>After that, I ran <code class="language-plaintext highlighter-rouge">binwalk</code> to identify the firmware layout. I’ve noticed binwalk kind of sucks lately so I was shocked it worked first try…</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
14845         0x39FD          JBOOT STAG header, image id: 2, timestamp 0xA020818, image size: 68182016 bytes, image JBOOT checksum: 0x8228, header JBOOT checksum: 0x5800
38809         0x9799          JBOOT STAG header, image id: 16, timestamp 0x14021610, image size: 554713088 bytes, image JBOOT checksum: 0x88, header JBOOT checksum: 0x2100
43684         0xAAA4          gzip compressed data, maximum compression, has original file name: "piggy", from Unix, last modified: 2012-12-17 11:00:09
262144        0x40000         TRX firmware header, little endian, image size: 6647808 bytes, CRC32: 0x924001AD, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x1390FC, rootfs offset: 0x0
262172        0x4001C         LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 3614368 bytes
1544444       0x1790FC        Squashfs filesystem, little endian, non-standard signature, version 3.0, size: 5359436 bytes, 854 inodes, blocksize: 65536 bytes, created: 2013-01-09 08:23:56
7667728       0x750010        bzip2 compressed data, block size = 900k
7733264       0x760010        bzip2 compressed data, block size = 900k
7798800       0x770010        bzip2 compressed data, block size = 900k
7864336       0x780010        bzip2 compressed data, block size = 900k
7929872       0x790010        bzip2 compressed data, block size = 900k
7995408       0x7A0010        bzip2 compressed data, block size = 900k
</code></pre></div></div>

<p>The interesting part was the SquashFS filesystem. Once extracted, I could browse the router filesystem like a normal directory tree.</p>

<h2 id="finding-the-web-server">Finding The Web Server</h2>

<p>Consumer routers usually expose a custom web interface, and the server-side binary is often where interesting bugs live. In this firmware, the <code class="language-plaintext highlighter-rouge">httpd</code> binary lived under <code class="language-plaintext highlighter-rouge">usr/sbin</code>.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>usr
├── bin
├── local
│   └── samba
│       ├── lib
│       │   ├── lmhosts
│       │   └── smb.conf -&gt; ../../../tmp/samba/private/smb.conf
│       ├── lock -&gt; ../../../var/lock
│       ├── nmbd
│       ├── private -&gt; ../../../tmp/samba/private
│       ├── smbd
│       ├── smb_pass
│       └── var -&gt; ../../../var
├── sbin
│   ├── dhcp6s
│   ├── dnsmasq
│   ├── dnsRedirectReplyd
│   ├── email
│   ├── emf
│   ├── et
│   ├── ftpc
│   ├── gproxy
│   ├── heartbeat
│   ├── httpd
│   └── httpsd.pem
└── tmp -&gt; ../tmp
</code></pre></div></div>

<p>There was also an RSA private key in the same area of the filesystem. I did not dig into it deeply for this first pass, but hardcoded keys in router firmware are always worth noting.</p>

<p>The <code class="language-plaintext highlighter-rouge">httpd</code> binary itself was a stripped 32-bit MIPS executable:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>httpd: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
</code></pre></div></div>

<h2 id="starting-static-analysis-in-ghidra">Starting Static Analysis In Ghidra</h2>

<p>From there, I loaded <code class="language-plaintext highlighter-rouge">httpd</code> into Ghidra and started with the basics:</p>

<ul>
  <li>Review strings</li>
  <li>Identify request handling paths</li>
  <li>Look for authentication handling</li>
  <li>Search for unsafe copy and formatting functions</li>
  <li>Trace whether user-controlled data reaches those calls</li>
</ul>

<p>Nothing obvious jumped out immediately from strings alone. The next pass was looking for unsafe function usage, especially calls like <code class="language-plaintext highlighter-rouge">strcpy</code>.</p>

<p><img src="/assets/images/router-firmware/ghidra1.png" alt="Ghidra view of strcpy references" /></p>

<p>Finding <code class="language-plaintext highlighter-rouge">strcpy</code> does not automatically mean there is a vulnerability. The useful question is whether an attacker can control the source buffer and whether the destination has a realistic size limit. That analysis takes more time, but these references gave me good starting points.</p>

<p>One smaller observation from reviewing the authentication flow was the web interface used HTTP Basic authentication, which means credentials are base64-encoded in the <code class="language-plaintext highlighter-rouge">Authorization</code> header. That is not encryption, so transport security matters. If the management interface is reachable over plain HTTP, the password is exposed to anyone who can observe the traffic.</p>

<h2 id="next-steps">Next Steps</h2>

<p>At this point, I needed a better way to debug the device. The next goals were:</p>

<ul>
  <li>Get UART working reliably</li>
  <li>Determine why telnet appeared enabled but was not accepting connections</li>
  <li>Patch the firmware or runtime configuration to enable remote access</li>
  <li>Continue tracing request handlers inside <code class="language-plaintext highlighter-rouge">httpd</code></li>
  <li>Confirm whether any unsafe copy patterns are reachable through the web interface</li>
</ul>

<p>This was a messy but useful first pass. I got a firmware dump, extracted the filesystem, identified the main web server binary, and started mapping the code paths that are most likely to matter.</p>

<p>Three devices were bricked in the making of this post.</p>]]></content><author><name>Zane</name></author><category term="hardware-hacking" /><category term="firmware" /><category term="reverse-engineering" /><category term="router-security" /><category term="hardware-hacking" /><summary type="html"><![CDATA[A first pass at hardware hacking a thrift store Netgear N600: desoldering flash, dumping firmware, unpacking SquashFS, and starting httpd reverse engineering.]]></summary></entry></feed>