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!

UTF-8 Visualizer- 1455

Sonar Source    Reference →Posted 1 Year Ago
  • UTF-8 is annoying to look at. Am I looking at the characters or the codepoints? So, Sonar source (after some research that would have benefited from this) built the UTF-8 Visualizer. Just adding here for a tool for later. UTF8!

Pwn2Own: WAN-to-LAN Exploit Showcase- 1454

Claroty Research    Reference →Posted 1 Year Ago
  • The authors decided to take a look at the TP-Link ER605 router and attack it from the WAN network. When looking at the attack surface, they decided to check out the dynamic DNS (DDNS) provider Comexe that was used by the router. There are many custom components to the DDNS, which is super scary on something that's externally accessible.
  • The device tries to access its DDNS servers of the Comexe servers. To get the servers IP, it tries to resolve it with a regular DNS query. After getting the IP address, a connection over UDP is made with encrypted data with a hardcoded key. Since there is no host validation via TLS or something similar, an attacker can just impersonate the process to receive and send requests to the client device! This requires a MitM to do.
  • With an easy authentication bypass in the bag, there is now more attack surface. When parsing the response code, it assumes it uses the length of the bytes for the length of a strncpy and copies the bytes, leading to a buffer overflow on the stack. With 800 bytes at their disposal, they could trivially overwrite EIP on the stack to get code execution. They found several more overflows like this but this was the most juicy.
  • Not so fast! Even though there are no stack canaries, the device has ASLR. Another buffer overflow they found was the copying of a user controlled buffer into the globals section that should only be 0x80 in size. Using this overflow, we can corrupt various parts of the globals to affect the program. For instance, even though the length of a DNS entry was 0x80, this can be corrupted to allow for larger values. Using this bug, we can write more data to the stack to corrupt a return length on the call, resulting in a large OOB. With this, they can read the address of libc from the stack.
  • Now, they can use the RET2LibC attack to get code execution on the device! From there, they wanted to pivot to another target - the Synology IP camera. Within this, they found a fairly simple buffer overflow onto the stack when parsing JSON data via sscanf(). Since this API requires no authentication, we're in business!
  • On the stack, was a function pointer that they could set to system and the parameter happened to be controlled by them as well! The binary used many protections, such as RELRO and NX but not stack canaries. Additionally, ASLR/PIE was used on the device but only 8 bits of random for the app code and 12 bits of random for the heap. So, how do we defeat this? Brute force!
  • The heap can easily be sprayed by providing a ton of data. The location of system appears to be brute forced, since 1/256 is doable on this program. They had some troubles with encoding as well, since everything had to be valid UTF-8 characters and couldn't contain actual nullbytes. But, they worked through this.
  • Overall, a solid post on the bug hunting experience in pwn2own! There's a major difference between real world exploits and CTFs which I enjoy seeing

Encoding Differentials: Why Charset Matters- 1453

