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!

Sherlock Yield Strategy Bug Bounty Post-Mortem- 1055

Sherlock    Reference →Posted 3 Years Ago
  • Sherlock is a blockchain auditing platform in the form of contests. They had a staking pool setup with Euler.
  • The function balanceOf on the EulerStrategy.sol is used to determine the current value held by the EulerStrategy. This result is then used to calculate the price per share of the user's withdrawing position.
  • The function balanceOf calls EUSDC.balanceOfUnderlying() underneath it. The problem with this is that the actual value being returned may be manipulated. Although the contract believes this will be an atomic call (no other paths ran), this is NOT the case.
  • Specifically, the totalAssets is updated prior to the call but the totalBalances is NOT updated yet. This results in computeExchangeRate() being higher than the actual value. An attacker could use this to change the price and obtain more money than anticipated.
  • This attack is only possible because arbitrary calls from 1inch can be made while swapping. To manipulate the pool, an attacker needs to transfer a large amount of USDC to Euler, inflating the price of computeExchangeRate. Since the price per share is higher, an attacker can redeem more USDC than they should be able to.
  • In the proof of concept, the author has to setup the stage for the swaps and stake money. In Foundry, it is possible to fast forward the chain to a different time in order to allow this POC to be possible. Neat! Once they have performed the swap, they setup the callback on 1inch.
  • With the setup done, we can make the call. The callback is hit on the withdraw. Upon hitting the callback, USDC is transferred to Euler while in the callback of 1inch. This cross-protocol reentrancy creates a manipulated exchange rate. At this point, we exit the position, collect our winnings and repay the flash loan.
  • To fix this vulnerability, a reentrancy lock was added in Euler. This bug was missed by Trail of Bits for Sherlock but found during an audit of Euler. However, it was only noted as a medium severity finding and never fixed as a result. Overall, an interesting article for a crazy cross protocol reentrancy. The POC is very well explained as well.

Different parsers, different results- 1054

Nnez    Reference →Posted 3 Years Ago
  • Gearbox is a composable leverage protocol. It allows a user to take leverage on collateral asset and use the borrowed funds through CreditAccount across DeFi. A common functionality for every lending and borrowing platform is a simple health check. This helps check is a users account is solvent or not.
  • How do users get their funds? There are routers (adapters) for every protocol, such as Uniswap and Curve. There is a sanity check at the beginning of the router that parses the user input for what tokens to swap. On UniswapV3, this same information for the path is parsed as well. However, this parsing is done slightly differently from a byte array.
  • Gearbox takes the byte array and parses the first 20 bytes as token A. It then takes the FINAL 20 bytes and parses this as token B. For Uniswap, it returns the first 20 bytes for token A, the next 3 bytes as the fee and the next 20 bytes as Token B. The big difference here is the parsing in token B because of the little fee in there!
  • The author came up with the following payload:
    abi.encodePacked(WBTC, poolFee, WETH, DAI)
    
  • Token A will always be WBTC. However, for Gearbox, token B will be DAI and Uniswap will see DAI. This discrepancy means that the health check for Gearbox can be bypassed.
  • To launch this attack, the author performed the following steps:
    1. Deploy a fake token on Uniswap. Make the pool 1fake=1WETH=0.0000000000001WBTC
    2. Provide a small amount of liquidity for our fake token with both WBTC and WETH with a big exchange rate.
    3. Make a swap payload with our fake token and WBTC. The sanity check will be on the pool for fake token and WBTC, while the actual money will be taken from WETH. abi.encodePacked(WBTC, poolFee, fake, poolFee, WETH).
    4. This results in an attacker swap for the ratio of the fake token to the other tokens. For instance, 1WETH for 0.0000000000001WBTC.
    5. Hint their fake token and claim they lost a bunch of WETH from the pool.
  • Overall, a super interesting bug! The path variable is a commonly used pattern for swaps. So, this is something to watch out for and something I've looked for in the past.

Superfluid Post Mortem Hack- 1053

