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!

Unicode Normalization Forms- 1435

Ken Whistler    Reference →Posted 1 Year Ago
  • Going from unicode to ASCII is required for some applications. How is this done though? This is a document that explains how this is done in the many different forms.
  • Canonical equivalence is when two characters represent the same abstract character but use different codepoints to get there. Compatibility equivalence is similar to this. However, the main difference is that it represents things that have visual differences but mean the same thing. For instance, stylistic changes like italics, linebreaking differences and others. The second case is weirder to normalize and great care must be put into it.
  • The specification talks about 4 different mechanisms for normalizing; they are all just combinations from above where C is for composition and K stands for compatibility.
    • NFD: Canonical Decomposition only.
    • NFC: Canonical Decomposition, followed by Canonical Composition.
    • NFKD: Compatibility Decomposition only.
    • NFKC: Compatibility Decomposition, followed by Canonical Composition
  • In the case of A with a dot on top (212B), the Canonical decomposition route will turn this into two separate characters: an A and a dot. In the Compatibility decomposition, this character remains the same. What's interesting though, is the alternative version of the A with a dot on top of C5. NFD will return the same thing decomposition as before but NFC will return C5.
  • The longer options just do BOTH steps. For example, 2^5 power is made up of the number 2 (0x32) and a raised 5 (2075). Both NFC and NFD decompose this into these two characters. However, NFKD and NFKC turn this into the characters 2 and 5 instead of the raised 5 character. Within NFKC and NFKD, the formatting distinctions are removed from the character.
  • This process is still somewhat confusing and non-obvious to me. None the less, it's interesting to keep this in mind when looking for bugs.

Cross-Site Scripting via Web Cache Poisoning and WAF bypass- 1434

