CVE-2026-76071: Netis NC63 Pre-Auth ipFilterList Stack Buffer Overflow via Unbounded sscanf Scansets
On this page
- Netis NC63 | CVE-2026-76071 | Pre-Auth ipFilterList Stack Buffer Overflow via Unbounded sscanf Scansets
- Zafiyet: Pre-Authentication Stack Buffer Overflow → Remote Code Execution Primitive
- CVE: CVE-2026-76071
- Resmi Başlık: Unauthenticated Pre-Auth Stack Buffer Overflow via sscanf %[^,] in skk_set.cgi ipFilterList Handler in Netis NC63
- CVE Durumu: VulnCheck tarafından atandı; yazı hazırlanırken kayıt detayları henüz populate edilmemişti
- Araştırmacı: Özcan Ersan (@ozcanpng)
- Giriş
- Etkilenen Hedef
- Kısa Özet
- Frontend ve External Request Formatı
- Authentication Boundary
- Handler ve Metadata Route
- Root Cause: Scanset Width Eksikliği
- Stack Corruption Analizi
- Dinamik Doğrulama
- Kontrollü Program Counter
- Observation-Only RCE Boundary
- Runtime Scope ve Limitler
- Güvenli Public Proof of Concept
- Etki
- Sınıflandırma
- Mitigation
- Disclosure Timeline
- Disclosure Note
- Referanslar
Netis NC63 | CVE-2026-76071 | Pre-Auth ipFilterList Stack Buffer Overflow via Unbounded sscanf Scansets
Zafiyet: Pre-Authentication Stack Buffer Overflow → Remote Code Execution Primitive
CVE: CVE-2026-76071
Resmi Başlık: Unauthenticated Pre-Auth Stack Buffer Overflow via sscanf %[^,] in skk_set.cgi ipFilterList Handler in Netis NC63
CVE Durumu: VulnCheck tarafından atandı; yazı hazırlanırken kayıt detayları henüz populate edilmemişti
Araştırmacı: Özcan Ersan (@ozcanpng)
Giriş
CVE-2026-76071, Netis NC63 /cgi-bin/skk_set.cgi endpoint’inin kullandığı generic MIB/value parser içindeki pre-auth stack-based buffer overflow zafiyetidir.
Request ipFilterList=mod seçtiğinde attacker-controlled destHost değeri FUN_0040f7f4 fonksiyonuna ulaşıyor. Type-0x0c parser değeri iki %[^,] conversion ile ayırıyor. İki destination array de 16 byte olmasına rağmen scanset’lerin hiçbirinde maximum field width yok.
Problem firmware’in sadece sscanf() kullanması değil. Asıl root cause capacity bilgisini taşımayan format: %[^,], destination boyutunu bilmeden comma olmayan byte’ları kopyalamaya devam ediyor. Direct HTTP request böylece saved stack state’i overwrite edebiliyor.
Original-hash production CGI üzerinde yapılan dinamik testler saved return-address ve PC control’ü doğruladı. Ayrı bir observation-only doğrulama, guarded /bin/sh argümanları loglayıp hiçbir şey çalıştırmazken attacker-controlled a0 marker’ının original binary’deki system() PLT path’ine ulaştığını gösterdi.
Etkilenen Hedef
| Alan | Değer |
|---|---|
| Vendor | Netis Systems Co., Ltd. |
| Ürün | Netis NC63 AC1200 Wireless Dual Band Gigabit MU-MIMO Router |
| Firmware | NC63_V3.0.0.3327 |
| Mimari | MIPS32r2 little-endian, o32 ABI, uClibc |
| CGI binary | /bin/netis.cgi |
| Endpoint | POST /cgi-bin/skk_set.cgi |
| Trigger | ipFilterList=mod |
| Dinamik test edilen parametre | destHost |
| Statik aynı-parser coverage | srcHost |
| Authentication | Validated path içinde yok |
193f6a5e2ce65972b1805bf076f8d3521379a8441c8aaeb5ad0ba174bbee0792 netis_NC63_V3.0.0.3327.bin
eb298774c27070dc595fefcabb4e8c12a46cb5f4fd08f91c3ca92282c3a289a2 squashfs-root/bin/netis.cgi
23faa747b7d2f067aa5431bcc227ceca97a7977cf3e7c372f715cbba57f9209b squashfs-root/bin/boa
e3fd0ee3013014d59b14a409fb4ee5bb546e7d32413758ab3ad4fb0f0d3dcc47 squashfs-root/lib/libapmib.so

