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
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
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:
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:
I only copied:
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:
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
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.
for example, the following gdToken
values were accepted as valid by the server for all account sessions on _glassdoor.com_
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:
Now, if we supply a token like ::9
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:
The flow starts with a POST request to
hackerone.integration-authentication.com/session
{F1976252}
The response contains two tokens: session and csrf.
The frontend takes those values and uses them to send a request to the oauth2 endpoint to generate the authorization link.
{F1976253}
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:
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
. 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:
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
The victim would have no idea of what happened
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
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:
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
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:
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