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!
snap-confine is a SUID-root program installed by default on Ubuntu. This is a package manager, similar to apt, for Linux distributions. While reviewing the source code of the package manager, they were about to quit. However, they found a typo within the main function of the program. When checking for permission checks, they noticed that the real_gid of the user was being compared with the getuid function and vice versa. Because of other checks above, this was not exploitable though. XDG_RUNTIME_DIR ENV variable was not set, then the contents of this buffer were passed to a helper program. However, this turned out to be unexploitable since they do not control the value and several ENV variables are cleared as defense in depth. snap-confine dynamically obtains the path to snap-update-ns and snap-discard-ns by reading its own path via /proc/self/exe. If snap-confine can be hardlinked into a directory that we own, then the helper script can be set to something that we control. Since this runs as root, this is quite impactful!fs.protected_hardlinks being set to 1. If this is set to 0, this is exploitable. After going through several issues with AppArmour profile, they eventually were able to figure out a work around to get an unconfined root shell. /tmp/snap.$SNAP_NAME/tmp or reuses the directory if it already exists. Once there, it bind mounts it onto /tmp inside the snap mount namespace. To prevent race conditions in this, calls are made with the O_NOFOLLOW and O_DIRECTORY flags. mount syscall will follow symlinks. To exploit this, it requires a very tight timing though. After the call to open but before the call to change the ownership (fchown), the symlink needs to be created. Then, the mount will follow the link. /tmp/snap.lxd can be monitored with inotify, pinning both processes to a single CPU and lowering snaps scheduling priority. That is quite the setup to relibly win the race! I had never heard of this setup; so, this may be a good trick to use in the future.libmount in the unmounting code. First, if the text (deleted) is found in the end of a text, the text is simply removed. For instance, an attacker could mount /tmp (deleted). When we attempt to delete this, it will actually delete /tmp. Neat!strncmp(user_id, uidstr, sz) == 0 is used. The sz of the string compare is calculated from the current users ID and NOT the larger between the two. As a result, 1000 and 100 would look the same, when truncated. realpath(). The second bug was an off by one buffer overflow/underflow in getcwd() IF the size of the buffer is one and the PATH of the resolving file is larger than PATH_MAX.