Kısa Özet
Unauthenticated HTTP client
|
| POST /cgi-bin/skk_set.cgi
| ipFilterList=mod
| destHost=1,0.0.0.0,<attacker-controlled comma-free bytes>
v
FUN_004138a0
v
FUN_004134c8
v
FUN_00410898(request, "ipFilterList")
v
FUN_0040f7f4(request, trigger, mib_table, pMib)
|
| get_request_param("destHost")
v
sscanf(value, "%d,%[^,],%[^,]", ...)
|
| iki char[16] destination
| maximum scanset width yok
v
saved fp overwrite -> saved ra overwrite -> controlled MIPS PC
Dinamik olarak test edilen ikinci component ile saved ra arasındaki mesafe tam 112 byte.
Frontend ve External Request Formatı
Extracted vendor frontend srcHost ve destHost değerlerini üç comma-separated field olarak oluşturup ipFilterList ile birlikte gönderiyor:
param.srcHost = $("#src_host").val();
param.srcHost += "," + $("#src_ip_1").val();
param.srcHost += "," + $("#src_ip_2").val();
param.destHost = $("#dest_host").val();
param.destHost += "," + $("#dest_ip_1").val();
param.destHost += "," + $("#dest_ip_2").val();
param.ipFilterList = $("#ip_action").val();
request({
url: "/cgi-bin/skk_set.cgi",
data: param
});

Browser-side IP validation direct client’ı sınırlamaz. Server kendi destination width kontrolünü yapmak zorunda, ancak bu parser yapmıyor.
Authentication Boundary
HTTP-shaped CGI validation request içinde Cookie veya Authorization header bulunmuyordu. Disposable rootfs içinde /tmp/boa_auth özellikle yoktu ve QEMU şunu kaydetti:
open("/tmp/boa_auth", O_RDONLY) = -1 ENOENT
Buna rağmen execution skk_set.cgi, ipFilterList=mod ve vulnerable destHost conversion üzerinden devam etti.

Broad CGI authorization failure, pre-auth erişimi sağlayan enabling condition’dır. CVE-2026-76071’in bağımsız memory-corruption root cause’u ise FUN_0040f7f4 içindeki widthless type-0x0c scanset parser’dır.
Handler ve Metadata Route
Statik analiz request path’ini şöyle map etti:
POST /cgi-bin/skk_set.cgi
-> top-level route row at 0x00434080
-> FUN_004138a0
-> FUN_004134c8 (trigger: ipFilterList)
-> FUN_00410898(request, "ipFilterList")
-> FUN_0040f7f4(request, trigger, mib_table, pMib)
Original libapmib.so metadata içinde:
id=0x8f name='srcHost' type=0x0c dst=0x21
id=0x90 name='destHost' type=0x0c dst=0x2a
İki isim de aynı vulnerable parser case’i seçiyor. Dynamic validation destHost ile yapıldı; srcHost, ayrı exploit iddiası değil aynı root cause için static coverage olarak sunuluyor.

Root Cause: Scanset Width Eksikliği
Normalize edilmiş Ghidra pseudocode:
case 0x0c:
value = get_request_param(request, metadata_name);
sscanf(value,
"%d,%[^,],%[^,]",
&selector,
first_ip_component, /* char[16] */
second_ip_component); /* char[16] */
*(char *)(destination + field_offset) = selector;
inet_aton(first_ip_component, destination + field_offset + 1);
inet_aton(second_ip_component, destination + field_offset + 5);
break;

%[^,], comma veya terminator görene kadar comma olmayan bütün byte’ları tüketir. Width belirtilmediği için sscanf, iki array’in de yalnızca 16 byte olduğunu bilemez.
Capacity-aware format kabaca şu şekilde olmalıdır:
"%d,%15[^,],%15[^,]"
Bu değişiklik ayrıca exact return-count ve semantic validation ile birlikte uygulanmalıdır. Buradaki örnek vendor patch’i değil, root cause’u açıklayan mitigation örneğidir.
Stack Corruption Analizi
FUN_0040f7f4, 0x1d0-byte frame oluşturup control state’i frame’in sonuna yakın kaydediyor:
0040f7f4 addiu sp,sp,-0x1d0
0040f7f8 sw ra,0x1cc(sp)
0040f7fc sw fp,0x1c8(sp)
0040f800 sw s0,0x1c4(sp)
İki scanset destination:
00410550 addiu t8,fp,0x14c # first char[16]
00410554 addiu v1,fp,0x15c # second char[16]
00410564 addiu a1,...,0x19d4 # "%d,%[^,],%[^,]"
00410570 jal sscanf
Saved ra, fp+0x1cc adresinde. İkinci destination ile arasındaki tam mesafe:
0x1cc - 0x15c = 0x70 = 112 byte

Dinamik Doğrulama
Kontrollü Program Counter
Isolated proof, 112 padding byte sonrasında 0x0040f7f4 adresinin üç low little-endian byte’ını kullandı. sscanf tarafından eklenen NUL dördüncü byte’ı tamamlayarak f4 f7 40 00 oluşturdu.
QEMU CPU tracing iki normal parser call ve attacker-controlled ra restore edildikten sonra üçüncü entry kaydetti:
parser_entry_hit=3 pc=0x0040f7f4
GPR28: ... s8 41414141 ra 0040f7f4
total_parser_entry_hits=3
PASS: third parser entry is the overwritten saved RA.