Stefan Schiller - Sonar Source    Reference →Posted 1 Year Ago
  • In the wide world of technology, there are many different byte encoding standards such as UTF-8, UTF-16 and Big5. In particular, we need a way to map bytes to characters. When we do this, there is an encoding on the server side from one charset to bytes and another charset to decode it, where most of the times these are the same. This article finds a crazy bug in modern browsers that can lead to XSS and other issues.
  • In HTTP, the charset attribute can be set on the Content-Type header, the meta tag within the HTML and finally the byte order mark of U+FeFF. If the browser can't determine the charset from the header, then it does some auto detection on it.
  • The idea of the attack is to get data sent to the browser as one character type but then interpreted as another when the browser receives it. Back in the day, this was used to get XSS via a UTF-7 encoding on Google. Modern browsers have banned UTF-7 though and most charsets aren't helpful for smuggling in characters, except one.
  • The ISO-2022-JP is a Japanese character encoding that must be supported. If a byte sequence of 0x1b 0x28 0x42 tell the charset to decode the next set of bytes as ASCII instead of the JP charset. What's even better, is that Chrome and Firefox will both autodetect this encoding for us to cause havoc.
  • The first attack they do is via negating backslash escaping. This requires having input at the top and bottom of the file, where double quotes are backslash escaped on the server side, preventing XSS. Once the escape sequence is added, the browser will switch from ASCII (the default in the mode) to Japanese character set. When this happens, most of the standard ASCII is the same besides two chars: 0x5C and 0x7E.
  • 0x5C is the yen character in this charset instead of the backslash! So, instead of the browser seeing a backslash in this encoding, it sees the yen! Now, we can execute arbitrary JS because we escaped the string. That's pretty neat!
  • The second technique is when data is controlled in two separate locations within an HTML tag, such as an attribute or plaintext section. The idea is to switch from ASCII to the JP one in the attribute. Then, within the plaintext, switch it back to ASCII. Since the double quote of the attribute was effectively skipped with the charset change, we're now INSIDE of the attribute with this data. After this, an attribute (because of the closing double quote) will be treated as HTML when it shouldn't be. Pretty neat!
  • How do we trigger the browser to see this encoding? According to the authors, direct control over the charset or via a meta tag is nice. If the charset isn't added, the auto detection finds it super easy, according to the author. I'm guessing that it looks for the escape sequences is all.
  • Overall, a great post on the complications of character encodings in the browser. The browsers ability to help the web page has once again added a security mishap to the world.

LiFi/Jumper Exploit- 1452

Rekt    Reference →Posted 1 Year Ago
  • LiFi is a DEX aggregation protocol. Recently, they added a new contract to the code. This contract took in input for a swap but allowed the calling of an arbitrary address with arbitrary data without any input validation.
  • Since an attacker controlled the call and data from the context of the contract, it was possible to abuse the approvals on ERC20 contracts with a confused-deputy-like attack. If a user had approvals on this contract, the funds could be stolen by an attacker. By doing this, they netted themselves about 10M.
  • According to the post, the Socket protocol hack earlier in 2024 was likely the same attacker. Such of the same code was used and it was a very similar attack.
  • The most interesting part to me is the timing of this. The contract had only been deployed for a few days and appeared to have NOT received an audit before launch. It's really true that every line of code released should be audited in some capacity. Otherwise, a bug can slip through the cracks.

Type Confusion in Uniswap LP Dark Pool- 1451

Zellic    Reference →Posted 1 Year Ago
  • Dark pools are private asset exchanges designed to provide additional liquidity and anonymity for trading large blocks of securities away from the public eye. Zellic was auditing a dark pool to withdraw and deposit when they found a bug.
  • The darkpool could hold funds for ETH, ERC20 and ERC712 tokens. The LP positions were being held as non-fungible ERC-712 tokens. For all of these funds, they consisted of three fields: asset type, amount and footer (hash). All of this information is stored within a single merkle tree but isn't domain separated at all.
  • Since these are not domain separated, type confusion bugs may be possible. In particular, use one note type in a function that expected another in order to force unexpected functionality to occur. The types of notes are not explained well in the article but I'm doing my best to understand the flow of it.
  • The function split() can be used to split a note into different positions. If this is called on a different asset, such as a fungible note, it will treat the second field as the amount even on a non-fungible note type. By using an NFT note for a function meant to be a fungible note, the liquidity from a previous transfer can be split. Now, when the attacker calls uniswapRemoveLiquidity, it will withdraw the funds from the other user.
  • To fix the bug, a domain separator on the different note types to prevent the type confusion. Type confusion bugs are all over the place but don't always work out because things need to line up. This was a good bug and an interesting read!

Chaining Three Bugs to Access All Your ServiceNow Data- 1450