Superfluid    Reference →Posted 3 Years Ago
  • Superfluid.sol allows for composable Superfluid agreements in a single transaction. I have no idea what this means (lolz). The important thing to note is that the main contract and the agreements have a concept of context (ctx).
  • The ctx is a serialized state for through the entire transaction for different agreement calls. This context includes information about the original msg.sender even.
  • The convention of callAgreement is to use a placeholder ctx so that Solidity can read it directly from the argument ctx that should be there.
  • The flow of operations is like so for verifying:
    1. Call deleteAnyFlowBad.
    2. Call callAgreement. This creates the ctx and puts a stamp on it.
    3. Call createFlow to verify that the calling host contract is authorized to do so.
    4. Call authorizeTokenAccess to hand over the ctx and deserialize the original call.
  • The vulnerability occurs in the parsing of the ctx structure within the function deleteAnyFlowBad. After everything is merged into a single byte array. The expected empty ctx can contain spoofed data, causing a MAJOR problem to occur. Since the abi decoder ignores the legit ctx, we have now tricked the code into parsing the wrong ctx.
  • With the ability to craft an arbitrary ctx, the calldata can be crafted to impersonate other users. This can be used to create IDA indexes on behalf of other users to steal tokens in their possession.
  • The contracts had been previous audits by several users and Peckshield. They have decided to add a bug bounty program and get more audits in the future. Overall, a super interesting bug in extremely complicated code.

Exploiting CVE-2022-42703- 1052

Seth Jenkins - Project Zero    Reference →Posted 3 Years Ago
  • The bug report CVE-2022-42703 by Jann Horn is a use after free on struct anon_vma in the memory management (MM) subsystem of the Linux kernel.
  • The vulnerability is extremely complex and particular to the subsystem. From reading the bug report, there appears to be an incorrect assumption made on reference counting for VMA objects that trickles down into other portions of code. This bad logic leads to a use after free.
  • By triggering the vulnerability above, the object folio->mapping can get a dangling reference to a anon_vma object. By calling madvise(..., MADV_PAGEOUT), the access on anon_vma can be repeated in the free state.
  • Within the structure, are several pointers. The route the author decided to go down for exploitation was to fill this with addresses and corrupt them. The function down_read_trylock() would corrupt the memory at a chosen address after some primitive hunting.
  • To get this to work though, we need to be able to supply our fake structure. Since anon_vma belongs to its own kmalloc cache, it's not simple to free and reclaim. The author points to a known technique to free all of the objects in the slab page, flush the percpu freelist and cause the virtual memory to get sent back to the regular allocator. With a spray, we can control this.
  • The arbitrary write had constraints on the write that would occur. It would increment the value by 0x100 if the 3 least significant bits and most significant bit were set. In the future, the value will be decremented back down, meaning that this has some limitations. We also don't know the KASLR slide, making this even harder.
  • On x86_64 Linux, when the CPU performs interrupts and exceptions, it will swap a respective stack that is mapped to static and non-randomized virtual addresses. This has been exploited in the past in order to exploit something to not need knowledge of the KASLR slide.
  • What's the game plan then? Force an interrupt to occur. Once this happens, we can use our arbitrary write to corrupt the registers in the stack frame in the kernel context. The author choose to interupt a call to copy_user since the data is controllable and there is a length value in a consistent register (RCX) that we can overwrite.
  • It turns out, that there is an interrupt for hardware breakpoints that is easy to trigger. So, the author wrote code to trigger this exploit method:
    1. Setup two processes: X and Y. Y is the original and X will ptrace Y.
    2. Set a hardware breakpoint at a known address in Y.
    3. Make a large number of uname requests. This is because copy_to_user is throughout.
    4. Trigger the breakpoint in the code for Y. This causes the location to be saved for the codes stack frame.
    5. Slightly after the frame is saved, use the UAF to write to process Y's stack frames saved length value.
  • The technique above works for reading out too much data. For reading a stack buffer to userland, this can be used to defeat the KASLR slide and stack cookies. If the technique is inverted, it can be used to write too many byes to the kernel as well. The target of this was prctl to create a stack overflow where none existed in the past.
  • Since the read leaks the stack cookie and KASLR slide, it is trivial to bypass both mitigations! Now, we can start a ROP chain. To mitigate this, they suggested randomizing these areas of memory. They do note that the mitigation doesn't work for local users (only remote) since a TLB timing side-channel can be used still.
  • Overall, interesting technique on Linux exploitation. It's cool to see such a powerful primitive discovered that can be used in other locations.