Lyubomir Tsirkov    Reference →Posted 1 Year Ago
  • The author was playing around with some functionality on a website. While doing this, they realized that part of the URL was being copied into an open graph tag. Given that open graph tags are likely handled at a different level, they wanted to ensure that client side issues were being handled.
  • They added a double quote to the URL to make it https://xss.2n.wtf/points/test" to no avail; the double quote was URL encoded. Now, they tried adding this via Burp Suite. To their amazement, the double quote had reflected back and had escaped the context of the URL! What was the issue? The URL encoding!
  • Seems like a non-issue at this point; it may not be exploitable. They decided to check out how the caching via Cloudflare worked. Cloudflare treated both the double quote (") and the URL encoded double quote (%22) as the same thing! According to here, you are supposed to URL encode double quotes, which is why the browser does it.
  • This creates a cache poisoning situation. Make a request with the raw double quote, get a user to click on the particular request WITH the double quote and you have stored XSS. At this point, they had to bypass the WAF as well. This was done via using slashes instead of spaces over and over again. The cache was per region as well, requiring a ton of requests to ensure a victim was hit by it.
  • Unfortunately, there were some limitations on this. First, everything had to be lowercased and colons couldn't be used. Second, a limit on the amount of characters in a URL was placed. So, they needed to find a way to run arbitrary code. They imported code from a different location (at some URL) in order to do this. Because of the colon restriction, they added the domain to make the request to in a different URL parameter.
  • Great find! This was probably missed for years because of the weird way to trigger it. XSS comes in weird spots; gotta look out for those reflected inputs!

Sei Blockchain DoS and Funds Stealing Bugs- 1433

usmannkhan    Reference →Posted 1 Year Ago
  • Sei Network is a layer 1 blockchain built on Cosmos with some pretty crazy functionality. In particular, there are two execution runtimes for smart contracts in both EVM and CosmWasm. The EVM can run in parallel as well.
  • In Cosmos, there is code that can run at the beginning or the end of a block, besides the regular transactions. If a Go panic occurs during either the begin or end blocker code, then it results in a chain halt. In Cosmos, some funds may not be spendable. This is because the tokens could be staked at that point. When calling SendCoinsAndWei() on a block specific account, the call used GetBalance() to get the amount of tokens to send. However, this includes the staked tokens, which cannot be transferred!
  • How do we give it some funds that are staked? I didn't think it would be possible to force somebody to have unspendable tokens. However, using vesting accounts, it is! The author created a vesting account for the block specific address via vesting create-vesting-account in the Sei CLI. The balance calculation still sees these tokens but they cannot be spent, leading to a crash.
  • To fix the issue, GetBalance() was simply changed to SpendableCoins. On top of that, they removed the panic just to be extra safe. The next bug is much more dangerous but I definitely enjoyed this first bug! While browsing the previous issues patch, they stumbled across the balance integration code for the EVM and Cosmos balances.
  • They noticed that calling AddBalance() with a negative number would actually add the other users token to your balance. Armed with this knowledge, they decided to hunt for use cases with user controllable data on calls to Transfer(). They found three integration points: EVM opcodes, top level EVM message and CosmWasm integration.
  • The integration between the two chains was the only unique aspect of this. By itself, the EVM module and CosmWasm module are known to be very safe. The internal message of a cross-environment call from CosmWasm to EVM was interesting now. The message MsgEVMTransaction allows for an amount to be set on it, which is a signed number! They quickly setup a Golang test to see if the transfer worked as expected... and it did!
  • At this point, all funds are at risk on the chain. All you have to do is make a call to transfer funds to a user and you can steal all of their funds. Neat! To make matters worse, you could steal all of the funds then become a supermajority validator! Since the active validator set is recaculated at the end of each block (instead of a waiting period), this results in an instant compromise. At this point, you would be able to control the stake in a PoS blockchain to create funds out of thin air. Of course, this can be used to attack other chains over IBC as well.
  • The end to end proof of concept is a fairly simple CosmWasm contract written in Rust that has a submessage for the EVM transfer. Pretty neat :) For the first bug, they got 75K. For the second bug, they got 2M.
  • Overall, two awesome bugs that were complex enough to be missed in an audit but were both obvious red flags if somebody took the time to read the code. Amazing finds! An interesting aspect they mention at the beginning of the article was that the issue was slated for release but before it had actually be shipped. This feels like a good sweet spot time to report bugs and still be get paid out.

No Way, PHP Strikes Again- 1432

WatchTower Labs    Reference →Posted 1 Year Ago
  • Orange Tsai (of course) found a vulnerability within PHP. In particular, they found an issue that affects XAMPP (a popular way for admins to deploy PHP apps) to get RCE.
  • The original post did not have many details about the bug itself. So, the author of this post started to dig into the issue. They noticed that it only affected CGI mode of PHP. When doing this, it parses HTTP requests and passes them to a PHP script to do processing. For instance, http://host/cgi.php?foo=bar turns into php.exe cgi.php foo=bar.
  • Naturally, you would think this is an obvious command injection vector for calling php.exe. In fact, CVE-2012-1823 was exactly this bug! The original bug was an issue in a URL lacks the = character between parameters, the data wasn't being properly encoded.
  • What's the new bug? Of course, it's Unicode! When PHP does processing on the input parameters, it will do best fit mapping of characters. This is crazy, as mapping unicode to ASCII feels impossible. In the CGI code for preventing command injection, it will escape hyphens to prevent extra parameters from being specified. However, a soft hyphen (0xAD) does not get escaped but PHP will convert this to a regular hyphen! Hence, we can add in our own parameters.
  • The actual exploit is hilariously simple. Make an HTTP request with %AD (soft hyphen) to smuggle in a dash. Now, we can control arbitrary parameters to PHP. Using the -d flag to control PHP configurations. Setting auto_prepend_file=php://payload alongside the allow_url_include flag to enable PHP URLs allows us to get code execution on the server.
  • The normalization code from unicode to ASCII was weird to me. I've read reports about this for years but have never seen anything actually do it. Apparently, unicode has a standard for normalization, where this is a Python implementation as well.
  • The bug is exploitable on a few different locales, which is fascinating. To me, there are two main takeaways. First, old bugs are good to know; many of the attack vectors from them are there. With new progression in security techniques, more bugs in these areas may fall out. Second, Unicode to ASCII things exist. Overall, great bug!

