2013
07.23

Summary

The CodeIgniter framework contains a function, xss_clean(), which is intended to filter out potential XSS attacks. From the CodeIgniter documentation:

The XSS filter looks for commonly used techniques to trigger Javascript or other types of code that attempt to hijack cookies or do other malicious things. If anything disallowed is encountered it is rendered safe by converting the data to character entities.

I identified a form of malicious input that would bypass the xss_clean() filtering, allowing for arbitrary JavaScript to be executed. The vulnerability has been patched in CodeIgniter 2.1.4. Any developers relying on xss_clean() as a form of protection should upgrade their application to the latest version of the framework.

This vulnerability has been designated CVE-2013-4891.

Vulnerability Details

The vulnerability is straightforward to understand. The xss_clean() function would only strip attributes from HTML tags that were properly closed. However, browsers which see unclosed tags can choose to parse them as though they were properly formed.

In this case, my proof of concept was:

1
<img src="a" onerror='eval(atob("cHJvbXB0KDEpOw=="))'

The lack of a > at the end meant that the onerror attribute wasn’t stripped by xss_clean(). However, browsers would parse this input as a valid img tag with src and onerror attributes. By using eval and atob, I was able to get arbitrary JavaScript to execute without having to worry about any additional filtering.

Disclosure Timeline

  • March 7th, 2013, 10:24 AM: Initial report to developers via email. Sent proof of concept.
  • March 7th, 2013, 10:27 AM: Reply from developer, investigating report
  • March 18th, 2013, 11:43 AM: Reply from developer, unable to reproduce vulnerability on latest development branch (3.0.x).
  • March 18th, 2013, 12:16 PM: Sent followup to developer, promising to reconfirm proof of concept.
  • March 18th, 2013, 12:50 PM: Email sent to developer confirming that the vulnerability exists in the latest stable release (2.1.3) but not in the development branch.
  • March 18th, 2013, 1:58 PM: Developer replies, looking in to merging fix from development branch into stable.
  • April 13th, 2013: Email to developer asking for status update
  • April 17th, 2013, 11:51 PM: Reply from developer, continuing to work on fix.
  • April 17th, 2013, 11:52 PM: Acknowledged reply from developer.
  • June 26th, 2013: Email to developer, informing them of plans to publish vulnerability details via blog on July 9th.
  • July 5th, 2013, 3:59 PM: Reply from developer. Patch has been applied to stable branch. Requesting confirmation of fix.
  • July 5th, 2013, 5:31 PM: Sent email confirming fix. Request ETA on release of new stable version.
  • July 8th, 2013, 10:13 AM: Reply from developer, planning to release new version within 24 hours.
  • July 8th, 2013, 5:38 PM: Email from developer. CodeIgniter 2.1.4 released publicly.
  • July 8th, 2013, 6:06 PM: Sent email acknowledging public release. Updated plans to publish blog post so that developers would have time to update.
  • July 23rd, 2013: Blog post published.

Note

The xss_clean() function employs multiple blacklists and as such is not a safe solution for preventing XSS. Researchers have discovered bypasses for the filtering in the past [1], [2], [3] and there is no guarantee that more bypasses won’t be discovered in the future.

2013
07.04

Summary

The validator module for Node.js contains functionality meant to filter potential XSS attacks (a filter called xss). In looking at the implementation I discovered several bypasses for this filtering including prior work by other researchers. Although the bypasses I reported have been patched, since my discovery at least one new bypass has been disclosed and remains unpatched.

In general, because the function’s filtering is blacklist-based it is likely that other bypasses will be discovered in the future. Accordingly I would urge Node.js developers not to use the xss filtering function of this package.

What were the bypasses?

All of these were tested in the latest version of Firefox:

Improper parsing of nested tags:

1
<s <onmouseover="alert(1)"> <s onmouseover="alert(1)">This is a test</s>

Incomplete filtering of javascript: URIs:

1
<a href="javascriptJ a V a S c R iPt::alert(1)" "<s>">test</a>

Improper handling of arrays passed in (this affects JavaScript because the toString method will automatically join the elements of the array together):

1
["<s ", " onmouseover='alert(1)'>", "foobar</s>"]

UI Redressing:

1
2
3
4
<div style="z-index: 9999999; background-color: green; width: 100%; height: 100%">
<h1>You have won</h1>Please click the link and enter your login details:
<a href="http://example.com/">http://good.com</a>
</div>

Nesting of forbidden strings:

