Skip to main content

Berkner Tech

Privilege Escalation on Embedded Linux

A foothold on an embedded Linux device is rarely root at first. Here are the privilege-escalation paths that are common on embedded systems.

Privilege Escalation on Embedded Linux

Privilege escalation paths on embedded Linux from a limited shell to root via SUID, cron, and weak permissions

Landing a shell on an embedded Linux device often gives you a limited account, not root. Getting from there to full control is privilege escalation, and embedded systems offer a rich set of paths because they are frequently built with convenience ahead of hardening. Here are the escalation routes I check first, and what they say about the device.

Why Embedded Escalation Is Often Easy

Embedded Linux is still Linux, so the classic privilege-escalation techniques apply, but the environment tilts in the attacker’s favor. These systems are built by teams focused on shipping a working product, often reusing a vendor SDK, with security hardening low on the list, so the userland is full of the shortcuts that escalation exploits.

Where a hardened server has had its SUID binaries pruned, its permissions tightened, and its services dropped to least privilege, an embedded device frequently has none of that. Many processes already run as root because it was easier, world-writable files abound, and old vulnerable binaries linger. Escalation is often a matter of checking the usual suspects and finding one of them open.

The Paths to Check

A handful of paths account for most embedded escalations, and a quick check reveals each:

Path to rootWhat you are looking forQuick check
Over-privileged servicesA service like httpd running as rootps w
SUID / SGID binariesA SUID-root binary that runs a shell or trusts inputfind / -perm -4000
Writable root filesA world-writable script or config root runsfind / -writable -type f
Cron / scheduled jobsA root job that runs a script you can writecat /etc/crontabs/root
Sudo and capabilitiesA sudo entry or a cap_setuid binarysudo -l ; getcap -r /
Old kernel or servicesKnown privesc CVEs in unpatched softwareuname -a

Enumerate First

Escalation starts with enumeration: gathering the facts that reveal a path, who am I, what runs, what is misconfigured. On a constrained device the standard tooling may be missing, so I lean on built-in commands.

id; uname -a; cat /etc/*release 2>/dev/null
ps w                      # what runs, and as whom
ls -la /etc/passwd /etc/shadow
Example output
uid=1000(app) gid=1000(app)
Linux device 3.10.14 #1 mips
PID  USER  COMMAND
214  root  /usr/sbin/httpd        # web server running as root
311  app   /usr/bin/cli

Already a lead: the web server runs as root, so a vulnerability in it is a direct path to root. The kernel version is old too, which raises the possibility of a kernel exploit as a fallback. A process list that shows services needlessly privileged is one of the first things I note.

SUID and SGID Binaries

SUID binaries run with the privileges of their owner regardless of who launches them, so a SUID-root binary that can be made to run arbitrary commands is an escalation. Embedded images often ship unnecessary SUID binaries, sometimes custom ones with exploitable behavior.

find / -perm -4000 -type f 2>/dev/null
Example output
/bin/busybox
/usr/bin/passwd
/usr/sbin/custom_helper      # a vendor binary, SUID root

The interesting entry is custom_helper, a vendor-specific SUID-root binary. Vendor SUID binaries are prime targets because they are not battle-tested like the standard tools, and they frequently call out to a shell, trust an environment variable, or run another program by relative path, any of which can be turned into root.

Writable Files Owned by Root

A file that root reads or executes but that I can write is an escalation waiting to happen: world-writable scripts run by root, writable configuration that controls a root process, or a writable binary in root’s path.

find / -writable -type f 2>/dev/null | grep -vE '^/(proc|sys|tmp|dev)'
ls -la /etc/init.d/ /etc/cron* 2>/dev/null
Example output
/etc/init.d/custom_start      # world-writable, runs as root at boot
/www/cgi-bin/                 # writable web root, served by root httpd

A world-writable init script that root runs at boot is a clean escalation, write a command into it and it executes as root on the next restart. The writable web root served by a root web server is another path, since dropping a script there may let me execute as root through the web interface.

Cron and Scheduled Tasks

Scheduled jobs running as root are a reliable escalation if I can influence what they run, a cron job that executes a script I can write, or that processes files in a directory I control.

cat /etc/crontabs/root 2>/dev/null; ls -la /etc/cron.d 2>/dev/null
Example output
*/5 * * * * /usr/bin/backup.sh
# backup.sh is writable by group 'app'  -> we are in group app