Asset Note    Reference →Posted 1 Year Ago
  • ServiceNow is a business platform similar to Salesforce. It has data from HR, to employee management, to many other things. ServiceNow is mostly cloud hosted but there is a self-hosted version that's a 20GB jar file.
  • ServiceNow has a ton of functionality. To better understand the vulnerability, the author puts out a list of things to know and goes through the routing of the app:
    • Table: All service now data is stored in tables. From users to pages to configurations. There is a simple way to update tables thorugh the UI with an included ACL.
    • Processors: Effectively, an API endpoint. A user can write code for their JavaScript engine Rhino to create custom endpoints. Since most setups are multi-tenent, there is a lot of sandboxing on it.
    • UI Pages: UI pages come from XML templates by the Apache Jelly library. These allow custom UIs on the app.
  • Executing custom code can be very hard to do. So, they started looking how this worked. They learned that there are multiple evaluations that occur - one for g and ${} then another for g2 and $[]. Double evaluation on templates is a known issue. In particular, if any content from stage 1 with the special tags gets into stage 2, then we have template injection.
  • Within the system, there was also the <g:no_escape> tag. Within one of the default templates, they found that the title of the page was injectable! In particular, they had something similar to register_globals, which meant that it was controllable anywhere, including the login page.
  • Sadly, a HTML santizier is ran over the page title, which could prevent the adding of arbitrary XML. While looking through the allowlist, they noticed that the style tag was allowed. From the context of the HTML parser, anything within the style is fine! But, this can still be processed in the rendering as XML later, allowing us to smuggle in payloads.
  • Without mitigations for Jelly injection, this would have been game over. So, they took a deep dive into how the evaluation process worked. It works in two stages.
    1. First, the j: is bound to Jelly core and g: is bound to ServiceNow custom tags. Then,j2: and g2: are bound to the null namespace, making them passed over.
    2. Next, it changes j2 to use Jelly Core and g2 to us the custom tags from ServiceNow with the original ones set to null to make it run over only the second set of tags.
  • With this, they tried creating their own namespace, which was rejected because Glide (Service Nows tags) can only be bound to g and g2. What about rebinding g from the null namespace TO the glide once again. Of course, another defense in depth measure was triggered to prevent this. However, it was a flimsy string match that was easy to bypass with using single quotes instead of double quotes! With that, OG glide tags are evaluated on the second pass, after our initial injection point.
  • Arbitrary JavaScript execution from a user clicking on a malicious link is pretty hype! But, the tables that could be accessed from this was limited. So, they wanted a permission break on a hardened instance. They looked into the file reading process to see a denylist of locations that couldn't be accessed, such as database creds. The denylist was super solid and didn't seem possible to bypass by itself.
  • However, this is when they had an idea something: "what if the processing for getBufferedReader() can mutate the path in some way?" It turned out that adding a .. into the middle of the path would be REMOVED when a clean up routine is done on the path. As a result, the denylist can be bypassed! Crazy that the normalization which is normally good for security actually caused the issue here.
  • Once there, ServiceNow has a mechanism to run arbitrary code on the box, by design. With all of the permissions we now have, it's game over. I love the hurdles that were required to get over this! The mitigations were well thought out and strong but enough of a deep dive led to compromise with the double eval on the templates.

Securing Developer Tools: Unpatched Code Vulnerabilities in Gogs- 1449

