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!
x-middleware-request header with the path of the middleware being executed. For every piece of middleware it sees, a colon-delimited path is added. If the middleware has already been seen, then the code simply skips the middleware. x-middleware-subrequest: my_path skips the check. If this is used for authentication/authorization, then it's a horrible vulnerability. The path is somewhat guessable and the header can be used as a polyglot as well.middleware or src/middleware. With the changing of the path, it actually makes it easier. Additionally, there is a now a recursive check with a maximum of 5. So, middleware: just needs to be repeated 5 times now.delegateCall was made to a contract controlled by the attacker. At this point, they were able to modify the Safe contract storage to change the proxy slot. By doing this, future calls the attacker made to the contract would go through their proxy to execute a delegateCall, allowing for complete ownership of the assets at the address. Stealing funds is trivial at this point.max_receipt_size. To me, this is interesting - small changes can create subtle new invariants that were not there before.incoming_receipt by other nodes, the exceeded size limit leads to a panic being hit. From their report, they were hunting for possible panics in the codebase and how to hit them. Aka, sink-to-source bug hunting.validate_receipt actually removes the validation of the receipt size. In particular, they ONLY want to check for size constraints during creation and not expansion. I would have expected a check to be added on the expansion code to not exceed the limit, so this patch surprised me.deviceid against the session token and other fields, making this pretty solid.deviceid in the headers that we don't own, the request authenticates us but believes it's a device initiated request because of the header. So, it assumes that the device is valid but it's really not. The explanation and the code snippet they provide helps a lot with this..env, containing deployment credentials, being leaked. This was done via the .gitignore file. For the initial deployment of two packages, this worked well..npmignore file to do this. Surprisingly, the presence of this file invalidated the .gitignore! This meant that the sensitive .env file was leaked. This contained a private key for the deployer account.cat /etc/passwd on a SQL query. Since this is the super user, it's effectively game over.AdmissionReview request generates a temporary NGINX configuration file using a template. Then, to test for validity, it runs nginx -t. Since the configuration file has user controlled inputs, the path is unauthenticated and the config is executed, this makes it a great attack surface.authreq parameter is used for authentication-related annotations. However, this field has zero input sanitization. Hence, it's possible to add arbitrary directives to the NGINX configuration file. There are several other variants of this on the authtls and mirror parameters. So, why is this injection a big deal?ssl_engine directive is able to load shared modules, without top-of-file restrictions like load_module. Doing this would allow for the execution of arbitrary code but requires a file to be on the system./proc file system. Using this, it's possible to access the contents of the file from the NGINX configuration. To make this race condition easier, making the Content-Length larger than the body will keep NGINX waiting. Sadly, this requires brute forcing PIDs and file descriptors, but that's worth the problem..so payload by abusing the file buffer feature.AdmissionReview request to the controller with directive injections. In particular, inject the ssl_engine to load the shared library from step 1.nginx -t can lead to code execution, making configuration injection a very serious vulnerability. vald. ContractCall on an Axelar gateway contract is made, the vald program will see this and vote on the Cosmos Axelar chain. To do this, they call ConfirmGatewayTxs. max_body_bytes setting. This is the maximum amount of bytes that can be in a request - 1MB. If this limit is exceeded, then the Axelar node will drop the request. These settings are seldomly changed and is the default value in the official setup instructions. By forcing the ConfirmGatewayTxs to be larger than 1MB, with excessive amounts of logs, the voting transactions from vald would be rejected!ContractCall logs on the Axelar Gateway.ConfirmGatewayTxs on Axelar with the txids from the events listed. At this point, vald detects the polls and tries to vote but fails because of the size of the HTTP request.block.timestamp in Solidity.deposit to the L2.deposit. resolveOrders is made to a contract. This code appeared to be very similar to the example integration and looked pretty safe. Upon closer inspection, the victim contract had not updated the protocol even when the interfaces had changed.resolver. This is a NOT intended to be a controlled element of settleOrder. In fact, it's passed in with new bytes(0) since only the contract itself should be able to set it. How was this controlled? What gives? The woes of multiple versions!ptr. When writing a value called the suffix, it's written at an offset depending on the passed in interactionLength. interactionLength is a full 32 byte word, which can overflow when doing ptr + interactionOffset + interactionLength. Because of this overflow, the pointer can be decreased to write the user controlled suffix to any location! Sounds like memory corruption!interactionLength value to overflow to point to the resolver address.resolver contract code wasn't in scope for audits, so it was ignored. In March of 2023, these auditors actually found the integer overflow while assessing the scope. However, shortly after, the code was completely rewritten so they didn't feel it was necessary to callout.