# Write Up 1337UP CTF 2024

---
## Warmup
### 1. Socials

Bài cho flag chia 3 phần ở 3 nền tảng:



Decode và mình có flag:
> INTIGRITI{h0p3_y0u_3nj0y_d4_c7f}
---
### 2. In Plain Sight

Bài stego cho ảnh:

Binwalk với pass trên mình có ảnh `flag.png`, chỉnh 1 chút có flag:

> INTIGRITI{w4rmup_fl46z}
---
### 3. Layers

Bài cho folder chưa các file thứ tự từ 0-55, lúc đầu mình lấy theo nội dung từ 0-55 nhưng decode không đem lại gì, sau mới phát hiện author không chia flag theo tên file mà theo thời gian tạo:


> INTIGRITI{7h3r35_l4y3r5_70_7h15_ch4ll3n63}
---
### 4. BabyFlow



> INTIGRITI{b4bypwn_9cdfb439c7876e703e307864c9167a15}
---
## Web
### 1. Pizza Paradise


> https://pizzaparadise.ctf.intigriti.io/robots.txt

> https://pizzaparadise.ctf.intigriti.io/secret_172346606e1d24062e891d537e917a90.html

```python!
curl --path-as-is "https://pizzaparadise.ctf.intigriti.io/topsecret_a9aedc6c39f654e55275ad8e65e316b3.php?download=/assets/images/../../../../../../var/www/html/topsecret_a9aedc6c39f654e55275ad8e65e316b3.php"
```

>INTIGRITI{70p_53cr37_m15510n_c0mpl373}
---
## OSINT
### 1. Trackdown


> INTIGRITI{Si_Cuisine_&_Mixology}
---
### 2. Trackdown 2

> INTIGRITI{Express_By_M_Village}
---
### 3. Bob L'éponge

Wu tham khảo:
> https://ctftime.org/writeup/22610

Tool: https://mattw.io/youtube-metadata/

> INTIGRITI{t4gs_4r3_m0stly_0bs0l3t3_zMlH7RH6psw}
---
### 4. No Comment



Ta có `/a/pq6TgwS`, khả năng là đường dẫn của 1 link trang nào đó, sau 1 hồi thử thì mình phát hiện:
`https://imgur.com/a/pq6TgwS`


Được 1 link pastebin kèm pass để mở:

Có được:
>25213a2e18213d2628150e0b2c00130e020d024004301e5b00040b0b4a1c430a302304052304094309
Mình thử giải mã bình thường nhưng không được, quay lại profile thì phát hiện author có để xor + pass:
```py!
# Given hexadecimal ciphertext
hex_ciphertext = "25213a2e18213d2628150e0b2c00130e020d024004301e5b00040b0b4a1c430a302304052304094309"
# Convert the hex string to bytes
ciphertext_bytes = bytes.fromhex(hex_ciphertext)
# Known flag format to use as a crib for XORing
crib =b"long_strange_trip"
# XOR function to apply crib-dragging
def xor_bytes(data, key):
return bytes([data[i] ^ key[i % len(key)] for i in range(len(data))])
# Apply XOR with the crib to see if any patterns emerge
resulting_plaintext = xor_bytes(ciphertext_bytes, crib)
print(resulting_plaintext)
```
> INTIGRITI{instagram.com/reel/C7xYShjMcV0}
---
### 5. Private Github Repository




Có thể thấy được dấu hiệu là file zip:
Mở ra chứa file:

```bash!
┌──(kali㉿kali)-[~/Desktop]
└─$ chmod 600 id_rsa
┌──(kali㉿kali)-[~/Desktop]
└─$ ssh-add id_rsa
Identity added: id_rsa (1337up)
┌──(kali㉿kali)-[~/Desktop]
└─$ ssh -T git@github.com
Hi nitrofany! You've successfully authenticated, but GitHub does not provide shell access.
```