1
<scrRedirecRedirect 302t 302ipt type="text/javascript">prompt(1);</scrRedirecRedirect 302t 302ipt>

I also noticed the following comment at the top of the file:

1
2
//This module is adapted from the CodeIgniter framework
//The license is available at http://codeigniter.com/

Searching around online, I came across CodeIgniter <= 2.1.1 xss_clean() Cross Site Scripting filter bypass, written by @kkotowicz, which provided me with more working bypasses.

Workaround

If you are a developer currently using the xss filter function from the validator package, you should consider replacing it with the escape filter function from the same package. This function replaces all instances of angle brackets (<, >), ampersands, and quotation marks, so no HTML tags will be processed.

Note that XSS prevention is not as simple as just escaping those characters. For more information, check out the OWASP XSS Prevention Cheat Sheet.

Disclosure Timeline

  • March 6th, 2013: Initial report to module maintainer via email. Sent proofs of concept, etc.
  • March 10th, 2013: Developer replies, creates public issue on GitHub to track the fix. Promises to resolve the issue “ASAP.”
  • April 17th, 2013, 10:56 PM: Sent pull request which resolves issues.
  • April 17th, 2013, 11:27 PM: Pull request merged by maintainer.
  • April 18th, 2013, 8:31 AM: New version (1.1.0) released containing patch.
  • May 6th, 2013: Additional bypass disclosed publicly by taku0.
2013
07.02

Summary

A signed Java applet distributed with a number of products by F5 Networks contained a vulnerability which allowed for arbitrary code execution on a local machine under specific circumstances.

The vulnerability has been assigned CVE-2013-0150 and F5 has put together its own security advisory which lists affected applications and provides information on which versions contained patched applets. In the future F5 will ask Oracle to blacklist the applets so that they can not be used in a malicious attack.

Applet Overview

The applet in question is intended to be used to download and execute software from F5 on a user’s machine. The general workflow is as follows:

  1. Applet downloads executable (provided as a parameter to the applet named source)
  2. Applet verifies signature for executable (the signature is also provided as a parameter)
  3. If the signature is valid, the executable is run by the applet.

The vulnerability that I discovered was not in the signature scheme; instead, it involved a bug in how the applet saved the newly downloaded executable.

Vulnerability Details

The applet allowed you to provide a local filename for the downloaded file via a parameter named filename. The file was intended to be saved in the system temporary directory (%TEMP%, /tmp, etc). However, the filename parameter was vulnerable to a directory traversal attack. As a result, the file saved anywhere on the filesystem and would overwrite existing file contents.

For an attack on Windows machines, I realized that the executable could be saved in C:\Users\USERNAME\AppData\Roaming\Microsoft\Windows\Programs\Startup, which would allow the application to be executed when the machine next booted up. The %TEMP% directory by default is C:\Users\USERNAME\AppData\Local\Temp, so my malicious parameter looked like ../../../Roaming/Microsoft/Windows/Start Menu/Programs/Startup/app.exe.

The only interaction required by a user was to allow the signed Java applet to run. That interaction might not even have been necessary if the user had chosen to trust applets signed by F5 Networks in the past.

Disclosure Timeline

  • April 25th, 2013, 8:12 PM: Initial report to security-reporting@f5.com including incorrect proof of concept URLs (lead to 404).
  • April 26th, 2013, 3:34 PM: Reply from F5. Report has been replicated internally, requested correct proof of concept URLs.
  • April 26th, 2013, 4:09 PM: Sent corrected proof of concept URLs to F5
  • April 26th, 2013, 4:22 PM: Reply from F5, asking for confirmation of vulnerability assessment.
  • April 26th, 2013, 4:35 PM: Sent email confirming vulnerability assessment
  • April 26th, 2013, 4:38 PM: Acknowledgement from F5 that the relevant teams were analyzing the report.
  • April 26th, 2013, 5:40 PM: Email from F5 asking about disclosure timeline and finding attribution details
  • April 26th, 2013, 5:50 PM: Replied to F5. Provided additional information about Oracle’s Java blacklist.
  • April 26th, 2013, 5:56 PM: Followup from F5 confirming receipt of additional information.
  • May 2nd, 2013: Followup from F5. Issue is being triaged and fixes are being scheduled.
  • May 21st, 2013: Followup from F5. Fixes are being built and tested, updated products will be released soon.
  • June 7th, 2013: Followup from F5. Patches in place for many products, working on public announcement.
  • June 21st, 2013: Followup from F5. Fixes continue to be developed and released.
  • July 2nd, 2013, 11:26 AM: Followup from F5. Public announcement has been made.
  • July 2nd, 2013 6:30 PM: Blog post published

