CVE-2024-58250: The passprompt plugin in pppd in ppp before 2.5.2 mishandles privileges

The passprompt plugin in PPP project do not loose privileges permanently, leading to a potential privilege escalation under specific conditions.

Timeline

  • 04/10/2024: PPP project’s maintainers contacted
  • 05/10/2024: PPP project’s maintainers first response and discussion on potential fix
  • 18/10/2024: Several GNU/Linux distributions' maintainers contacted by PPP project’s maintainers for bug coordination
  • 25/10/2024: Fix issued by PPP project’s maintainers
  • 31/12/2024: PPP version 2.5.2 released
  • 22/04/2025: CVE-2024-58250 published

Affected versions

  • PPP version 2.5.1 and older

Acknowledgments

Thanks to Paul Mackerras from the PPP project for his reactivity and technical analysis.

Thanks to Debian, Gentoo and Fedora maintainers for coordination on the fix, in particular: Salvatore Bonaccorso, Chris Boot and Jaco Kroon.

PPP project and pppd

The PPP project’s README file defines the goal of the entire project which includes the pppd binary:

The Point-to-Point Protocol (PPP) provides a standard way to establish a network connection over a serial link. At present, this package supports IP and IPV6 and the protocols layered above them, such as TCP and UDP.

This PPP implementation consists of two parts:

  • Kernel code, which establishes a network interface and passes packets between the serial port, the kernel networking code and the PPP daemon (pppd). This code is implemented using STREAMS modules on Solaris, and as a line discipline under Linux.

  • The PPP daemon (pppd), which negotiates with the peer to establish the link and sets up the ppp network interface. Pppd includes support for authentication, so you can control which other systems may make a PPP connection and what IP addresses they may use.

In the source code of the PPP project, there are also several plugins included, implemented as shared libraries. Amongst them, the passprompt.so plugin could be used to prompt users their passwords when authenticating on a PPP link.

On several distributions, including the Debian one I used during my test, the pppd is set as a setUID root binary and therefor starts its execution as the root user. It then relinquishes privileges using the set*id() family system calls.

Issue

The passprompt plugin code does not permanently relinquish its privileges because it uses the seteuid() and setegid() system calls. An attacker that can inject code after these calls could call back setuid() to regain root privileges (ppp/pppd/plugins/passprompt.c):

41 static int promptpass(char *user, char *passwd)  
42 {  
...  
65    /* we are the child, exec the program */  
66    char *argv[5], fdstr[32];  
67    ppp_sys_close();  
68    closelog();  
69    close(p[0]);  
70    ret = seteuid(getuid());  
71    if (ret != 0) {  
72  warn("Couldn't set effective user id");  
73    }  
74    ret = setegid(getgid());  
75    if (ret != 0) {  
76  warn("Couldn't set effective user id");  
77    }  
78    sprintf(fdstr, "%d", p[1]);  
79    argv[0] = promptprog;  
80    argv[1] = strdup(user);  
81    argv[2] = strdup(ppp_remote_name());  
82    argv[3] = fdstr;  
83    argv[4] = 0;  
84    execv(*argv, argv);

The promptprog variable at line 79 could be controlled via the command-line, as such:

/usr/sbin/pppd plugin passprompt.so promptprog /tmp/rogue call <peer>

Where /tmp/rogue would contains code such as the following:

// Regain privs  
setuid(0);
setgid(0);

These are unlikely conditions for several reasons:

  • it is not how pppd is started by default on any distribution mentioned previously
  • it would require the passprompt.so plugin to be used and only privileged users can ask the pppd binary to start with that plugin
  • it would require that the attacker controls the path to the promptprog binary (/tmp/rogue in the above example) or has a code-execution vulnerability in the passprompt.so plugin

The issue was fixed by removing the passprompt.so entirely as this code was deemed too old: fix commit on github.

The CVE was rated “critical” I think mainly because the impact would be great, but the preconditions to trigger the bug are numerous, so I would not have rated it so high.

References