```bash!
┌──(kali㉿kali)-[~]
└─$ git clone git@github.com:nitrofany/1337up.git
Cloning into '1337up'...
remote: Enumerating objects: 13, done.
remote: Counting objects: 100% (13/13), done.
remote: Compressing objects: 100% (9/9), done.
Receiving objects: 100% (13/13), done.
remote: Total 13 (delta 0), reused 10 (delta 0), pack-reused 0 (from 0)
┌──(kali㉿kali)-[~]
└─$ cd 1337up
ls -la
total 20
drwxrwxr-x 4 kali kali 4096 Nov 15 22:18 .
drwx------ 29 kali kali 4096 Nov 15 22:18 ..
drwxrwxr-x 2 kali kali 4096 Nov 15 22:18 config
drwxrwxr-x 8 kali kali 4096 Nov 15 22:18 .git
-rw-rw-r-- 1 kali kali 106 Nov 15 22:18 readme.md
┌──(kali㉿kali)-[~/1337up]
└─$ cat readme.md
Hey, Tiffany! You will need to save this repo in your user space and implement changes we agreed earlier.
┌──(kali㉿kali)-[~/1337up]
└─$ ls -la config
total 12
drwxrwxr-x 2 kali kali 4096 Nov 15 22:18 .
drwxrwxr-x 4 kali kali 4096 Nov 15 22:18 ..
-rw-rw-r-- 1 kali kali 44 Nov 15 22:18 .env
┌──(kali㉿kali)-[~/1337up]
└─$ cat config/.env
flag=replace with production INTIGRITI{...}
```
```py=
┌──(kali㉿kali)-[~/1337up]
└─$ git clone git@github.com:nitrofany/01189998819991197253.git
Cloning into '01189998819991197253'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (3/3), done.
┌──(kali㉿kali)-[~/1337up]
└─$ ls
01189998819991197253 config readme.md
┌──(kali㉿kali)-[~/1337up]
└─$ cd 01189998819991197253
┌──(kali㉿kali)-[~/1337up/01189998819991197253]
└─$ ls
flag.md
┌──(kali㉿kali)-[~/1337up/01189998819991197253]
└─$ cat flag.md
# INTIGRITI{9e0121bb8bce15ead3d7f529a81b77b4}
```

> INTIGRITI{9e0121bb8bce15ead3d7f529a81b77b4}
---
## Misc
### 1. Triage Bot v2


Lấy ID bot, đưa về server riêng mình:
> https://discord.com/oauth2/authorize?client_id=1301271615426007175&permissions=0&scope=botapplications.commands


Bot được update tính năng mới là `!read_report`, nhưng khi mình chọn, nó trả lời:
> You don't have permissions to run this command! If you are a triager, please contact IT to be assigned the triage role.
Điều đó có nghĩa là chỉ khi là `triage`, mình mới có thể đọc được report, vì đã cho bot vào server của riêng mình nên mình có thể tự set role luôn:


Vì nó random nên có thể thử từng số report, mình thử 0 thì có được flag luôn:
> INTIGRITI{4n07h3r_y34r_4n07h3r_7r1463_b07}
---
### 2. Quick Recovery

Bài cho ảnh và file python:

```python=
from PIL import Image, ImageDraw
from itertools import permutations
import subprocess
qr_code_image = Image.open("qr_code.png")
width, height = qr_code_image.size
half_width, half_height = width // 2, height // 2
squares = {
"1": (0, 0, half_width, half_height),
"2": (half_width, 0, width, half_height),
"3": (0, half_height, half_width, height),
"4": (half_width, half_height, width, height)
}
def split_square_into_triangles(img, box):
x0, y0, x1, y1 = box
a_triangle_points = [(x0, y0), (x1, y0), (x0, y1)]
b_triangle_points = [(x1, y1), (x1, y0), (x0, y1)]
def crop_triangle(points):
mask = Image.new("L", img.size, 0)
draw = ImageDraw.Draw(mask)
draw.polygon(points, fill=255)
triangle_img = Image.new("RGBA", img.size)
triangle_img.paste(img, (0, 0), mask)
return triangle_img.crop((x0, y0, x1, y1))
return crop_triangle(a_triangle_points), crop_triangle(b_triangle_points)
triangle_images = {}
for key, box in squares.items():
triangle_images[f"{key}a"], triangle_images[f"{key}b"] = split_square_into_triangles(
qr_code_image, box)
a_order = ["1", "2", "3", "4"] # UPDATE ME
b_order = ["1", "2", "3", "4"] # UPDATE ME
final_positions = [
(0, 0),
(half_width, 0),
(0, half_height),
(half_width, half_height)
]
reconstructed_image = Image.new("RGBA", qr_code_image.size)
for i in range(4):
a_triangle = triangle_images[f"{a_order[i]}a"]
b_triangle = triangle_images[f"{b_order[i]}b"]
combined_square = Image.new("RGBA", (half_width, half_height))
combined_square.paste(a_triangle, (0, 0))
combined_square.paste(b_triangle, (0, 0), b_triangle)
reconstructed_image.paste(combined_square, final_positions[i])
reconstructed_image.save("obscured.png")
print("Reconstructed QR code saved as 'obscured.png'")
```
Script fix:
```python=
from PIL import Image, ImageDraw
# Load the uploaded QR code image
uploaded_qr_path = "/mnt/data/obscured.png"
qr_code_image = Image.open(uploaded_qr_path)
# Image dimensions and partition points
width, height = qr_code_image.size
half_width, half_height = width // 2, height // 2
# Define quadrants for splitting the image
squares = {
"1": (0, 0, half_width, half_height),
"2": (half_width, 0, width, half_height),
"3": (0, half_height, half_width, height),
"4": (half_width, half_height, width, height)
}
# Function to split a square into two triangles
def split_square_into_triangles(img, box):
x0, y0, x1, y1 = box
a_triangle_points = [(x0, y0), (x1, y0), (x0, y1)]
b_triangle_points = [(x1, y1), (x1, y0), (x0, y1)]
def crop_triangle(points):
mask = Image.new("L", img.size, 0)
draw = ImageDraw.Draw(mask)
draw.polygon(points, fill=255)
triangle_img = Image.new("RGBA", img.size)
triangle_img.paste(img, (0, 0), mask)
return triangle_img.crop((x0, y0, x1, y1))
return crop_triangle(a_triangle_points), crop_triangle(b_triangle_points)
# Split all quadrants into triangles
triangle_images = {}
for key, box in squares.items():
triangle_images[f"{key}a"], triangle_images[f"{key}b"] = split_square_into_triangles(
qr_code_image, box)
# Define order (assuming a PIN of 1234 for testing)
a_order = ["1", "2", "3", "4"] # Example PIN order
b_order = ["4", "3", "2", "1"] # Reverse of the above
# Positions for pasting the reconstructed squares
final_positions = [
(0, 0),
(half_width, 0),
(0, half_height),
(half_width, half_height)
]
# Reconstruct the QR code
reconstructed_image = Image.new("RGBA", qr_code_image.size)
for i in range(4):
a_triangle = triangle_images[f"{a_order[i]}a"]
b_triangle = triangle_images[f"{b_order[i]}b"]
combined_square = Image.new("RGBA", (half_width, half_height))
combined_square.paste(a_triangle, (0, 0))
combined_square.paste(b_triangle, (0, 0), b_triangle)
reconstructed_image.paste(combined_square, final_positions[i])
# Save the reconstructed QR code
reconstructed_path = "/mnt/data/reconstructed_qr.png"
reconstructed_image.save(reconstructed_path)
reconstructed_path
```

