Note: This article has been reviewed by Adafruit prior to publication.
This article will highlight the process that lead to me finding a XSS (Cross site scripting) vulnerability on adafruit.com
As usual, I will not only show the vulnerability but also the methodology used and interesting things that happened along the way.
Before attacking any target, you need to make sure you are allowed to do so. That involves both your local laws and the target’s stance on vulnerability research.
I am not a lawyer so I will not comment on the first part. When it comes to checking your targets vulnerability disclosure policy a good place to look at is https://<TARGET>/.well-known/security.txt
If we check out https://www.adafruit.com/.well-known/security.txt we find that they have a bug bounty program. The disclosure guidelines and in-scope domains for Adafruit.com can be found at: https://www.adafruit.com/reportingsecurityissues. All reports should be submitted to security@adafruit.com
This policy is very important for us because we now know we can hunt for and what not. Also, we know where and where not to hunt. If in doubt, we can always come back here and check whether the action we intend to perform or report we intend to submit is covered by the responsible disclosure policy
Before starting to test you should try and avoid the risk of compromising some other users data. For this purpose you should set up two accounts. One that you intend to compromise and one that you perform the testing with.
Also, we want all of our traffic to be routed through a MITM proxy like burp. Once that is done, we can start hunting.
Before starting any automated testing it first makes sense to get a feel for your target. The vulnerabilities usually hide at the roads least traveled.
When creating your account you should realize this happens on accounts.adafruit.com. Thinking back to our scope, this target is fair game.
When looking at the top of the webshop we see a bunch of links.
They point to
So without any subdomain enumeration or spidering, we already found a bunch of attack surface.
From using the site a bit the most promising features in the shop seem to be:
Each of these features has a bunch of ways to be attacked.
Thinking of shipping addresses, we might be able to smuggle something into our address that might get used insecurely somewhere else on the site. I created a bunch of addresses that contained HTML and Java Script. Some of the used characters get sanitized by the backend. I am unsure, whether they get sanitized before being saved in the database or when they are being used. I tried to bypass the sanitization by injecting null bytes, random data, double encoding and other tricks without any success.
Furthermore, I verified if my testing account was able to modify, delete or view the address of my main account via an IDOR (Insecure Direct Object reference). Whenever I found some place that would access address information in any way I would replay the request with the Address ID of the second account. No endpoint seemed to either reflect user input unsanitized nor did they seem to allow manipulation or data leakage of other users accounts.
The same goes for wishlists. I made sure that private wishlists were unaccessible even after having been made public once. I stuffed payloads at every place possible, using URL parameters and POST bodies.
None of my inputs got reflected insecurely nor was I able to manipulate, delete or access wishlists I was not supposed to. Adding items to the wishlist shows every item has a unique ID. I tried adding every possible item to my wishlist.
This could have resulted in
This resulted in funny wishlist costs like 19.81654E… however an overflow was not able to be achived. I was also unable to find unlisted items, some out of stock items could be added or items with missing descriptions or images… however this is not a vulnerability.
When accessing the other subdomains like forums.adafruit.com I realized that they are using oauth. Oauth is notoriously easy to mess up when implementing, resulting in possible account takeovers. I tried to redirect the authentication flow using implicit grant types as described by Portswigger without any success.
I was hoping that I could upload a profile picture to maybe find a upload filter bypass that could lead to RCE. When checking out account.adafruit.com however I found that I can only choose from a set of avatars and select a background color.
Looking at the POST generated when changing the profile, I found that the color value gets reflected in the CSS. This could potentially lead to a CSS injection vulnerability. Theoretically one could implement a CSS Keylogger.
I changed the parameter from user[favorite_color]=#471010
to user[favorite_color]=#471010;position: absolute
.
I already got my hopes up when I saw that the size of my avatar had changed.
However, when checking the backend response, I saw that even though the backend returned a HTTP 200 (OK), error messages were embedded in the HTML.
The injection worked only in the frontend due to the HTTP 200, however the injected color did not get saved in the backend.
The errors are
I tried finding valid shorter colors and short CSS for a POC and confusing the parsing in the backend. Both without any success.
When checking out the wishlist I found that I can have adafruit generate me a quote.
Using this feature results in a POST to quote.php
POST /quote.php HTTP/2
Host: www.adafruit.com
[...]
coupon=&wid=599267&shipto=1433403&billto=1433403&shipping_text=DHL+Express+%281+x+0.17lbs%29+%28Express+Worldwide%29&shipping_cost=%2443.77
The shipto and billto, as anywhere else that I found, were IDOR proof. However some parameters seemed odd.
shipping_text
- The text displayed on the final quote, possible attack vector for reflected DOM / XSS vulnerabilitiesshipping_cost
- The shipping cost, this should definitely not be at the users controlWhen modifying these values, I found that indeed the modified values get reflected in the DOM.
Turns out those values get passed into Javascript. I tried injecting HTML or JavaScript using many tricks, but I was unable to turn this into an XSS. I was aware that this is out of scope but thought Adafruit might still be interested in knowing about this.
When checking the endpoint again after the 22nd of November I found that the POST to quote.php now contains a quote_verification
parameter that looks like some sort of hash.
When modifying the shipping_text
or shipping_cost
the request fails.
While trying to find less tested functionalities, I started digging into the order history logic of adafriut. I was hoping there might be some IDOR here or a place that reflects user input unsanitized for example in PDFs…
When checking out the order details I found that the address is reflected. Also, there is a “view printable invoice” function that also reflects the address.
In order to inject payloads into the order history logic I actually needed to order things. The cheapest product I was able to find is the 5$ gift card because it has no shipping.
Upon buying a gift card I found that I could input “special Instructions”. These will be seen by the recipient of the gift card. As has become second nature, I injected some HTML in there. Pretty much throwing things at the wall to see what sticks.
I was hoping this would be put unsanitized into a Mail sent to the recipient or displayed on using the gift card on another account. This was not the case.
However when I checked the “Printable invoice”, I found that indeed my injected HTML was part of the invoice and got rendered.
From looking at the URL I found that the endpoint reflecting my user input in the DOM is PHP.
https://www.adafruit.com/invoice.php?order_id=XXXXXXXXXX
I was hoping to get an RCE(remote code execution) PoC by injecting something like <? php phpinfo(); ?>
.
I have spent a lot of time researching how to bypass the Cloudflare WAF. One way is to expose the actual IP address of the webshop behind cloudflare. I had to use OSINT methods as I had not yet found an SSRF (Server Side Request Forgery) or another way to leak the real IP of the webshop. All IPs I was able to find this way did not host Adafruits Webshop.
So I had to find a way to confuse Cloudflares WAF. After lots of research and not working payloads, I finally was able to sneak past an XSS payload.
“><img only src=1 onerror=alert()>
All my reports have been handled very fast.
The tone was friendly and my efforts felt welcome.
10/10 would pentest again 👍
Cheers, Ori