← Back to Writeups
CUSTOMEasyMisc

Адский круг (Adskiy Krug)

XESXOR9/2/20263 min read
#misc#custom#easy#base64_decoding#nested_archive_extraction#rot13_cipher#scripting_automation#linux

Адский круг (Adskiy Krug)

Platform: hackerlab | Category: Misc | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2025-12-25 | Status: Solved Techniques: base64_decoding, nested_archive_extraction, rot13_cipher, scripting_automation

Summary

Task: ~800 nested ZIP archives with encoded passwords. Solution: Automated extraction using Base64→ROT13 decoding for each password.

Адский круг — Hackerlab

Description

Архивы имеют случайные названия. Паролем к каждому архиву является строка, которая выводится при попытке его разархививации. Она закодирована в ROT13→Base64. Раскодируйте её и укажите в качестве пароля.

Analysis

The task consists of ~800 nested ZIP archives. Each archive:

  1. Is password-protected
  2. Contains the next archive with a random name
  3. Shows an encoded hint string when attempting to extract

Important observation: Despite the description saying "ROT13→Base64", the actual decoding order is reversed:

  • Base64 → ROT13 (first Base64, then ROT13) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Example:

Encoded: UDY4TDRV
Base64 decode: P68L4U
ROT13: C68Y4H  ← this is the password

Solution

Step 1: Structure Analysis

bash
# Download and extract the initial archive
unzip task.zip
# Get 1DKD81.zip

# View contents
unzip -l 1DKD81.zip
# Archive:  1DKD81.zip
#   Length      Date    Time    Name
# ---------  ---------- -----   ----
#     12345  2024-01-01 00:00   UDY4TDRV

The string "UDY4TDRV" is the encoded password.

Step 2: Password Decoding

python
import base64
import codecs
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

encoded = "UDY4TDRV"
# Base64 decode
decoded_b64 = base64.b64decode(encoded).decode()  # "P68L4U"
# ROT13
password = codecs.decode(decoded_b64, 'rot_13')   # "C68Y4H"

Step 3: Automation

python
#!/usr/bin/env python3
"""
Automatic extraction of ~800 nested archives
"""
import zipfile
import base64
import codecs
import os
import glob

def decode_password(encoded):
    """Base64 → ROT13"""
    try:
        decoded = base64.b64decode(encoded).decode('utf-8')
        return codecs.decode(decoded, 'rot_13')
    except:
        return encoded
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

def extract_nested(start_zip, work_dir='.'):
    os.chdir(work_dir)
    current_zip = start_zip
    count = 0
    
    while True:
        count += 1
        print(f"[{count}] Processing: {current_zip}")
        
        try:
            with zipfile.ZipFile(current_zip, 'r') as zf:
                # Get the filename inside (this is the encoded password)
                names = zf.namelist()
                if not names:
                    print(f"Empty archive: {current_zip}")
                    break
                
                inner_name = names[0]
                
                # If this is win.zip — final archive
                if inner_name == 'win.zip' or current_zip == 'win.zip':
                    print(f"\n[!] Found final archive!")
                    # Show contents of win.zip
                    with zipfile.ZipFile('win.zip', 'r') as win:
                        for name in win.namelist():
                            print(f"    Password hint: {name}")
                    break
                
                # Decode password
                password = decode_password(inner_name)
                print(f"    Encoded: {inner_name} → Password: {password}")
                
                # Extract with password
                zf.extractall(pwd=password.encode())
                
                # Remove processed archive
                os.remove(current_zip)
                
                # Find next zip
                zips = glob.glob('*.zip')
                if not zips:
                    print("No more zip files found")
                    break
                current_zip = zips[0]
                
        except Exception as e:
            print(f"Error: {e}")
            break
    
    print(f"\nTotal archives processed: {count}")
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

if __name__ == '__main__':
    extract_nested('1DKD81.zip')

Step 4: Execution and Result

bash
python3 solve.py
# [1] Processing: 1DKD81.zip
#     Encoded: UDY4TDRV → Password: C68Y4H
# [2] Processing: next_random.zip
#     ...
# [~800] Processing: final.zip
#
# [!] Found final archive!
#     Password hint: CODEBY{REDACTED}

The final archive win.zip contains a file whose name is the flag.

Notes

  1. Decoding order: The description can be misleading. Always check both orders (A→B and B→A)
  2. ROT13 property: ROT13(ROT13(x)) = x, so encode and decode are the same operation
  3. zipfile in Python: Supports passwords via pwd=password.encode()
  4. Final archive: Often contains a hint in the filename, not in the contents ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