Disclosing information with a side-channel in Django- 1051

Dennis Brinkrolf - Sonar Source    Reference →Posted 3 Years Ago
  • Django is an open source Python framework used for web applications. It is used by many organizations as the backend web server for a website.
  • Django is an MVC (model view controller) framework. It has a templating engine to allow for simple integration between the different sections of code - Django Templating Language (DTL).
  • The author gives a simple example: showing registered users of a database with some control over the fields being sorted on, such as starting letter or something else. The Python code returns a list of objects for the context of the template. The template runs {% for e in users|dictsort:sort % to iterate through all of the usernames in a filtered way.
  • What's the problem? It's an issue with the resolution process of the sort itself. Under the hood, dictsort uses the built in function sorted with a custom function to decide the order. The sort attempts to exclude all sensitive values in the object (__ by convention).
  • Django has done a wonderful job to prevent access to unauthorized things. What's the problem? The lookup attempts 4 different ways for searching: dictionary lookup, attribute lookup (class), list-index lookup and method call without arguments. The FINAL option allows us to call arbitrary functions without parameters. According to the author, they claim this could be used to delete application files or any bad things.
  • Instead of showing this route, they take it another way: a sorting oracle. The idea is that since we fully control the sorting key, we can learn information about the service. For instance, if we sort based upon the first character of the password, we can learn if a password is ABOVE or BELOW our password. Nice! :)
  • With full control over the data in the database for a given password, this could be used to extract a ton of sensitive information. In Django, passwords are hashed with an unknown secret and a random salt though. How is this possible then? First, a trick: if two values are the same, then it will always be the same order - the unsorted form.
  • With the unsorted form at hand, users can be put into groups. If a column has a particular character ('a') then it will be put first or 'b' be put second. What if two column characters are the same? We will know this since we KNOW the unsorted order of the column! This allows for the discerning of the end of one group and the start of another.
  • The key to this scheme is the unsorted items to understand the ending of a group. This requires a character to be EXACTLY the same in every single group. Another assumption that is made is that every character is represented. Given enough entries (like a password hash) this is likely though.
  • The attack has many requirements but allows for the extraction of sensitive data via a side channel of sorting. To fix this vulnerability, the maintainers restricted the ability of _proper_resolver to remove list and function invocation. Overall, amazing post on a crazy side channel!

Cool vulns don't live long - Netgear and Pwn2Own - 1050

Kevin Denis - SynAckTiv    Reference →Posted 3 Years Ago
  • Pwn2Own is a competition focused on finding vulnerabilities in high value targets. In this rendition of Pwn2Own for IoT devices, they choose the Netgear Router RAX30. Pwn2Own has many tough issues:
    • Bugs must be in the most recent version of the firmware. Although this seems obvious, this means a patch the day before may change the exploit pattern.
    • You can only enter once per device and duplicates don't get payouts. This means that an awesome bug may have been found by somebody else.
  • While looking at the binaries, puhttpsniff caught their eye. Using the strings command shows some HTTP parsing, which is always interesting. The binary inserts NFLOG iptable rules to run callbacks.
  • While reviewing the command, there is a super trivial command injection vulnerability when constructing IP tables with the User Agent header. The exploit payload is curl --user-agent "a\";/sbin/reboot;\"" http://192.168.1.1. This was on the LAN side.
  • With access to the devices while running via the previous bug, they started hunting for more. By listing socket information, they learned that SSH and telnet are open for IPv4 and IPv6 on the LAN. When running nmap on the device from the WAN on IPv4 (default), nothing comes up though.
  • However, from our learnings from earlier, the device is listening on IPv6 as well. This is because the the IP table rules for IPv6 are only applied to the LAN interface and the WAN if a public IPv6 address is provided. If a link-local address is used on the WAN interface (same network segment), all of the services are accessible. Pretty neat bug!
  • With a bypass for accessing restricted services, what else can we do? /etc/shadow has a hardcoded password that was easily cracked via John the Ripper. Now, the telnet port can be used to connect. Additionally, the telnet prompt had hardcoded commands but had a special bad door sh to escape this.
  • The full steps for the second chain are as follows:
    1. Ping the address of the WAN interface. This can be used to find the link-local address of the device. I don't fully understand IPv6 networking so I'm pretty much copying and pasting here.
    2. Launch telnet with the cracked credentials.
    3. sh command to escape the shell.
    4. Since the user with the cracked password has uid 0, we are now root.
  • This was a very novel vulnerability in the routing tables; I will definitely keep this in mind for future assessments. Sadly, both the networking bug and command injection were fixed a few days before the event. So so so sad. Regardless, amazing findings!

