CVE-2026-73673: Netis NC63 Unauthenticated Firmware Update with Missing Cryptographic Firmware Authentication
On this page
- Netis NC63 | CVE-2026-73673 | Unauthenticated Firmware Update with Missing Cryptographic Firmware Authentication
- Vulnerability: Unauthenticated Firmware Update -> Missing Cryptographic Firmware Authentication
- CVE: CVE-2026-73673
- Researcher: Özcan Ersan (@ozcanpng)
- Introduction
- Affected Target
- Short Version
- Root Cause 1: Boa Allows .cgi Requests
- Root Cause 2: netis.cgi Trusts the Boundary
- Firmware Update Path
- Non-Destructive Runtime Validation
- Privilege Context
- Impact
- Classification
- Disclosure Note
- References
- Final Thoughts
Netis NC63 | CVE-2026-73673 | Unauthenticated Firmware Update with Missing Cryptographic Firmware Authentication
Vulnerability: Unauthenticated Firmware Update -> Missing Cryptographic Firmware Authentication
CVE: CVE-2026-73673
Researcher: Özcan Ersan (@ozcanpng)
Introduction
This post documents CVE-2026-73673, an unauthenticated firmware update with missing cryptographic firmware authentication in the Netis NC63 router firmware V3.0.0.3327.
The affected firmware exposes /cgi-bin/upload_fw.cgi through Boa and netis.cgi. The request can reach the firmware update path without a valid administrator session, and the firmware container validation relies on a forgeable checksum and static product strings instead of a cryptographic signature.
The testing described here was performed against the original vendor firmware binaries in an isolated MIPS32 little-endian runtime. No physical router was flashed, no MTD write was performed, and the proof-of-concept artifact was intentionally non-bootable.
Affected Target
| Field | Value |
|---|---|
| Vendor | Netis Systems Co., Ltd. |
| Product | Netis NC63 Wireless AC1200 Router |
| Firmware | NC63_V3.0.0.3327 |
| Architecture | MIPS32 little-endian |
| Web server | Boa/0.94.14rc21 |
| CGI binary | /bin/netis.cgi |
| Firmware updater | /bin/fwd |
| Endpoint | POST /cgi-bin/upload_fw.cgi |
The tested image matches the vendor’s NC63 download entry:
- Firmware name:
netis_NC63_V3.0.0.3327 - Published date:
01-02-2024 - Listed size:
7.02MB - Vendor page: Netis NC63 downloads

Short Version
The vulnerable chain is:
Unauthenticated HTTP client
|
| POST /cgi-bin/upload_fw.cgi
| multipart/form-data field: update
v
Boa
|
| broad ".cgi" authorization branch
v
/bin/netis.cgi
|
| reads /tmp/boa_auth but does not enforce it before dispatch
v
FUN_004144dc
|
| validates cr6c header, checksum, and NC63/NETISVC product tag
v
RunSystemCmd("fwd <shmid> <size> ...")
v
/bin/fwd
|
v
open("/dev/mtdblock0", O_RDWR)
The critical issue is not one isolated bug. It is a boundary failure:
- Boa records an authentication result in
/tmp/boa_auth. - Boa then allows every path containing
.cgi. netis.cgireads/tmp/boa_authbut does not use it as an authorization gate before invoking the requested handler.- The firmware update handler starts
/bin/fwd. - The update format is not protected by cryptographic firmware authentication.
Root Cause 1: Boa Allows .cgi Requests
The authentication state is first derived from the submitted cookie. For CGI requests, Boa writes that state into /tmp/boa_auth.
However, a later authorization branch treats any pathname containing .cgi as authorized. That means a failed cookie check can still become an allowed CGI request.
The relevant decompiled logic is equivalent to:
auth = 0;
if (cookie_matches_expected_value)
auth = 1;
if (strstr(request_path, ".cgi") != NULL)
system("echo <auth> >/tmp/boa_auth");
if (is_public_static_resource(request_path) ||
strstr(request_path, "config.dat") != NULL ||
strstr(request_path, ".cgi") != NULL) {
auth = 1;
}
if (auth == 0)
redirect_to_login();

Additional Ghidra evidence for the same Boa authorization routine:

This decision happens before CGI execution, so it is not just a frontend visibility problem.
Root Cause 2: netis.cgi Trusts the Boundary
Inside netis.cgi, the authentication state is read from /tmp/boa_auth through FUN_00404c70() and assigned to a global variable.
The important part is what does not happen: the dispatcher does not reject the request before resolving and invoking the CGI handler.
apmib_init();
parse_parameters(¶ms, argv[1]);
DAT_00439e30 = FUN_00404c70();
if (strstr(cgi_name, ".cgi") != NULL)
FUN_00405244(cgi_name, ¶ms, output_path);

The related Ghidra views show the /tmp/boa_auth reader, netis.cgi main flow, CGI dispatcher, and handler resolver:




The upload endpoint is present in the privileged CGI list and is dispatched to the upload handler.
Firmware Update Path
The upload handler for upload_fw.cgi reaches FUN_004144dc. The handler stages uploaded multipart data in shared memory and eventually invokes /bin/fwd.
/cgi-bin/upload_fw.cgi
-> FUN_004144dc
-> isUpgrade("upload_fw")
-> RunSystemCmd("fwd %d %d > /dev/null 2>&1 &", shmid, size)
-> /bin/fwd

FUN_004144dc parses the shared-memory upload identifiers, prepares the static NC63/NETISVC product marker, attaches the uploaded buffer, and then enters the validation path:

The final handler branch calls the firmware daemon through FUN_004028e0() after the validation branch does not stop the upload:

The normal firmware container path checks the cr6c header, an additive checksum, and the static product suffix NC63/NETISVC. I did not identify a digital signature check, trusted certificate, signed manifest, or public-key verification routine protecting the update image.
That distinction matters: a checksum can detect accidental corruption, but it does not prove that firmware came from the vendor.
Non-Destructive Runtime Validation
The request was reproduced through the original Boa, netis.cgi, and fwd binaries in an isolated runtime. The test request did not include a Cookie or Authorization header.
Minimal request shape:
POST /cgi-bin/upload_fw.cgi HTTP/1.0
Host: 127.0.0.1:28081
Content-Type: multipart/form-data; boundary=BOUNDARY
Content-Length: ...
--BOUNDARY
Content-Disposition: form-data; name="update"; filename="probe.bin"
Content-Type: application/octet-stream
<harmless cr6c validation container>
--BOUNDARY--
The runtime returned:
HTTP/1.0 200 OK
Set-Cookie: path=/ ;Max-Age=0
["SUCCESS"]

The harmless probe reached the original updater and crossed the firmware validation boundary. The isolated runtime intentionally did not expose MTD devices, so the execution stopped at:
open("/dev/mtdblock0", O_RDWR) = -1 ENOENT


This was the intended safety boundary: validation and updater reachability were confirmed, but no flash write was possible in the test environment.
Privilege Context
The original Boa configuration runs the web server as root:
User root
Group root

On a physical router, that privilege context is sufficient for the firmware updater path to access MTD block devices.
Impact
An unauthenticated attacker who can reach the management interface may be able to submit a structurally valid unsigned firmware image.
Potential impact includes:
- persistent router compromise
- replacement of trusted firmware with attacker-controlled firmware
- traffic interception or modification
- credential theft
- botnet enrollment
- device bricking
The default realistic exposure is usually adjacent-network access to the router management interface. If remote management is enabled or the interface is otherwise exposed, the attack surface can become network-reachable.
Classification
Suggested primary weaknesses:
- CWE-306: Missing Authentication for Critical Function
- CWE-494: Download of Code Without Integrity Check
CWE-306 captures the missing authentication boundary protecting the firmware-update function, while CWE-494 captures the lack of sufficient cryptographic firmware authentication for firmware supplied to the updater.
Suggested CVSS v3.1:
8.8 (High)
CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
This score assumes the typical deployment in which the router management interface is reachable from the adjacent local network. Deployments exposing the management interface over a routed or WAN-accessible network may warrant a different Attack Vector assessment.
Disclosure Note
VulnCheck provided CVE-2026-73673 for this finding in disclosure correspondence. The CVE record may not be populated immediately at the time this post is published.
I intentionally avoided destructive validation. The proof-of-concept used for reporting was a non-bootable validation container designed to stop before any real flash write.
References
- CVE-2026-73673
- CVE-2026-73673 PoC and Evidence Repository
- Netis NC63 Firmware Download Page
- VulnCheck
Final Thoughts
This bug is a good example of why embedded authentication boundaries need to be verified at the binary level. The frontend may look protected, the web server may calculate an authentication result, and the CGI process may even read that result. None of that matters if the privileged handler dispatch still happens unconditionally.
In this firmware, the result is a critical update path reachable without authentication and without cryptographic firmware authentication.