An Exploration of JSON Interoperability Vulnerabilities - 1431

Jake Miller    Reference →Posted 1 Year Ago
  • HTTP Smuggling is just a difference in understanding of HTML parsers. What about differences in parsers for other things? The Bishop Fox article dives into differences between JSON implementations. There are several different versions of JSON specs and some spots where the specification isn't tight. Why does this matter? Differences parsers can lead to the same data having different meanings!
  • The first issue mentioned is inconsistent duplicate key precedence, such as {"test": 1, "test": 2} - one can either take the first or the second in this case. In the given example of a validate-proxy pattern, where one app validates then ships off the original data untouched, this can be problem. For instance, the validation code would see 1 but the actual processing data would see 2.
  • Next, is key collision via various means. Character truncation is the next class. There are various means that keys or data are altered from the original by removing bytes. The example shows invalid unicode, extra double quote and stray backslash. In the validate-proxy pattern, this can be used to get JSON processed on one side but used on another.
  • In the same group of classes is comment truncation. Apparently some JSON implementations support comments! Additionally, there are quoteless strings in JSON. Using a quoteless string with a comment, one parser would see it as a comment while the other would just see it as a string. This seems fairly infeasible, as I've never seen any language support either of these features. Apparently, going from Golangs GoJay to Javas library would do this.
  • It's not just deserialization that can have issues - it's also serialization! For instance, the object obj = {"test": 1, "test": 2}. When using obj["test"], it would return 1. But, when doing obj.toString() it would return 2. Sometimes, reserializing doesn't provide as much protection as you'd expect.
  • Floats and numbers have their issues too. When numbers are above the max or below the minimum, some parsers do different things. Some large numbers are converted to infinity symbol. Others have very serious rounding. The Go JSON parser will take the large number to change it to 0.
  • The final group are just random things they found along the way. Some JSON parsers allow for random garbage at the end, which allows for things like CSRF attacks. They found a few segfaults in JSON parsers as well. They even took some time to look at binary JSON parsers, which all had fairly similar issues.
  • The article finishes off with a list of issues in implementations from various languages. Ranging from Python to Go to Rust. I personally found this extremely useful, as it helps isolate specific bugs in parsers to exploit. Golang has an interesting doc on their native JSON parsers weirdness even.
  • How do we mitigate these types of issues? In the parsers themselves, generate errors instead of handling weird things and follow the spec to a tee. For people building applications, it's a little bit harder. Validating and repackaging input instead of validating then passing on the original is a good way to be secure on this. Additionally, the more rigorous the checks, the better. Ensure there aren't extra keys, duplicate keys, invalid characters, etc. on the data. This will help prevent most issues.
  • Overall, an awesome article into the world of JSON parsers. With how complicated software stacks are today, many of these combinations of parsers are common, leading to major issues. At the end, they reference an article from 2016 called Parsing JSON is a Minefield that goes over the spec and some additional functionality that they call extensions. From the list of parsers tested, most of the language built parsers (Rust serde, Golang JSON, etc.) didn't fall victim to any of these issues. I also found this Github that has graphs and such on all JSON parsers compatibility.

PQShield plugs timing leak- 1430

Antoon Purnal - PQShield    Reference →Posted 1 Year Ago
  • Constant time cryptography is a method of preventing side channel leaks via timing differences on various operations. Without this, it'd be possible to learn about the cryptographic operations that are occurring.
  • Compilers transform source code into machine code. The machine code is where the timing matters but we typically don't read this. While auditing ML-KEM, they noticed that Clang undid some of the constant time measuring in the name of optimization.
  • The authors posted a demo of exploiting the timing differences in the key encapsulation to extract the key. Overall, this brings up an interesting issue - where do our compilers fail? Most code-level things are correct but it cannot understand what sections can't be optimized.

