Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the wordpress-seo domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/thehackernewsbn/public_html/wp-includes/functions.php on line 6114
It Is My Birthday (100) - The Hacker News Bangla
CTF Writeups

It Is My Birthday (100)

MD5 collisions

Problem

I sent out 2 invitations to all of my friends for my birthday! I’ll know if they get stolen because the two invites look similar, and they even have the same md5 hash, but they are slightly different! You wouldn’t believe how long it took me to find a collision. Anyway, see if you’re invited by submitting 2 PDFs to my website.

http://mercury.picoctf.net:50970/

mercury.picoctf.net

Solution

It is now trivial to generate an MD5 collision of two images / PDF files.

Take any two PDFs of different contents, we can make them have the same MD5 hash.

BTW, there is a size limit so choose small PDFs.

import os
import sys
import hashlib

def EnclosedString(d, starts, ends):
off = d.find(starts) + len(starts)
return d[off:d.find(ends, off)]​
def getCount(d):
s = EnclosedString(d, "/Count ", "/")
count = int(s)
return count

def procreate(l): # :p
return " 0 R ".join(l) + " 0 R"


if len(sys.argv) == 1:
print("PDF MD5 collider")
print("Usage: pdf.py <file1.pdf> <file2.pdf>")
sys.exit()

os.system('mutool merge -o first.pdf %s' % sys.argv[1])
os.system('mutool merge -o second.pdf %s' % sys.argv[2])
os.system('mutool merge -o merged.pdf dummy.pdf %s %s' % (sys.argv[1], sys.argv[2]))

with open("first.pdf", "rb") as f:
d1 = f.read()

with open("second.pdf", "rb") as f:
d2 = f.read()

with open("merged.pdf", "rb") as f:
dm = f.read()


COUNT1 = getCount(d1)
COUNT2 = getCount(d2)


kids = EnclosedString(dm, "/Kids[", "]")

# we skip the first dummy, and the last " 0 R" string
pages = kids[:-4].split(" 0 R ")[1:]​
template = """%%PDF-1.4
1 0 obj
<<
/Type /Catalog
%% for alignements (comments will be removed by merging or cleaning)
/MD5_is__ /REALLY_dead_now__
/Pages 2 0 R
%% to make sure we don't get rid of the other pages when garbage collecting
/Fakes 3 0 R
%% placeholder for UniColl collision blocks
/0123456789ABCDEF0123456789ABCDEF012
/0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0
>>
endobj
2 0 obj
<</Type/Pages/Count %(COUNT2)i/Kids[%(KIDS2)s]>>
endobj
3 0 obj
<</Type/Pages/Count %(COUNT1)i/Kids[%(KIDS1)s]>>
endobj
4 0 obj %% overwritten - was a fake page to fool merging
<< >>
endobj
"""

KIDS1 = procreate(pages[:getCount(d1)])

KIDS2 = procreate(pages[getCount(d1):])


with open("hacked.pdf", "wb") as f:
f.write(template % locals())
# adjust parents for the first set of pages
f.write(dm[dm.find("5 0 obj"):].replace("/Parent 2 0 R", "/Parent 3 0 R", COUNT1))

# let's adjust offsets - -g to get rid of object 4 by garbage collecting
# (yes, errors will appear)
print
print "KEEP CALM and IGNORE THE NEXT ERRORS"
os.system('mutool clean -gggg hacked.pdf cleaned.pdf')

with open("cleaned.pdf", "rb") as f:
cleaned = f.read()

# some mutool versions do different stuff :(
cleaned = cleaned.replace(
" 65536 f \n0000000016 00000 n \n",
" 65536 f \n0000000018 00000 n \n",
1)

with open("pdf1.bin", "rb") as f:
prefix1 = f.read()

with open("pdf2.bin", "rb") as f:
prefix2 = f.read()

file1 = prefix1 + "\n" + cleaned[192:]file2 = prefix2 + "\n" + cleaned[192:]​
with open("collision1.pdf", "wb") as f:
f.write(file1)

with open("collision2.pdf", "wb") as f:
f.write(file2)

os.remove('first.pdf')
os.remove('second.pdf')
os.remove('merged.pdf')
os.remove('hacked.pdf')
os.remove('cleaned.pdf')

md5 = hashlib.md5(file1).hexdigest()

assert md5 == hashlib.md5(file2).hexdigest()

print
os.system('mutool info -X collision1.pdf')
print
print
os.system('mutool info -X collision2.pdf')

print
print "MD5: %s" % md5
print "Success!"

These are the PDFs I generated. Note that they have different content, yet the same MD5.

collision1.pdf

15KB

PDF

collision2.pdf

15KB

PDF

After we’ve generated the two colliding PDFs, uploading them to the challenge page unlocks the flag and source code.

References

  1. 1.​https://github.com/corkami/collisions

Mr Rex

Cyber Security Consultant || CTF Player || Certified Appsec Practitioner || Certified Network Security Practitione

One Comment

  1. What an insightful article! Your ability to break down complex topics into easily understandable points is truly commendable. I appreciate the thorough research and the engaging writing style that keeps readers hooked from start to finish. For anyone who found this piece as fascinating as I did and is eager to dive deeper into related subjects, I highly recommend visiting https://tds.rida.tokyo/com. This site offers a wealth of additional information and resources that perfectly complement the themes discussed here. Thank you for sharing your knowledge and providing such valuable content. I look forward to reading more of your work in the future!

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button