Закодированный мопс (Encoded Pug)
Закодированный мопс (Encoded Pug)
Platform: hackerlab | Category: Misc | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2025-12-24 | Status: Solved Techniques: Image steganography via LSB XOR difference, Multi-layer encoding chain (Base64 → Binary → ASCII → Octal → ZIP → HEX → PNG), PNG file structure analysis (IEND chunk trailer detection), Visual steganography (flag visible in image difference)
Summary
Task: Decode a 111MB file with multi-layer encoding to find a hidden flag. Solution: Sequential decoding (Base64 → Binary → Octal → ZIP → HEX → PNG), then extract hidden PNG after IEND chunk and XOR both images to reveal LSB-hidden flag.
Закодированный мопс (Encoded Pug) — HackerLab
Description
Сможете ли вы получить мопса и флаг из этого файла на 110 метров? Зависит от вас ;)
(Can you get the pug and flag from this 110-meter file? It depends on you ;))
File: task_pug.zip → task_pug.dat (111MB ASCII)
Analysis
This task is a classic multi-layer encoding challenge. The name "Encoded Pug" directly indicates multi-layer encoding. The hint "110 meters" refers to the file size (111MB), and "depends on you" suggests the need for sequential decoding.
Solution
Step 1: Base64 → Binary strings
bash read-only • copy allowed# Extract the archive unzip task_pug.zip # Get task_pug.dat (111MB ASCII file) # Check the contents head -c 1000 task_pug.dat # See Base64-encoded data
python read-only • copy allowedimport base64 with open('task_pug.dat', 'r') as f: data = f.read() # Decode Base64 decoded = base64.b64decode(data) print(decoded[:200]) # See binary strings: 00110001 00110010 00110000 ...
Step 2: Binary → ASCII (decimal numbers)
python read-only • copy allowed# Split into 8-bit groups and convert to characters binary_str = decoded.decode('ascii') binary_groups = binary_str.split() ascii_chars = ''.join(chr(int(b, 2)) for b in binary_groups) print(ascii_chars[:100]) # See numbers: 120 113 3 4 24 0 0 0 ...
Step 3: ASCII numbers → Octal → Bytes
python read-only • copy allowed# Numbers turn out to be octal values numbers = ascii_chars.split() bytes_data = bytes(int(n, 8) for n in numbers) # Check the signature print(bytes_data[:4]) # b'PK\x03\x04' - this is a ZIP archive!
Step 4: ZIP → file.txt (HEX data)
python read-only • copy allowed# Save as ZIP with open('extracted.zip', 'wb') as f: f.write(bytes_data)
bash read-only • copy allowedunzip extracted.zip # Get file.txt with hex data
Step 5: HEX → PNG image
python read-only • copy allowedwith open('file.txt', 'r') as f: hex_data = f.read().strip() png_data = bytes.fromhex(hex_data) with open('pug.png', 'wb') as f: f.write(png_data)
Step 6: Detecting hidden PNG
bash read-only • copy allowedexiftool pug.png # Warning: Trailer data after PNG IEND chunk # There's data after the PNG end!
Step 7: Extracting both PNGs
python read-only • copy allowed# PNG signature and IEND chunk PNG_SIGNATURE = b'\x89PNG\r\n\x1a\n' IEND_CHUNK = b'IEND\xaeB`\x82' with open('pug.png', 'rb') as f: data = f.read() # Find all PNG images images = [] start = 0 while True: sig_pos = data.find(PNG_SIGNATURE, start) if sig_pos == -1: break end_pos = data.find(IEND_CHUNK, sig_pos) if end_pos == -1: break end_pos += len(IEND_CHUNK) images.append(data[sig_pos:end_pos]) start = end_pos # Save both images for i, img in enumerate(images): with open(f'extracted_{i}.png', 'wb') as f: f.write(img) print(f"Extracted {len(images)} images") # Extracted 2 images
Step 8: XOR two images to reveal the flag
python read-only • copy allowedfrom PIL import Image import numpy as np # Load both images img1 = np.array(Image.open('extracted_0.png')) img2 = np.array(Image.open('extracted_1.png')) # XOR images diff = np.bitwise_xor(img1, img2) # Enhance differences for visualization # LSB differences will be very weak (values 0 or 1) diff_enhanced = (diff > 0).astype(np.uint8) * 255 # Save the result Image.fromarray(diff_enhanced).save('flag_revealed.png')
Result
XOR of two images reveals the flag text hidden in LSB (Least Significant Bit) differences between the images:
CODEBY{REDACTED}
Full Solution Script
python read-only • copy allowed#!/usr/bin/env python3 """ Encoded Pug - Multi-layer decoding challenge """ import base64 import zipfile import io from PIL import Image import numpy as np def solve(): # Step 1: Read and decode Base64 with open('task_pug.dat', 'r') as f: data = f.read() decoded = base64.b64decode(data) # Step 2: Binary strings to ASCII binary_str = decoded.decode('ascii') binary_groups = binary_str.split() ascii_chars = ''.join(chr(int(b, 2)) for b in binary_groups) # Step 3: ASCII (octal) to bytes numbers = ascii_chars.split() zip_data = bytes(int(n, 8) for n in numbers) # Step 4: Extract ZIP with zipfile.ZipFile(io.BytesIO(zip_data)) as zf: hex_data = zf.read('file.txt').decode('ascii').strip() # Step 5: HEX to PNG png_data = bytes.fromhex(hex_data) # Step 6-7: Extract both PNGs PNG_SIG = b'\x89PNG\r\n\x1a\n' IEND = b'IEND\xaeB`\x82' images = [] start = 0 while True: sig = png_data.find(PNG_SIG, start) if sig == -1: break end = png_data.find(IEND, sig) + len(IEND) images.append(Image.open(io.BytesIO(png_data[sig:end]))) start = end # Step 8: XOR images arr1 = np.array(images[0]) arr2 = np.array(images[1]) diff = np.bitwise_xor(arr1, arr2) # Enhance LSB differences result = (diff > 0).astype(np.uint8) * 255 Image.fromarray(result).save('flag.png') print("Flag saved to flag.png") print("Flag: CODEBY{REDACTED}") if __name__ == '__main__': solve()
Decoding Chain
task_pug.dat (111MB Base64)
↓ base64 decode
Binary strings (00110001 00110010...)
↓ binary to ASCII
Decimal numbers (120 113 3 4...)
↓ interpret as octal
ZIP archive (PK header)
↓ unzip
file.txt (HEX data)
↓ hex decode
PNG with hidden PNG after IEND
↓ extract both PNGs
Two identical pug images
↓ XOR / LSB difference
Flag revealed: CODEBY{REDACTED}