Hacking Millions of Modems (and Investigating Who Hacked My Modem)- 1429

Sam Curry    Reference →Posted 1 Year Ago
  • While testing, Sam Curry noticed that his modem was compromised. All requests being sent through it were being forwarded to a different domain. Years later, he decided to investigate the Cox ISP for security issues and get to the bottom of it.
  • The first struggle was being hit with a login page. Without any business account they reviewed some JavaScript to find a bunch of routes. There were over 100 at /api/cbma alone. Curous about this device related functionality, they figured it was behind a reverse proxy on another host. How do you tell though? Using a route to /api/cbma/example (invalid route) would return a 500 while other routes wouldn't. Seeing diffs between routes is a clear indicator of a reverse proxy.
  • After reverse engineering some headers for authentication (many of which were just hardcoded), they were able to make some requests as an anonymous user. At this point, they wanted to find the API documentation for extra hidden routes. Since it was using spring, they knew they could look for swagger files at /swagger-ui/index.html.
  • For whatever reason, the page wasn't loading and was caught in an infinite loop, which he found via looking at the network traffic in the browser. First, the routing was going through the HOST of the page instead of the actual URL. After figuring this out, they got 500s because of weird Nginx rules. So, they added a URL encoded slash at the end of the request which didn't hit the specific rule in Nginx but still returned the wanted data. Neat!
  • Based on the Swagger file, they decided to use Burp Intruder to fuzz the endpoints to see which ones required authentication. For whatever reason, they had a perfect 50/50 split. Why is this? For a reason that the author didn't understand, by making multiple requests the authentication wasn't required! So, it was possible to interact with arbitrary endpoints as an authenticated user. Weird!
  • Sam used this to access their own modem at home. Additionally, they could update business accounts to retrieve PII, MAC addresses and other things. There's still more to the craziness though.
  • Any hardware modifications to other devices required a special encrypted value. From reversing the JavaScript, they found a key that was being used. The device pin happened to be encrypted with this; so, this made for some easy testing. Encrypting for the device parameters wasn't as easy though; it required a bunch of information like mac address, account number and other things.
  • Since Sam didn't know how to get an account ID of an arbitrary user, they decided to remove some of the values and provide garbage for the others. Luckily for him, the only necessary parameter was the MAC address! To test this, they updated their modem settings to change the SSID and it worked! They had the ability to change settings of arbitrary modems.
  • Sam was satisfied with his research. He thought this was the likely vulnerability that the hacker in his network had found. So, they reported the bug to the ISP, who swiftly took everything down and patched it. In a twist, Cox said that this functionality was added in 2023, while the exploitation of his device had happened in 2021. Regardless, an awesome post on discovering and exploiting weird functionality with sick recon techniques.

Molding lies into reality || Exploiting CVE-2024-4358- 1428

SinSinology    Reference →Posted 1 Year Ago
  • SinSinology saw an advisory for an RCE bug via deserialization in Telerik, a report management solution. Although it was authenticated, it was interesting to the author of the post. Hence, they decided to dive into the bug and see if they could find an authentication bypass.
  • The product is a powerful solution with processing of many different types of files and creation of charts and other graphics on the server side to present to the user. As a result, the author thought this was prime for a deserialization issue.
  • When performing the deserialization, if a type is unknown then it will attempt to find it based upon passed in XML. In particular, a provided ResourceDictionary can specify the execution path for it. Using the ProcessStartInfo parameter, it's possible to execute cmd on Windows. The author provides much more detail on the path for doing this though.
  • While setting up the software to try to find the vulnerability described above, they discovered an authentication bypass. At start up, the installing user is supposed to call Register to add the administrative user. However, nothing stops another user from calling this, adding a System Admin role. This effectively creates a backdoor user.
  • I found the beginning of this post hard to read with all of the code snippets. If you were trying to understand this specific piece of software well (unlike me who wants to just understand the vulnerability), then it would be super useful though. The author runs a course on dotnet hacking, which I'm sure would be filled with juicy C# knowledge after reading this.
  • The authentication bypass found is fairly common, according to the author. So, something to keep an eye out for. As far as the deserialization... there's a lot of custom handling here for generic types. This is 100% a red flag.

