Bookworm
Bookworm
Platform: CUSTOM | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: book_cipher_decryption, page_line_character_indexing, pdf_text_extraction
Summary
Walkthrough of the CUSTOM Crypto Bookworm. Task: Decrypt a book cipher using a PDF book and coordinate file with page-line-character format. Solution: Extract text from PDF with PyMuPDF, skip page numbers and chapter headers, then retrieve cha
Description
"This book can tell you secret if you'll use that file to read it"
Given:
- Book: "Do Androids Dream of Electric Sheep?" by Philip K. Dick (PDF, 111 pages)
- Cipher file:
bookworm.txtwith 17 lines inpage - line - character_positionformat
Contents of bookworm.txt:
71 - 15 - 12
79 - 12 - 49
43 - 8 - 63
89 - 24 - 8
13 - 26 - 27
22 - 6 - 54
34 - 8 - 51
58 - 7 - 50
41 - 15 - 65
11 - 19 - 23
101 - 9 - 85
33 - 11 - 62
94 - 14 - 78
69 - 16 - 12
37 - 13 - 36
89 - 14 - 51
95 - 22 - 37
Analysis
This is a classic book cipher in page-line-character format:
- Each line points to a specific character in the book
- Format:
page - line - character_position - 17 coordinates = 17 flag characters
PDF extraction specifics:
- PyMuPDF (fitz) extracts text with line breaks
- First line of each page is the page number (needs to be skipped)
- Pages with chapter beginnings have an additional header line (ONE, TWO, ..., TWENTY)
- Paragraph indentation (leading whitespace) needs to be removed before counting character position
Solution
python read-only • copy allowed#!/usr/bin/env python3 """ Book cipher solver for Crypto Kids 20 - Bookworm Uses PyMuPDF (fitz) for PDF text extraction """ import fitz # PyMuPDF # Chapter headers that appear on some pages CHAPTER_HEADERS = [ "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "TEN", "ELEVEN", "TWELVE", "THIRTEEN", "FOURTEEN", "FIFTEEN", "SIXTEEN", "SEVENTEEN", "EIGHTEEN", "NINETEEN", "TWENTY" ] def extract_character(pdf_path, page_num, line_num, char_pos): """ Extract a single character from PDF using page-line-character coordinates. Args: pdf_path: Path to PDF file page_num: 1-indexed page number line_num: 1-indexed line number (after skipping page number header) char_pos: 1-indexed character position (after stripping leading whitespace) Returns: The character at the specified position """ doc = fitz.open(pdf_path) # PDF pages are 0-indexed page = doc[page_num - 1] text = page.get_text() lines = text.split('\n') # Skip the page number header (first line) lines = lines[1:] # Check if first line is a chapter header and skip it if lines and lines[0].strip() in CHAPTER_HEADERS: lines = lines[1:] # Get the target line (1-indexed) target_line = lines[line_num - 1] # Strip leading whitespace before counting character position target_line = target_line.lstrip() # Get character at position (1-indexed) char = target_line[char_pos - 1] doc.close() return char def solve_bookworm(pdf_path, cipher_file): """ Solve the book cipher by extracting characters from PDF. """ # Parse cipher file coordinates = [] with open(cipher_file, 'r') as f: for line in f: parts = line.strip().split(' - ') if len(parts) == 3: page = int(parts[0]) line_num = int(parts[1]) char_pos = int(parts[2]) coordinates.append((page, line_num, char_pos)) # Extract characters flag = "" for page, line_num, char_pos in coordinates: char = extract_character(pdf_path, page, line_num, char_pos) flag += char print(f"Page {page:3d}, Line {line_num:2d}, Char {char_pos:2d} -> '{char}'") return flag if __name__ == "__main__": pdf_path = "do_androids_dream.pdf" cipher_file = "bookworm.txt" flag = solve_bookworm(pdf_path, cipher_file) print(f"\nFlag: {flag}")
Step-by-step execution:
- Open the PDF using PyMuPDF
- For each coordinate (page, line, char):
- Extract the page text
- Skip the first line (page number)
- If the page starts a chapter - skip the chapter header
- Remove leading whitespace from the target line
- Extract the character by position (1-indexed)
- Concatenate all 17 characters
Result: REDACTED (spbctf + sci-fi is good)
Pitfalls
- PDF text extraction: Different tools (PyMuPDF, pdftotext, pdfplumber) produce different line breaks
- Page headers: Page numbers and chapter headers need to be skipped
- Whitespace: Paragraph indentation affects character position counting
- Indexing: Important to understand whether 0-indexed or 1-indexed numbering is used
Key Takeaways / Lessons
- Mastering book_cipher_decryption, page_line_character_indexing, pdf_text_extraction was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-03-09 • CUSTOM • Bookworm • Difficulty: Medium