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!
claude-code-action to trigger actions when users create an issue. This executed some code in the repository's context, but not much else is said. The title and description were given directly to Claude for usage. However, the title was able to hijack Claude's actions using this input.glthub-actions/cline. The fork oft he regular package contained a package.json with a preinstall script that executed a remote shell.NPM_RELEASE_TOKEN and several other tokens.skill-creator helps write tests that check that Claude did what you expected for a given prompt. This is similar to softweare tests. This is important for A) understanding the capabililties, B) catching regressions and C) detecting whether the base model has outgrown the skill. The metrics show the time, the pass/fail rate and amount of tokens used. All really important evals! This now has A/B testing too.sandbox attribute, depsite being in a dive and having a name that contained sandbox in it. This means that the iframe is considered same origin and can access content within the parent page.src via data:text.... Normally, this would mean it has the null origin. Luckily for the author, the page calls document.open() and document.write() on the iframes document before the URI loads! This means that the iframe's origin is from the parent. In practice, this means that any code injected into the textarea is evaulated on the full origin of the target site. This creates DOM XSS but they needed a remote way to exploit it.postMessage handler that doesn't check the origin. So, any cross-origin window is able to call Evalulate on the editor. With this, they can achieve full XSS via some user interaction. The chain is a drag-n-drop switcheroo with an XSS payload onto the parent page that is triggered by a postMessage.virtio_snd_handle_rx_xfer() there is a code for computing the proper size to use. This takes the size of a buffer and subtracts the size of a struct from it. However, this calculation can underflow by using a small buffer, giving us the first bug. In virtio_snd_pcm_in_cb() the usage of a buffer vs. the allocation is slightly off. First, the allocation size and the bounds check have an 8 byte difference, allowing for an 8 byte OOB write. The final bug was missing bounds check in the edge case of user provided values, creating another OOB write. This happened because the actual buffer allocation size was taken into account.virtio-9p. With each P9_TXATTRCREATE a host-side buffer is allocated with a name and value field, where the size is arbitrarily controlled. It can be written back to and read through later. An allocation on demand with a choosen size, fully controllable contents and the ability to free as needed. This is perfect for heap exploitation!0x210-0x2f0 bin it's overallocated. After reurning this chunk, a write to it will corrupt the size of the FOLLOWING chunk ahead of it with a user controlled value of 0x400. Now, the chunk has a complete overlap with the chunk ahead of it. This is a super useful state to be in.V9fsFidState, they were able to produce an arbitrary read/write from the tcache poisioning primitive. By allocating an overlapped chunk with this object, there is a pointer that is directly controllable. This can be used for both reads and writes, which is effectively game over at this point.codeChallenge was in the URL when the SSO page loaded. The parent page generates the PKCE and includes the challenge as part of the SSO URL. This is a problem though: the challenge must be generated and unique to the request! If an attacker can set it, then it bypasses the entire purpose of PKCE.postMessage. A lesser-known mechanism is the MessageChannel API. This creates a private, bidirectional communication pipe between two contexts. Once a MessagePort is transferred, the channel is considered secure. This functionality was used on SSO login embedded within an iframe and used to deliver OAuth authorization codes ot the parent application.postMessage, it's common to miss an origin check. With MessageChannel, it works differently. You create a channel with two ports, you send one of the ports to another page, and use the other port yourself. There's no need to verify the origin because it's inherently private. So, how does the other page get this port? postMessage of course!postMessage(). The parent responds with a MessagePort.MessagePort.postMessage with the port was using a * as the sender. Additionally, the origin wasn't being validated on the sender. In practice, this means that the first submitted port wins.isMaster() without checking isReadOnly(). With a narrower prompt focusing on this, they found 3 bugs and a fourth on in JWT aud claim lacking verification.