Home > Writeups > DEADROP Misc 5 - Flatearth Committee Minutes

DEADROP Misc 5 - Flatearth Committee Minutes

Meeting minutes from the agency's Flat Earth Contingency Planning Committee hide four flag pieces across the docx XML structure, white-on-white text, an XML comment, a custom document property, and a Word comment. A .docx is a ZIP, unzip it.

Flatearth Committee Minutes

Challenge Description

A Word document. Meeting minutes of the agency's internal Flat Earth Contingency Planning Committee. Agenda item 3: edge monitoring infrastructure budget, $50M contingency for "unknown edge entities." The Intern asked if birds were real. The meeting was suspended for eleven minutes.

We are provide with flatearth_committee_minutes.docx, nothing in the document looks wrong when you open it.


Step 0: A .docx is a ZIP

This is the foundational insight. A .docx file is a ZIP archive containing XML files. Everything Word stores, text, formatting, comments, metadata, custom properties, lives in those XML files.

unzip flatearth_committee_minutes.docx -d extracted/
ls extracted/
[Content_Types].xml
_rels/
docProps/
word/
ls extracted/word/
_rels/
comments.xml
document.xml
fontTable.xml
footnotes.xml
numbering.xml
settings.xml
styles.xml
ls extracted/docProps/
app.xml
core.xml
custom.xml     ← non-standard, worth investigating

Three files to examine: word/document.xml, word/comments.xml, and docProps/custom.xml.


Piece 1: White-on-White Text (word/document.xml)

grep -i 'FFFFFF' word/document.xml

The document uses white text in two places: the table header cells (white text on dark blue background, visible) and one additional paragraph with white text on a white background, invisible.

<w:rPr>
  <w:color w:val="FFFFFF"/>
  <w:sz w:val="22"/>
</w:rPr>
<w:t>DEADROP{docx</w:t>

The table header runs also have <w:b/> bold markers. This run doesn't. That's the distinguishing characteristic, white, body-sized, non-bold text buried in what appears to be a blank line.

P1: DEADROP{docx


Piece 2: XML Comment (word/document.xml)

grep '<!--' word/document.xml
<w:body><!-- _xml_ --><w:p>...

An XML comment immediately after the opening <w:body> tag. Invisible in any rendered view. Invisible in Word's "Show Markup" mode. Only visible in the raw XML.

P2: _xml_


Piece 3: Custom Document Property (docProps/custom.xml)

cat docProps/custom.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="..." xmlns:vt="...">
  <property ... name="Classification">
    <vt:lpwstr>TOP SECRET</vt:lpwstr>
  </property>
  <property ... name="Committee">
    <vt:lpwstr>Flat Earth Contingency Planning</vt:lpwstr>
  </property>
  <property ... name="SessionKey">
    <vt:lpwstr>archae</vt:lpwstr>
  </property>
  <property ... name="PreparedBy">
    <vt:lpwstr>Unit 7 (Acting)</vt:lpwstr>
  </property>
</Properties>

Custom document properties are accessible via File → Properties → Custom in Word. The Classification and Committee properties look like legitimate metadata. SessionKey contains the fragment.

P3: archae


Piece 4: Word Comment (word/comments.xml)

cat word/comments.xml
<w:comment w:id="0" w:author="Unit 7" w:date="...">
  <w:p>
    <w:r>
      <w:t>ology}</w:t>
    </w:r>
  </w:p>
</w:comment>

A Word comment attached to the word "contingency" in the budget discussion section. In Word's rendered view this appears as a small comment bubble in the margin, easy to miss if you're not looking for comments, and the comment text looks like it could be a truncated annotation. In the raw XML it's unambiguous.

P4: ology}


Assembling the Flag

P1 + P2 + P3 + P4:

DEADROP{docx + _xml_ + archae + ology}
= DEADROP{docx_xml_archaeology}

Automated Solve Script

import zipfile, re

def solve(path='flatearth_committee_minutes.docx'):
    with zipfile.ZipFile(path) as z:
        doc = z.read('word/document.xml').decode()
        custom = z.read('docProps/custom.xml').decode()
        comments = z.read('word/comments.xml').decode()

    # P1: white non-bold text runs
    p1 = ''.join(re.findall(
        r'<w:rPr>(?:(?!<w:b/>).)*?<w:color w:val="FFFFFF"/>(?:(?!<w:b/>).)*?</w:rPr>\s*<w:t[^>]*>([^<]+)</w:t>',
        doc, re.DOTALL
    )).strip()

    # P2: XML comment
    p2 = ''.join(re.findall(r'<!--(.*?)-->', doc, re.DOTALL)).strip()

    # P3: SessionKey custom property
    m = re.search(r'name="SessionKey".*?<vt:lpwstr>([^<]+)</vt:lpwstr>', custom, re.DOTALL)
    p3 = m.group(1).strip() if m else ''

    # P4: Word comment text
    p4 = ''.join(re.findall(r'<w:t[^>]*>([^<]+)</w:t>', comments)).strip()

    print(f'P1: {p1!r}')
    print(f'P2: {p2!r}')
    print(f'P3: {p3!r}')
    print(f'P4: {p4!r}')
    print(f'\n*** FLAG: {p1+p2+p3+p4} ***')

solve()

Key Takeaways

1. Office formats are ZIP archives full of XML. This is the single most useful thing to know for Office format forensics. Anything that can be stored in a Word document, text, properties, comments, macros, embedded objects, revision history, is in one of those XML files and is readable with any text editor or grep.

2. Four different hiding locations, four different access methods. White text requires checking color attributes. XML comments require raw source inspection (Word never renders them). Custom properties are in a separate file. Word comments are visible in the UI but easy to miss, and completely explicit in the XML. None of these are accessible through normal document reading.

3. grep on unpacked XML is faster than any GUI. unzip && grep -r 'FFFFFF' . finds the white text in milliseconds. A manual hunt through Word's interface could take much longer and still miss the XML comment and custom properties entirely.


Flag

DEADROP{docx_xml_archaeology}

< Back to All Writeups