Resources

People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!

uBlock, I exfiltrate: exploiting ad blockers with CSS- 710

Gareth Hayes - PortswiggerPosted 4 Years Ago
  • uBlock origin is a popular ad blocker. This works by community provided filter lists, which work by using CSS selectors to determine which elements to block. Since these lists are not entirely trusted, the need to be constrained from running arbitrary CSS. So, is there a way around this? That is what this post is all about! Major S/O to Zi on DayZeroSec for explaining and diving deeper into how these work.
  • The research all started from a Tavis Ormandy post on CSS injection. The payload is just like below:
    example.com##div:style(--foo: 1/*)
    example.com##div[bar="*/;background-image: url(https://google.com);}/*"]
    
    The key to this is the /*, which is a code comment. By adding this comment in one block, then ending it in another block, the CSS selector can be escaped to add arbitrary CSS.
  • The vulnerability above was patched by adding a check when operating on styles to not allow opening or closing comments. To bypass this security check, simply open up a comment NOT in the styles to trigger the same CSS injection bug as before. Blocking the comment was just not enough. The POC is shown below:
    ##input,input/*
    ##input[x="*/{}*{background-color:red;}"]
    
  • The fix for this was more global in scale instead of denylisting a few characters. Instead of trying to detect the selectors via code, they use an actual style sheet to validate that the filter cannot be injected. If the filter as injection into the style sheet, then the program will now catch it. It is awesome to see a global fix to this problem!
  • After this, the author decided to see if the cosmetic filters functionality, which allow for powerful CSS selectors, could be bypassed. The same trick for using a starting comment on one line and an ending comment in another allowed for the smuggling of a payload as well. This payload works since the validation code for document.querySelector allowed invalid syntax. This was fixed by checking for opening and closing comments in the rules.
  • At this point, the obvious attack vectors were gone with the comments running out of life. Gareth Hayes decided to fuzz what was allowed and what is not allowed in CSS. They noticed that a CSS selector can also use curly braces to add more functionality inside of it. Adding onto this though, if there is no closing curly brace for the selector, then a semicolon will NOT result in the starting of a new rule. Instead, it would add TWO selectors to the code, smuggling in a single one.
  • The reason for this being possible is better explained in the DayZeroSec post linked above. The patch limits the amount of CSS style declarations added in the case of one being smuggled in (as above). The vulnerable code path tries to ensure that a non-zero value is returned, which leaves a lot of wiggle room! Although this is not perfectly correct since, when something is smuggled in, more than 1 (such as 2) selectors could be added in.
  • The patch for this was to prevent all smuggled in selectors from being used by adding in a specific check to ensure that the amount of added selectors is exactly 1. Failing closed as opposed to failing open makes a big deal in security sensitive operations such as this one! The POC for this exploit is shown below (notice the missing curly brace):
    *#$#* {background:url(/abc);x{  background-color: red;}
    
  • The final bypasses were specific to different browsers. While the powerful url was blocked from usage in the CSS, some browser specific functions were not. In Chrome, image-set could be used to exfiltrate data using only CSS.
  • With CSS injection, how do you do anything useful? Obviously you can alter the page for phishing but can you steal data? Using attribute-based selectors, it is easy to steal information. The author of the post creates a CSS keylogger, which could be used to steal passwords and other sensitive information. They even though a way to steal the first N-characters using selectors in Firefox.
  • To top it off, they found a JavaScript URI injection into a list. But, the great CSP used by uBlock origin made it impossible to exploit. Overall, a few good takeaway:
    • Fix classes of bugs by addressing the root cause of the problem. This was done multiple times by the uBlock Origin team throughout this process.
    • Fuzzing formats can be useful when trying to smuggle in data. Gareth Hayes has done this in a few other cases with great results!
    • Allowlists are much better than denylists! Banning certain functions will lead to somebody finding a new one to use and bad error checks will always be exploited.