Observation-Only RCE Boundary
İkinci private validation saved ra değerini original production system() PLT path’i 0x00423ab0 adresine yönlendirdi. Request-controlled data return anında exact MIPS a0 argument olarak kaldı. Disposable runtime /bin/sh yerine observation-only program mount etti:
argv[0]=</bin/sh>
argv[1]=<-c>
argv[2]=<NC63_IPFILTER_RCE_PROOF>
CONTROLLED_MARKER_PREFIX_REACHED
PASS: attacker-controlled request data reached system() as exact a0.
PASS: guarded /bin/sh recorded argv and executed no command.
Guard yalnızca final execution boundary’yi değiştiriyor. Overflow, saved ra, PC, a0 ve original system() PLT transfer production CGI içinde gerçekleşiyor.
Runtime Scope ve Limitler
Test edilen /bin/netis.cgi, vendor-extracted production binary ile byte-identical. Physical flash-backed MIB qemu-user altında bulunmadığından disposable rootfs zeroed MIB state allocate eden ve bir packed-field alignment ayarı yapan disclosed lab-only libapmib.so accommodation kullandı.
Bu accommodation netis.cgi, request parser, vulnerable sscanf, local buffer’lar, frame layout, epilogue, saved-return offset veya system() path’i değiştirmedi. Static MIB metadata original vendor library’den alındı.
Production CGI fixed-base, stack canary ve RELRO yok, executable stack ve RWX segment bulunuyor.

Physical router exploit reliability, deployed-kernel randomization ve default WAN management exposure ayrıca test edilmedi.
Güvenli Public Proof of Concept
Public repository’de malformed destHost için dry-run-by-default generator bulunuyor:
python3 poc/poc.py
Request göndermek explicit authorized target gerektiriyor:
python3 poc/poc.py --target http://192.168.1.1 --send
Public payload 115-byte B component kullanıyor ve CGI process’i crash edebilir. Return-to-system değeri, command, shellcode, reverse shell veya persistence içermiyor. RCE boundary hazır saldırı zinciri olarak değil, non-destructive runtime evidence ile belgeleniyor.
Etki
Management interface’e erişebilen unauthenticated attacker, unbounded destHost scanset’ini exploit ederek privileged router-management context içinde attacker-selected command çalıştırabilir. Original Boa configuration User root ve Group root kullanıyor.
Olası etkiler:
- tam router compromise
- router configuration ve stored secret’ların disclosure veya modification’ı
- trusted configuration ve service’lerin değiştirilmesi
- DNS, firewall ve routing manipulation
- traffic interception, modification veya redirection
- post-compromise erişim üzerinden credential theft
- olası bir follow-on action olarak persistent compromise
- management service veya cihaz disruption
Default realistic exposure genellikle router management interface’e adjacent-network access’tir. Remote management aktifse veya interface başka bir şekilde exposed ise attack surface network-reachable hale gelebilir. Persistence ve physical-device exploit reliability dinamik olarak test edilmedi.
Sınıflandırma
Bu yazı hazırlanırken CVE ID atanmış fakat kayıt henüz populate edilmemişti. Aşağıdakiler researcher assessment’tır:
Researcher-assessed CVSS v3.1 (typical adjacent management network): 8.8
CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Conditional routable-management score: 9.8
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- CWE-121: Stack-based Buffer Overflow
- Related: CWE-120 — Buffer Copy without Checking Size of Input
Mitigation
- İki scanset de 16-byte destination’a göre sınırlandırılmalı; örneğin
%15[^,]. - Tam olarak üç conversion başarılı olmadıkça işlem reddedilmeli.
- Oversized serialized value parsing öncesinde reddedilmeli.
- İki IP değeri de storage öncesinde server-side doğrulanmalı.
- Privileged CGI dispatch öncesinde administrator authorization uygulanmalı.
- Bütün metadata parser case’leri widthless
%sve%[...]için audit edilmeli. - Binary stack canary, PIE, NX ve RELRO ile yeniden build edilmeli.
Disclosure Timeline
- 2026-08-16: discovery ve isolated production-binary validation tamamlandı.
- Ağustos 2026: VulnCheck’e raporlandı.
- 2026-08-20: VulnCheck
CVE-2026-76071ID’sini atadı ve public disclosure izni verdi. - 2026-08-20: public disclosure paketi hazırlandı; remote publication explicit push onayını bekliyor.
Disclosure Note
VulnCheck, CVE-2026-76071 ID’sini 2026-08-20 tarihinde atadı ve public disclosure izni verdi.
Destructive validation bilinçli olarak yapılmadı. Public proof of concept yalnızca non-executable overlong-field pattern kullanıyor. RCE validation observation-only /bin/sh -c boundary’sinde durduruldu ve hiçbir komut çalıştırılmadı.
Referanslar
- CVE-2026-76071
- CVE-2026-76071 PoC and Evidence Repository
- VulnCheck
- Netis NC63 Firmware Download Page
- CVE-2026-73673
Physical router flash’lanmadı. Gerçek command, reverse shell, persistence, external connection, credential theft veya destructive operation kullanılmadı.