Vẫn cần fix tay lại 1 chút:

> INTIGRITI{7h475_h0w_y0u_r3c0n57ruc7_qr_c0d3}
---
## Forensics
### 1. Logging


```python=
┌──(kali㉿kali)-[~/Desktop]
└─$ grep -oP 'GET .*?(?= HTTP/1.1)' app.log > extracted_urls.txt
┌──(kali㉿kali)-[~/Desktop]
└─$ cat extracted_urls.txt | python3 -c "
import sys
from urllib.parse import unquote
for line in sys.stdin:
print(unquote(line.strip()))
" > url_decode.log
┌──(kali㉿kali)-[~/Desktop]
└─$ strings url_decode.log | grep -E "IMIT 0,1" | grep "!=CHAR" | grep -oP "!=CHAR\(\K[0-9,]+(?=\))"
```

> INTIGRITI{5q1_log_analys1s_f0r_7h3_w1n!}
---
### 2. CTF Mind Tricks



> INTIGRITI{hidden_in_music_1337}
---
### 3. Hoarded Flag



Có thể thấy được ta đã có file flag, nhưng nó đã bị mã hoá bằng pass, cần tìm được pass đúng:
Strings với cú pháp:
```python=
strings memory_dump.raw | grep -i "flag.7z"
```
Có được pass:

> ScaredToDeathScaredToLook1312
Giải nén file `flag.zip` với pass vừa tìm được, mình có flag cuối:

> INTIGRITI{7h3_m3m0ry_h0ld5_7h3_53cr375}
---
### 4. Password Management

Bài cho file `.E01`, đề cập đến việc file bị xóa, đầu tiên mình load file vào `ftkimager`, kiểm tra trong các thư mục như `bin`, `deleted`, có được 1 số ảnh, trong đó có 1 ảnh này khả nghi:

Có thể thấy được ta đã tìm được pass `SevenSuns397260`, nhưng để làm gì thì mình tiếp tục tìm kiếm trong file, Dạng này giống như bài `ilikemedw` từ `KMACTF2024`, mình kiểm tra các browser, phát hiện được thông tin ở trình duyệt `firefox`:
Ở đường dẫn:
```bash=
root/Users/cat/AppData/Roaming/Mozilla/Firefox/Profiles/0wzgz3ay.default-release/logins.json
```
Mình phát hiện thông tin này:
>{"nextId":2,"logins":[{"id":1,"hostname":"https://super-really-real-bank.com","httpRealm":null,"formSubmitURL":"","usernameField":"","passwordField":"","encryptedUsername":"MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECPe0E72Bq278BAiGUkKH0HzoSg==","encryptedPassword":"MFoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECNF/qseckS4/BDBYcrwEBJ5T2lxluEtiFPFfypt58TEfdDyZBaUN/7HFp4I02jsv/XKzazHG/NOHFNk=","guid":"{c3899385-98e9-4900-b9de-1fabf67ed153}","encType":1,"timeCreated":1728583953428,"timeLastUsed":1728583953428,"timePasswordChanged":1728583953428,"timesUsed":1,"syncCounter":1,"everSynced":false,"encryptedUnknownFields":"MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECN2bf71w2iK/BAiWVos1I88Zqw=="}],"potentiallyVulnerablePasswords":[],"dismissedBreachAlertsByLoginGUID":{},"version":3}

