{"repro_id":"REPRO-2026-00258","version":6,"title":"Cudy LT300 3.0 OS command injection","repro_type":"security","status":"published","severity":"high","description":"Cudy LT300 3.0 running firmware prior to 2.5.12 contains an OS command injection vulnerability. Reproduce: access the router's administrative interface or an exposed service running firmware <2.5.12, identify a parameter passed to a shell, and inject OS commands.","root_cause":"## Summary\nCVE-2026-32833 is an OS command injection in the Cudy LT300 3.0 firmware LuCI web administration interface. The vulnerable firmware 2.4.5 exposes the product HTTP endpoint `POST /cgi-bin/luci/admin/system/systime`; authenticated attacker-controlled form data for the system time setting reaches the original firmware `usr/lib/lua/luci/model/cbi/system/systime.lua` handler. In the vulnerable image, the `cbid.system.ntp.current` value is interpolated into a shell command of the form `date -s '%s'` without shell metacharacter sanitization, allowing a payload containing a quote and command separator to execute arbitrary commands.\n\n## Impact\n- Package/component affected: Cudy LT300 3.0 firmware LuCI/uhttpd administrative web interface, specifically `model/cbi/system/systime.lua` behind `/cgi-bin/luci/admin/system/systime`.\n- Affected versions: firmware before 2.5.12; reproduced against `LT300V3-R100-2.4.5-20250519-131314`.\n- Fixed/control version: `LT300V3-R100-2.5.12-20260518-234632`.\n- Risk level and consequences: high. An attacker with access to the administrative interface/session can execute shell commands in the router firmware context by submitting crafted time-setting form data. On a physical router this can lead to device compromise, persistence, traffic interception, or pivoting from the router.\n\n## Impact Parity\n- Disclosed/claimed maximum impact: code execution / OS command injection via a router administrative interface API endpoint.\n- Reproduced impact from this run: code execution. The reproduction created attacker-chosen proof files with controlled contents by sending HTTP POST requests to the original firmware `/cgi-bin/luci/admin/system/systime` endpoint through the firmware `uhttpd` server and original `/www/cgi-bin/luci` LuCI dispatcher/CBI path.\n- Parity: `full`.\n- Not demonstrated: no post-exploitation persistence or interactive shell was attempted; the proof stops after deterministic command execution side effects.\n\n## Root Cause\nThe root cause is unsafe construction of a shell command from HTTP form input in the LuCI system time handler. In the vulnerable 2.4.5 firmware, the system time page includes bytecode string markers showing the affected flow: `timeclock`, `date -s '%s'`, and `fork_exec`. When the CBI form is submitted with the manual time-setting option, the user-supplied `cbid.system.ntp.current`/time value is used in a `date -s '<user value>'` command. Because the value is surrounded by single quotes but not sanitized before shell execution, an attacker can close the quote and append a shell command, e.g. `2025-01-01 12:00:00'; echo MARKER > /tmp/proof; #`.\n\nThe fixed 2.5.12 firmware preserves the same endpoint and command structure but adds a `gsub` sanitization marker in the same `systime.lua` handler before `fork_exec`. The fixed negative-control runs reached the same product endpoint and form handler but did not create the attacker-controlled proof files.\n\nNo public fix commit was provided in the ticket. The reproduction compares vendor firmware images directly: vulnerable `LT300V3-R100-2.4.5-20250519-131314` versus fixed `LT300V3-R100-2.5.12-20260518-234632`.\n\n## Reproduction Steps\n1. Use `bundle/repro/reproduction_steps.sh`.\n2. The script:\n   - Downloads or reuses the vendor LT300 V3 2.4.5 and 2.5.12 firmware zip files.\n   - Extracts their SquashFS root filesystems.\n   - Runs the firmware `usr/sbin/uhttpd` under `qemu-mipsel` and `proot`.\n   - Uses the original firmware `/www/cgi-bin/luci`, `luci.dispatcher`, `luci.cbi`, and `usr/lib/lua/luci/model/cbi/system/systime.lua` path for the endpoint.\n   - Adds only emulation shims for router-only services/hardware state (`ubus` session, `bdinfo`, and route-forbidden state) so the firmware web interface can run outside physical hardware; it does not replace the vulnerable endpoint handler.\n   - Sends two vulnerable HTTP POST attempts to `/cgi-bin/luci/admin/system/systime` with payloads in `cbid.system.ntp.current`.\n   - Sends two equivalent fixed-version negative-control attempts through the same endpoint.\n3. Expected evidence:\n   - Vulnerable attempts create proof files containing `VULN_ATTEMPT_1_COMMAND_EXECUTED` and `VULN_ATTEMPT_2_COMMAND_EXECUTED`.\n   - Fixed attempts do not create proof files.\n   - GET requests for both versions render the original `System Time` CBI form and include `cbid.system.ntp.current`, proving endpoint reachability.\n\n## Evidence\nPrimary runtime evidence:\n- `bundle/logs/reproduction_steps.log` records successful runs.\n- `bundle/repro/runtime_manifest.json` records the runtime endpoint path, `service_started=true`, `healthcheck_passed=true`, and `target_path_reached=true`.\n- `bundle/logs/artifacts/product/proof_summary.txt` contains:\n  - `vuln attempt 1 proof: VULN_ATTEMPT_1_COMMAND_EXECUTED`\n  - `vuln attempt 2 proof: VULN_ATTEMPT_2_COMMAND_EXECUTED`\n  - `fixed attempt 1 no proof file (negative control passed)`\n  - `fixed attempt 2 no proof file (negative control passed)`\n- `bundle/logs/artifacts/http/vuln_attempt1_request.txt` and `bundle/logs/artifacts/http/vuln_attempt2_request.txt` contain the malicious POST fields sent to `/cgi-bin/luci/admin/system/systime`.\n- `bundle/logs/artifacts/http/vuln_attempt1_response_headers.txt` shows `HTTP/1.1 200 OK` and `X-CBI-State: 1` for a submitted form.\n- `bundle/logs/artifacts/http/fixed_attempt1_response_headers.txt` shows the fixed endpoint was reached but did not accept the injected payload as a successful proof-producing command.\n- `bundle/logs/code_comparison.txt` and `bundle/logs/artifacts/product/code_identity.txt` capture firmware code identity and string-level handler differences. The vulnerable handler shows `timeclock`, `date -s '%s'`, and `fork_exec`; the fixed handler additionally shows `gsub`.\n\nEnvironment details:\n- Firmware rootfs execution used `qemu-mipsel` and `proot` in user-mode emulation.\n- The production-facing server was the firmware `uhttpd` binary serving the original firmware web root.\n- The entrypoint was the HTTP endpoint `/cgi-bin/luci/admin/system/systime`.\n\n## Recommendations / Next Steps\n- Upgrade Cudy LT300 3.0 devices to firmware 2.5.12 or newer.\n- Do not build shell commands by concatenating or formatting raw HTTP form values. Use argument-vector APIs where possible, or strictly validate time values against a narrow date/time grammar before invoking any command.\n- If shell invocation is unavoidable, reject metacharacters such as quotes, semicolons, backticks, pipes, ampersands, redirection operators, `$()`, and newlines, and add regression tests for command-injection payloads.\n- Add endpoint-level tests for `/cgi-bin/luci/admin/system/systime` verifying that invalid time values fail closed and cannot create shell side effects.\n\n## Additional Notes\n- Idempotency confirmation: `bundle/repro/reproduction_steps.sh` was run successfully multiple times consecutively after fixing non-root SquashFS extraction handling. Each run recreated fresh proof artifacts and revalidated both vulnerable and fixed firmware behavior.\n- The run uses emulation shims for hardware/session dependencies that are normally present on a physical router. The vulnerability proof itself is not a reimplemented handler: the HTTP request traverses firmware `uhttpd`, original `/www/cgi-bin/luci`, LuCI dispatcher/CBI, and the original firmware `systime.lua` bytecode handler.\n- The fixed-version negative control uses the actual fixed firmware handler rather than a hand-written surrogate.\n","cve_id":"CVE-2026-32833","source_url":"vendor-firmware:Cudy LT300 V3","package":{"name":"Cudy LT300 V3 firmware","ecosystem":"firmware","affected_versions":"All firmware versions prior to 2.5.12 (confirmed vulnerable: 2.4.1, 2.4.5)","fixed_version":"2.5.12 (released 20-May-2026)"},"reproduced_at":"2026-07-06T10:50:14.116340+00:00","duration_secs":5639.0,"tool_calls":425,"handoffs":3,"total_cost_usd":24.003993280000017,"agent_costs":{"hypothesis_generator":0.009426,"judge":0.059816,"repro":17.605077330000004,"support":0.06325695,"vuln_variant":6.266417},"cost_breakdown":{"hypothesis_generator":{"accounts/fireworks/models/glm-5p2":0.009426},"judge":{"gpt-5.4-mini":0.059816},"repro":{"accounts/fireworks/routers/glm-5p2-fast":3.288381330000001,"gpt-5.5":14.316696},"support":{"accounts/fireworks/routers/glm-5p2-fast":0.06325695},"vuln_variant":{"gpt-5.5":6.266417}},"quality":{"confidence":"high","idempotent_verified":false,"community_verifications":0},"environment":{"sandbox_image":"ghcr.io/n3mes1s/pruva-sandbox@sha256:8096b2518d6022e13d68f885c3b8ded6b4fe607098b1a1ccbfb99abc004d1dc1"},"published_at":"2026-07-06T10:50:50.616870+00:00","retracted":false,"artifacts":[{"path":"bundle/repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":16212,"category":"reproduction_script"},{"path":"bundle/repro/rca_report.md","filename":"rca_report.md","size":7483,"category":"analysis"},{"path":"bundle/vuln_variant/reproduction_steps.sh","filename":"reproduction_steps.sh","size":14370,"category":"reproduction_script"},{"path":"bundle/vuln_variant/rca_report.md","filename":"rca_report.md","size":8301,"category":"analysis"},{"path":"bundle/logs/code_comparison.txt","filename":"code_comparison.txt","size":482,"category":"other"},{"path":"bundle/artifact_promotion_manifest.json","filename":"artifact_promotion_manifest.json","size":14577,"category":"other"},{"path":"bundle/artifact_promotion_report.json","filename":"artifact_promotion_report.json","size":14595,"category":"other"},{"path":"bundle/vuln_variant/source_identity.json","filename":"source_identity.json","size":1454,"category":"other"},{"path":"bundle/vuln_variant/root_cause_equivalence.json","filename":"root_cause_equivalence.json","size":1447,"category":"other"},{"path":"bundle/repro/runtime_manifest.json","filename":"runtime_manifest.json","size":1621,"category":"other"},{"path":"bundle/repro/validation_verdict.json","filename":"validation_verdict.json","size":777,"category":"other"},{"path":"bundle/logs/reproduction_steps.log","filename":"reproduction_steps.log","size":2561,"category":"log"},{"path":"bundle/logs/artifacts/http/response.txt","filename":"response.txt","size":405,"category":"other"},{"path":"bundle/logs/artifacts/product/proof_summary.txt","filename":"proof_summary.txt","size":220,"category":"other"},{"path":"bundle/logs/artifacts/http/vuln_attempt1_request.txt","filename":"vuln_attempt1_request.txt","size":402,"category":"other"},{"path":"bundle/logs/artifacts/http/vuln_attempt1_response_headers.txt","filename":"vuln_attempt1_response_headers.txt","size":316,"category":"other"},{"path":"bundle/logs/artifacts/http/vuln_attempt1_response_body.txt","filename":"vuln_attempt1_response_body.txt","size":2121,"category":"other"},{"path":"bundle/logs/artifacts/http/vuln_attempt2_request.txt","filename":"vuln_attempt2_request.txt","size":402,"category":"other"},{"path":"bundle/logs/artifacts/http/vuln_attempt2_response_headers.txt","filename":"vuln_attempt2_response_headers.txt","size":316,"category":"other"},{"path":"bundle/logs/artifacts/http/vuln_attempt2_response_body.txt","filename":"vuln_attempt2_response_body.txt","size":2121,"category":"other"},{"path":"bundle/logs/artifacts/http/fixed_attempt1_request.txt","filename":"fixed_attempt1_request.txt","size":404,"category":"other"},{"path":"bundle/logs/artifacts/http/fixed_attempt1_response_headers.txt","filename":"fixed_attempt1_response_headers.txt","size":255,"category":"other"},{"path":"bundle/logs/artifacts/http/fixed_attempt1_response_body.txt","filename":"fixed_attempt1_response_body.txt","size":9623,"category":"other"},{"path":"bundle/logs/artifacts/http/fixed_attempt2_request.txt","filename":"fixed_attempt2_request.txt","size":404,"category":"other"},{"path":"bundle/logs/artifacts/http/fixed_attempt2_response_headers.txt","filename":"fixed_attempt2_response_headers.txt","size":255,"category":"other"},{"path":"bundle/logs/artifacts/http/fixed_attempt2_response_body.txt","filename":"fixed_attempt2_response_body.txt","size":9623,"category":"other"},{"path":"bundle/logs/artifacts/product/code_identity.txt","filename":"code_identity.txt","size":1835,"category":"other"},{"path":"bundle/logs/artifacts/product/uhttpd_runtime.log","filename":"uhttpd_runtime.log","size":1988,"category":"log"},{"path":"bundle/vuln_variant/patch_analysis.md","filename":"patch_analysis.md","size":5620,"category":"documentation"},{"path":"bundle/vuln_variant/variant_manifest.json","filename":"variant_manifest.json","size":3620,"category":"other"},{"path":"bundle/vuln_variant/validation_verdict.json","filename":"validation_verdict.json","size":2590,"category":"other"},{"path":"bundle/vuln_variant/runtime_manifest.json","filename":"runtime_manifest.json","size":1409,"category":"other"},{"path":"bundle/logs/vuln_variant/reproduction_steps.log","filename":"reproduction_steps.log","size":2303,"category":"log"},{"path":"bundle/logs/vuln_variant/product/proof_summary.txt","filename":"proof_summary.txt","size":93,"category":"other"},{"path":"bundle/logs/vuln_variant/http/vuln_rpc_request.json","filename":"vuln_rpc_request.json","size":168,"category":"other"},{"path":"bundle/logs/vuln_variant/http/vuln_rpc_response_headers.txt","filename":"vuln_rpc_response_headers.txt","size":261,"category":"other"},{"path":"bundle/logs/vuln_variant/http/vuln_rpc_response_body.txt","filename":"vuln_rpc_response_body.txt","size":38,"category":"other"},{"path":"bundle/logs/vuln_variant/http/fixed_rpc_request.json","filename":"fixed_rpc_request.json","size":170,"category":"other"},{"path":"bundle/logs/vuln_variant/http/fixed_rpc_response_headers.txt","filename":"fixed_rpc_response_headers.txt","size":230,"category":"other"},{"path":"bundle/logs/vuln_variant/http/fixed_rpc_response_body.txt","filename":"fixed_rpc_response_body.txt","size":187,"category":"other"},{"path":"bundle/logs/vuln_variant/product/code_identity.txt","filename":"code_identity.txt","size":3017,"category":"other"}]}