Conclusion

Signed Java applets are still a source of major insecurity beyond the many vulnerabilities discovered in the JVM itself. Developers working with signed Java applets should be especially cautions

This vulnerability was reported to F5 Networks in accordance with their policies on reporting suspected vulnerabilities. I want to thank them for their thoroughness and commend them for their smooth disclosure and remediation process.

2013
06.26

Summary

It was possible for a third party website to retrieve information about the currently logged in user via the Bitbucket API. This data included email addresses, SSH public keys, and repository information.

This vulnerability was first reported and patched in mid-2012. It was rediscovered and patched again in mid-2013.

What is JSONP?

From http://en.wikipedia.org/wiki/JSONP:

JSONP or “JSON with padding” is a communication technique used in JavaScript. It provides a method to request data from a server in a different domain, something prohibited by typical web browsers because of the same origin policy.

I gave a presentation late last year which has a bit more information on JSONP and more examples of sites that had JSONP-related vulnerabilities in the past (including the original instance of this vulnerability).

Vulnerability Details: Mid-2012

Back in 2012, Bitbucket’s REST API was accessible at https://api.bitbucket.org/1.0/. The API at that URL was meant to use OAuth tokens for authentication. In my investigation, I discovered that the API could also be accessed via a second URL, https://bitbucket.org/!api/1.0/. The difference between the two was that the second URL could authenticate requests using the browser’s cookies. Both forms of the REST API supported JSONP via a callback parameter passed in the URL.

As a result, it was possible for a third-party website to add a <script> tag that would pull in user information via JSONP API queries. To demonstrate the issue I built a proof of concept page which would make simple queries to the API. Those queries could uncover user information, email addressed, and SSH keys linked to an account. More complicated queries (ie: those involving repositories that the currently logged in user could access) were possible as well although not built in to the proof of concept.

I reported the issue to security@atlassian.com and it was patched approximately six hours later.

Vulnerability Details: Mid-2013

In mid-2013 I retested the site and discovered, to my surprise, that the vulnerability had reappeared. Looking at the API documentation, the main endpoint had been moved to https://bitbucket.org/api/1.0. Now no matter which URL I used I was able to extract user information via JSONP. In addition, although the API endpoints I used to fetch email and SSH public key information were no longer documented, my existing proof of concept worked without any modifications. I reported the issue to Atlassian again and it was patched again.

Disclosure Timeline

  • May 30th, 2012 11:08 AM: Initial email with proof of concept sent to security@atlassian.com.
  • May 30th, 2012 6:48 PM: Response from Atlassian, fix has been rolled out. Ask for confirmation that issue has been resolved.
  • May 30th, 2012 6:58 PM: Reply to Atlassian confirming fix.
  • June 11th, 2013 11:11 AM: Email sent to security@atlassian.com informing them that vulnerability has reappeared. Included previous correspondence, link to proof of concept.
  • June 11th, 2013 8:36 PM: Reply from Atlassian, claim that vulnerability was patched last year and that patch was still in place.
  • June 11th, 2013 8:56 PM: Email sent to Atlassian, disputing assertion that vulnerability is patched. Screenshots and additional information related to proof of concept attached.
  • June 11th, 2013 9:09 PM: Reply from Atlassian, confirming that vulnerability has reappeared.
  • Prior to June 26th, 2013: Testing confirms that vulnerability is patched in production.
  • June 26th, 2013: Published blog post
2013
06.23

Summary

The JS-YAML module for Node.js contained a code execution vulnerability prior to version 2.0.5. The maintainers of JS-YAML have patched this vulnerability and, beginning in version 2.1.0, have provided a safeLoad method for parsing YAML. Developers that use this module should make sure they have upgraded and should strongly consider porting their code to use the new safeLoad method.

Update: The code execution vulnerability has been assigned CVE-2013-4660.

Vulnerability Details

The module allowed code execution due to a custom data-type that it defined and parsed called !!js/function. The way it would parse the data was to create a new Function object in JavaScript based on the input, which is equivalent to calling eval on the input:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function resolveJavascriptFunction(object /*, explicit*/) {
  /*jslint evil:true*/
  var func;

  try {
    func = new Function('return ' + object);
    return func();
  } catch (error) {
    return NIL;
  }
}

That meant the code snippet below, when run, would execute code instead of simply defining a function:

1
2
3
4
5
6
7
8
var yaml = require('js-yaml');

x = "test: !!js/function > \n  \
function f() { \n    \
console.log(1); \n  \
}();"

yaml.load(x);

The vulnerability was patched under 24 hours after I initially contacted the module’s maintainers: the esprima package is now used to parse the input and validate it before execution.[1]

Why switch to safeLoad

Starting in version 2.1.0, the JS-YAML module provides a safeLoad function for use by developers. It is recommended by the maintainers as the default, in place of the load function demonstrated above.

The main difference between the two functions is what types are parsed:

  • safeLoad recognizes a “DEFAULT_SAFE_SCHEMA“, which parses “all supported YAML types, without unsafe ones (!!js/undefined, !!js/regexp and !!js/function)”
  • load recognizes a “DEFAULT_FULL_SCHEMA“, which parses all tags (including those which safeLoad does not).

A proof of concept from the module’s README demonstrates why load can be dangerous:

1
2
3
4
var untrusted_code = '"toString": !<tag:yaml.org,2002:js/function> "function (){very_evil_thing();}"';

// I'm just converting that string, what could possibly go wrong?
require('js-yaml').load(untrusted_code) + ''

By overriding functions like toString with arbitrary code or providing other unexpected input, an attacker can perform malicious actions as part of a Node application. Switching to safeLoad mitigates this risk because “dangerous” types are not parsed.

Conclusion

Developers using the JS-YAML module should make sure that they are working with an up-to-date version and should strongly consider porting their code to use safeLoad in place of load, especially when accepting YAML derived from user input.

If you’re interested in the security of Node modules, be sure to check out the Node Security project.

2013
06.04

Summary

At the beginning of May I found and reported a security vulnerability in Coinbase, a Bitcoin exchange. The vulnerability I reported allowed an attacker to steal the CSRF token for the currently logged in user, which meant that an attacker could bypass the site’s CSRF protection. Coinbase acknowledged the report and fixed the underlying vulnerability.

Details

Several URLs on coinbase.com used to return valid JavaScript which was intended to populate modal dialog boxes on the site. One example of such a URL was https://coinbase.com/api_key. The content of those dialog boxes was generated dynamically based on the currently logged in user.

If the dialog box contained a form, the complete HTML for that form was included in the response. That HTML included a hidden form field (authenticity_token) which is the CSRF token used by the site for that user. Because these URLs returned valid JavaScript via a GET request, a third-party website (whether malicious or benign) could extract the HTML for the dialog box, including the CSRF token.

