# qr-mania

### **Info**

| Field      | Value      |
| ---------- | ---------- |
| Category   | Forensics  |
| Difficulty | **Medium** |

***

## **Challenge Overview**

The title of the challenge is **qr-mania** which points us in a certain direction.

Firstly, I started analyzing the file without opening it in **wireshark**, which is, in my opinion, the best way to start in this kind of challenges.

Commands used for basic anaylsis:

```bash
file
exiftool
binwalk
```

However, **binwalk** sucesfully retrieved some **.PNG headers** so I extract them all using

```bash
foremost -T challenge.png
```

We've got a lot of **.pngs file**. Using **exiftool** on anyone of them, we see:

```
Comment: x/69
```

**69** is clearly the size of the **flag** and **x** must be the position of the letter decoded in that **QR**. So I made a **Bash script** to sort them in the right order.

```bash
mkdir -p sorted
exiftool -p '$Comment $FileName' *.png \
| grep -E '^[0-9]+/[0-9]+' \
| awk -F'[ /]' '{print $1, $NF}' \
| sort -n \
| awk '{printf "cp \"%s\" output/%03d.png\n", $2, NR}' \
| bash
```

### **Exploitation Scripts**

Afterwards, I realized that I can't just decode each **QR code** because some of them were **invalid**. Consequently, I made a script to convert all that colored **QRs** in **black and white QRs** that can easily be decoded.

```python
import cv2
import numpy as np
import os

INPUT_DIR = "."
OUTPUT_DIR = "output"
os.makedirs(OUTPUT_DIR, exist_ok=True)

for filename in os.listdir(INPUT_DIR):
    if not filename.lower().endswith(".png"):
        continue

    input_path = os.path.join(INPUT_DIR, filename)
    output_path = os.path.join(OUTPUT_DIR, filename)

    img = cv2.imread(input_path)
    if img is None:
        print(f"[!] Could not read {filename}")
        continue

    # convert to HSV
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h, s, v = cv2.split(hsv)

    # threshold on saturation
    _, bw = cv2.threshold(
        s, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU
    )

    # ensure black on white
    if np.mean(bw) < 127:
        bw = cv2.bitwise_not(bw)

    # add quiet zone
    bw = cv2.copyMakeBorder(
        bw, 20, 20, 20, 20,
        cv2.BORDER_CONSTANT, value=255
    )

    cv2.imwrite(output_path, bw)
    print(f"[+] Processed {filename}")

print("[✓] Done")
```

We've got only one thing to do. Decode all of them and get the **flag**:

```python
import cv2
import os
import re

INPUT_DIR = "output"
detector = cv2.QRCodeDetector()

def extract_number(filename):
    match = re.search(r'\d+', filename)
    return int(match.group()) if match else 0

files = [
    f for f in os.listdir(INPUT_DIR)
    if f.lower().endswith(".png")
]
files.sort(key=extract_number)

results = []

for filename in files:
    path = os.path.join(INPUT_DIR, filename)
    img = cv2.imread(path)
    if img is None:
        results.append("")
        continue

    data, _, _ = detector.detectAndDecode(img)
    if data:
        results.append(data)
    else:
        results.append("")

# afișare finală
for line in results:
    if line:
        print(line, end="")
```

*made by k0d*
