Problem

I recovered as much data as I could. Can you recover the flag?

Attachment: A data blob

Solution

To start with, file tells us nothing about the file, so we pop it into a hex editor and we can see familiar things:

  1. ...snip...
  2. 0000230: 8114 0407 1715 1501 820b 7461 626c 6564 ..........tabled
  3. 0000240: 6174 6164 6174 6105 4352 4541 5445 2054 atadata.CREATE T
  4. 0000250: 4142 4c45 2022 6461 7461 2220 280a 0960 ABLE "data" (..`
  5. 0000260: 4944 6009 494e 5445 4745 5220 4e4f 5420 ID`.INTEGER NOT
  6. 0000270: 4e55 4c4c 2050 5249 4d41 5259 204b 4559 NULL PRIMARY KEY
  7. 0000280: 2041 5554 4f49 4e43 5245 4d45 4e54 2055 AUTOINCREMENT U
  8. 0000290: 4e49 5155 452c 0a09 6044 6174 6160 0942 NIQUE,..`Data`.B
  9. ...snip...

So, it seems it’s a SQLite database, but the header magic (section 1.2 here) is missing, let’s look closely.

A proper file:

  1. 0000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300 SQLite format 3.
  2. 0000010: 0400 0101 0040 2020 0000 000b 0000 000b .....@ ........
  3. 0000020: 0000 0000 0000 0000 0000 0002 0000 0004 ................

Our file:

  1. 0000000: 2033 0004 0001 0100 4020 2000 0000 0b00 3......@ .....
  2. 0000010: 0000 0b00 0000 0000 0000 0000 0000 0200 ................
  3. 0000020: 0000 0400 0000 0000 0000 0000 0000 0100 ................

Seems that “SQLite format " is missing, we add it and get a working database file!
Now let’s check the contents:

  1. sqlite> .schema
  2. CREATE TABLE `category` (
  3. `ID` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
  4. `Cat` TEXT NOT NULL
  5. );
  6. CREATE TABLE "data" (
  7. `ID` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
  8. `Data` BLOB NOT NULL,
  9. `Cat` INTEGER NOT NULL
  10. );
  11. sqlite> SELECT * FROM category;
  12. 1|CHRM
  13. 2|IDAT
  14. 3|ICCP
  15. 4|IHDR
  16. 5|TEXT
  17. 6|TIME
  18. 7|PLTE
  19. 8|TRNS
  20. sqlite> SELECT ID, length(hex(Data))/2, Cat FROM data;
  21. 1|265|2
  22. 2|265|2
  23. 3|265|2
  24. 4|265|2
  25. 5|265|2
  26. 6|768|7
  27. 7|13|4
  28. 8|265|2
  29. 9|265|2
  30. 10|255|2
  31. 11|265|2
  32. 12|2|8
  33. 13|265|2
  34. 14|265|2

So it looks like we have a make-your-own-PNG kit, categorised chunks stored as BLOBs. After looking around more, we could say the following:

  • We have one IHDR chunk
  • We have one PLTE chunk
  • We have one tRNS chunk
  • We have 11 IDAT chunks

We’re in luck, that’s just enough to make a PNG!

I have to admit, this challenge, took me a long time to figure out, mostly because I had no idea what order the IDAT chunks should be, maybe there is some way to determine that based on their header flags, but I could not have get it working, so in the end, a good old try and error saved the day.

Before the final code, let’s stop a second and talk about the PNG format, because it’s a pretty neat one.
A PNG file consists of parts called chunks, each of which has to be stored like so:

  • Length: A four-byte unsigned integer, specifies the size of the Data field
  • Type: Four byte ASCII sequence
  • Data: Whatever it needs to be, depends on the Type
  • CRC: A four byte integer, calculated from Type and Data fields using the crc32 hash function

With that information the code should be easy to understand:

  1. import sqlite3
  2. import struct
  3. import zlib
  4. import struct
  5. db = sqlite3.connect('data2')
  6. def get_chunk(typ, data):
  7. ret = struct.pack('>l', len(data))
  8. ret = ret + typ
  9. ret = ret + data
  10. ret = ret + struct.pack('>l', zlib.crc32(typ + data))
  11. return ret
  12. def fetch_one(cat):
  13. q = db.execute('SELECT Data from data WHERE cat=?', [cat])
  14. r = q.fetchone()
  15. return ''.join([str(x) for x in r[0]])
  16. def fetch_idat(i):
  17. q = db.execute('SELECT Data from data WHERE ID=?', [i])
  18. r = q.fetchone()
  19. return ''.join([str(x) for x in r[0]])
  20. png = open('flag.png', 'wb')
  21. png.write('\x89\x50\x4E\x47\x0D\x0A\x1A\x0A') # PNG magic
  22. png.write(get_chunk('IHDR', fetch_one(4)))
  23. png.write(get_chunk('PLTE', fetch_one(7)))
  24. png.write(get_chunk('tRNS', fetch_one(8)))
  25. dat = [4, 11, 2, 1, 14, 3, 9, 8, 5, 13, 10]
  26. for d in dat:
  27. png.write(get_chunk('IDAT', fetch_idat(d)))
  28. png.write(get_chunk('IEND', ''))
  29. png.close()

This produces a 600x600 PNG file with the flag mirrored (because what kind of forensic challenge would it be if there was no image mirroring?).

Flag: SharifCTF{A3FBFC944D5CA155B0C04C97823986B6}