For example, here is what I saw when I browsed to https://coinbase.com/api_key:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
$('#api_key_modal').html("<div class=\"modal-header\">\n
  <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-hidden=\"true\">&times;<\/button>\n
  <h4>Show API Key<\/h4>\n
<\/div>\n
<div class=\"modal-body\">\n
  <form accept-charset=\"UTF-8\" action=\"/api_key/authenticate\" class=\"form-inline\" data-remote=\"true\" method=\"post\">
<div style=\"margin:0;padding:0;display:inline\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" />
<input name=\"authenticity_token\" type=\"hidden\" value=\"qGeCEw6MkTPZt2dLZJ43ftXmR36gYYNXWRwywKcsxE4=\" />
<\/div>\n
    \n
    \n
    <p>Please type your password to continue.<\/p>\n
    <p>\n
      <input class=\"focus\" id=\"password\" name=\"password\" placeholder=\"password\" type=\"password\" />\n
      <a href=\"#\" class=\"btn btn-primary submit-form\" data-disable-with=\"Verify\">Verify<\/a>\n
    <\/p>\n
<\/form><\/div>\n
<div class=\"modal-footer\">\n
  <a href=\"#\" class=\"btn\" data-dismiss=\"modal\">Close<\/a>\n
<\/div>");
$('#api_key_modal').modal('show');

Other sensitive information could also be disclosed under limited circumstances. For example, if the user had authenticated to see their API key the dialog box would not have prompted for authentication, as it does above, but instead would return the account’s API key.

The solution here was simple: the server-side scripts should not have been returning valid JavaScript as part of a GET request. The Coinbase developers have modified all of their dialog endpoints to require a POST request, which prevents them from being loaded via a <script> tag.

Disclosure Timeline

  • May 1st, 3:18 PM: Submitted basic details of vulnerability to whitehat@coinbase.com.
  • May 1st, 9:56 PM: Sent clarification of initial report, including additional information about CSRF leakage.
  • May 6th, 5:03 PM: Sent followup email, asking for feedback on the initial report.
  • May 6th, 5:04 PM: Provided additional vulnerable URL, as well as clarification of the initial report.
  • May 7th, 12:24 AM: Received reply: “Thank you for the disclosure, we appreciate it. This issue was already reported by others and we are working on a fix.”
  • May 19th, 6:44 PM: Informed Coinbase of intentions to publish this blog post on May 28th.
  • May 28th, 9:00 AM: Discovered that developers had attempted to patch the website but that they had forgotten to remove an endpoint.
  • May 28th, 9:29 AM: Emailed Coinbase to inform them of the incomplete patch. Informed them that publication of the blog post would be delayed by a week so they would have time to fix the vulnerability.
  • June 4th, 12:00 AM: Confirmed that final endpoint had been patched.
  • June 4th, 12:45 AM: Public disclosure via blog post
2013
04.16

Summary

I reported 3 vulnerabilities to the Yandex bug bounty program shortly after it launched in September. It has taken almost 6 months to resolve the issues completely, thanks to some mis-communication and a lack of followup on my part. Overall my experience was not too negative, but I would not recommend the program to other researchers.

Vulnerability #1: Reflected XSS in pass.yandex.ru

This was a fairly straightforward reflected XSS vulnerability. Browsing to https://pass.yandex.ru/login?retpath=http://yandex.ru/%3C/script%3E%3Cscript%3Ealert%281%29%3C/script%3E in Firefox would lead to a JavaScript alert box being displayed in the browser.

According to the Yandex security team my report was a duplicate: this makes sense given the simplicity of the vulnerability. This vulnerability has been patched.

Vulnerability #2: CSRF in passport.yandex.com

Again, a fairly straightforward vulnerability. The form for updating personal information (first/last name, city, etc) was not protected against a CSRF attack.

When I initially reported this vulnerability to Yandex I never received a reply. I failed to follow up until 3 months later; at that point, I was told that only my reflected XSS report was showing up in their system. I used their feedback form to submit all three reports separately; in retrospect I should have sent it via email.

Regardless, when I followed up I noted that the CSRF issue had been fixed but that the solution was vulnerable to clickjacking. That report was declared a duplicate (again, makes sense given the simplicity of the vulnerability and the amount of time that had passed) and I was told of plans to add an X-Frame-Options header to protect against clickjacking. So far the header has not been added and the vulnerability still exists.

Vulnerability #3: Email header splitting via mail.yandex.com

This vulnerability was the most complicated of the three that I submitted. When sending an email on mail.yandex.com, a POST request is made to https://mail.yandex.com/neo2/handlers/do-send-json.jsx. The from_name parameter sent as part of that request did not escape newline characters, which allowed an attacker to forge the From: header in an email and include arbitrary additional headers in the email.

I again failed to follow up until 3 months after my initial report. At that point I was told:

We cannot verify the security issue you have reported because it is unexploitable.
Every person can do it (add various headers) with tool like Telnet by connecting SMTP server.
If you can describe how it can be exploitable by malicious man don’t hesitate to contact us.

I pointed out that most other web-based email clients (eg: Gmail, etc) do not allow you to specify an arbitrary From: header. Subsequently I received a followup email telling me that I was eligible for a reward of $160.

Disclosure Timeline

  • September 23rd, 2012: Reports sent to Yandex via submission form.
  • September 25th, 2012: Reply from Yandex indicating that the reflected XSS report was a duplicate.
  • January 16th, 2013, 12:26 AM: Retested previously reported vulnerabilities. Sent followup email to Yandex security team, asking for updates on previous reports / providing feedback based on retest).
  • January 16th, 2013, 6:00 AM: Received reply from Yandex indicating that only one of my initial three reports was received. Responded to the other two “new” reports (details outlined previously above).
  • January 16th, 2013, 10:03 AM: Replied with feedback regarding email header splitting vulnerability.
  • January 16th, 2013, 11:04 AM: Reply from Yandex acknowledging that email header splitting vulnerability was eligible for a reward.
  • February 16th, 2013: Sent followup to Yandex, asking for update on reward and on patches for vulnerabilities.
  • March 1st, 2013: Sent email to Yandex informing them of plans to publish this post on April 16th, 90 days after the followup on January 16th.
  • March 8th, 2013: Reply from Yandex acknowledging the reward and asking me to inform them again once the post is published.
  • March 12th, 2013: Reply from Yandex, bounty money has been transferred to my account.
  • April 16th, 2013: Published this blog post.