Bypassing Veeam Authentication CVE-2024-29849- 1427

SinSinology    Reference →Posted 1 Year Ago
  • Veeam published a CVSS 9.8 score for a complete authentication bypass vulnerability on their product. The author decided to take the time to understand the issue and write it up. Since they only have information from the CVE, they'll have to do some reverse engineering for this.
  • The author goes down the authentication flow to find some weirdness with SSO. In particular, when using VMWare SSO, a SAML XML document is provided. One of the controlled values is the URL to check against.
  • Since an attacker can provide their own SAML server to check again, it's trivial to bypass the authentication! If you can validate yourself, then you can easily exploit this.
  • The core vulnerability is simple - bad input validation on sensitive values. However, this would have required a fairly deep understanding of SAML and the authentication flow to exploit.
  • The author of this post ended up looking for issues in Veeam themselves after this. When doing JWT authentication on the Recovery Orchestrator, it had a hardcoded JWT. Since this is hardcoded, we can sign our own JWTs.

Zoom Session Takeover - Cookie Tossing Payloads, OAuth Dirty Dancing, Browser Permissions Hijacking, and WAF abuse- 1426

Harel Security    Reference →Posted 1 Year Ago
  • On Zoom, the cookie _zm_csp_script_nonce was used on every single page as part of the CSP script-src field. The CSP was set within an HTML tag that wasn't being escaped. So, it was possible to escape the context of the string to add in our own HTML.
  • The badly reflected data was put into the CSP nonce field. The CSP nonce uses single quotes while the HTML uses double quotes. Additionally, the second nonce will be parsed. So, using a double quote within the first nonce, using a valid nonce after that then putting our HTML within this cookie gives us XSS on the page. Pretty neat!
  • Cookie tossing is a technique that abuses the domain flag on a cookie. By having XSS on any subdomain of Zoom, setting the _zm_csp_script_nonce now leads to XSS on the main page. After hunting on other subdomains, they found a post-based XSS to use with this.
  • How does the chain work to get XSS on the main page? This is how we get XSS on the main Zoom page.
    1. Make a POST request to the vulnerable endpoint on the Zoom subdomain with the XSS payload.
    2. Payload will set the cookie via cookie tossing with the long path trick.
    3. Redirect the page to the Zoom main page.
  • To prove maximum impact, the authors decided to use some OAuth dirty dancing with XSS. Frankly, this is a little out of my league to write here but is interesting none-the-less. By using the XSS, they're able to abuse the Google login flow to get an OAuth token that was never processed.
  • For some stupid reason, the Zoom team rated this as a medium since they hadn't proved persistence - that was a mistake! The Zoom team wanted a password reset to demonstrate persistence. Instead, the authors decided to use the cookie aspect of the XSS to get persistence. As long as the cookie lives, the attacker will make access to their account.
  • Second, they realized that zoom.us has permissions for video and audio. Since the user had already approved this, the attacker could turn this on camera silently then send it to themselves. The aspect of the extra permissions was interesting and not something that I had considered before.
  • The final technique was a denial of service via abusing the WAF. By using the POST based XSS to set any cookie, an attacker could set this with an obvious XSS payload. By doing this, the WAF will reject all requests to Zoom until the cookies are cleared. I had never seen this before so something interesting to see.
  • A crazy string of bugs to cause serious havoc on Zoom. I personally really enjoyed this post with the diagrams and unique takes on exploit development. Great work!