A backup script that runs every five minutes as root and is writable by my group is an escalation on a timer. Appending a command that copies a shell to a SUID-root location, or adds my key, gives root within five minutes. Scheduled jobs are easy to overlook and easy to abuse when their scripts are not locked down.

Sudo and Capabilities

If sudo is present, its configuration may allow my user to run specific commands as root, and a command that can spawn a shell or write a file is enough. Linux capabilities on binaries are the modern equivalent, granting slices of root power that can sometimes be escalated.

sudo -l 2>/dev/null
getcap -r / 2>/dev/null
Example output
User app may run: /usr/bin/tcpdump
/usr/bin/python3 = cap_setuid+ep      # python with setuid capability

Python carrying the setuid capability is an immediate win: a one-line script that sets the UID to zero and execs a shell gives root directly. A sudo entry for a command with a known shell escape works the same way. Both are convenience choices that hand a limited user the keys.

Old Kernels and Root Processes

When configuration is tight, the software version becomes the path. Embedded devices run old kernels and userland for years, so a privilege-escalation vulnerability patched upstream long ago may still be live, and a local kernel exploit is a fallback when the easier paths are closed. It is less clean and risks crashing the device, so I reach for it after the configuration checks, but on embedded targets it frequently works.

The web server running as root is also worth attacking directly. Embedded web interfaces are notorious for shelling out to system commands with user input, so a parameter that reaches a system call gives command execution as the web server’s user, which here is root. When a root process has an injectable input, that is often the shortest path, and it ties the network attack surface directly to full device control.

A Worked Escalation Chain

A real escalation often chains several small issues. Starting as the limited app user, enumeration finds a world-writable script run by a root cron job every five minutes; appending a single line that copies a shell and sets it SUID-root, then waiting one cron cycle, yields a root shell, no exploit, just one misconfiguration leveraged.

# app user appends to a root-run, world-writable cron script
echo 'cp /bin/sh /tmp/rootsh; chmod 4755 /tmp/rootsh' >> /etc/cron.d/backup.sh
# wait one cron cycle, then:
/tmp/rootsh -p
id
Example output
uid=1000(app) euid=0(root)
# root shell obtained via a writable cron script

Each link in that chain is an ordinary embedded oversight, a world-writable file, a root cron job, a permissive umask, and individually none looks alarming; combined, they are a clean path from a limited foothold to root. This is why escalation findings are reported as chains: fixing any single link, locking the script’s permissions, breaks the path, and showing the whole chain makes both the risk and the cheapest fix obvious.

Hardening against Escalation

The defenses are well understood and mostly about discipline: run each service as a dedicated unprivileged user instead of root, remove every SUID bit that is not strictly required, tighten file and directory permissions so nothing root relies on is writable by others, lock down scheduled jobs, and keep the kernel and userland patched.

The deeper fix is cultural, build the image with least privilege as the default rather than retrofitting it after an assessment finds the gaps. A hardened build that drops privileges, prunes SUID binaries, and sets restrictive permissions closes not just the paths a test found but the ones the next test would find. Escalation difficulty is a reliable proxy for how seriously a device was hardened, and an easy escalation almost always signals a systemic convenience-over-security habit worth addressing at the build level.

Where This Fits

Testing how far an initial foothold can be escalated, and mapping the paths to root, is a core part of an embedded penetration test. If you want to know how easily a foothold on your device becomes full control, that is the kind of work we do at Berkner Tech.

Share:

More Posts