Cacti: Unauthenticated Remote Code Execution- 1049

Stefan Schiller - Sonar Source    Reference →Posted 3 Years Ago
  • Cacti is an open source monitoring solution used by many different companies. They found this initially by scanning for bugs with their tool.
  • The application is written in PHP. At the very beginning of the app, an authorization check is performed. If the IP address corresponds to a hostname in a table, then it will allow the user to make the request. Otherwise, it will deny it.
  • While trying to figure out the IP, many user controlled headers are able to influence this request. A plethora of headers like HTTP_X_FORWARDED can be used to control the IP. Since the IP is used by authentication purposes, this is a complete authentication bypass.
  • Using their tool, there appeared to be a command injection vulnerability within the user provided poller_id parameter for the bad sync proc_open. This creates a fairly trivial command injection vulnerability that can be used to get code execution.
  • The first mitigation was to make the headers being accepted configurable but off by default. This is helpful for situations where this is behind a reverse proxy. For the command injection, cacti_escapeshellarg was used on the variable in two separate places to prevent a regression from occurring.
  • Overall, their tool seems impressive! They keep reporting high impact issues like this one. Secondly, trusting headers that are user controlled for sensitive operations is a common problem. Good find!

RCE via SSTI on Spring Boot Error Page with Akamai WAF Bypass- 1048

pmnh & Dark9T    Reference →Posted 3 Years Ago
  • Spring Boot has a pretty famous issue: when data is reflected within an error message, the Exception message uses Spring Expression Language (SpEL). For instance, $(7*7) will render 49. The authors of this post found an outdated version of Spring Boot being used which had a parameter reflected in an error message. Simple, right?
  • The SpEL language is a templating language that can be used within Spring. When having an injection via server-side rendering, this can be used to execute Java methods, construct objects and other data to eventually get code execution. For information on SSTI in SpEL, spel-injection is a good link.
  • Once they discover the SSTI, they try to try the simple payload ${T(java.lang.Runtime)}. This is a SpEL shorthand for referencing a Java class by name. However, the Akamai WAF blocked this request, even though they knew it was vulnerable. Now, we must learn why this was blocked by the WAF and how to work around it, which took over 500 requests to do.
  • With Java-based code injection vulnerabilities, the first issue is finding out how to load arbitrary classes. 1${2.class} will output java.lang.Integer. To create an arbitrary class, the author tried 1${2.class.forName("java.lang.String")} which was rejected based upon the function forName.
  • They needed to be able to create a string. The most straight forward route to RCE is .exec() is why. Reading the Java documentation (which is usually amazing), gave them the function toString() to get a non-static reference to a character. With this, they could use a toString() on an integer to return the proper character in a string they needed. Progress!
  • With the ability to create arbitrary strings, we're getting closer. With a string at hand, we need to find a way to call java.lang.Runtime.exec. They used a known technique as follows:
    • Use reflection (introspection feature) to get access to the Class.forName function to get an arbitrary package.
    • Build a String with the value java.lang.Runtime to pass to the function.
    • Use reflection to get access to the getRuntime function.
    • Build a string for exec using the crazy method above.
    • Call exec with our string.
  • A GET request has a limitation of 2kb. Since each character was 45 bytes long, this limited us to 45 characters in our payload. They tried to find an optimization, but couldn't find one after hours of trying. Eventually, they settled for a small payload in the exec function.
  • After hours of grinding, a simple uname -a response could be seen! The bug had been exploited and the WAF bypassed. To me, the interesting part of the article isn't the bypass itself but the thoughts around bypassing the WAF and problem solving.

