Обычная капибара (Usual Capybara)
Обычная капибара (Usual Capybara)
Platform: hackerlab | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: Windows | Author: XESXOR | Date: 2026-04-06 | Status: Solved Techniques: aes_128_cbc_decryption, embedded_pe_extraction, hex_string_as_raw_key, openssl_static_analysis, single_byte_xor_decryption, upx_unpacking
Summary
Task: UPX-packed PE64 with oversized .data section containing a decoy JPEG dropper and a hidden XOR-encrypted PE binary. Solution: unpack UPX, discover hidden PE via single-byte XOR 0xFA, extract it, identify AES-128-CBC parameters from OpenSSL-linked code, decrypt embedded image to reveal flag.
Обычная капибара (Usual Capybara) — hackerlab
Description
Что такое, это обычная капибара. (What is this, it's an ordinary capybara.)
English summary: we are given usual_capybara.zip containing capybara.exe — a UPX-packed PE64 Windows executable. The goal is to reverse engineer the binary and find the hidden flag. The challenge involves multiple layers of obfuscation: UPX packing, a decoy JPEG dropper, a hidden XOR-encrypted PE binary, and AES-128-CBC encrypted image containing the flag.
Analysis
Step 1: UPX Unpacking
The binary capybara.exe is a PE32+ executable (console) x86-64 packed with UPX. Standard unpacking works without issues:
bash read-only • copy allowedupx -d capybara.exe -o capybara_unpacked.exe
UPX unpacked successfully — no anti-unpacking tricks this time (unlike the related "Замок Капибар" challenge from the same event).
Step 2: Outer Binary — Decoy JPEG Dropper
After unpacking, analysis with radare2 reveals the structure of the outer binary:
- The binary has a large
.datasection: 0x638C0 = 408,064 bytes — suspiciously oversized - The main function (
fcn.004015ad) performs XOR decryption on embedded data at VA0x451020using keys 0xCD and 0x0B (effectively XOR 0xC6), with size 0x167CA bytes (~92KB) - It writes the decrypted result to
capybara.jpgand prints: "I'm an ordinary capybara, what do you need?" - The extracted JPEG is a meme image of a capybara at a desk — a decoy/distraction
Key strings found in the outer binary:
capybara.jpgI'm an ordinary capybara, what do you need?UPX0,UPX1
Step 3: Discovery of Hidden PE (the key breakthrough)
The .data section is 408KB but the JPEG is only ~92KB. That leaves approximately 315KB of unexplained data before the JPEG region.
Examining the raw bytes revealed a pattern: large runs of 0xFA bytes. Since 0xFA XOR 0xFA = 0x00, this strongly suggests XOR encryption with key 0xFA over data that contains many null bytes — exactly what you'd expect from a PE binary's padding regions.
Testing XOR 0xFA at .data offset +0x20:
python read-only • copy alloweddata[0x2620] ^ 0xFA # = 0x4D = 'M' data[0x2621] ^ 0xFA # = 0x5A = 'Z'
MZ header found! A complete PE64 executable is hidden inside the .data section, encrypted with single-byte XOR 0xFA.
Step 4: Hidden Binary — OpenSSL + AES
The extracted hidden PE is a MinGW-compiled binary with OpenSSL 1.0.2o statically linked (AES, RSA, SHA functions visible in strings).
Key function fcn.004016a7 (called from main at 0x4013c2) reveals:
- Sets locale to
"Rus" - Constructs two hex strings on the stack via
movabsinstructions:- Key string:
"7128793add980992ffabbca234123ff" - IV string:
"12390dfffaabb24312ccaff123ddbca"
- Key string:
- Sets encrypted data size:
0x1a3a0(107,424 bytes) - Calls
fcn.00401550which:- Passes the key string to
AES_set_decrypt_key(viafcn.00401790) with 128 bits - Calls AES-CBC decryption (
fcn.004027d0) on encrypted data at VA0x41b020 - Writes decrypted output to
./capy.jpg
- Passes the key string to
The AES implementation is OpenSSL's optimized x86-64 assembly, recognizable by MixColumns operations with rol, xor, shr 0x20, and GF(2^8) multiply patterns.
Solution
Extraction Script
python read-only • copy allowed#!/usr/bin/env python3 """ Solve script for Usual Capybara (hackerlab) Multi-layer reverse: UPX -> XOR 0xFA hidden PE -> AES-128-CBC decryption -> flag image """ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from pathlib import Path # Step 1: Extract hidden PE from unpacked outer binary outer = Path('capybara_unpacked.exe').read_bytes() blob_start = 0x2620 # .data + 0x20 blob_size = 315392 # size of hidden PE # XOR decrypt with single-byte key 0xFA embedded = bytes([outer[blob_start + i] ^ 0xFA for i in range(blob_size)]) Path('hidden.exe').write_bytes(embedded) print(f"[+] Extracted hidden PE: {len(embedded)} bytes") assert embedded[:2] == b'MZ', "MZ header check failed!" # Step 2: Decrypt the AES-encrypted image from hidden PE hidden = Path('hidden.exe').read_bytes() enc_offset = 0x19620 # encrypted blob offset in hidden.exe (.data section) enc_size = 0x1a3a0 # 107,424 bytes enc_data = hidden[enc_offset:enc_offset + enc_size] # Key insight: hex strings are used as raw ASCII bytes, NOT hex-decoded # AES-128 uses first 16 characters of each string key = b"7128793add980992" # first 16 chars of "7128793add980992ffabbca234123ff" iv = b"12390dfffaabb243" # first 16 chars of "12390dfffaabb24312ccaff123ddbca" cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) dec = cipher.decryptor() result = dec.update(enc_data) + dec.finalize() Path('capy_decrypted.jpg').write_bytes(result) print(f"[+] Decrypted image: {len(result)} bytes") print(f"[+] JPEG header: {result[:3]}") print("[+] Flag is in the decrypted image: CODEBY{REDACTED}")
Step-by-Step Summary
- Unpack UPX:
upx -d capybara.exe -o capybara_unpacked.exe - Analyze outer binary: discover it drops a decoy
capybara.jpg(meme image) but has ~315KB of unexplained data in.data - Find hidden PE: recognize 0xFA byte pattern → XOR 0xFA reveals MZ header at
.data+0x20 - Extract hidden PE: XOR decrypt 315KB blob → valid PE64 with statically linked OpenSSL
- Analyze hidden binary: identify AES-128-CBC decryption with key/IV constructed on stack via
movabs - Decrypt image: use first 16 ASCII chars of each hex string as raw key/IV bytes
- Read flag: decrypted
capy.jpgshows capybara with coffee cup and flag text overlay