Kotik (Cat)
Kotik (Cat)
Platform: duckerz | Category: Misc | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-01-08 | Status: Solved Techniques: file_appending, header_repair, winzip_aes_cracking, zip_extraction
Summary
Task: extract a flag hidden inside a JPEG image file. Solution: repair the corrupted JPEG header, find an appended ZIP archive via PK signature, extract the WinZip AES-256 encrypted ZIP, crack the password with hashcat using rockyou.txt, and read the flag.
Kotik (Cat) - duckerz CTF
Description
Original (Russian):
"Зачем мой друг постоянно кидает мне этих котиков, может он хочет мне что то сказать?"
Translation:
"Why does my friend keep sending me these cats, maybe he wants to tell me something?"
Files provided: kotik.jpg (124KB)
Analysis
Step 1: Initial File Inspection
bash read-only • copy allowed$ file kotik.jpg kotik.jpg: data
The file command returns "data" instead of "JPEG image" - this indicates a corrupted or modified file header.
Step 2: Header Analysis
bash read-only • copy allowed$ xxd kotik.jpg | head -1 00000000: ddf8 dde0 0010 4a46 4946 0001 0100 0001 ......JFIF......
Problem identified: First bytes are ddf8dde0 instead of standard JPEG magic ffd8ffe0.
The bytes appear to be XORed or modified. Standard JPEG header:
FF D8- JPEG SOI (Start of Image)FF E0- APP0 marker
Step 3: Fix JPEG Header
bash read-only • copy allowed# Fix the header bytes printf '\xff\xd8\xff\xe0' | dd of=kotik.jpg bs=1 count=4 conv=notrunc
After fixing, the image displays a "demotivator" meme with a cat:
- Text: "Демотиватор" (Demotivator)
- Subtext: "нет блин мотиватор" (no, it's a motivator)
Step 4: Search for Hidden Data
bash read-only • copy allowed$ strings kotik.jpg | grep -i flag flag.txt $ xxd kotik.jpg | tail -20 # Found PK signature (0x504B) - ZIP archive marker
Step 5: Locate and Extract ZIP Archive
bash read-only • copy allowed# Find ZIP signature offset $ xxd kotik.jpg | grep "504b" 0001e2f1: ... 504b 0304 ... # Calculate offset (0x1e2f1 = 123761) $ dd if=kotik.jpg of=hidden.zip bs=1 skip=123761
Step 6: Analyze ZIP Structure
bash read-only • copy allowed$ 7z l hidden.zip # flag.txt - 47 bytes uncompressed # Method: AES-256 (WinZip AES encryption) # Compression method 99 indicates WinZip AES
Step 7: Extract Hash for Cracking
The ZIP uses WinZip AES-256 encryption. Manual parsing of ZIP structure reveals:
Salt (16 bytes): f70c6b895f0b778392844ad1e609103b
Verification (2 bytes): 3889
Encrypted data: 49 bytes
Auth code (10 bytes): 937e00d85e6d320c68ef
Hashcat format (mode 13600):
$zip2$*0*3*0*f70c6b895f0b778392844ad1e609103b*3889*31*[encrypted_data_hex]*937e00d85e6d320c68ef*$/zip2$
Step 8: Crack Password
bash read-only • copy allowed$ hashcat -m 13600 hash.txt rockyou.txt # Password found: lifehouse
Step 9: Extract Flag
bash read-only • copy allowed$ 7z x hidden.zip -plifehouse $ cat flag.txt DUCKERZ{REDACTED}
Solution Script
python read-only • copy allowed#!/usr/bin/env python3 """ Kotik (duckerz CTF) - Steganography + ZIP AES cracking """ import subprocess import struct def fix_jpeg_header(filename): """Fix corrupted JPEG header""" with open(filename, 'r+b') as f: # Standard JPEG header: FF D8 FF E0 f.write(b'\xff\xd8\xff\xe0') print(f"[+] Fixed JPEG header in {filename}") def find_zip_offset(filename): """Find PK signature (ZIP) in file""" with open(filename, 'rb') as f: data = f.read() # Search for PK\x03\x04 (local file header) offset = data.find(b'PK\x03\x04') if offset != -1: print(f"[+] Found ZIP at offset: {offset} (0x{offset:x})") return offset return None def extract_zip(filename, offset, output): """Extract embedded ZIP from file""" with open(filename, 'rb') as f: f.seek(offset) data = f.read() with open(output, 'wb') as f: f.write(data) print(f"[+] Extracted ZIP to {output}") def create_hashcat_hash(zipfile): """ Parse WinZip AES ZIP and create hashcat hash Mode 13600 format for WinZip AES """ with open(zipfile, 'rb') as f: data = f.read() # Find AES extra field (0x9901) # This is simplified - real implementation needs full ZIP parsing # Key components needed: # - Salt (8 or 16 bytes depending on key size) # - Password verification value (2 bytes) # - Encrypted data # - Authentication code (10 bytes) print("[*] Parse ZIP structure manually or use zip2john") print("[*] Then crack with: hashcat -m 13600 hash.txt rockyou.txt") if __name__ == "__main__": # Step 1: Fix header fix_jpeg_header("kotik.jpg") # Step 2: Find and extract ZIP offset = find_zip_offset("kotik.jpg") if offset: extract_zip("kotik.jpg", offset, "hidden.zip") # Step 3: Crack password (use hashcat externally) # hashcat -m 13600 hash.txt rockyou.txt # Password: lifehouse # Step 4: Extract with password # 7z x hidden.zip -plifehouse
Techniques Summary
| Step | Technique | Tool |
|---|---|---|
| 1 | Header analysis | file, xxd |
| 2 | Header repair | dd, printf |
| 3 | Hidden data search | strings, xxd |
| 4 | Archive extraction | dd |
| 5 | Password cracking | hashcat (mode 13600) |
| 6 | Final extraction | 7z |
| |
Lessons Learned
- Always verify file headers - Even if extension is correct, headers can be corrupted/modified
- Check file endings - Archives are often appended to images (file concatenation steganography)
- WinZip AES cracking - Requires proper hash extraction; auth code position is critical
- rockyou.txt - Still effective for many CTF password challenges
Related Techniques
- File header manipulation (XOR, byte swap)
- Steganography by file concatenation
- ZIP structure analysis
- Password hash extraction and cracking