Python Challenge - Level 19
- Link: http://www.pythonchallenge.com/pc/hex/bin.html
- Username: butter
- Password: fly
Problem

The page title is please! and the HTML source contains an email message from [email protected] containing a base64-encoded attachment named indian.wav.
Inspecting the map of India and the title hints at byte ordering: endianness (Indian ↔ Endian).
Solution
The WAV audio file has reversed byte order (big-endian vs little-endian for 16-bit PCM samples). Swapping every pair of adjacent bytes (bytes 2k and 2k + 1) fixes the audio stream.
When played, the corrected audio clearly speaks: "Sorry!".
Visiting sorry.html leads to: "what? 'sorry'? it's not you, it's the other idiot."
The next page is therefore idiot2.html.
Python
import base64
import email
import wave
# Read MIME email and extract audio payload
with open("email.txt", "r") as f:
msg = email.message_from_file(f)
for part in msg.walk():
if part.get_content_type() == "audio/x-wav":
audio_data = part.get_payload(decode=True)
with open("indian.wav", "wb") as w:
w.write(audio_data)
# Read frames and swap byte order (16-bit samples = 2 bytes per sample)
with wave.open("indian.wav", "rb") as w_in:
params = w_in.getparams()
frames = bytearray(w_in.readframes(w_in.getnframes()))
# Swap adjacent bytes
for i in range(0, len(frames), 2):
frames[i], frames[i + 1] = frames[i + 1], frames[i]
with wave.open("fixed.wav", "wb") as w_out:
w_out.setparams(params)
w_out.writeframes(frames)
Go
package main
import (
"encoding/binary"
"io"
"os"
)
func main() {
data, err := os.ReadFile("indian.wav")
if err != nil {
panic(err)
}
// WAV header is 44 bytes; audio PCM data follows
header := data[:44]
pcm := make([]byte, len(data[44:]))
copy(pcm, data[44:])
// Swap byte pairs (16-bit little-endian <-> big-endian)
for i := 0; i < len(pcm)-1; i += 2 {
pcm[i], pcm[i+1] = pcm[i+1], pcm[i]
}
out, err := os.Create("fixed.wav")
if err != nil {
panic(err)
}
defer out.Close()
out.Write(header)
out.Write(pcm)
}
Rust
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut data = fs::read("indian.wav")?;
// In a standard canonical WAV, PCM audio data starts after the 44-byte header
if data.len() > 44 {
let pcm = &mut data[44..];
for chunk in pcm.chunks_exact_mut(2) {
chunk.swap(0, 1);
}
}
fs::write("fixed.wav", &data)?;
println!("Audio fixed: plays 'Sorry!'");
Ok(())
}
Language Comparison
| Feature | Python | Go | Rust |
|---|---|---|---|
| WAV / Audio Parsing | Standard library wave module parses headers and samples |
No stdlib audio package; manual header offset or go-audio/wav |
No stdlib audio package; hound crate or manual header parsing |
| Byte Swapping | Mutable bytearray indexing frames[i], frames[i+1] = frames[i+1], frames[i] |
pcm[i], pcm[i+1] = pcm[i+1], pcm[i] on byte slices |
chunk.swap(0, 1) on chunks_exact_mut(2) iterator |
| MIME Email Decoding | email package in stdlib automatically parses multipart payloads |
net/mail in stdlib parses headers; MIME parts parsed via mime/multipart |
mailparse crate |
Key insight: Rust's chunks_exact_mut(2) iterator method provides a safe, idiomatic, bounds-check-optimized way to swap pairs of bytes without manual index management. Python's built-in wave module simplifies WAV metadata inspection, while Go's slice manipulation allows direct manipulation of the PCM payload.
Next Level
The audio says "Sorry", which directs to sorry.html, leading to: