Root From a World-Writable XML

Root From a World-Writable XML

Some vulnerabilities are subtle. This one is a world-writable XML file, an unsanitized string, and a root daemon that believes whatever the XML tells it. Chain those together and any app on a Honeywell CT47 — no permissions, no exploit primitives, no user interaction — gets uid=0. Verified on hardware.

The door

Honeywell's logger fleet is configured from /storage/IPSM/honeywell/persist/hxlogger.xml — sitting in a directory mounted world-writable, permissions 666, so the EZConfig app (a different UID) can edit it. That design decision means every app on the device can edit it too. This is the same class of mistake as the WWAN engineering mode: a legitimate OEM convenience feature that dissolved the trust boundary it was supposed to sit behind.

The injection

The HxLogger system app parses that XML and, for "dynamic log" commands, builds a shell command:

echo -n 'file <FileName> func <FuncName> +p'> /sys/kernel/debug/dynamic_debug/control

command.fileName is read straight from the XML with no sanitization. Close the quote, slip a command in, reopen:

FileName: "';id>/sdcard/p;echo -n '"
FuncName: "foo"

Shell parses as:
  echo -n 'file '           → prints "file "
  id>/sdcard/p              → executes id, writes output
  echo -n ' func foo +p'> ... → completes the control-file write

Why this reaches root

service hxlogger_kerdynalog /system/bin/hxlogger_daemon_copy.sh kernel_dynamic_logs
    class core
    user  root
    group shell everybody system sdcard_rw log

The trigger is SystemProperties.set(), which fires the init.rc wildcard trigger on property:persist.vendor.hsm.kerdynalog.requestcmd=* — see the companion piece on wildcard triggers. init is PID 1. The service is declared user root. Whatever the XML says, init runs as root.

The 91-byte budget

Android's property service caps values at PROP_VALUE_MAX = 91 bytes. The full injected string must fit:

"echo -n 'file "         = 14 bytes
" func foo +p'> "         = 15 bytes
"/sys/kernel/debug/..."    = 39 bytes     → 68 bytes overhead, 23 for payload
"/proc/dynamic_debug/..."  = 28 bytes     → 57 bytes overhead, 34 for payload

Our payload: FileName ";id>/sdcard/p;" (15 chars) → total 70 bytes ✓

Thirty-four characters of command as root. You don't need more.

The restart problem — and the kgsl trick

The XML is only re-read on service start or when the HxLogger UI resumes. The obvious broadcast is a trap: honeywell.hxlogger.change.config.file triggers a write (config → XML), not a re-read. And the 91-byte overflow crash we tried as a state-reset doesn't reliably kill the service — the crash dialog appears, the process may live on.

So we made the OS do the murder. The CT47 runs a Qualcomm kgsl GPU driver, and /dev/kgsl-3d0 lets you allocate GPU buffers that are never drawn or released — a silent GPU memory leak. Android's Low Memory Killer reaps background services first. HxLogger dies, memory is freed, we relaunch clean:

1. Inject clean payload into hxlogger.xml
2. Open /dev/kgsl-3d0, allocate, never release   → LMK kills hxlogger
3. am start -n honeywell.hxlogger/.activity.MainActivity
4. HxLogger re-reads XML → SystemProperties.set() → init fires root service
5. Verify: /data/local/tmp/p contains "uid=0(root)"   ✓

SELinux saw the whole thing

The daemon's SELinux context is denied dac_override and blocked reading the /sdcard symlink — and it didn't matter, because writing to /data/local/tmp/ is allowed. The policy polices the edges of the blast radius, not the center. A root shell that can write where the policy permits is still a root shell.

Remediation: restrict the XML to 0600 system:system; sanitize every value before it reaches a shell; drop user root from logger services that only need /sys/kernel/debug writes via a constrained helper; replace wildcard property triggers with exact values; and audit every path where config becomes command.

One world-writable file. One unsanitized string. One root service. The most dangerous code on this device is the code that was trying to be convenient.


frankSx / frankhacks.blogspot.com — companion pieces: the WWAN engineering mode, the AutoInstall signature-hash collision, and the wildcard trigger / low-severity findings from the same device.

Comments

Popular Posts