Reverse engineering my cable modem and turning it into an SDR- 1047

stdw    Reference →Posted 3 Years Ago
  • The author had an old cable modem sitting in their closet. While browsing some forums, they learned that the device had a built in spectrum analyzer for diagnostics. So, they wondered, if a cable modem and TV tuner do the same thing, then can this be turned into an SDR? Let's find out!
  • To initially gain access, they setup a UART console. This gave them a wealth of information, such as the OS and processor being used. However, upon booting the serial console is disabled. The only interesting thing is going into the bootloader prompt to load a new image or read/write to memory locations directly.
  • The bootloader is a good target but cannot be done blindly. Upon looking at the board, they learned that all persistent storage was on a single SPI flash chip. Can we dump this? The winbond 25Q32JV yields a datasheet with pinouts and is supported by flashrom!
  • We have a problem though: dumping a SPI flash chip requires power. If we give the board power, the chip will be used, preventing us from reading out the data we want. The solution? Remove the VCC board by carefully lifting its pin from the pad. The author also did this with the chip select (CS) line, since it's common for it to be wired to VCC as always on.
  • To connect to the chip, the author wires up to all of the pins on the chip. Additionally, they put a jumper wire onto the pads without legs and connect the leg to the jumper whenever they want to boot normally. Now, by using flashrom and a Raspberry Pi, the image can be completely dumped. They even have the commands they ran!
  • What's in the image? The author could see some credentials but that was at. Luckily, the tool bcm2-utils has the capability to dump, parse and modify configuration files from Broadcom cable modems. They modify the images configuration to turn on the serial console and have a simple telnet password. Neat!
  • They boot back into the device with serial and are greeted with a nice eCos console to pop a root shell. While there, they couldn't find any commands to turn on the spectrum analyzer so they started reversing the code of the OS in Ghidra. So many interesting quirks from reversing! Like, the call command can be used to call any code at any location.
  • While reversing, they came across the code to enable the bandpower measurement for a given frequency range in the eCos console. The code indicated that the analyzer just changed a few memory mapped registers, indicating that it was just at a slightly higher power. When they read this memory location, they as raw IQ data!
  • They wrote some magic scripts to trigger the code that was mentioned above. They put the registers to look at particular ranges, making this into an SDR. Parsing FM radio signals from possible via some code they wrote. Mission accomplished!
  • Overall, awesome article on reverse engineering. Some of the tricks, such as lifting the legs off the chip. Good write up!

Asus M25 NAS Vulnerability- 1046

One Key    Reference →Posted 3 Years Ago
  • One Key has recently created an automated scanner for scripting languages and compiled binaries. To start with, they support Python and PHP scripting languages with various bug classes, such as command injection, path traversal and many others.
  • This analysis tool led to the discovery of 15 critical bugs in 6 different vendors. All of these will be fixed besides an Asus NAS bug since it's been EOL'ed. It seems they gave the tool the firmware of the device and it was able to magically discover bugs. Pretty neat!
  • While parsing cookies, the code exec("cookie_user -c ".$_COOKIE['CookieID'])); is ran. This is used to execute PHP code dynamically. Since the string CookieID is controlled by us, this can be used inject our own code into the program.
  • Overall, the bug they found is pretty simple. It's neat that the tool is able to tear apart firmware and find these sorts of bugs in the code. This could be a good tool for security researchers looking for quick bugs or vendors as a check prior to launching their product.