2013
04.16

Summary

I found a reflected XSS vulnerability in JW Player 5. The developers at LongTail Video have released JW Player 6, which is not vulnerable to this issue. They do not plan to update JW Player 5 to mitigate this vulnerability; I would encourage people to discontinue using JW Player 5 or manually patch/build their own version as a result.

How does it work?

I was aware of a previous vulnerability in JW Player 5 which the developers had attempted to patch. The original issue is documented in the LongTail Video Trac as Ticket 1626; the problem was that the playerReady parameter to the SWF accepted an arbitrary string and used it as a callback function to ExternalInterface.call. That behavior is roughly equivalent to calling eval on the value of the playerReady parameter and allowed for XSS and other badness. There are two relevant diffs, which made changes as follows:

  • 2164: Instead of an arbitrary callback string, the callback string may only contain alpha-numeric characters and periods.
  • 2165: Instead of a callback string containing only alpha-numeric characters and periods, the callback string can have any characters except for curly braces and parentheses.

So, the challenge for exploitation was to find a way to execute JavaScript without parentheses or braces. Turns out this was really easy[1], [2]: I used the following code (the comment was needed because of the context in which the code was being executed):

1
document.location=window.name+'//'+

I could then embed arbitrary JavaScript into window.name by convincing users to click on links with ‘target’ attributes I specified.

Proof of Concept

A proof of concept is available at sandboxing.me. The JavaScript executes in the context of the player’s domain, not in the context of sandboxing.me.

How can it be patched

  1. Discontinue use of JW Player 5. Upgrade to JW Player 6 or use another player.
  2. If you would like to build your own SWF, you can apply the changes to src/com/longtailvideo/jwplayer/player/JavascriptAPI.as from revision 2267. This disables the use of dynamic callbacks for playerReady altogether. If you rely on a custom playerReady callback, you will need to modify your JavaScript; you will need to declare a function on your page called playerReady which in turn calls your custom function.

Disclosure Timeline

  • December 28th, 2012, 11:10 AM: Contacted LongTail Video via their support form with details of the vulnerability.
  • December 28th, 2012, 11:39 AM: Received reply asking me to upgrade to JW Player 6.
  • December 28th, 2012, 5:52 PM: Sent reply asking for clarification about the end-of-life status of JW Player 5 and whether a patch would be released.
  • December 29th, 2012: Received response from developer: “There may be a 5.11 version out to address some of these issues, but since they are already fixed in V6, 5.10 might be the last version of the V5 player.”
  • April 16th, 2013: Published this blog post
2013
03.15

Summary

Because of a security vulnerability in fitbit.com, malicious third-party websites could have extracted personal information from logged in users.

What is Fitbit

According to http://www.fitbit.com/company:

Fitbit is dedicated to helping people lead healthier, more active lives. We take a common sense approach to fitness, and believe that the key is to make it easier for consumers to be more active, eat smarter, and get enough sleep — in short, that small changes to your daily routine can add up to big results. To that end, we aim to create innovative, inspiring products and online services that harness the power of new technologies to make people more aware of their everyday activities and motivate them to do more.

The company produces a number of devices which all sync data to fitbit.com. This data can then be viewed, visualized, shared, etc. I was given a device as a holiday present and I’ve enjoyed it a lot.

What is JSONP?

From http://en.wikipedia.org/wiki/JSONP:

JSONP or “JSON with padding” is a communication technique used in JavaScript. It provides a method to request data from a server in a different domain, something prohibited by typical web browsers because of the same origin policy.

I gave a presentation late last year which has a bit more information on JSONP and more examples of sites that had JSONP-related vulnerabilities in the past.

In this case, fitbit.com did not use JSONP by default but did allow attackers to request data in a JSONP format. An attacker who requested data in this way could read out data that is normally restricted to users on the Fitbit website, including data on calories burned and steps taken.

Examples

Here are some examples of URLs and the responses that were returned by fitbit.com using my account (I own a Fitbit device):

http://www.fitbit.com/graph/getGraphData?apiFormat=json&type=minutesActive&dateFrom=2012-12-23&dateTo=2012-12-23&callback=a

 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