Sonar Source - Paul and Thomas    Reference →Posted 1 Year Ago
  • Gogs is an open source solution for self-hosting source code with similar functionality to Github and Gitlab. Under the hood, Gogs users allow for pushing and pulling to Git repos over SSH via the Golang package golang.org/x/crypto/ssh for most complicated things. On top of this functionality, Gogs adds authorization checks.
  • When interacting with the SSH server for git, there are two types of commands: env and shell requests. The env requests calls the env command to get ENV variables. Although, they protect against command injection they do NOT protect against the cousin argument injection. We can control arguments of the env command!
  • Well, sort of. The actual code concatenates a key and value for the command with an equals sign with an empty string check on the values. So, we need an argument that will allow for the equals sign and be valid. While looking around, the flag --split-string can be used to have a proper command AND get command execution. So, RCE on the server via connecting via SSH has been completed!
  • The actual exploitation requires a tricky scenario. First, SSH has to be enabled (meaning it is not default). Second, the attacker needs a valid SSH key or self registration needs to be turned on. Finally, the version of the env binaries matters, as the Alpine Linux version wasn't exploitable, for instance.
  • They found three other vulnerabilities in the next blog post. The next argument injection was within the git diff command. By using the undocumented --output flag, we can open and close an arbitrary file. By forcing this into an error path, the file will be empty.
  • This is where things get wild! If the .git/HEAD command is corrupted, then it will function as a bare repo. If the git repo is controlled, then we can add malicious configs (such as core.fsmonitior to get code execution on the next git command that is run.
  • The next argument injection was in git tag. By specifying the --file flag on the command, the file will be used as the tag message. In a future command, just read the tag message for an arbitrary file read. This was not mentioned in the article but was included on their argument injection vectors site.
  • The final vulnerability was simple file deletion - no directory traversal needed! By deleting the .git/head from a repo, it's treated as a bare directory. For whatever reason, bare repos have special files that do things like execute code. So, using the same primitive as in the second argument injection, code execution is easy.
  • git is scary and so is argument injection. Great blog on an undervalued bug class!

Universal Code Execution by Chaining Messages in Browser Extensions- 1448

Space Raccoon    Reference →Posted 1 Year Ago
  • The article begins with discussions on how communication can be done from isolated contexts in browsers. Between webpages, there is postMessage. From content scripts, there is sendMessage. For background scripts, there is native messaging for communicating with apps running the background with an extension.
  • They wanted to find an application where they could go from web page to RCE. So, they wrote a query to look through all extensions for usage of native messaging, over 250K users and used content scripts. This narrowed it down to 200ish where they started looking.
  • Smart Card extensions were a common within this category. Many companies want PKI cards to be used, but since they are not natively supported by browsers they're in extensions. One of these was Extension B with over 2 million users.
  • The extension is injected into every page, as you'd expect. The content script listens for messages then passes them to the background script, which simply passes it to the native application. Even though there is a source check within the event listener, the origin is stored within the postMessage data itself instead of the actual origin. Yikes! Now we're getting somewhere!
  • The native application accepts data from the initial postMessage that we made to the extension. On the GetCertLib action, the field PKCS11Lib is directly concatenated with a user controlled field to make a DLL path. By forcing a download in the browser then triggering this flow, it's trivial to get code execution within the context of the native application.
  • An awesome post on browser security protections and a terrible blunder that led to RCE. Great find in such a popular app!

You Can’t Always Win Racing the (Key)cloak- 1447

Maor Abutbul - CyberArk    Reference →Posted 1 Year Ago
  • Keycloak is an open source IAM solution with user management and SSO. They decided to tackle some race conditions within the system relating to user management.
  • From reading the James Kettle post on race conditions, the authors were curious about race conditions in the registration process of users. While reading the source code, they noticed that the requirement for the email verification was added in a later step after creating the user. So, their thought was making a login request AFTER the user was created but BEFORE the restriction was added.
  • While digging into this, they noticed that the ORM used in the program used database transactions. This means that the database writes are grouped together, making the race condition impossible to hit. Regardless, it was interesting seeing their methodology for finding these multi-step types of bugs.
  • They found a classic limit overrun race condition within the rate limiting for API key creation but no multi-step issues.
  • I felt like the post overhyped some of the issues tbh. But, I enjoyed the race condition discussions on it.

Fix Infinite Feegrant Bug- 1446

Cosmos SDK    Reference →Posted 1 Year Ago
  • The fee grant module in the Cosmos SDK is used for setting up a different account to pass for gas on your behalf. After an allowlisted amount was set for a given user, they were given a specific allowance.
  • When using the fee grant module as a user, the amount of fees that a user had access to was never being updated. So, once you had the allowance, it was effectively infinite! This is wild that it got passed testing imo.
  • From reading the code snippets, it appears that the original developer thought that the function Accept() was updating the value. It appears that it was only checking the value. So, the author of the PR had to add code to modify the allowance object to subtract funds from it.