Story of a weird CSRF bug
Heyyy Everyoneeee,
A couple of months ago I found an upload endpoint which was vulnerable to csrf ,but when I started with creating the poc for it . I realized that it’s not going to be that easy as it looks.
It was a Hackerone program, I asked them for disclosure permission but didn’t get any response, so I will be referring to the target as redacted.com
Some details about the application, it was a bidding site where users were required to upload verification documents like (driving licence, passport, etc) to access the bidding section of the site.
Looking more into this in the verification document upload endpoint, I noticed that it can be submitted only once, which means once you have uploaded the document you can’t make any changes to it afterwards . It is then manually verified by the staff.
This was request made when a user uploads a verification document:
This rings a bell right?
There is no csrf tokenin this request, it means it might be vulnerable to csrf. I quickly created a csrf poc for it using the fetch method.
When I opened this html file in my browser , the response of the request was 403 forbidden.
Later I realized it’s because of the Origin check . In the request screenshot you can see the Origin header value is set to https://www.redacted.com, suppose if my csrf poc url was hosted on the domain attacker.com.
Eg. https://www.attacker.com/csrf.html then the Origin header value would have been https://www.attacker.com when the fetch request was made.
As https://www.attacker.com doesn’t matches with https://www.redacted.com ,the Origin check failed and that’s why the server returned a 403 forbidden error.
Then I tried some variations like redacted.commxyz, redacted.com.xyz ,xyz.redacted.com in the Origin header, but none of them worked. Sometimes the developer makes some mistake in the regex, like they forgot to escape the dot character ,etc . In those cases it can be bypassed by using such variations.
Then I completely removed the Origin header from the request and forward the request and noticed that it worked 200 ok response, the file was successfully uploaded.
If there is no Origin header in the request the application doesn’t validates it, this is similar to the scenario when there is a Referer check in place so you used the meta tag with the content attribute set to no-referrer
So I started searching if there’s any way which would allow me to make a request without the Origin header being sent in the request. I found this tweet
Mr_7h3xc4 on Twitter: "1. Base64 encode your csrf form2. Use data uri:data:text/html;base64, 3 . set this as src attribute for iframe on your server.4. Visit this link. Request will be sent without referer header. / Twitter"
1. Base64 encode your csrf form2. Use data uri:data:text/html;base64, 3 . set this as src attribute for iframe on your server.4. Visit this link. Request will be sent without referer header.
When I tried to follow this, it worked. The request was sent without the Origin header. But it was only working when I used the Form method inside an iframe with data protocol, the Origin header would always be sent in case of fetch and other similar related methods.
We overcame one problem which was the Origin header , now there was one more to deal with. As we are stuck with the Form method, we can’t actually make a csrf poc interaction-less .
If you take a look at the vulnerable request again:
Look at the line no 12, there is a filename parameter then after that we have the image data. The problem is that we can’t actually include the filename parameter and the image data on our own in the csrf poc.
Because they are included only when file upload input is used. You can read this writeup for more information on this:
Here it is, the file upload CSRF
The current poc would be similar to this:
As you can see in this gif, to exploit this csrf vulnerability we have to tell the victim to click on the upload button, and then choose a file from their system.
Due to the required user interaction this makes it so unrealistic that the victim is going to upload a file as we told them to do so.
At this moment, I remembered about watching a video by Liveoverflow where he was talking about the bugs found in Google Cloud Platform (GCP) during the $100k Hacking Prize competition.
Make sure to watch this video completely before moving on, as Liveoverflow has explained it very well here:
One of the bugs submitted during this event was:
[GCP] The File uploading CSRF in Google Cloud Shell Editor
In this blog, the researcher @obmihail along with the csrf bug details ,has also shared details regarding a bug found in multipart requests parsing which allowed him to upload any files with his own content, which isn’t possible with the html form.
Upon submitting the above form, the below request body will be sent:
-----------------------------37419614939920406503637463242
Content-Disposition: form-data; name="target"
file:///home/userhome/folder
-----------------------------37419614939920406503637463242
Content-Disposition: form-data; name="upload; filename=the_filename; x"
the content of file
-----------------------------37419614939920406503637463242--
Using this multipart requests parsing bug , I could easily overcome the problem which I stated previously as I have now full control over the file contents:
>To exploit this csrf vulnerability we have to tell the victim to click on the upload button, and then choose a file from their system.
I can now perform the csrf attack without any user interaction.
Here is the final CSRF POC:
Focusing more on the name attribute: name=’\”;name=file;filename=d0B5jW1O_400x400.png;x’ , this trick allows me to upload any fake document without depending on the victim to upload any file , an image file gets upload automatically without any user interaction
I submitted the report after verifying that the final poc actually worked and it was marked as duplicate :(
I was invited to that report so I looked into it and found that someone had already reported this issue but he wasn’t able to provide a csrf poc without user interaction, also to overcome the Origin validation he demonstrated a poc which only works in a very lower version of Mozilla Firefox along with that it required the victim to upload a document for the poc to work. His report was closed as Informative as it required too much of user interaction.
But my poc didn’t require any user interaction and also worked in the latest version of Firefox. I explained to the triager how my report is different from the Original report
During that time Firefox 84.0.2 was the latest version. The triager then forwarded the report details to the team
The team acknowledged the issue and found that it is indeed different from the duplicate report, this report was triaged and rewarded as a Medium Severity bug which would allow me to submit fake verification documents on behalf of the user.
As the verification documents could only be submitted once , due to the fake documents the victim’s account will not get verified. He would have to create a new one. The attack surface was only limited to Firefox users and newly created acc which haven’t submitted any verification documents that's why the severity was lowered to Medium.
I learned a lot while creating a poc so this was a win-win situation for me .
I will be publishing another writeup at the end of this year so stay tuned :)
Sya Everyoneeee
Story of a weird CSRF bug was originally published in InfoSec Write-ups on Medium, where people are continuing the conversation by highlighting and responding to this story.