a({
    "result": {
        "chart": {
            "columnWidth": 70,
            "datasets": [
                [{
                        "color": "a3e0e0",
                        "description": "0.08 hours lightly active on Sun, Dec 23",
                        "label": "",
                        "name": "1356220800000",
                        "url": "/activities/2012/12/23",
                        "value": 0.08
                    }
                ],
                [{
                        "color": "ffbb33",
                        "description": "0.12 hours fairly active on Sun, Dec 23",
                        "label": "",
                        "name": "1356220800000",
                        "url": "/activities/2012/12/23",
                        "value": 0.12
                    }
                ],
                [{
                        "color": "ff3366",
                        "description": "0 hours very active on Sun, Dec 23",
                        "label": "",
                        "name": "1356220800000",
                        "url": "/activities/2012/12/23",
                        "value": 0
                    }
                ]
            ],
            "expiresOn": 1361055371096,
            "gridCategoryAlpha": 0,
            "gridValueAlpha": 100,
            "isCanvasBorderOn": true,
            "isDescEnabled": true,
            "isLinkEnabled": true,
            "isSkipZeros": false,
            "maxAge": 300,
            "plotAreaAlpha": 100,
            "plotAreaBorderAlpha": 0,
            "plotAreaColor": "\#ffffff",
            "plotAreaMarginsBottom": 40,
            "plotAreaMarginsLeft": 40,
            "plotAreaMarginsRight": 20,
            "plotAreaMarginsTop": 5,
            "precision": 2,
            "rotateNames": 0,
            "timePeriod": "INTRADAY",
            "valuesCategoryEnabled": true,
            "valuesCategoryFrequency": 6,
            "valuesValueEnabled": true,
            "valuesValueFrequency": 1,
            "valuesValueIntegersOnly": false,
            "yMax": 3.12,
            "yMin": 0
        },
        "success": true
    }
});

http://www.fitbit.com/ajaxapi?callback=a&request={“serviceCalls”:[{“name”:”trackerSettings”,”method”:”getAlarms”},{“name”:”device”,”method”:”getOwnerDevices”}]}

 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
a({
    "ajaxResponse": {
        "result": [
            [],
            [{
                    "id": 111,
                    "type": "GALILEO",
                    "battery": "High",
                    "lastSyncTime": 1361044707000,
                    "productName": "Zip",
                    "settingsUrl": "/settings/device/tracker"
                }
            ]
        ],
        "newResult": {
            "getAlarms": {
                "status": 200,
                "result": []
            },
            "getOwnerDevices": {
                "status": 200,
                "result": [{
                        "id": 111,
                        "type": "GALILEO",
                        "battery": "High",
                        "lastSyncTime": 1361044707000,
                        "productName": "Zip",
                        "settingsUrl": "/settings/device/tracker"
                    }
                ]
            }
        }
    }
});

There may be other pages on the site which disclose similar information or have other forms of security vulnerabilities. This is not the first security or privacy issue related to Fitbit that has been publicly disclosed. [1] [2]

Disclosure Timeline

  • December 23rd, 2012: Initial email with proofs of concept sent to Fitbit support.
  • December 25th, 2012: Reply from Fitbit, details have been sent to engineering department.
  • January 2nd, 2013: Email from recruiter at Fitbit. Was asked to reach out to me because of my report. Asked if I would be interested in a possible software engineering role there.
  • January 2nd, 2013: Replied to recruiter, explained I’m already happily employed.
  • January 2nd, 2013: Email from recruiter acknowledging reply.
  • February 9th, 2013, 1:38 AM: Sent followup to support, asking for update on issue.
  • February 9th, 2013, 4:59 PM: Reply from support. Issues were passed off to engineering, no updates to provide at the current time.
  • February 9th, 2013, 5:15 PM: Sent followup to support, asking for ETA on fixes.
  • February 16th, 2013: Sent email to support. Informed them of intention to write this post and publish it in a little over a week.
  • February 17th, 2013: Reply from support. Issues were passed off to engineering, support will follow up with engineering.
  • February 18th, 2013: Reply from support. Engineering has been contacted.
  • February 21st, 2013, 9:09 AM: Reply from support. Issue is schedule to be patched on March 15th. Told to expect direct contact from engineering.
  • February 21st, 2013, 9:16 AM: Sent email to support. Agreed to hold off publication of post until after March 15th.
  • March 9th, 2013, 2:03 PM: Reply from support asking if engineering had followed up directly.
  • March 9th, 2013, 2:03 PM: Sent email to support indicating that no followup had occurred.
  • March 10th, 2013, 1:51 PM: Reply from support, following up with engineering.
  • March 10th, 2013, 11:31 PM: Reply from support, engineering has released a preliminary fix.
  • March 10th, 2013, 11:43 PM: Sent email to support, confirming that fix appears to mitigate vulnerabilities.
  • March 15th, 2013: Published post, contacted support with link to post.
