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!

How does the False Top-up attack break through the defense of the exchange?- 1437

SlowMist    Reference →Posted 1 Year Ago
  • How does a crypto exchange know when you sent it funds? Well, there is an address associated with the exchange. In particular, this is unique per user. A transfer is made to this address, which tells the exchange that you sent funds to them. The exchange needs to see this transfer happen via scanning for blocks. Finally, a specific amount of confirmations happen, validating that no reorg will occur. At this point, your exchange account will be credited with funds.
  • Recently, Kraken feel victim to a false top off attack. This articles goes into several ways that this can be exploited, from their own experience. In the case of CertiK on Kraken, it was pretty simple! Kraken was looking for a deposit made to this address within a successful transaction via logs. However, the attackers sent over the funds in a contract then reverted this function call, only to be handled (and ignored) by an upper level call. All Kraken was looking for was a transfer within the transaction that on a successful call.
  • Normally, I would guess that an event would be emitted for this to work. However, since it was transferring ETH directly, there is no event emitted. So, some weird and custom shenanigans has to be done for this. Apparently, the parsing being done was this didn't consider the case that CeritK tried. According to the Tweet, they tried this as several exchanges but only one of them worked.
  • The first two are simple issues with blockchain confirmations. If a fork happens after only a single confirmation or it simply looks in the mempool, then you're super duper F'ed since the transfer could have already reverted. Certain quirks of systems that are unique to it can cause issues as well.
  • The next set of issues deal with failures. If the transaction reverts, then the deposit shouldn't be counted, because the funds were never sent there in the first place. Type confusion can be an issue as well; getting one event to be processed as another type, such as in Felix's Polygon bug. Missing checks on the emitter of the smart contract are common as well.
  • There is also double spending issues, where you can provide the same transaction twice or something like that. In the case of Bitcoin, where ownership is confusing, it may be possible to make a transfer accessible to an account but YOU actually have control over the funds still.
  • They have a few other mentions of previous issues in other blockchains. The specific case study they reveal is about the TON blockchain integration issue, made by telegram. On the TON blockchain, is a cross contract call fails then it can bounce back. When this happens, a callback is made to the calling contract to fix whatever just happened. In the logs of the message, the field in_msg is used to determine the information. However, out_msgs will be the bounceback if it fails. If you only look at the in_msg and it bounces back, then leads to a false transfer.
  • How do we prevent this? Rigorous transaction matching needs to be done in order for this to be secure. It must be a perfect match and nothing else. Second, checking balances before and after a TX is a good source of truth. Finally, having detection's in place is helpful; this sounds easy, but is non-trivial given that there exist a failure in the system that allowed this to happen in the first place.
  • Overall, it's a good article on tricking off-chain infrastructure, which I really enjoyed. I also found a partial payments issue with integration with XRP wallets that was interesting while researching more into this.

Kraken Exchange Hack Drama- 1436

Nick Percoco - Kraken    Reference →Posted 1 Year Ago
  • Kraken is a cryptocurrency exchange similar to Coinbase. Recently, CertiK found and exploited a vulnerability on Kraken to create arbitrary funds within their account. This is the drama that unfolded from the perspective of Kraken, CertiK and then my personal thoughts on it.
  • While reviewing the Kraken application, Certik failed to spot the different transfer statuses. By abusing this, a partially made deposit was able to receive funds. By doing this, an attacker could effectively print money.
  • After discovering the vulnerability, they exploited the issue multiple times to print the funds into the account. At this point, CertiK wanted to see if it was possible to withdraw the funds. The initial whitehat had withdrawn $4 (and was KYCed) but two other actors had stolen much more, including 3M! They sent some funds through Tornado Cash as well for some reason.
  • After knowing about the bug for 5 days, they finally reported it with very vague details. Even with the vague details, the Kraken team wsa able to find and triage the bug within some new code in 47ish minutes. Why didn't the team give full details? CertiK wanted full impact information of the vulnerability to talk to their business development team. I'm guessing they wanted this information to have ground to arrange juicy bounty.
  • Initially, CertiK didn't come clean out the transactions and funds that were stolen; this was discovered by the Kraken team and the funds were not immediately returned. To me, this felt like straight blackhat hacking... they stole funds and then effectively exhorted the company to try to get a bounty.
  • CertiK tried to defend their actions... they claimed they took out a lot of money and let it sit to see if there was any detection on the Kraken side. To their credit, they were right in the defenses being bad. However, it's NOT their place to test. Given they hacked the company, disclosed secrets to other folks and didn't tell them about the funds, I am curious what sort of legal ramifications this will have.
  • From the perspective of CertiK, I disagree with how much money they stole but understand their perspective. If you find a bug and steal $4, the company may claim that they had protections in place that would make bigger impacts impossible. Since you didn't test it, you can't prove or disprove otherwise, leading to a low bounty. Unfortunately, as a bug bounty hunter, we are at the mercy of the company being upfront and truthful. If not, they will eventually pay the price for crossing too many people.
  • A day after this all happened, CertiK released a Q/A about it. There is no way the CertiK team actually thought that stealing these large sums of money was actually the right thing to do; I think they're just claiming it's okay hoping that other people will go along with it instead of admitting their mistake. What happens if the bells rang at Kraken of a hack before they had a chance to report it? Now, they look like a real hacker taking out 3M. They also claim that Kraken is asking for more back than what was stolen. However, CertiK put funds into Tornado and ChangeNow that are untraceable.
  • Overall, this was a failure all around. CertiK found an impactful bug but A) went too far exploiting it and B) a bad job exploiting it. Being more upfront to Kraken about the bug and exploit transactions, this would have gone better. From the Kraken side, bugs happen and that's how life is. Monitoring is a crucial part of security of a platform, as we can't always predict when stuff will go wrong. However, monitoring for everything isn't always trivial for catching all bugs. Makes me wonder what they had in place and what allowed this to skate by. There is never a dull day in crypto!

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.