Polyglot craft — Corkami and PoC||GTFO (format superposition as a discipline)
The deliberate-polyglot tradition: a body of proofs-of-concept, a taxonomy, and finally a generator (mitra) that together answer the question which formats compose, and why — by building files that are simultaneously a PDF, a ZIP, a TAR, an ISO, an iNES ROM, and a boot sector, and then writing down the structural rule that made each composition possible.
| Field | Value |
|---|---|
| Kind | Craft tradition + PoC corpus + a polyglot generator (mitra) + a document series (PoC||GTFO) |
| Language | Python (mitra, mocky), NASM/x86, LaTeX (pdflatex), and the file formats themselves |
| License | MIT (corkami/mitra, corkami/pocs mini corpus); PoC||GTFO issues released as free PDFs |
| Repository | corkami/mitra · corkami/pocs · corkami/docs · angea/pocorgtfo |
| Documentation | "Abusing file formats" (= PoC||GTFO 7:6) · mitra/README.md · PDF tricks |
| First release | Corkami PoCs from c. 2009; PoC||GTFO 0x00 on 2013-08-05; mitra 0.3 dated 2023-03-25 in mitra.py |
| Axis profile | Multiplicity 3 / Reflexivity 1 / Closure 1 / Mutability 0 |
| Index anchoring | Mixed by construction — a footer-anchored host (ZIP EOCD, PDF startxref) wrapping header-anchored and offset-anchored guests |
| Dispatch owner | Consumer sniffing (libmagic / TrID / the renderer's own recovery heuristics); occasionally the shell, via a renamed extension |
Latest revision surveyed:
corkami/mitraat95e1d2a7,corkami/pocsat6d277c83,corkami/docsatfd339bf6,angea/pocorgtfoat933c020f(2024-02-11). Platform: format-level, so none — the artifacts are byte streams; the verification below was run on Linux withfile5.x, Info-ZIPunzip, GNUtarand Python 3.
Overview
What it solves
This subject is the catalog's control group for thesis 5 — that portability has migrated from the format to the access layer. Polyglot craft is the old strategy taken to its limit: reach is bought by satisfying every parser's grammar at once, in one immutable byte stream, with no substrate, no loader cooperation, and no runtime. redbean/APE is the engineering-grade descendant of exactly this technique; everything APE does to a PE/ELF/Mach-O/ZIP/shell-script header, Corkami had already done to a PDF/JPEG/ZIP/TAR/ISO header, first as art and then as a security argument.
Concretely, the tradition solves three separate problems that the rest of this catalog keeps colliding with:
- A taxonomy of composability. Which pairs of formats can share a byte stream, and — the useful part — from what structural property of each format does that follow?
mitrais that taxonomy compiled into code: 48 format parsers, each declaring a handful of tolerance flags and offsets, plus a driver that mechanically tries four layouts on every ordered pair. ItsREADME.mdpublishes the resulting matrix: 288 format combinations over the surveyed set. This is the open question in cluster A of the source outline, already answered for a concrete corpus. - A demonstration argument. A bad format design is not a vulnerability, so it is never fixed. Albertini's response is to make bad design cheap to exploit and therefore visible.
- A publication vehicle. PoC||GTFO ships each issue as one file that is at once a readable journal, an archive of the code its articles describe, and a running joke about the file's own type — which is the autological move this catalog is named for.
Design philosophy
The opening of "Abusing file formats" states the position that the whole tradition rests on:
"First, you must realize that a file has no intrinsic meaning. The meaning of a file - its type, its validity, its contents - can be different for each parser or interpreter."
And immediately after, the vocabulary the rest of this page uses:
"A polyglot is a file that has different types simultaneously, which may bypass filters and avoid security counter-measures. A multiple-personality (let's call them 'multi') file is one that is interpreted differently depending on the parser. These files may look innocent (or corrupted) to one interpreter, malicious to another. A chimera is a polyglot where the same data is interpreted as different types, which is a more advanced kind of filter bypass."
Three terms, three different claims, and it is worth keeping them apart because the catalog's axes score them differently:
| Term | Claim | Axis consequence |
|---|---|---|
| Polyglot | One stream, n types, all valid simultaneously | Multiplicity n; parsers agree that the file is also their type |
| Multi (formerly schizophrenic) | One stream, one nominal type, different content per parser | Multiplicity 1, but a parser differential — the interesting failure mode |
| Chimera | Polyglot where the same bytes are the payload of several types | Multiplicity n with no duplication — the dedup story of cluster E |
Mock (mocky.py) | One valid type, plus foreign magic planted where sniffers look | Multiplicity 1 claimed as n — attacks the dispatcher, not the parsers |
The schizophrenic label is Albertini's 2014 talk title (slides, slides/1406-SchizophrenicFiles.pdf in corkami/docs); the current corpus consistently renames it to multi, and this page follows the current usage while noting the older term because most citations of the work still use it.
The engineering advice the tradition distils out of ten years of PoCs is stated as a short list of format-design rules in mitra/README.md:
"Enforcing a magic at offset zero should be standard. Starting at offset zero and not enforcing a magic at zero is still exploitable (PS, MP4). Starting at any offset makes polyglots trivial. Enforcing a footer (like
XZ,ID3v1) is a great way to check if a file isn't truncated, and prevents 'naturally' appended data. Most formats have a way to store parasite data, except very simple ones."
Those four sentences are the generalizable rule set this page unpacks. They also predict, correctly, which two formats in mitra's whole table combine with nothing.
How it works
The four layouts
mitra enumerates exactly four ways two files can share a stream. The names are the tool's own, and its output filenames encode which was used (mitra.py):
| Layout | Filename tag | Shape | Requires |
|---|---|---|---|
| Stack | S(off)-A-B | A then B appended | A.bAppData and B.start_o > len(A) |
| Cavity | C(off)-A_B | A written into B's leading dead space | A.bAppData and B.precav_s >= len(A) |
| Parasite | P(off)-A[B] | B wrapped in a comment/extension chunk of A | A.bParasite, A.parasite_o <= B.start_o + B.precav_s, A.parasite_s >= len(B) |
| Zipper | Z(o-o-o)-A^B | the two files interleave, each hiding the other's spans as comments | A.bZipper and both bParasite — only TAR and DICOM host it today |
The predicates are literal source. isStackOk in mitra.py is the entire "suffix-tolerant ∘ prefix-tolerant" rule of the source outline, written as four lines of Python:
# mitra.py — isStackOk (abridged)
if not ftype1.bAppData: # host must tolerate appended data
result = False
if ftype2.start_o == 0: # guest must tolerate a non-zero start offset
return False
elif len(ftype1.data) >= ftype2.start_o:
result = False # ... and the host must fit inside that tolerancestart_o is the guest's maximum tolerated start offset, not its actual one — the number of bytes of foreign prefix it will forgive. That single field is the whole prefix-tolerance axis, and its values are startlingly concrete:
| Format | start_o | Source |
|---|---|---|
| ZIP / RAR / 7z / ARJ | 4 MiB | self.start_o = 4*1024*1024 # no actual downward limit (parsers/zip_.py) |
1016 | self.start_o = 1024 - 8 (parsers/pdf.py) | |
| ISO 9660 | 0x8000 | as a cavity, precav_s = 0x8000 (parsers/iso.py) |
| DICOM | 128 | the DICM magic sits after a 128-byte preamble |
| TAR | 0 + slack | magic \0ustar at MAGIC_o = 0x100 (parsers/tar.py) |
| Everything else | 0 | magic enforced at offset zero |
1024 - 8 is not folklore; it is the arithmetic of "the whole %PDF-1.x signature has to be present in the first kilobyte", and it is directly observable. pocs/pdf/1016garbage.pdf is 1648 bytes of which the first 1016 are junk:
$ od -A d -c pocs/pdf/1016garbage.pdf | head -1
0000000 001 P K 003 004 \0 J F i f R a r ! 032 \a
$ od -A d -c pocs/pdf/1016garbage.pdf | sed -n '5p'
0001008 360 361 362 363 364 365 366 367 % P D F - 1 . 4The signature lands at byte 1016 exactly, so the eight-byte magic ends at 1024. The junk is not random: it opens with PK\3\4, JFif, Rar!\x1a\x07, <html>, Acsp and PK\5\6 — a deliberate demonstration that the tolerated prefix is a free-fire zone for other formats' magic.
The format model: tolerance as data
Every mitra parser subclasses FType (parsers/__init__.py), whose constructor is the taxonomy — eleven fields, of which three are booleans and the rest are offsets or sizes:
# mitra/parsers/__init__.py — FType.__init__ (abridged, comments verbatim)
self.cut = None # minimal cut generic to that format
self.prewrap = 0 # [minimal] size of data to be added before the parasite
self.postwrap = 0 # [minimal] size of data to be added after the parasite
self.start_o = 0 # where the format should start in the file
self.bAppData = True # does it tolerate appended data - quite common
self.bParasite = False # does it tolerate any parasite - quite common
self.parasite_o = None # min offset of a parasite (=cut + prewrap ?)
self.parasite_s = None # max size of a parasite
self.precav_o = 0 # (fixed) offset of a pre-cavity
self.precav_s = 0 # (max) size of pre-cavityand four methods — identify, getCut, wrap, fixformat — which the README reduces to a single sentence:
"For example, in a chunk-based format, just find where to
cutthe file, thenwrapforeign data in a new chunk and insert the chunk. So you just need to teach Mitra how toidentifythe type, where tocut, and how towrap."
PNG is the canonical instance and is 30 lines total (parsers/png.py): cut at 8 (immediately after the signature, before IHDR), prewrap = 2*4 (a 4-byte length and a 4-byte type), postwrap = 4 (the CRC), and:
# mitra/parsers/png.py
def wrap(self, data, type_=b"cOMM"):
return b"".join([
int4b(len(data)), type_, data,
int4b(binascii.crc32(type_ + data) % 0x100000000)
])cOMM is chosen for its capitalization, not its spelling: in the PNG chunk grammar the case of each of the four letters carries a property bit. The lowercase first letter makes the chunk ancillary — a decoder that does not recognise it must skip it rather than fail — and the uppercase fourth letter makes it unsafe to copy, which is honest, since an editor must not carry the parasite across a re-encode. (The uppercase second letter claims a public, registered type, which cOMM of course is not; nothing enforces that bit.) PNG's forward-compatibility rule is therefore the parasite mechanism, and it is a rule the format got right for maintainability and cannot revoke without breaking every decoder.
WASM is structurally identical and even shorter: section id 0 is the custom section, so the wrapper is \0 + LEB128 length + a name-length-prefixed blob (parsers/wasm.py), and bAppData = True # via Wrappending — appended data is legal because you can always append another custom section. A format designed in 2015 with an explicit extension point inherits the same composability as PNG from 1996. That is not an accident of either spec; it is what "tolerate unknown chunks" means.
The host/guest asymmetry
Composition is not symmetric, and mitra's matrix is deliberately a full square rather than a triangle. S(x)-PDF-ZIP and S(x)-ZIP-PDF are different files with different failure modes. The four ZIP-family formats and PDF/ISO/DICOM/TAR each combine with 30–41 of the surveyed formats, while ELF, PNG, GIF, PE, Java and BPG each combine with 6–8 — and every one of those 6–8 is "as a host to one of the eight promiscuous guests", never as a guest themselves, because their magic is enforced at zero.
Two formats in the whole table compose with nothing: XZ and ID3v1. The reason is one line each. parsers/xz.py:
self.bAppData = False # Required matching footer
self.bParasite = False # No known strategyand parsers/id3v1.py:
self.bAppData = False # it's a footerA mandatory footer is the only structural feature in this corpus that defeats every layout at once. It kills stacking (nothing may follow), it kills being stacked onto (start_o == 0), and if the format also has no comment/extension chunk it kills parasitism. This is the single most transferable finding on the page, and it is the exact inverse of the property that makes ZIP the universal suffix parasite — see ZIP parasitism and footer-indexed formats.
Format identity and multiplicity
What the bytes are
There is no one artifact here, so multiplicity is measured per specimen. The PoC||GTFO series is the best-documented run, because its README.md records, per issue, exactly what the released file simultaneously is. Reproduced and, where marked ✓, verified first-hand against the release blobs at angea/pocorgtfo@933c020f:
| Issue | Date | Simultaneously | Verified |
|---|---|---|---|
0x00 | 2013-08-05 | PDF only | — |
0x01 | 2013-10-06 | ZIP, PDF | ✓ 6 files; EOCD 63 bytes from EOF (release) |
0x02 | 2013-12-28 | MBR, ZIP, PDF — "This OS is also a PDF" (article) | — |
0x03 | 2014-03-02 | JPG, AES(PNG), ZIP, AFSK audio, PDF | ✓ FF D8 at 0, %PDF at 25 inside a FF FE comment (release) |
0x04 | 2014-06-27 | TrueCrypt volume, ZIP, PDF | — |
0x05 | 2014-08-10 | ISO, SWF, ZIP, PDF | — |
0x06 | 2014-11-25 | TAR, ZIP, PDF (article) | ✓ ustar at 257; first TAR member is named %PDF-1.5 (release) |
0x07 | 2015-03-19 | BPG, HTML, ZIP, PDF — the "Funky Files" issue | — |
0x08 | 2015-06-20 | Shell script, ZIP, PDF | — |
0x09 | 2015-09-14 | WavPack, ZIP, PDF | — |
0x10 | 2016-01-16 | LSMV (a TAS movie), ZIP, PDF | — |
0x11 | 2016-03-17 | Ruby, HTML, ZIP, PDF | — |
0x12 | 2016-06-18 | APK, ZIP, PDF | — |
0x13 | 2016-10-04 | PostScript, ZIP, PDF | — |
0x14 | 2017-03-20 | iNES ROM, ZIP, PDF — plus MD5 hashquines and a collision | — |
0x15 | 2017-06-17 | ILDA (laser-projector frames), ZIP, PDF | — |
0x16 | 2017-10-20 | Bash (also Python, WebIDE), ZIP, PDF | — |
0x17 | 2017-12-30 | Apollo Guidance Computer source, ZIP, PDF | — |
0x18 | 2018-06-26 | HTML, PDF, ZIP — with a SHA-1 collision | — |
0x19 | 2019-03-27 | HTML, PDF, ZIP — with an MD5 pileup across PE/PDF/PNG/MP4 | — |
0x20 | 2020-01-21 | PDF, ZIP — signed | — |
0x21 | 2022-02-12 | PCAPNG, PDF, ZIP | — |
0x22 | 2024-02-12 | ISO, ZIP, PDF + mocks: TAR, DICOM, XMS, PIF… | ✓ DICM at 128, CD001 at 32769, EOCD comment covers the PDF trailer (release) |
Two structural constants across twenty-three issues are worth naming. ZIP appears in every polyglot issue from 0x01 onward — it is the invariant host, for reasons developed under Index anchoring. And PDF is always the presentation type, because PDF is the only widely-deployed format that tolerates a kilobyte of leading garbage, tolerates arbitrary appended data, and is parsed bottom-up, so it can be the guest of a header-anchored format and the host of a footer-anchored one at the same time.
The prefix/suffix partial order
The source outline asks for "a partial order of prefix-tolerant / suffix-tolerant / neither" that would predict polyglots. mitra's field set delivers it, with one refinement: the order is over four properties, not two, and the fourth (interior parasitism) is what rescues formats that are strict at both ends.
| Property | Field | Formats in the surveyed set |
|---|---|---|
| Prefix-tolerant (scanned magic) | start_o > 0 | ZIP, 7z, ARJ, RAR (4 MiB); PDF (1016); TAR, DICOM, ISO (fixed offsets) |
| Prefix-tolerant (cavity) | precav_s > 0 | ISO (0x8000), PDF-as-cavity (1018, parsers/pdfc.py) |
| Suffix-tolerant | bAppData | almost everything — the default in FType is True |
| Interior-tolerant | bParasite | every chunked format; PNG, WASM, JPEG, JP2, RIFF, ELF, PE, NES, GZIP, PostScript |
| None of the above | — | XZ, ID3v1 |
The predictive rule that falls out: a pair composes iff one member is interior- or prefix-tolerant at a distance that exceeds the other member's size, or one is suffix-tolerant and the other prefix-tolerant. Everything else in the matrix is bookkeeping about how much space, which is why the tool records the swap offsets in the output filename: Z(80-162-286)-DICOM^TIFF.…dcm.tif.
Note the deliberate line this catalog draws: a mock is not a polyglot. mocky.py plants foreign magic in a valid PDF's slack until file --keep-going --raw reports fourteen types for a file that is only a PDF. That is Multiplicity 1 attacking the dispatcher, and it belongs in threat model and parser differentials, not here — but issue 0x22 ships both in one file, which is why the file output on it is so long.
Chimeras: multiplicity without duplication
The chimera is the case where multiplicity costs no bytes. The worked example in "Abusing file formats" is the JPG/PDF/ZIP chimera, whose byte map the article prints in full; the load-bearing observation is that all three formats store JPEG data uncompressed and contiguously, so one copy serves all three:
- The ZIP's local file header is followed immediately by stored file data — and by a duplicate of the filename, which the PDF's
endstreamkeyword is made to occupy. - The PDF stores an image XObject as a raw
streamof exactly those bytes. - The JPEG is those bytes, reached from offset 0 by way of a second JFIF header planted before them.
"Even better, we only have one copy of the image data; this copy is reused by each of the forms of the chimera."
The same technique in a different pair is pocs/poly/zgip, a ZIP/GZIP chimera whose Deflate stream is shared: "Just to prove that while Zip and Gzip can use the same compression algorithm, neither is an encapsulation of the other." This is the sharing/duplication question of cluster E answered at the format level rather than the store level — see content-addressed chunking for the same idea when the sharing is between artifacts instead of within one.
Index anchoring and random access
Why ZIP is always the host
ZIP's index is the End of Central Directory record, found by scanning backwards from EOF for PK\x05\x06 within a 64 KiB window (the maximum comment length). Nothing about that procedure references offset 0. "Abusing file formats" states the historical reason, which is not the one usually given:
"ZIP doesn't require magic at offset zero, and like PDF it's parsed from the bottom up. In this case, it's not to allow for incremental updates; rather, it's to limit those time-consuming floppy swaps when a multi-volume archive is created on the fly, on external storage."
The consequence, measured on the real releases:
| File | Size | first PK\x03\x04 | EOCD offset | bytes after EOCD | EOCD.cd_off field | resolves how |
|---|---|---|---|---|---|---|
pocorgtfo01.pdf | 3 790 438 | 3 505 149 | 3 790 375 | 63 | 284 738 | relative — +3 505 149 lands on PK\x01\x02 |
pocorgtfo06.pdf | 101 508 878 | 10 672 929 | 101 508 814 | 64 | 90 824 610 | relative — +10 672 929 lands on PK\x01\x02 |
pocorgtfo22.pdf | 53 215 888 | 9 600 | 53 215 810 | 78 (56 in-comment) | 53 214 138 | absolute — already correct |
Both behaviours are legal-ish and both work, which is exactly the problem. Info-ZIP prints warning: 3505149 extra bytes at beginning or within zipfile (attempting to process anyway) and self-corrects by taking the delta between the EOCD's claimed central-directory offset and where it actually found it. Issue 0x22 instead pre-fixes the pointers, so no warning appears — the fixup Albertini describes under "Fixing Absolute Pointers" and that zip -A performs. A ZIP index is therefore only conditionally random-access: the offsets are relative to a base the format never records. Consumers reconstruct that base by search. That is the mechanism by which the same archive is simultaneously "valid" and "damaged", and it is the seam every parser differential in the ZIP ecosystem is built on.
PDF's two anchors, and Julia Wolf's trick
PDF is the rare format that is anchored at both ends: a header signature that must appear within the first kilobyte, and a startxref footer giving the byte offset of the cross-reference table. Because the footer is authoritative, PDF supports incremental update, and because the header is merely "within 1 KiB", it is prefix-tolerant.
The canonical way to nest a ZIP inside a PDF exploits the space between those anchors. From "Abusing file formats":
"A good way to embed a ZIP in a PDF, as Julia Wolf showed us with napkins in PoC||GTFO 1:05, is to create a fake stream object after the xref, where the trailer object is present, before the startxref pointer."
with the layout given as PDF signature → objects → cross-reference table → (extra stream object containing the ZIP) → trailer → startxref. The reason is quantitative, not aesthetic: the EOCD must be within 64 KiB of EOF, and the cross-reference table grows linearly with the object count, so a ZIP placed among the normal objects gets pushed out of the window on any large document.
pocorgtfo01.pdf is that layout, verifiable byte-for-byte:
startxref → 3 504 383 → "169 0 obj <</Type /XRef /Index [0 170] /Size 170 …"
3 505 127 "999 0 obj\n<<>>\nstream\n" ← the fake object
3 505 149 PK 03 04 … ← the ZIP begins
3 790 375 PK 05 06 (comment length 0)
3 790 438 (EOF) "endstream\nendobj\nstartxref\n3504383\n%%EOF\n"Object 999 is declared after the object that startxref points at, so no PDF pointer needs adjusting and the xref stream stays byte-identical to what a normal producer emits. mutool clean will happily renormalize such a file — the tradition's advice is to run it, because "it modifies very little, yet rebuilds the XREF table and adjusts objects lengths, which turns your hand-made tolerated PDF into one that looks perfectly standard."
pocorgtfo22.pdf uses the complementary trick: the EOCD really is last, and its 56-byte comment field swallows the PDF's own trailer —
PK 05 06 … comment_len = 56
comment = "\0\0\0\0" "endstream\nendobj\nstartxref\nstartxref\n37074454\n%%EOF\n"— with four leading NUL bytes so that a ZIP tool displaying the comment as a C string shows an empty one. The archive's index is the last structure in the file and the PDF's %%EOF is the last structure in the file, because one is nested in the other's variable-length tail.
Offset-anchored guests
The third anchoring style — magic at a fixed non-zero offset — is the reason a single file can carry so many claimed types at once. pocorgtfo22.pdf, read directly:
| Offset | Bytes | Claimed format |
|---|---|---|
0 | %PDF-1.5 | PDF header |
128 | DICM | DICOM (128-byte preamble by spec) |
9 600 | PK\x03\x04 | ZIP local file header |
32 769 | CD001 | ISO 9660 Primary Volume Descriptor |
53 215 810 | PK\x05\x06 | ZIP EOCD |
A format that puts its magic at a fixed offset has donated every byte before that offset to whoever wants it. DICOM donates 128 bytes; ISO 9660 donates 32 KiB; TAR donates 257 bytes of which the first 100 are a filename field — which is precisely why pocorgtfo06.pdf's first TAR member is named %PDF-1.5:
$ tar -tvf pocorgtfo06.pdf | head -1
-rw-r--r-- Manul/Laphroaig 0 2014-10-06 22:33 %PDF-1.5A zero-length file whose name is the other format's signature, with \0ustar \0 at 257 and a valid header checksum. The TAR half is not hiding in the PDF; the PDF's identity is a field of the TAR half. That is the article's own title — "This TAR archive is a PDF!" — meant literally.
Cost of a partial read
For the catalog's comparison purposes: a polyglot has no unified index, so the cost of a ranged read is whatever the chosen parse costs, times nothing shared. Fetching pocorgtfo22.pdf's ZIP listing needs the last 64 KiB plus the central directory; fetching its first PDF page needs the last few KiB (for startxref) plus the object graph reachable from /Root; fetching the ISO's volume descriptor needs bytes 32 769..32 774. Three parses, three access patterns, zero shared machinery — the opposite of the single b-tree story in SQLite as an application file format. Whether any of those parses survives HTTP range access is exactly the question in range-request access; the answer for ZIP-in-PDF is yes for ZIP, awkwardly for PDF, and it is awkward because the base-offset reconstruction above requires locating PK\x03\x04 by scan.
Reflexivity and query surface
This is the axis on which polyglot craft scores lowest, and the absence is a finding.
A polyglot carries no schema, no manifest, no self-description. Nothing in pocorgtfo22.pdf announces "I am also an ISO". There is no equivalent of SELF's sqlite_schema, no sqlelf virtual table, no Wasm component type export. The artifact's multiplicity is implicit — recoverable only by trying every parser you own. That is the definition of the problem, not a shortcoming of the implementations: the entire craft depends on each parser believing the file belongs to it and to nobody else.
What interrogation surface exists is entirely out-of-band and heuristic, and the tradition's own tooling makes the point:
$ file pocorgtfo22.pdf
pocorgtfo22.pdf: tar archive
$ file --keep-going --raw pocorgtfo22.pdf
pocorgtfo22.pdf: tar archive
- DR-DOS executable (COM)
- Windows Program Information File for 145
- PDF document, version 1.5
- ISO 9660 CD-ROM filesystem data (DOS/MBR boot sector) 'CDROM'
- DICOM medical imaging data
- Nintendo DS ROM image: "%PDF-1.5"
…Two things follow, and both are load-bearing for the catalog:
- Default sniffing reports one type, and it is the wrong one.
filewithout--keep-goingstops at the first match, and its ordering is by internal magic-entry strength, not by what the file "is". A 53 MB journal identifies astar archive. ThemitraREADME's own demonstration is blunter: a plain PDF, aftermocky.py --combine, still passespdftotextandpdfinfocleanly whilefilecalls it a TAR. Content sniffing is not a query surface; it is a guess with a stable tie-break. - Multiplicity is only ever a lower bound.
file --keep-goingfinds magic; it does not validate, as the README says explicitly: "(it does not validate the formats, but at least gives you some information)". A reported type may be a mock; an unreported type may be a real, valid parse whose magic libmagic has no entry for.
The nearest thing to genuine self-interrogation in the corpus is the PDFLaTeX quine — a file that is simultaneously its own TeX source and the PDF that compiling that source produces (pocs/pdf/quine.pdf; the technique, \pdfcompresslevel=0 plus \immediate\pdfobj stream file {…}, is in "Abusing file formats"). That is reflexivity in the autological sense this catalog cares about — the artifact contains a complete description of how to rebuild itself — but it is still not queryable: there is no language in which to ask it a question, only a compiler that reproduces it. Score: 1, incidental.
The contrast to draw explicitly, because it is thesis 2 of the source outline: PDF, ZIP and TAR are all formats without a carried schema, and all three have accreted exactly the conventions that thesis predicts — libmagic entries, zip -A fixups, "attempting to process anyway" recovery, per-reader tolerance of a truncated %PDF-1. signature. The polyglot exists in the gap between the spec and the accreted conventions. A format that carried its own schema would not have that gap in the same shape.
Closure, dedup, and size model
What travels
PoC||GTFO's ZIP half is a real closure claim, if a modest one: each issue carries the code, patches, ROMs and tarballs its own articles discuss — the "feelies". This is the one axis where the artifact is doing something redbean also does, and doing it first. Verified contents of pocorgtfo03.pdf's archive (15 members, 14.8 MB):
alexander.txt bochs-2.6.2.patch bochs-20140203.patch defusing.zip
despair.txt lasta.txt lastq.txt netwatch-337f8b1.tar.gz
nokiacipher.png packed saucers.txt tamadec.txt tetranglix.tar.bz2
pocorgtfo02.pdf pocorgtfo03-encrypt.pyNote pocorgtfo02.pdf — the previous issue, carried inside this one, itself a ZIP/MBR/PDF polyglot. And pocorgtfo03-encrypt.py, the script that produces this issue's AngeCryption layer: the artifact carries its own construction recipe, which is the closest the corpus comes to the reproducibility story in embedded provenance.
But this is payload bundling, not dependency closure. Nothing in the file lets a reader resolve what netwatch-337f8b1.tar.gz needs to build, and nothing enumerates the transitive set. There is no equivalent of a Nix closure or a DT_NEEDED graph. Score: 1.
Size model
Polyglot size is additive minus overlap, and the overlap term is the whole craft:
| Layout | Size cost | Example |
|---|---|---|
| Stack | len(A) + len(B) exactly | any ZIP appended to anything |
| Cavity | max(len(A), len(B)) when A fits the cavity | ISO's 32 KiB system area absorbs A for free |
| Parasite | len(A) + len(B) + wrap (wrap ≈ 12 for PNG) | PNG cOMM: 4 length + 4 type + 4 CRC |
| Chimera | max(len(A), len(B)) — payload shared | JPG/PDF/ZIP: one copy of the image; ZIP/GZIP: one Deflate stream |
Near-polyglot (--overlap) | as chimera, plus the recovered bytes in the filename | O(5-204){424D4E0100}.bmp.jpg — 5 overwritten bytes recorded out-of-band |
The chimera row is the only one that beats concatenation, and it beats it only when the two formats agree on a payload encoding. Albertini's list of what makes that possible is short and worth reading as a compatibility table: uncompressed JPEG data (JPG/PDF/ZIP), raw Deflate (ZIP/GZIP/PNG), and — with ascii-zip-style Huffman abuse — a Deflate stream that is also printable ASCII, which is how Rosetta Flash worked.
The hard size constraint the tradition keeps hitting is the opposite one — formats with a required exact size. From "Abusing file formats":
"It's common that ROM and disk images require a specific rounded size, and there is often no workaround to this. You can merge a PDF and an Apple II floppy image, but only if the PDF fits in the 143,360 byte disk image."
The workaround used for pocs/poly/Apple2PDF was to move up to a 2 MB hard-disk image; the workaround in pocs/poly/SnesMd (a Super NES + Sega Megadrive + PDF triple) was to put the PDF at the bottom of the ROM rather than after it, "because the exact rom size is critical for SMC". A size-exact format is prefix- and suffix-hostile even when it is interior-tolerant — a fifth column the FType model does not have a field for, and the one gap in the taxonomy this survey found.
Mutability, dispatch, and trust
Mutability: zero, by construction
A polyglot is the most brittle artifact in this catalog. Its correctness is a conjunction of n invariants, several of which are checksums over overlapping ranges: PNG per-chunk CRC32, ZIP per-member CRC32, TAR's header checksum, PDF's startxref offset and per-stream /Length, the iNES trainer flag, MBR's 0xAA55. Change one byte in the shared region and you must recompute every dependent field in every format — which is why mitra parsers carry a fixformat hook and why parsers/pdf.py contains a full xref/startxref rewriter whose comment reads "dumb [start]xref fix: fixes old-school xref with no holes, with hardcoded \n".
The artifact is therefore not a state store, not transactional, and not incrementally editable in any sense the SELF/selfdb line means. Score: 0. PDF's incremental-update feature is the one exception in principle — appending a new xref section is legal — but doing so in a polyglot moves EOF and so invalidates the ZIP's 64 KiB EOCD window and any footer-anchored guest. Editing collapses the superposition.
There is one striking mutability-adjacent exhibit: the MD5 hashquines in issue 0x14, files that display their own MD5 hash. That is self-reference without self-modification — the fixed point is found by collision search at build time, not maintained at runtime — and it belongs with the measurement discussion of what "the artifact knows about itself" can be made to mean without a query engine.
Dispatch: the consumer, and that is the vulnerability
For every other subject in this catalog, dispatch is owned by a named component: the kernel for binfmt_misc, the loader for ld.so, the shell for a shebang. For a polyglot, dispatch is owned by whichever consumer happens to open the file, using whichever of three incompatible mechanisms it prefers:
| Mechanism | Used by | What the polyglot does to it |
|---|---|---|
| Filename extension | shells, desktop environments, most upload paths | pocorgtfo07.pdf → rename to .html and it is a working web page |
| Magic sniffing | file/libmagic, TrID, AV, IDS, browsers | reports one type; a mock makes it report the attacker's chosen type |
| The parser's own recovery | PDF readers, ZIP tools, media players | accepts the file that the sniffer said was something else |
The security consequence is the tradition's whole argument, and it is stated plainly:
"Testing various polyglots on Encase showed that nearly all of them were reported as a single file type, with no warnings whatsoever."
and, on the ZIP-scanning divergence that breaks signatures:
"This is likely why some modern tools take a different approach, ignoring the official structure of a ZIP. These extractors start at offset zero and look for a sequence of Local File Headers. … Sadly, doing this differently makes ZIP multi possible, which can be critical as it can break signatures and the complete chain of trust of a standard system."
That sentence is the bridge to parser differentials and to the JAR/APK signing problem in ZIP parasitism: a verifier that enumerates members top-down and an extractor that enumerates them from the central directory can disagree about which bytes were signed.
Trust: blacklists, and why they lose
The one deployed mitigation the corpus documents is Adobe's, and it is instructive precisely because it half-worked:
"For security reasons, Adobe Reader, the standard PDF reader, has blacklisted known magic signatures such as PNG or PE since version 10.1.5. It is thus not possible anymore to have a valid polyglot that would open in Adobe Reader as PDF. This is a good security measure even if it breaks compatibility with older releases of PoC||GTFO."
The immediate bypass is a lesson about what a magic number actually is:
"However, it's critical to blacklist the actual signature as opposed to what is commonly appearing in files. JFIF files typically start with the signature, SOI, and an APP0 segment, which make the file start with
FF D8 FF E0. However, the signature itself is onlyFF D8, which can lead to a blacklist bypass by using a different segment or different marker right after the signature."
pocorgtfo03.pdf is that bypass, and its first 30 bytes still show it:
FF D8 JPEG SOI — the entire real magic
00 00 00 10 4A 46 49 46 … "JFIF" text, but NOT preceded by FF E0
FF FE 00 22 COM segment, length 0x22
0A 25 50 44 46 2D 31 2E 35 0A "\n%PDF-1.5\n" ← at offset 25
39 39 39 20 30 20 6F 62 6A … "999 0 obj\n<<>>\nstream\n"The JPEG decoder skips from FF D8 to the next marker and finds the comment; the PDF reader finds %PDF-1.5 inside that comment, comfortably within its 1 KiB budget; the blacklist, which was looking for FF D8 FF E0, sees nothing. (Adobe subsequently fixed the JFIF signature check, and the README records that pocorgtfo03.pdf has not opened in Adobe Reader since March 2014 — the mitigation eventually landed, three releases and one bypass later.)
The structural recommendation the tradition offers instead of blacklisting is a whitelist tightening, and it is the single most actionable line in the corpus:
"Requiring the PDF signature to appear earlier in the file - even just in the first 64 bytes instead of a whole kilobyte - would proactively prevent a lot of polyglot types, as most recent formats are dense at the start of the file."
That is a quantitative trust claim — reduce start_o from 1016 to 56 and the set of formats that fit in the prefix collapses to almost nothing — and it is the kind of claim the threat model page can test directly against the mitra matrix.
Finally, on signing: issue 0x20 is "polyglot: PDF, ZIP — Signed", which is the corpus quietly conceding the point. A digitally signed PDF fixes a byte range; once the artifact is signed, the polyglot is frozen, and every technique above becomes a build-time-only affordance. The general problem — signing an artifact whose bytes several verifiers disagree about the extent of — is the subject of embedded provenance, and it has no answer here.
Strengths
- The taxonomy is executable.
mitrais not a description of which formats compose; it is a program that tries, and its 288-combination matrix is regenerable. A claim in this area is falsifiable in one command. - Zero infrastructure. No loader, no kernel registration, no runtime, no substrate. A polyglot works on any system that has any of its parsers, including systems built decades apart —
pocs/poly/SnesMdtargets a 1988 console, a 1990 console and a PDF reader from the same 512 KiB. - Chimeras achieve multiplicity with no size penalty, which no other technique in this catalog does — the payload is shared rather than duplicated.
- The corpus is a real regression suite for parsers. The
minicollection (pocs/mini/README.md) — a valid, minimal, self-describing file for each of ~60 formats, with rules ("should be fully valid… shows what they are… should be made as small as possible") — is directly reusable as fixtures, and thepdf/PoCs form a compatibility matrix across readers. - The design advice generalizes. "Magic at zero, mandatory footer, an explicit comment chunk instead of tolerated garbage" is four rules that a new format can adopt for free and that measurably shrink its composability surface.
Weaknesses
- No reflexivity whatsoever. The artifact cannot be asked what it is. Every downstream consumer re-derives multiplicity by brute force, and the answer is a lower bound.
- Fragility is multiplicative.
nformats meannsets of checksums and offsets over overlapping ranges; the file is effectively immutable after construction, and normalization tools routinely destroy it ("the library — too smart for its britches — removed your dummy chunk, recompressed your intentionally uncompressed data…"). - Compatibility is a heisenbug problem, in the tradition's own words: "a single font in a PDF might become corrupted. One image — and only one image! — might go missing." The boundary that matters is not valid/invalid but "good enough" versus "let's try to recover".
- Reach is per-parser and decays. Every specimen in the corpus is a bet on specific implementations; Adobe's blacklist retired issue
0x03and0x05from the flagship reader within months of each. - It is fundamentally the wrong tool for deployment. Albertini says so himself: "Archiving files together is much more natural than making a polyglot file. Although opening a polyglot file may be transparent for the targeted software, it's not a natural action for user."
Key design decisions and trade-offs
| Decision | Rationale | Trade-off |
|---|---|---|
Model a format as five booleans + four offsets (FType) | Composability is decidable from tolerance properties alone; no need to model semantics | Misses size-exact formats (ROM/disk images), which the model calls composable and reality does not |
| Enumerate four layouts (Stack / Cavity / Parasite / Zipper) rather than search | Each maps to one structural property, so a hit is explained, not just found | Layouts outside the four (interleaved chimeras, encoding-level sharing) still need hand-crafting |
| ZIP as the invariant host of every PoC||GTFO issue | Backwards-scanned EOCD + 64 KiB comment makes it the universal suffix parasite | Inherits ZIP's relative-offset ambiguity — "damaged" warnings, and a signing surface |
| Put the ZIP in a fake PDF object after the xref | Keeps EOCD inside the 64 KiB window regardless of object count; no PDF pointer needs fixing | Relies on readers tolerating an undeclared object between xref and trailer — legal by omission, not by statement |
| PDF as the presentation type everywhere | The only common format that is prefix-tolerant (1 KiB), suffix-tolerant, and footer-authoritative at once | That very tolerance is what got PDF a magic blacklist; the technique attracted the mitigation |
| Chimera (shared payload) over concatenation where encodings agree | Multiplicity at max(len) instead of sum(len) | Requires both formats to store the payload uncompressed or with the same codec; brittle to any re-encode |
Publish mocky.py (mocks) alongside real polyglots | Separates "attacks the parsers" from "attacks the dispatcher" — different mitigations | Muddies type-counting: file --keep-going output for issue 0x22 lists types the file is not |
| Record swap offsets in the output filename | The metadata has nowhere else to live — the artifact carries no self-description | Out-of-band metadata: rename the file and the provenance is gone. The exact gap SELF closes with a table |
Where this sits in the catalog
- Against thesis 1 ("every binary format eventually reimplements a database, badly"). Supported, with a twist: PDF's
xrefis a hand-maintained primary-key index over objects, andmitra'sparsers/pdf.pyhas to reimplement it ("only very standard object declarations") to insert one payload. ZIP goes further and stores two copies of the same relation — filenames appear in both the local file headers and the central directory — which is a denormalized index maintained by hand, and Albertini's closing section asks for exactly the schema fix a database person would: "One that doesn't duplicate file names between Central Directory and Local File Headers?" - For thesis 2 ("self-description is what makes a format survivable"). Strongly supported in the negative. Every format in the corpus that lacks a carried schema has accreted a recovery convention (
file's heuristics,zip -A, "attempting to process anyway", truncated-signature tolerance), and the polyglot lives in the gap between spec and convention. XZ and ID3v1, the two formats that compose with nothing, are also the two that enforce a structural invariant end-to-end. - Against thesis 3 ("the container is a tax"). Complicated. The chimera is the counter-example: when host and guest agree on an encoding, the container costs zero bytes. The tax is real for stacking and parasitism and is exactly quantifiable — 12 bytes per PNG chunk,
2 + 2per JPEG segment,~30 + filenameper ZIP member. - For thesis 5 ("portability migrated from the format to the access layer"). This page is the strongest evidence for the thesis, precisely by exhausting the alternative. Twenty-three issues of format-level cleverness produced artifacts whose reach shrinks as parsers tighten, that cannot be edited, and whose type nobody can query. See the comparison table for the side-by-side against APE and SELF.
Sources
- "Abusing file formats" —
corkami/docs/AbusingFileFormats/README.md(the maintained expansion of PoC||GTFO 7:6, "Funky Files, the Novella!") — identification, chunk vs. pointer structures, appended data, trailing space, chimeras, blacklisting, normalization corkami/mitra/README.md— the 288-combination compatibility matrix, the four layouts, near-polyglots, script-polyglot comment/heredoc/terminator table, and the format-design recommendationscorkami/mitra/parsers/__init__.py— theFTypemodel:bAppData,bParasite,parasite_o/s,start_o,precav_o/s,cut/wrap/fixformatcorkami/mitra/mitra.py—isStackOk/isCavOk/isParasiteOk/isZipperOk, the composability predicates- Format parsers cited individually:
png.py·zip_.py·pdf.py·pdfc.py·tar.py·iso.py·wasm.py·nes.py·xz.py·id3v1.py corkami/pocs—pdf/1016garbage.pdf,pdf/tiny.pdf,mini/README.md,poly/SnesMd/snes_md.txt,poly/zgip/README.md,poly/CorkaMIX/README.md,poly/Apple2PDF/AppleII.pdfangea/pocorgtfo/README.md— per-issue polyglot composition, release hashes, and thefile --keep-going/tridoutput for issue0x22- Release blobs read directly:
pocorgtfo01.pdf·pocorgtfo03.pdf·pocorgtfo06.pdf·pocorgtfo22.pdf - Articles: "This ZIP is also a PDF" (1:05, Julia Wolf) · "This OS is also a PDF" (2:08) · "This PDF is a JPEG" (3:03) · "This TAR archive is a PDF!" (6:04) · "Funky Files, the Novella!" (7:06) · "Mitra and Mocky: Near-polyglots and Mocks" (22:03) · "Inside out; or, Abusing archive file formats" (22:05)
- Talks: "Schizophrenic files" (Area41 / MRMCD 2014, w/ Gynvael Coldwind) · "Funky file formats" (31C3, 2014) · "Generating weird files" (Pass the Salt 2021) ·
corkami/docs/talks.mdindexes the rest - PoC||GTFO official mirror ·
corkami/docs/PDF/PDF.md— PDF signature and structure tricks - Format specifications: PDF 32000-1:2008 (ISO 32000-1, Adobe copy) · PNG (Third Edition), W3C · ZIP
APPNOTE.TXT - Related in this tree: Cosmopolitan/APE · ZIP parasitism · parser differentials · footer-indexed formats · boot hybrids · threat model · embedded provenance · concepts · comparison · open questions