2013
03.14

Summary

eBay has changed its responsible disclosure policy for security researchers. The older policy asked researchers to “Allow us a reasonable amount of time (at least 30 days from when we receive your disclosure under this process to respond to the issue before disclosing it to others.” The new policy asks researchers to “wait until notified that the vulnerability has been resolved before disclosing it to others.” This new policy means that eBay can ignore vulnerabilities reported by researchers with no repercussions.

Backstory

I just published a post about two security vulnerabilities I found in my.ebay.com. I first reported these vulnerabilities to eBay in May 2012. While I was never told when the vulnerabilities would be patched, eBay’s responsible disclosure policy provided the following guidelines:

  • Share the security issue with us before making it public on message boards, mailing lists, and other forums.
  • Allow us a reasonable amount of time (at least 30 days from when we receive your disclosure under this process to respond to the issue before disclosing it to others.
  • Provide full details of the security issue, including Proof-of-Concept URL and the details of the system where the tests were conducted.

You can see these guidelines in their original form thanks to the Internet Archive.

In February, I contacted eBay to try and figure out why the vulnerabilities had not been patched. I also informed them that, per their responsible disclosure policy, I was planning to publish the details of the vulnerabilities I had discovered. They sent me the following email back:

Hi Neal,

We thank you for your work and your dedication to keeping the eBay community secure. We ask that you wait until we’ve fixed the reported vulnerability before disclosure, as per our Guidelines for Responsible Disclosure. Although this issue has not been fixed, it is in the queue and scheduled to be fixed. As a token of appreciation, we would like to send you some eBay branded gifts and acknowledge you on our Security Researcher’s Acknowledgment page once this vulnerability is fixed. Please send us your address if you’d like us to send you a few eBay goodies.

Thanks,

eBay Security Research

At that point, their responsible disclosure policy was identical to the one I presented above. I sent back the following reply:

Hey,

Just to be clear, your guidelines for responsible disclosure say the following:

Allow us a reasonable amount of time (at least 30 days from when we receive your disclosure under this process to respond to the issue before disclosing it to others.”

This report was originally filed and acknowledged on May 11th, 2012. Over 30 days later (June 12th, 2012) I asked for an update and was told there was no update. It is now February 11th, 2013, many months beyond 30 days. Under your policy I am fully within my rights to disclose the issue to others without fear of reprisal.

As a courtesy I will hold off publishing details of this issue until March 14th, which is 31 days from today.

Best,
 Neal

And received a response from eBay:

Hi Neal,

We appreciate your patience in this process and your continuous support in keeping eBay secure. We will keep you updated on the progress.

Thanks,
eBay Security Research

I received no further emails from eBay.

Shifting Policy

After publishing my post today, I returned to the responsible disclosure page and found the following new set of guidelines:

  • Share the security issue with us before making it public on message boards, mailing lists, and other forums.
  • We request that you wait until notified that the vulnerability has been resolved before disclosing it to others. We take the security of our customers very seriously, however some vulnerabilities take longer than others to resolve. There are several teams involved in working on these vulnerabilities depending on which site has the vulnerability and what function is being exploited.
  • Provide full details of the security issue, including Proof-of-Concept URL and the details of the system where the tests were conducted.

The second guideline, which changes the disclosure timeline for a vulnerability from “at least 30 days” to “whenever eBay decides to patch an issue,” is the only difference. The guideline is designed to sound reasonable and responsible; it reminds us that eBay takes security seriously, it points out the complexities of coordinating patches between many teams, etc. But while some security vulnerabilities may take a long time to be patched, many are fairly straightforward and should not need to take more than a month from disclosure to patch.

In addition, it is the job of eBay’s security team to manage the expectations of outside security researchers. While researchers may have been entitled to release details of a vulnerability after 30 days under the old policy, in general many researchers are happy to hold off on public disclosure if the company is working actively to address the issue. Personally, I have reported plenty of vulnerabilities where I have had to wait months for a patch to be released. As long as the vendor provides some assurances that a patch is in the works, I am more than happy to withhold public disclosure.

Conclusion

The fact that the guideline was (silently) changed between when my email was sent and when my post was published speaks volumes about security at eBay. It tells me that someone at eBay saw my email and decided it was easier to change the responsible disclosure policy and send me “eBay goodies” than it was to follow up on my report. If that’s how eBay feels about responsible disclosure, I won’t be searching for any vulnerabilities on their sites in the future.