Safe

Enumeration
As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
nmap -sS --min-rate 5000 -p- -T5 -Pn -n 10.10.10.147 -oN allPorts
-sSuse the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.--min-rate 5000nmap will try to keep the sending rate at or above 5000 packets per second.-p-scanning the entire port range, from 1 to 65535.-T5insane mode, it is the fastest mode of the nmap time template.-Pnassume the host is online.-nscan without reverse DNS resolution.-oNsave the scan result into a file, in this case the allports file.
Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:
nmap -sC -sV -p22,80,1337 10.10.10.147 -oN targeted
-sCperforms the scan using the default set of scripts.-sVenables version detection.-oNsave the scan result into file, in this case the targeted file.
The website shows the Apache2 Debian Default Page.

There is a comment in the source code saying that a binary called myapp, which is running on port 1337, can be downloaded from here.

Let's download it.
wget http://10.10.10.147/myapp; chmod +x myapp
If we run the binary, we'll see an output that looks like the one from the uptime command. Then, it will ask for input, and it will print back the input we just entered.
./myapp
If we try to enter a bunch of A characters, the program crashes. There could potentially be a buffer overflow vulnerability.
./myapp
Exploitation
For further inspection of the binary, let's open ghidra and create a new project. Select Non-Shared Project, select the project directory, and set the name of the project.

Then, import the myapp binary. We will see that it is a 64 bits ELF binary.

Drag and drop the binary into the CodeBrowser.

Then, press Ok, and Analyze.

Under Functions, select the main function, and we'll see that the local_78 variable is saving the input that the user enters. Note that the buffer has a size of 112 bytes. If we enter more than the buffer size then we start overwriting the stack and the program crashes.

Buffer Overflow
Let's start by finding the offset. Run the binary with gdb.
gdb ./myapp
Create a pattern of 200 bytes long.
gef➤ pattern create 200
Now, copy the pattern, run the program, and enter it. The program should crash.
gef➤ r
Now, we know that there are 120 bytes before starting to overwrite RSP, which is the register that controls the flow of the program.
gef➤ pattern offset $rsp
To prove that we have control of RSP, send a string with 120 A characters, 8 B characters, and 20 C characters.
What do you want me to echo back? AAA...AAABBB...BBCCC...CCC
As seen, the B characters appear in RSP. If we check the memory protections of the binary, we'll see that NX is enabled. Which means that it is not possible to execute code from the stack.
gef➤ checksec
Going back to the Ghidra CodeBrowser we'll see that there is another function called test. This function is copying the value of RSP and pasting it into RDI. Then, it is doing a JMP order to R13.

We can try to exploit ROP.
The idea is to be able to run the system() function with /bin/sh as an argument so the program spawns a shell. To use arguments in functions, we'll have to store those arguments in the following registries, ordered from first to last, before using them.
The strategy will be to put the /bin/sh string in RSP. As the test function is copying the value of RSP into RDI, the system() function will look for the argument in RDI, which will be /bin/bash\x00. The top of the stack will be copied to RDI, so we have to put the string right before RSP, in this case, the string is 8 bytes long, so the junk will be 112 bytes long followed by the /bin/bash\x00 string.
As the test function is doing a JMP order to R13, we have to search for a gadget pop r13. We find a gadget with ropper, that does pop r13; pop r14; pop r15. This way we can load the address of the system() function into R13, and fill R14 and R15 with null bytes.
ropper --file myapp --search "pop r13"
Now we need to know the address of system() to load it into R13.
objdump -D myapp | grep system
Finally, we need the address of the test function itself, so we can run it.
objdump -D myapp | grep test
Let's go through the whole process again. The final payload will consist of the junk, which is 112 bytes long, plus the /bin/sh\x00 string.
The junk, which is 112 bytes long, plus the
/bin/sh\x00string. This whole thing will be 120 bytes long, then we'll reach RSP. We do this because the test function is copying the top of the stack and pasting it into RDI, which will be the first argument for thesystem()function.Then, we need to run the
system()function. Astestis doing a JMP order to R13, we found a gadget that makes apop r13, making it possible to put the address ofsystem()into R13. The gadget also doespop r14; pop r15, so we fill it with null bytes.Finally, we call the test function so everything gets executed and a shell is spawned.
The final script will look like this.
Run the script, and get a shell as user. Then, we'll be able to grab the user flag.
python overflow.py
Privilege Escalation
To get a more stable shell, as port 22 is open, we can create a pair of SSH keys, copy the public key.
ssh-keygen
cat ~/.ssh/id_rsa.pub | tr -d '\n' | xclip -sel clip
And put it into the authorized_keys file of the victim machine.
echo "ssh-rsa AAA...o50= root@alfa8sa" > /home/user/.ssh/authorized_keys
Now login via SSH without a password as user.
ssh user@10.10.10.147
There are a few images and one interesting file called MyPasswords.kdbx in user home directory.
ls -la /home/user
Let's transfer all the images and the KeePass file to our local machine.
scp user@10.10.10.147:/home/user/IMG* .
scp user@10.10.10.147:/home/user/MyPasswords.kdbx .
The .kdbx files contain a password database but are only accessible with a passphrase. In addition to the passphrase, you can also protect the file with a file key. We could get the hash of the .kdbx file encrypted with every image, and try to break it with john.
for img in $(ls | grep .JPG); do keepass2john -k $img MyPasswords.kdbx >> hashes;done
Then, try to break one of the hashes.
john -w=/usr/share/wordlists/rockyou.txt hashes
Now that we have a password, we just have to go one by one image and see what is the right combination of the password and image. We'll see that the right image is IMG_0547.JPG.
keepassxc MyPasswords.kdbx

There is one entry with the password of the root user.

Become the root user, and all we have to do is reap the harvest and take the root flag.
su root
Last updated
Was this helpful?