Đây là lịch sử đăng nhập web `https://super-really-real-bank.com` thế nhưng vấn đề là nó đã bị mã hóa, giờ mình cần giải mã được:
Để decrypt được, mình sử dụng tool này:
> https://github.com/unode/firefox_decrypt
> python3 firefox_decrypt.py 0wzgz3ay.default-release

Để decrypt được, ta cần mật khẩu, có lẽ mật khẩu mình tìm được lúc đầu sẽ áp dụng vào đầy:
`SevenSuns397260`

>INTIGRITI{4n_unf0r7un473_53r135_0f_m1574k35}
---
### 5. The Puzzled Protocol

Bài cho file `pcap`, mở file đọc qua mình thấy có một số dãy nhìn có vẻ như là `base64`, thế nhưng khi decode lại không có nghĩa:

Sau 1 lúc với GPT, mình biết được đó là `xor+bas64`, key là `0xaa`:
Để lọc ra các gói chứa các đoạn đó, mình sort theo length packet:

Có thể thấy 6 gói chứa các đoạn mã hóa đó:
```bash=
w6PDpMO+w6PDrcO4w6PDvsOjw5HDp8Olw67DqMO/w7nDtcOlw7zCmcO4w7jDo8OuwpnDtQ==
w6PDpMO+w6PDrcO4w6PDvsOjw5HDrMKew6HCmcO1w6zDpsKew63Dlw==
w6PDpMO+w6PDrcO4w6PDvsOjw5HDrMKew6HCmcO1w6zDpsKew63Dlw==
w6PDpMO+w6PDrcO4w6PDvsOjw5HDrMKew6HCmcO1w6zDpsKew63Dlw==
w77CmcO4w6fCm8Okwp7DpsO1w6nCmsOkw77DuMKaw6bDlw==
w67DpMO6wpnDtcKZw7nDqcKew7rCmcO1
```
Script giải mã:
```python=
import base64
# Hàm giải mã XOR
def xor_decrypt(data, key):
return ''.join(chr(ord(c) ^ key) for c in data)
# Giải mã Base64
def decode_base64(encoded_data):
return base64.b64decode(encoded_data).decode('utf-8')
# Giải mã với khóa XOR
def decrypt_with_key(encoded_data, key):
encrypted_data = decode_base64(encoded_data)
return xor_decrypt(encrypted_data, key)
# Giải mã danh sách các chuỗi Base64 với khóa đã biết
def decrypt_multiple(encoded_list, key, output_file):
with open(output_file, 'w', encoding='utf-8') as file: # Mở file để ghi
for encoded_data in encoded_list:
decrypted_data = decrypt_with_key(encoded_data, key)
result = f"Encoded: {encoded_data} | Decrypted: {decrypted_data}\n"
file.write(result) # Ghi kết quả vào file
print(result) # Hiển thị kết quả ra console
# Danh sách các chuỗi mã hóa
encoded_list = [
"w6PDpMO+w6PDrcO4w6PDvsOjw5HDp8Olw67DqMO/w7nDtcOlw7zCmcO4w7jDo8OuwpnDtQ==",
"w6PDpMO+w6PDrcO4w6PDvsOjw5HDrMKew6HCmcO1w6zDpsKew63Dlw==",
"w6PDpMO+w6PDrcO4w6PDvsOjw5HDrMKew6HCmcO1w6zDpsKew63Dlw==",
"w6PDpMO+w6PDrcO4w6PDvsOjw5HDrMKew6HCmcO1w6zDpsKew63Dlw==",
"w77CmcO4w6fCm8Okwp7DpsO1w6nCmsOkw77DuMKaw6bDlw==",
"w67DpMO6wpnDtcKZw7nDqcKew7rCmcO1"
]
# Khóa XOR đúng
key = 0xaa
# Ghi kết quả vào file output_multiple.txt
decrypt_multiple(encoded_list, key, "output_multiple.txt")
```

> Flag: INTIGRITI{MODBUS_OV3RRID3_DNP3_3SC4P3_T3RM1N4L_C0NTR0L}
---