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!

5 RCEs in npm for $15,000- 622

Robert ChenPosted 4 Years Ago
  • Package managers are a complicated ecosystem. We are installing code to run on our system. In the context of most package managers, we must choose to execute this code by calling functionality. This means that the package being installed should not be able to compromise the computer prior to being executed. With this in mind, we need to guarantee that this is the case!
  • The package node-tar promises that any extraction will NOT overwrite files outside the given directory. Since NPM install deals with tarballs, finding a bypass would be super interesting!
  • The first vulnerability is exactly this. The tarball extraction validates that an absolute path is not being used. But, this check looks weak, as only a single substring comparison; this comparison simply tried to strip off the beginning path if it had a '/'. Putting /// (three slashes) would change to // (two slashes), bypassing the filter. Now, we can write a file to any location upon installation!
  • To patch this vulnerability, some serious validation had to be done. The bulk of the patch used the isAbsolute function to check to see if the path was absolute or not. If we could find a way to get a difference between the resolve (executor) and isAbsolute (parser) we could bypass this check.
  • On Windows, C:/ can mark an absolute path. Using C:/different/root, the path.resolve function has a weird special case when the second parameter is an absolute path; it just resolves it! Additionally, the double dot (../) is only done between path deliminators. As a result, C:../ is a valid drive that we can use. This allowed for a minimal directory traversal (one directory up). Using a symlinked package, this could be used to do more damage though.
  • node-tar handles hardlinks and symbolic links (but not NPM). But, there is a guarantee that these will not overwrite files out of the current directory. However, there is a directory cache which executes these checks. If we could get a fake entry in the cache, we could use it to do the classic symlink and hardlink attack, resulting in arbitrary files being written to the system!
  • The first POC was fairly simple. Add a folder, tar this folder and remove the directory. At this point, the legit file system is different than cached system. By adding a symbolic link with the same name as our folder, extracting the archive, it will simply trust the file being written to on the symbolic link. Damn, that's real bad!
  • To patch the original bug, they just remove the entry from the directory cache. To bypass this, implementation specific Windows code could be used to cause havoc on Unix. The check for \\ should be checked on Windows but NOT Unix. Now, the same bug could bypass the filtering functionality because the FS was Unix and not Windows. The same exploit as before works except that the file name a\\x can be used instead in the cache.
  • The NEXT patch was even more defense-in-depth than before with complete path normalization (which seems great). However, MacOS does additional normalization of paths, violating the assumptions for the directory cache. Using unicode craziness on MacOs, it is possible to desync the cache and file system to use the same exploit as before.
  • These bugs were going to be endless... as a result, the project decided to drop the cache entirely if a symlink was discovered, killing the bug class. Good on them!
  • Overall, I enjoyed the content of the article but I found some of the explanations to be extremely clear; this forced me to put a lot of time into the article to figure out what the bug was. Once I understood the bugs, they were awesome though! Parsing & action is really hard to do as different systems have different expectations.