TL;DR getting an MD5 collision of these two images is now(*) trivial and instant.
⟷
Don't play with fire, don't rely on MD5.
Branch: master. New pull request. GIF; Portable Executable; MP4 and others. Get a file to get another file's hash or a given hash: impossible. Hash Animation:Master (A:M) is a fully featured, intuitive, fun to learn and use 3D animation software package. It has been created to make 3D animation affordable and easy enough for everyone – no matter if you are an animation expert at home or new to animation in general.
(*) Colliding any pair of files has been possible for many years, but it takes several hours each time, with no shortcut.This page provide tricks specific to file formats and pre-computed collision prefixes to make collision instant.
git clone
. Run Script. Done.By Ange Albertini and Marc Stevens.
- Attacks
- Identical prefix
- Chosen-prefix collisions
- Exploitations
- Standard strategy
- JPG
- PNG
- MP4 and others
- Uncommon strategies
- PolyColls: collisions of different file types
- PileUps (multi-collision)
- Use cases
- Failures
- Standard strategy
The goal is to explore extensively existing attacks - and show on the way how weak MD5 is (instant collisions of any JPG, PNG, PDF, MP4, PE...) -and also explore in detail common file formats to determinehow they can be exploited with present or with future attacks.
Indeed, the same file format trick can be used on several hashes(the same JPG tricks were used for MD5,malicious SHA-1 and SHA1),as long as the collisions follow the same byte patterns.
This document is not about new attacks (the most recent one was documented in 2012),but about new forms of exploitations of existing attacks.
Current status - as of December 2018 - of known attacks:
- get a file to get another file's hash or a given hash: impossible
- it's still even not practical with MD2.
- works for simpler hashes(*)
- get two different files with the same MD5: instant
- examples: 1 ⟷ 2
- make two arbitrary files get the same MD5: a few hours (72 hours.core)
- examples: 1 ⟷ 2
- make two arbitrary files of specific file formats (PNG, JPG, PE...) get the same MD5: instant
- read below
- get two different files with the same SHA1: 6500 years.core
- get two different PDFs with the same SHA-1 to show a different picture: instant (the prefixes are already computed)
(*) example with crypt - thanks Sven!
MD5 and SHA1 work with blocks of 64 bytes.
If two contents A & B have the same hash, then appending the same contents C to both will keep the same hash.
Collisions work by inserting at a block boundary a number of computed collision blocksthat depends on what came before in the file.These collision blocks are very random-looking with some minor differences(that follow a specific pattern for each attack)and they will introduce tiny differences while eventuallygetting hashes the same value after these blocks.
These differences are abused to craft valid files with specific properties.
File formats also work top-down, and most of them work by byte-level chunks.
Some 'comment' chunks can be inserted to align file chunks to block boundaries,to align specific structures to collision blocks differences,to hide the rest of the collision blocks randomness from the file parsers,and to hide otherwise valid content from the parser (so that it will see another content).
These 'comment' chunks are often not officially real comments:they are just used as data containers that are ignored by the parser(for example, PNG chunks with a lowercase-starting ID are ancillary, not critical).
Most of the time, a difference in the collision blocks is used to modify the length of a comment chunk,which is typically declared just before the data of this chunk:in the gap between the smaller and the longer version of this chunk,another comment chunk is declared to jump over one file's content
A
.After this file content A
, just append another file content B
.Since file formats usually define a terminator that will make parsers stop after it,
A
will terminate parsing, which will make the appended content B
ignored.So typically at least two comments are needed - often three:
- alignment
- hide collision blocks
- hide one file content (for re-usable collisions)
These common properties of file formats make it possible - they are not typically seen as weaknesses, but they can be detected or normalized out:
- dummy chunks - used as comments
- more than one comment
- huge comments (lengths: 64b for MP4, 32b for PNG -> trivial collisions. 16b for JPG, 8b for GIF -> no generic collision for GIF, limited for JPG)
- store any data in a comment (UTF8 could be enforced)
- store anything after the terminator (usually used only for malicious purposes)
- no integrity check. CRC32 in PNG are usually ignored, which would prevent PNG re-usable collisions otherwise.
- flat structure: ASN.1 defines parent structure with the length of all the enclosed substructures,which prevents these constructs: you'd need to abuse a length, but also the length of the parent.
- put a comment before the header - this makes generic re-usable collisions possible.
Identical prefix
- Define an arbitrary prefix - its content and length don't matter.
- The prefix is padded to the next 64-byte block.
- Collision block(s) are computed depending on the prefix and appended.Both sides are very random. The differences are predetermined by the attack.
- After this[these] block[s], the hash value is the same despite the file differences.
- Any arbitrary identical suffix can be added.
Prefix | = | Prefix |
---|---|---|
Collision A | ≠ | Collision B |
Suffix | = | Suffix |
Both files are almost identical (their content have only a few bits of differences)
Exploitation:
Bundle two contents, then either:
- Data exploit: run code that checks for differences and displays one or the other (typically trivial since differences are known in advance).
- Structure exploit: exploit file structure (typically, the length of a comment) to hide one content or show the other (depends on the file format and its parsers).
Two files with this structure:
Prefix | = | Prefix |
---|---|---|
Collision A | ≠ | Collision B |
A | = | |
= | B |
will show either A or B.
FastColl (MD5)
Final version in 2009.
- time: a few seconds of computation
- space: two blocks
- differences: no control before, no control after.FastColl difference mask:
- exploitation: hard
The differences aren't near the start/end of the blocks, so it's very hard to exploit since you don't control any nearby byte.A potential solution is to brute-force the surrounding bytes - cf PoCGTFO 14:10.
Examples:
With an empty prefix:
Other examples, with an identical prefix: 1 ⟷ 2
Variant: there is a single-block MD5 collision but it takes five weeks of computation.
UniColl (MD5)
Documented in 2012, implemented in 2017
UniColl lets you control a few bytes in the collision blocks,before and after the first difference, which makes it an identical-prefix collision with some controllable differences, almost like a chosen-prefix collision.This is very handy, and even better the difference can be very predictable:in the case of
m2+= 2^8
(a.k.a. N=1
/ m2 9
in HashClash poc_no.sh script),the difference is +1 on the 9th byte, which makes it very exploitable,as you can even think about the collision in your head:the 9th character of that sentence will be replaced with the next one: 0
replaced by 1
, a
replaced by b
..- time: a few minutes (depends on the amount of byte you want to control )
- space: two blocks
- differences:
- exploitation: very easy - controlled bytes before and after the difference, and the difference is predictable. The only restrictions are alignment and that you 'only' control 10 bytes after the difference.
Examples with
N=1
and 20 bytes of set text in the collision blocks:UniColl has less control than a true chosen-prefix collision,but it's much faster especially since it takes only two blocks.
It was used in the Google CTF 2018,where the frequency of a certificate serial changes and limitations on the lengths prevented the use of chosen-prefix collisions.
Shattered (SHA1)
Documented in 2013, computed in 2017.
- time: 6500 years.CPU and 110 year.GPU
- space: two blocks
- differences:
- exploitation: medium. The differences are right at the start and at the end of the collision blocks. So no control before and after a length in the prefix/in the suffix:PNG stores its length before the chunk type, so it won't work.However it will work with JP2 files when they use the JFIF form (the same as JPG),and likely MP4 and other atom/box formats if you use long lengths on 64bits(in this case, they're placed after the atom type).
The difference between collision blocks of each side is this Xor mask:
Examples: PoC||GTFO 0x18 is using the computed SHA1 prefixes,re-using the image directly from PDFLaTeX source (see article 18:10),but also checking the value of the prefixes via JavaScript in the HTML page (the file is polyglot, ZIP HTML and PDF).
Chosen-prefix collisions
They allow to collide any content. They don't exist for SHA-1 yet.
? | ≠ | ? |
---|---|---|
Collision A | ≠ | Collision B |
- take two arbitrary prefixes
- pad the shortest to be as long as the longest. both are padded to the next block - minus 12 bytes
- these 12 bytes of random data will be added on both sides to randomize the birthday search
- X near-collision blocks will be computed and appended.The fewer blocks, the longer the computation.Ex: 400 kHours for one block. 72 hours.cores for nine blocks with HashClash.
Chosen-prefix collisions are almighty, but they can take a long time just for a pair of files.
HashClash (MD5)
Final version in 2009.
Examples: let's collide
yes
and no
. It took three hours on 24 cores.Here is a log of the whole operation.
Attacks summary
Hash | Name | Date | Duration | Prefix type | Control near diff |
---|---|---|---|---|---|
MD5 | FastColl | 2009 | 2s | Identical | none |
UniColl | 2012 | 7-40min | Identical | 4-10 bytes | |
HashClash | 2009 | 72h | Chosen | none | |
SHA1 | Shattered | 2013 | 6500yr | Identical | prefix & suffix |
Identical prefix collisions is usually seen as (very) limited, but chosen-prefix is time consuming.
Another approach is to craft re-usable prefixes via either identical-prefix attack such as UniColl - or chosen-prefix to overcome some limitations - but re-use that prefix pair in combinations with two payloads like a classic identical prefix attack.
Once the prefix pair has been computed, it makes colliding two contents instant:it's just a matter of massaging file data (according to specific file formats) so that it fits the file formats specifications and the precomputed prefix requirements.
Standard strategy
Classic collisions of two valid files with the same file type.
JPG
Theoretical limitations and workarounds:
- the Application segment should in theory right after the Start of Image marker.In practice, this is not necessary, so our collision can be generic: the only limitation is the size of the smallest image.
- a comment's length is stored on two bytes, so the amount it can store is limited to it's limited to 65536 bytes (roughly the size of a 400x400 photo)
- rather than jumping over a complete JPG file, one can split that file in its segments, and add jump trampolines between segmentscomments over each image segmentshow comments trampoline work
- while most of a JPG structure is made of segments that are all limited to 65536 bytes in size,the actual compressed data is stored in the Entropy Coded Segment which doesn't respect its limitations:its size is unkown in advance and grows beyond that limit.It grows with the size of the image, making most of the file size in a baseline (non progressive) image.To make the whole image fit into 64kb chunks, the easy way is to first try to save the image as progressive (which any software can do, and splits the ECS in typically up to six scans). The more advanced way is to use JPEGTran with its 'wizard'
--scans
command line parameter and define custom scans.
There's no other restriction besides the scans segments,so an MD5 collision of two arbitrary JPGs is instant, and needs no chosen-prefix collision, just UniColl.
With the script:
Examples:
⟷
custom scans
2 MD5-colliding JPGs
Here's an example of JPEGTran scans definitionto turn a 1944x2508 RGB image into a 100% JPG with 20 scans in which they all fit in 64 kb.
Result:
a 1944x2508 RGB image as a 100% JPG with 20 scans
PNG
Theoretical limitations and workarounds:
- PNG uses CRC32 at the end of its chunks, which would prevent the use of collision blocks, but in practice they're ignored.
- the image meta data (dimensions, color space...) are stored in the
IHDR
chunk,which should in theory be right after the signature (ie, before any potential comment),so it would mean that we can only precompute collisions of images with the same meta data.However, that chunk can actually be after a comment block (in the vast majority of readers), so we can put the collision data before the header,which enables to collide any pair of PNG with a single precomputation.
Since a PNG chunk has a length on four bytes, there's no need to modify the structure of either file: we can jump over a whole image in one go.
We can insert as many discarded chunks as we want, so we can add one for alignment, then one which length will be altered by a UniColl. so the length will be
00
75
and 01
75
.So an MD5 collision of two arbitrary PNG images is instant, with no prerequisite (no computation, just some minor file changes), and needs no chosen-prefix collision, just UniColl.
With the script:
Examples:
⟷
2 MD5-colliding PNGs with different properties
Here is a recording of the whole operation.
incompatibility
Most readers accept flawlessly PNG files that start with a chunk that is not
IHDR
.However, some (such as Safari and Preview - any other?) don't tolerate it.In this case, the image header and its properties (dimensions, color space) must be first, before any collision blocks.
In this case, both colliding files must have the same properties.Again, UniColl is enough, and of course the computed prefix pair can be reused for any other pair of files with the same properties
Here is a script to collide any pair of such files that launches UniColl if needed to compute the prefix pair.
Examples:
⟷
⟷
2 pairs of MD5-colliding PNGs with identical properties for maximum compatibility
Here is a recording of the whole operation when UniColl is invoked,
and another one when the prefix has been already computed.
GIF
GIF is tricky:
- it stores its meta data in the header before any comment is possible, so there can't be a generic prefix for all GIF files.
- if the file has a global palette, it is also stored before a comment is possible too.
- its comment chunks are limited to a single byte in length, so a maximum of 256 bytes!
However, the comment chunks follow a peculiar structure: it's a chain of
<length:1>
<data:length>
until a null length is defined.So it makes any non-null byte a valid 'jump forward'. Which makes it suitable to be used with FastColl,as shown in PoC||GTFO 14:11.So at least, even if we can't have a generic prefix, we can collide any pair of GIF of same metadata (dimensions, palette) and we only need a second of FastColl to compute its prefix.
Now the problem is that we can't jump over a whole image like PNG or over a big structure like JPG.
A possible workaround is to massage the compressed data or to chunk the image in tiny areas like in the case of the GIF Hashquine, but this is not optimal.
Another idea that works generically is that the image data is also stored using this
length data
sequence structure:so if we take two GIFs with no animation, we only have to:- normalize the palette
- set the first frame duration to the maximum
- craft a comment that will jump to the start of the first frame data, so that the comment will sled over the image data as a comment,and end the same way: until a null length is encountered. Then the parser will meet the next frame, and display it.
With a minor setup (only a few hundred bytes of overhead), we can sled over any GIF image and work around the 256 bytes limitation.This idea was suggested by Marc, and it's brilliant!
So in the end, the current GIF limitations for instant MD5 collisions are:
- no animation
- the images have to be normalized to the same palette - see
gifsicle --use-colormap web
- the images have to be the same dimensions
- after 11 minutes, both files will show the same image
An easy shortcut to normalize still GIF images is to make them animation frames of the same image,then we can use a script to re-use or compute FastColl blocks to make a file pair that shows each of them.
Examples:
⟷
2 MD5-colliding GIFs - pics by KidMoGraph
Here is a recording of the whole operation.
Portable Executable
The Portable Executable has a peculiar structure:
- the old DOS header is almost useless, and points to the next structure, the PE header.The DOS headers has no other role. DOS headers can be exchanged between executables.
- the DOS header has to be at offset 0, and has a fixed length of a full block, and the pointer is at the end of the structure,beyond UniColl's reach: so only chosen-prefix collision is useful to collide PE files this way.
- The PE header and what follows defines the whole file.
So the strategy is:
- the PE header can be moved down to leave room for collision blocks after the DOS header.
- The DOS header can be exploited (via chosen-prefix collisions) to point to two different offsets, where two different PE headers will be moved.
- The sections can be put next to each other, after the
DOS/Collisions/Header1/Header2
structure. You just need to apply a delta to the offsets of the two section tables.
This means that it's possible to instantly collide any pair of PE executables. Even if they use different subsystems or architecture.
While executables collisions is usually trivial via any loader, this kind of exploitation here is transparent: the code is identical and loaded at the same address.
Examples: tweakPNG.exe (GUI) ⟷ fastcoll.exe (CLI)
Here is a script to generate instant MD5 collisions of Windows Executables.
MP4 and others
This format's container is a sequence of
Length Type Value
chunks called Atoms.The length is a 32 bit big-endian and covers itself, the type and the value, so the minimum normal length is 8(the type is a 4 ASCII characters string).If the length is null, then the atom takes the rest of the file - such as
jp2c
atoms in JP2 files.If it's 1, then the Type is followed by a 64bit length, changing the atom to Type Length Value
, making it compatible with other collisions like Shattered.Some atoms contain other atoms: in this cases, they're called boxes. That's why this otherwise unnamed structure is called 'atom/box'.
This 'atom/box' format used in MP4 is actually a derivate of Apple Quicktime,and is used by many other formats (JP2, HEIF, F4V).
The first atom type is usually
ftyp
, which enables to differentiate the actual file format.The format is quite permissive:just chain
free
atoms, abuse one's length with UniColl, then jump over the first payload.For MP4 files, the only thing to add is to adjust the
stco
(Sample Table - Chunk Offsets) or co64
(the 64 bit equivalent) tables, since they are absolute(!) offsets pointing to the mdat
movie data - and they are actually enforced!This gives a script that instantly collides any arbitrary video - andas mentioned, it may work on other format than MP4.
Examples (videos by KidMoGraph):
- 32b lengths (standard) collision1.mp4 ⟷ collision2.mp4
? ⟷?️ - 64b lengths collisionl1.mp4 ⟷ collisionl2.mp4
☀️ ⟷?
Note that some viewers (OS X, Safari, FireFox) don't allow a file that starts with an Atom that is not
ftyp
.In this case, the prefix have to cover this, and it's not so generic, but besides it's the same strategy - only limited to a single file type.JPEG2000
JPEG2000 files usually start with the Atom/Box structure like MP4,then the last atom
jp2c
is typically until the end of the file (null length),then from this point on it follows the JFIF structure, like JPEG (starting with FF 4F
as a segment marker).The pure-JFIF form is also tolerated, in which case collision is like JPEG:Shattered-compatible, but with comments limited to 64Kb.
On the other hand, if you manipulate JPEG2000 files with the Atom/Box,you don't have this limitation.
As mentioned before, if you're trying to collide this structure andif there are more restriction - for example starting with a
free
atom is not tolerated by some format -then you can compute another UniColl prefix pairs specific to this format:JPEG2000 seems to enforce a 'jP '
atom first before the usual ftyp
,but besides, that's the only restriction: there's no need to relocate anything.So the resulting script is even simpler!
Examples: collision1.jp2 ⟷ collision2.jp2
about Shattered
Shattered exploitation was not a PDF trick, but a JPG trick in a PDF.
It only enabled a PDF to contain a JPG-compressed object that could have two different contents.Both PDFs needed to be totally identical beside.
Note that the documents can be totally normal, and can just clip the collision JPG and display it in difference places, such as multi-page documents.
Examples: the Shattered paper, modified ⟷ the Shattered paper, original
the Shattered paper using a colliding JPG in two places
PDF collisions with MD5
With MD5 (and other collision patterns), we can do PDF collisions at document level,with no restrictions at all on either file!
PDF has a very different structure from other file formats.It uses object numbers and references to define a tree.The whole document depends on the Root element.
This (valid) PDF
is equivalent to:
Tricks:
- Storing unused objects in a PDF is tolerated.
- Skipping any object numbers is also OK. There's even an official way to skip numbers in the
XREF
table.
So storing two document trees in the same file is OK.We just need to make the root object refer to either root object of both documents.
So we just need to take two documents,renumber objects and references so that there is no overlap,craft a collision so that the element number referenced as Root object can be changed while keeping the same hash value,which is a perfect fit for UniColl with
N=1
, and adjust the XREF
table accordingly.This way, we can safely collide any pair of PDFs, no matter the page numbers, dimensions, images...
comments
PDF can store foreign data in two ways:
- as a line comment, in which the only forbidden characters are newline (
r
andn
).This can be used inside a dictionary object, to modify for example an object reference, via UniColl.So this is a valid PDF object even if it contains binary collision blocks - just retry until you have no newline characters: - as a stream object, in which case any data is possible, but since we're inside an object, we can't alter the whole PDF structure,so it requires a chosen-prefix collision to modify the structure outside the containing stream object.
colliding text
The first case makes it possible to highlight the beauty of UniColl, a collision where differences are predictable,so you can write poetry over colliding data - thanks Jurph!
Rather than modifying the structure of the document and fool parsers,we'll just use collision blocks directly to produce directly text,with alternate reading!
Examples: poeMD5 A ⟷ poeMD5 B
A true cryptographic artistic creation :)
(Note I screwed up with Adobe compatibility, but that's my fault, not UniColl's)
colliding document structure
Whether you use UniColl as inline comment or chosen-prefix in a dummy stream object, the strategy is similar:shuffle objects numbers around, then make Root object point to different objects, so unlike Shattered, this means instant collision of any arbitrary pair of PDF, at document level.
A useful trick is that
mutool clean
output is reliably predictable,so it can be used to normalize PDFs as input, and fix your merged PDF while keeping the important parts of the file unmodified.MuTool doesn't discard bogus key/values - unless asked, and keep them in the same order,so using fake dictionary entries such as /MD5_is /REALLY_dead_now__
is perfect to align things predictably without needing another kind of comments.However it won't keep comments in dictionaries (so no inline-comment trick)An easy way to do the object-shuffling operation without hassle is just to merge both PDF filesvia
mutool merge
then split the /Pages
object in two.To make room for this object, just merge in front of the two documents a dummy PDF.
Optionally, create a fake reference to the dangling arrayto prevent garbage collection from deleting the second set of pages.
Example:with this script,it takes less than a second to collide the two public PDF papers like Spectre and Meltdown:
Examples: spectre.pdf ⟷ meltdown.pdf
Possible extension: chain UniColl blocks to also keep pairs of the various non-critical objectsthat can be referenced in the Root object - such as
Outlines
, Names
, AcroForm
and Additional Actions (AA
) - in the original source files.in PDFLaTeX
The previous techniques work with just a pair of PDF files,but it's also possible to do it directly from TeX sourcesvia specific PDFTeX operators.
You can define objects directly - including dummy key and values for alignments - and define empty objects to reserve some object slots by including this at the very start of your TeX sources:
Don't forget to normalize PDFLaTeX output - with
mutool
for example - if needed:PDFLaTeX is hard to get reproducible builds across distributions - you may even want to hook the time on execution to get the exact hash if required.JPG in PDF
You could expect JPG to be only images, but in a PDF and some PDF readers (non browsers, such as Evince and Adobe Reader),it can be used as page content just like any other embedded object, that is embedded in a JPEG image.
To store the JPEG data losslessly, store it as grayscale 100%, then either use a picture of single row/column,or repeat the data line 8 times (since JPEG blocks are 8x8), and your data is stored losslessly and referenced by the PDF pages.
Examples of SHA-1 colliding two PDFs via JPEG page data (a grayscale picture rendering colors) as vector page content:
If ⟷ Shattered - the movie
2 SHA-1 colliding PDFs with image data stored as JPG
It's possible to reference the colliding JPG twice: as a page content, losslessly, which also refers to itself as a lossy image to be displayed.Again, the image to be displayed is grayscale, but the page content can render some colors via PDF operators.
The top of the image shows the page content repeated 8 times.
Examples of SHA-1 colliding two PDFs via JPEG used as page data and picture to be displayed:
Skulls & Crossbones ⟷ Golden Axe
2 SHA-1 collidings PDF with JPG used as image and page content
Uncommon strategies
Collisions are usually about two valid files of the same type.
MultiColls: multiple collisions chain
Nothing prevents to chain several collision blocks, and have more than two contents with the same hash value.An example of that are Hashquines - that shows their own MD5 value. The PoCGTFO 14 file contains 609 FastColl collisions, to do that through two file types in the same file.
Validity
A different strategy would be to kill the file type to bypass scanning as a corrupted file.Just overwriting the magic signature will be enough. Appending both files (as valid or invalid) with a format that doesn't need to be at offset 0 (archive, like ZIP/RAR/...) would reveal another file type.
This enables polyglot collisions without using a chosen-prefix collision:
- use UniColl to enable or disable a magic signature, for example a PNG:
- append a ZIP archive
While technically both files are a valid ZIP, since most parser return the first file type found and they start scanning at offset 0, they will see a different file type.
Examples:
⟷ invalid
PolyColls: collisions of different file types
It's also possible to have both side of a collision with different types to lower suspicion:
Attack scenario:
- send
holiday.jpg
- get it whitelisted
- send
evil.exe
, which has the same MD5.
In these cases, a chosen-prefix collision is requiredif both file formats need to start at offset 0.
Some examples of polycoll layouts:
PDF/JPG polycoll
PE/PNG polycoll
PE - JPG
Since a PE header is usually smaller than 0x500 bytes, it's a perfect fit for a JPG comment:
- start with DOS/JPG headers
- JPEG-comment jumps over PE Header
- Put the full JPG image
- Put the whole PE specifications
Once again, the collision is instant
Examples: fastcoll.exe ⟷ Marc.jpg
PDF - PE
Merging a PDF with a dummy file with
mutool
is a good generic way to reorder objectsand then get the first two objects discardable (dummy page and content),which is a perfect fit for a hosting stream
object of unknown length as 1 0
,and its length referenced further (after collision blocks) in the second object.The only problem is that
mutool
will always inline the length - and remove the length reference,so it has to be re-inserted in the PDF instead of the value,but most reference 2 0 R
will be smaller than hardcoded lengths.Thankfully this can be fixed without altering any object offset,so no need to patch the XREF.Here's a script to, for example, instantly collide a PDF viewer (Sumatra is lightweight and standalone) and a PDF document:
Examples: Poster.pdf ⟷ Sumatra.exe
a PDF viewer showing a PDF (itself showing a PDF) with the same MD5
PDF - PNG
Similarly, it's possible to collide for example arbitrary PDF and PNG files with no restriction on either side. This is instant, re-usable and generic.
Examples: Hello.pdf ⟷ 1x1.png
PileUps (multi-collision)
Cryptographic collisions are not limited to two files!
As demonstrated in the Nostradamus experiment in 2008,chaining collisions makes it possible to collide more than two files.
The first collisions can be identical or chosen-prefix, the next ones have to be chosen-prefix.
You can call them multi-collisions, I prefer pileups - it's shorter :)
PE - PNG - MP4 - PDF
Combining all previously acquired knowledge,I used 3 chosen-prefix collisions to craft 4 different prefixes for different file types:document (PDF), video (MP4), executable (PE) and image (PNG).
diagram of a PE/PNG/MP4/PDF pileup
This script is generic and instant:
Examples: commodore.pdf ⟷ diagram.png ⟷ kidmo.mp4 ⟷ sumatra18.exe
Since you may only distribute a single fileand it's impossible to guess the other prefix values from it,a solution is to embed all prefixes of the collision in JavaScript codeand insert it in your PoCs,turning your files into HTML polyglots to easily share the related colliding files.
The issue 19 of 'PoC or GTFO' is such a pileup and polyglot,combining a 80-page document generated with PDFLaTeX, a PDF viewer for Windows,a PNG diagram and a short 'collision' MP4 video by KidMoGraphwith an HTML payload to generate the other files from the PDF release(and a ZIP archive too):
Use cases
Better discard MD5 altogether, because file introspection is just too time-consuming and too risky!
Gotta collide 'em all!
Another use of instant, re-usable and generic collisions would be to hide any file of a given type - say PNG - behind dummy files (or the same file every time) - which is actually just by concatenating it to the same prefix after stripping the signature - you could even do that at library level!
From a strict parsing perspective,all your files will show the same content,and the evil images would be revealed as a file with the same MD5 as previously collected.
Let's take two files:
⟷
and collide them with the same PNG.
They now show the same dummy image, and they're absolutely identical until the 2nd image at file level!
⟷
Their evil payload is hidden behind a file with the same MD5 respectively.
Incriminating files
Another use case for collisions is to hide something incriminating inside something innocent,but desirable: if the only thing to collect evidence is comparing weak hashes,then you can't deny that you don't have the other file (showing incriminating content but hiding innocent content).
Softwares typically focus on (quick) parsing, not on detailed file analysis.
an image showing different previews under different tabs of EnCase Forensic
Failures
Not all formats can have generic prefixes that can be re-used:if some kind of data holder can't be inserted between the magic signatureand the standard headers that are critical and specific to each file,then generic collisions are not possible.
Of course, one might still turn the old files into a new one,and even use code to branch out to two different payloads,but it's more like porting payloads than colliding file structure.
ELF
The ELF header is required at offset 0 and contains critical information such as 32b/64b,endianness and ABI right from the beginning,so it's impossible to have a universal prefix then collision blocksbefore critical parameters that are specific to the original file.
Mach-O
Mach-O don't even start with the same magic for 32b (
feedface
) and 64b (feedfacf
).Soon after, there is the number and size of commands (such as segment definition, symtab, version,...).Like ELF, re-usable collisions are not possible.
Java Class
Right from the start magic are located the versions (which can be troublesome)but the constant pool count which is quite specific to each file,so no universal collisions for all files.
However, many files still have a common version and we can pad the shortest constant pool to the longuest count.First, insert a UTF8 literal to align information,then declare another one with its length abused by a UniColl (the length is stored on 16 bytes as big endian).
However this will require code manipulation since all pool indexes will be shifted.
Instant MD5 re-usable collisions of Java Class should be possible, but require code analysis and modification.
TAR
TL;DR No re-usable collision for TAR files, no other strategy than chosen-prefix.
Tape Archives are a sequence of concatenated header and file contents, all aligned to 512 bytes.
There's no central structure to the whole file. So no global header or comment of any kind to abuse.
A trick would be to start a dummy file of variable length, but the length is always at the same offset, which is not compatible with UniColl, which means only chosen-prefix collisions is useful here.
ZIP
TL;DR There's no generic re-usable collision for ZIP.It should be possible to collide two files in 2h.core (36 times faster than chosen-prefix)
ZIP archives are a sandwich of 3 layers (at least).First comes the files' content (sequence of
Local File Header
structures, one per archived file or directory),then some index (again, a sequence of Central Directory
),then a single structure that points to this index (End Of Central Directory
).The order of these layers can't be moved around.Some parser only need the file content's structure, but that's not a correct way to parse and it can be abused.
Because of this required order, there's no generic prefix that could help for any collision.
non generic approach
Another approach could be to just merge both archives, with their merged layers, and using UniColl - but with N=2, which introduces a difference on the 4th byte - to kill the magic signature of the
End of Central Directory
.This means one could collide two arbitrary ZIP with a single UniColl and 24 bytes of set prefix.
A typical End of Central Directory, which is 22 bytes if the comment is empty:
If we use this as prefix (padd the prefix to 16 bits) for UniColl and
N=2
, the difference is on the 4th byte, killing the magic .P .K 05 06
by changing it predictably to .P .K 05 86
This is not generic at all, but much faster than chosen-prefix collision:
A problem is that some parsers still parse ZIP files upside-down even if they should be parsed bottom-up:a way to make sure that both files are properly parsed is to chain two UniColl blocks,to enable/disable each
End of Central Directory
.To prevent ZIP parsers from complaining about unused space,one can abuse
Extra Fields
,file comments in Central Directory
and archive comments in End of Central Directory
.Example: here is an assembly source that describes the structure of a dual ZIP,that can host two different archive files.
After two unicoll computations, it gives the two colliding files:collision1.zip ⟷ collision2.zip
Exploitations summary
Format | Generic? | FastColl | UniColl | Shattered | HashClash |
---|---|---|---|---|---|
Y | x | x | |||
JPG | Y (1) | x | x (2) | x | |
PNG | Y/N (3) | x | x | ||
MP4 | Y (4) | x | x (5) | x | |
PE | Y | x | |||
GIF | N | x | x | ||
ZIP | N | x (6) | x | ||
ELF | N | x | |||
TAR | N | x | |||
Mach-O | N | x | |||
Class | N | x |
- JPG: has some limitations on data that can be improved to some extend by manipulating scans encoding.
- PDF w/ JPG is the initial implementation of the Shattered attack, but it's just a pure JPG trick in a PDF document.
- PNG: Safari requires PNG to have their
IHDR
chunk in first slot, before any collision block. Doing so prevents a generic prefix, in which case the collision is limited to specific dimensions, color space, BPP and interlacing. - Atom/Box formats like MP4 may work with the same prefix for different subformats. Some subformats like JPEG2000 or HEIF require extra grooming, but the exploit strategy is the same - it's just that the collision is not possible between sub-formats, only with a pair of prefix for a specific sub-format.
- Atom/Box is Shattered-compatible when using 64bit lengths.
- For better compatibility, ZIP needs two UniColl for a complete archive, and this collisions depend on both files contents.
Test files
Here are free (copyright-free, PII-free) test colliding pairs.
Papers:
- 2004
- MD5 To Be Considered Harmful Someday - Dan Kaminsky
- Practical Attacks on Digital Signatures Using MD5 Message Digest - Ondredj Mikle
- 2005:
- A Note on Practical Value of Single Hash Collisions for Special File Formats - Max Gebhardt, Georg Illies, Werner Schindler
- 2014:
- Malicious Hashing: Eve’s Variant of SHA-1 - Ange Albertini, Jean-Philippe Aumasson, Maria Eichlseder, Florian Mendel, Martin Schläffer
- 2017:
- The first collision for full SHA-1 - Marc Stevens, Elie Bursztein, Pierre Karpman, Ange Albertini, Yarik Markov
- Postscript that shows its own MD5 by Gregor 'Greg' Kopf
- A PDF That Shows Its Own MD5 by Mako
- This GIF shows its own MD5! by Kristoffer 'spq' Janke
- This PDF is an NES ROM that prints its own MD5 hash! by Evan Sultanik, Evan Teran
- 2018:
- Easy SHA-1 Colliding PDFs with PDFLaTeX. by Ange Albertini
Presentations:
- 2017 Exploiting Hash Collisions at BlackAlps:
- 2019 KILL MD5 at Pass the Salt:
Workshop:
- 2019 CollTris
All this was possible thanks to Marc Stevens,not only for his cryptographic contributions, but also for his permanent help and suggestions!
Thanks also to Philippe Teuwen for his extensive feedback for file formats in general.
Thanks to Rafał Hirsz for his permanent help on JavaScript.
Kill MD5!
Unless you actively check for malformations or collisions blocks in files, don't use MD5!
It's not a cryptographic hash, it's a toy function!