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!

cr8escape: New Vulnerability in CRI-O Container Engine - 801

John Walker & Manoj Ahuje - CrowdStrike    Reference →Posted 4 Years Ago
  • CRI-O is an implementation of the Kubernetes Container Runtime interface. It is a lightweight altnerative to using Docker with Kubernetes.
  • The CRI-O interface uses the pinns utility to set kernel options for a pod. Recently, pinns added support for sysctl. Pinns will blindly add kernel parameters, including sysctl without any validation. This is an interesting primitive: can we do anything with this?
  • The sysctl flag kernel.core_pattern can be used for how the kernel should react to a core dump. In this case, we will set this to a script that we control within our container to be triggered. Since this needs to be an absolute path, we also need to find the full path of the script. This can be done via the mount command.
  • It does appear that this is not the standard type of container escape where an attacker has code execution within the context of a box. Instead, they need to be able to specify parameters within the YAML file for the initialization of the pod. Although this is cool, it is not as impactful as it could be, since control over the pod settings is required.

Dirty Pipe Vulnerability - 800

Max Kellermann     Reference →Posted 4 Years Ago
  • The author of this article is not a security researcher; they are a software developer. They found this vulnerability in the Linux kernel while trying to track down a software bug. In particular, a CRC at the end of a file kept getting corrupted and they couldn't figure out why. The corruption just kept on happening and they HAD to get to the bottom of it.
  • The corruption was happening within the CRC of a log file. The functionality works like this:
    1. Web service writes a ZIP header.
    2. Splice is used to send all compressed files. This syscall is used for copying data between one file to another in kernel space, which helps with I/O performance.
    3. write is called to write the directory file header. This is 50 4b 01 02 1e 03 14 00. This is EXACTLY the corruption that is happening.
  • After debugging this for a while, they came to the conclusion that this MUST be a kernel bug. They wrote up a quick proof of concept to demonstrate the bug; this was composed of two progams running simultanously. The first program was writing data to a file, acting as the the log splitter. The second program was running splice followed by a call to write, simulating the zip generator. Even though the write was NOT occuring on the call to splice, the string from the second write appeared to be in the file being read. Specifically, this problem existed in pipes.
  • The Linux kernels smallest unit of memory managed by the CPU is a page (4kB). If an application requests memory from the kernel, it will get a number of pages; all I/O happens with pages. For instance, if you read in a file, the kernel wil first copy a number of 4kB chunks form the hard disk into kernel memory called a page cache. A pipe is a unidirectional file descriptor used for inter-process communication. One end is for pushing (writing) data, while the other end of it pulls the data.
  • When using a pipe, a reference to a page cache entry is made via the data structure pipe_buffer. These objects have a reference to the backing memory page and have several flags for what permissions and things are allowed. The main bug is that the flags for this are NOT initialized when using pipes. In Linux 5.8, the flag PIPE_BUF_FLAG_CAN_MERGE was added, which indicates whether a page table is mergable or not. Practically, a poisoned set of flags, namely PIPE_BUF_FLAG_CAN_MERGE, allows data to be written to a page cache that the pipe does not own!
  • This can facilitate the ability to write and change the file data in the page cache even if it’s opened as read-only, giving a privilege escalation primitive. The data is never written to disk unless the page cache entry is considered dirty. Since the accidental write does not mark the page cache as dirty, the write to disk will never occur on disk; it will only happen in the cache. As a result, the file can be changed in memory, but never be written to disk, making this a real sly attack.
  • To exploit this vulnerability, the following steps need to happen:
    1. Create a pipe.
    2. Fill the pipe with arbitrary data. We want to set the PIPE_BUF_FLAG_CAN_MERGE flag in ALL of the entries.
    3. Drain the pipe (remove).
    4. Splice the data on the target file that is opened read only, just before the target offset that we want to overwrite.
    5. Write data into the pipe. This data will overwrite the cache file page instead of the data in the proepr buffer.
  • The author has a final note on this bug: "To make this vulnerability more interesting, it not only works without write permissions, it also works with immutable files, on read-only btrfs snapshots and on read-only mounts (including CD-ROM mounts). That is because the page cache is always writable (by the kernel), and writing to a pipe never checks any permissions."
  • To me, it's pretty crazy how the lack of initialization of a value caused such major problems. Even in the Linux kernel security hardened memory management system, there are security vulnerabilities. DayZeroSec has a good synopsis on this vulnerability as well.

webOS Revisited - Even More Mistaken Identities- 799

Andreas - recurity    Reference →Posted 4 Years Ago
  • WebOS is the operating system used in LG TVs. WebOS is web, but within an OS! Many things sound JavaScript-y because it is NodeJS running under the hood.
  • The Notification Manager is used by internal system services in order to manage notifications, such as alerting about system- and app updates etc. It cannot be called by regular applications and runs as root. The permission systems works based upon an allowlist of app Ids. Due to a logic flaw within notificationmgr/Settings.cpp, the notifications can be bypassing by sending a request through the luna-send-pub tool. This is not a vulnerability by itself, but does open up a new attack vector for us.
  • The luna://com.webos.notification/createAlert API of Notifications Manager allows for actions to be defined, such as onclick and many more. When calling these, there is a check to validate that the user has the proper permissions to call these APIs.
  • However, the permissions are handled on the API calls themselves via logic and not the file permissions on the operating system. It turns out that the nesting of these URIs is possible. Since the verification ONLY happens on the first URI, the verification can pass while the second call can now perform arbitrary actions. In this case, they call the normally denied luna://com.webos.service.downloadmanager/download.
  • There are MANY other ways to compromise the system with other calls. The important note is that not all of the data was properly verified on the request, which leads to an authentication bypass. Overall, good post on a two bugs that lead to privilege escalation.

AutoWarp: Critical Cross-Account Vulnerability in Microsoft Azure Automation Service- 798

Yanir Tsarimi - Orca Security     Reference →Posted 4 Years Ago
  • Microsoft Azure Automation allows customers to execute automation code in a managed fashion. You can schedule jobs, provide input and output, and more. Each customer’s automation code runs inside a sandbox, isolated from other customers’ code executing on the same virtual machine.
  • The researcher details their full process for finding the vulnerability. When trying to find a cross-client escape, the first thing to do is get a shell on the machine. Now, you can explore how the system works in full, including open ports, files, logs and many other things.
  • While looking through the logs, an interesting line popped up: http://127.0.0.1:40008. Within the web service (C#, which is easy to decompile), they reverse engineered the service to see what it did. Among the interesting things was a GET request to get tokens to allow for calls to be made in your account. By itself, this is fine.
  • The author noticed that the ports would jump around randomly within about 10 ports. So, they ran a port scan within the service and noticed that ports 4000-4010 were always being used. When making the API call to get the JWT token, you can request it for ANY of the accounts in the virtual machine! By making this call to other ports, you would get credentials for other accounts.
  • When vulnerabilities like this are found, it makes me think that there was no pentest on this. This is such a trivial vulnerability to allow for cross-account credential theft. Overall, good write up besides all of the marketing bots and non-sense.

Escaping Privleged Containers for Fun- 797

Jordy Zomer    Reference →Posted 4 Years Ago
  • Many people run containers for security reasons. Containers have great properties, such as resource isolation. However, sometimes, this isolation is a bad thing, which results in a feature called --privileged to be used. These opens up a whole new attack surface for escaping containers.
  • The author decided to hunt for call_usermodehelper_* family of functions because of CVE-2022-0492 .While grepping through the Linux source code they ran into the function call_usermodehelper (used to run a program/script in user mode) within the core dump functionality.
  • When reading through core, they found out where this was called: " If the first character of this file is a pipe symbol (|), then the remainder of the line is interpreted as the command-line for a user-space program (or script) that is to be executed." If the file within this pattern starts with a pipe (|) then it will run our program outside of the context of the container.
  • An obvious prerequisite of this is that the binary needs to be reachable by the host operating system. The folders within OverlayFS (Docker file system) are mounted and easily reachable. To find the location of the mount, the mount command can be ran from the context of the container to find the FULL file path of the container.
  • How do we trigger this script? Cause a core dump by getting a program to segfault. Writing real bad C is easy enough to do for this to work.
  • To run this exploit now, the following steps are used:
    1. Compile a binary with the code to run.
    2. Find out the path of the file system in the container via the mount command.
    3. Write the FULL path of the exploit file to /proc/sys/kernel/core_pattern.
    4. Cause a core dump with the bad C file.
  • Overall, this write up is real cool! Containers are not magic; they are features of the Linux Operating system being used. By understanding how resources are isolated, we can escape the container in question.

Oh Snap! More Lemmings (Local Privilege Escalation in snap-confine)- 796

Qualys    Reference →Posted 4 Years Ago
  • 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.
  • Again, while doing source code review, they noticed a code path where an uninitialized variable could be used. If the 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!
  • The above is impossible to exploit in a default configuration because of the configuration 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.
  • When setting up the snap sandbox (mount namespace), it is done by creating a temporary directory at /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.
  • But, they were NOT careful enough. The 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.
  • To relibly win this race condition, the file /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.
  • What does this primitive do? Inside the snap mount namespace, an attacker can bind-mount a world-writable, non-sticky directory onto /tmp, or an attacker can bind-mount any other part of the filesystem onto /tmp. The rest of the article goes over two case studies (Ubuntu Desktop and Ubuntu Server) to get a root shell.
  • The exploitation method was done on two different Ubuntu types: Desktop and Server. The main idea is to trick the directory being used in the request to be owned by the user instead of the program. Then, since we own this directory, an attacker can add their own scripts, which will run as root by the program. This required bypassing the App Armour configurations with complain profiles (instead of stop) and abusing some defaults snap libraries.
  • While going through the attack methods above, they explored MANY different avenues that did not work out. In the process, they found bugs in 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!
  • The similar unmounting effect can be done in certain situations because of a string truncation issue while verifying the user id. When checking the ownership, the code 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.
  • To wrap this up, they found two more bugs within gblic. The first one is an uninitialized memory read via a strange and unlikely flow within 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.
  • Overall, I love the in depth nature of the exploitation process. From finding the bug, the methodology behind the exploit, the failures and everything in between. These Qualys reports feel like what Security Research is all about. This is worth spending lots of time on in order to understand fully; many things had to be skipped above so that this resource was not the same length as the article!

Multiple vulnerabilities in Concrete CMS – part2 (PrivEsc/SSRF/etc)- 795

Adrian Tiron - Fortbridge    Reference →Posted 4 Years Ago
  • Concrete is a Content Management platform similar to Wordpress.
  • User groups can have hierarchical structures and inherit permissions from each other. On the main endpoint simply dragging a lower privileged user into the admin group did nothing. However, they found another API that did had the group add but WITHOUT the permission check.
  • The authors found a piece of functionality that allowed for the downloading of a remote file; this is a clear SSRF bug waiting to happen. In order to make the exploitation harder, they decided to block the usage of AWS metadata endpoints and certain file extensions were blocked as well. How do we bypass this?
  • The extension denylist can be bypassed with a PHP trick. Some PHP engines will throw out extra parts of a URL path (/info.php/test.html) but still see the extension as HTML. Using this, a one shot GET exploit or a file disclosure bug is possible still.
  • They REALLY wanted the metadata though. So, they used the classic DNS rebinding technique causing a time of check vs. time of use problem (TOCTOU). Rebinder is a service for easily testing and using DNS rebinding in the wild.
  • The final vulnerability was link poisioning on a password reset link. The host header is commonly used for generating the password reset link when multiple hosts are used. However, this can be manipulated by an attacker. By sending a host header with an attacker controlled host, when a user clicks on the reset link, they will get the reset token.
  • My biggest takeaway from the easiness of the DNS rebinding with other tools! In the future, I may try this attack when SSRF IP denylists are being done. I have also seen hidden APIs not get updated properly with security functions.

Catching bugs in VMware: Carbon Black Cloud Workload Appliance and vRealize Operations Manager- 794

https://swarm.ptsecurity.com/catching-bugs-in-vmware-carbon-black-cloud-workload-appliance-and-vrealize-operations-manager/    Reference →Posted 4 Years Ago
  • Carbon Black Cloud Workload Appliance is the first target and VMware vRealize Operations Manager is the second target.
  • While reviewing the application, they found a service called getServiceToken API, which has 100% access in Spring. This API is internal authentication routes sends back a valid JWT to make further requests. Can we hit it? No, we cannot.
  • Now, there are routing and proxy servers they do a fair amount of route rewritting. The rule had two main points: the route /acs/api/v1/service-token will be redirected to /no_cloud and /acs/ will be forwarded to the backend. When using a denylists, it is important that the frontend and backend servers agree on a route perfectly.
  • From there, they did the best thing ever: they read the documentation. In the documentation, the Envoy points out that normalization of the path is disable by default. This means that URL encoding the path with the denylist will get the request forwarded as normal. Because, later on, the Tomcat server will normalize it themselves. Now, this is super user privileges for everything.
  • Moving to Operations Manager, they found several unauthenticated endpoints. One of the endpoints (/casa/nodes/thumbprints), takes an IP address as a parameter. Since this added a path, we can put a question mark (?) to use our complete path.
  • Using this SSRF, they could call authenticated endpoints with a GET request to steal passwords between nodes and other things. This was awesome since it gave us access to endpoints we normally would not have access to. To make this even better, we can steal this auth token by sending a request to ourselves.
  • Once we have these credentials, it is possible to reset the admin password. Since this is noisy and not fun, they wanted to find something else though. They found a fairly classic directy traversal that could be used to write a JSP-shell into the web directory. Now, we can access this endpoint ourselves to execute arbitrary commands on the device.
  • Overall, some great bugs! In particular, I enjoyed the non-traditional SSRF exploit and the URL encoding for the auth bypass. As we see here, denylists never seem to work

CVE-2022-0492 Affecting Cgroups: Can Containers Escape?- 793

Yuval Avrahami - Palo Alto Networks    Reference →Posted 4 Years Ago
  • Control Groups (cgroups) are a Linux feature that allows for admins to limit and isolate resources for processes. Cgroups are managed via cgroupfs, a management API exposed as a file system; editing this file system will affect the cgroups. There are many different cgroup subsystems, such as the memory group.
  • Each subsystem is mounted at /sys/fs/cgroup/<subsystem>. Within these cgroups, there can be childs as well, such as docker. The vulnerability in this post is within cgroups.
  • One of the features within v1 of the cgroup functionality is release_agent. It allows admins to run a script upon the termination of a process within a c group. This is done by writing to the cgroupfs filesystem at /sys/fs/cgroup/memory/release_agent. This script runs with root permissions with access to all namespaces.
  • CVE-2022-0492 simply does NOT check that the process setting the release_agent file has admin privileges (CAP_SYS_ADMIN). As a result, anybody who can set this file is able to escalate privileges. Can this be used as a container escape?
  • The Linux filesystem sets the owner of the file to be root. As a result, only the root user or users with the CAP_DAC_OVERRIDE capability are allowed to do this. Although root only being able to edit this doesn't seem like a problem, the root user may not have full capabilities. Having a namespace within a different container is a different story though.
  • By default, cgroups in containers are mounted read only. A work around for this is to use the unshare SYSCALL to create a child user and cgroup namespace. However, the release_agent is only in the root cgroup, making this not exploitable in some cases. As a result, only the root container process can set the release agent. Interesting!
  • An additional method is possible with the CAP_SYS_ADMIN permission we can mount to the cgroupfs with no questions asked. With this, it would be possible to set the release_agent file for privilege escalation.
  • SELinux and AppArmour prevent BOTH of theses attack from the gate. This is because these prevent mounting to the cgroupfs from the container, making all of the attacks possible. To see if you are vulnerable, there is a list in the article and a script as well.
  • Overall, a super interesting container escape from a simple bug. The analysis of exploitability was fascinating. Defense-in-depth does work to prevent attacks!

Finding an unseen SQL Injection by bypassing escape functions in mysqljs/mysql- 792

Stypr - Flatt Security    Reference →Posted 4 Years Ago
  • MySQL is the most popular MySQL package in NodeJS. By using this properly, all queries are safe against SQL injection with inputs as strings via prepared statements and escaping. However, what happens when the input for the prepared statement is NOT a string? Madness!
  • An example query is shown below:
    connection.query(
    "SELECT * FROM accounts WHERE username = ? AND password = ?",
     [username, password], func...)
    
    Normally, we would expect a string (like 'admin') as the input for the username and password fields. If the query is setup insecurely, we may be able to put arbitrary objects into these fields. I would call this an unintended use case.
  • If an object is inserted, then weird things to start to happen. In particular, inserting a nested object with a 1 will evaluate to true within MySQL. With the query above, sending object like below will bypass the logic:
    data = {
      username: "admin",
      password: {
        password: 1,
      },
    };
    
  • For demonstrating this technique, the author even has a here. This call passes the opinion to SQLString to do the actual formatting. Within SqlString.js, the function format can be found. The logic of this is as follows:
    1. Iterate through each of the provided values.
    2. Convert each data type into something that can be understood by regex. For objects, this is done by objectToValues.
    3. Take the value and escape all malicious characters using a regex.
    4. Replace the placeholder in the query with the actual value.
  • The function objectToValues works by getting the value of each key in the object then running the escape code on each KEY and VALUE. Once done, it sets the SQL to be `<key>` = <value> in the SQL. The backticks are used for literal SQL statements, such as the username column. Interesting and this is NOT how this should work.
  • As a result, when the object is inserted, we are actually altering the query itself! From the payload that we used above, it turns into the query ...`password = password` = 1. For whatever reason, ...`password = password` = 1 will evaluate to true (tested locally in MySQL repl), allowing any user to authenticate as that user. Wow, what a crazy chain of events that makes this work!
  • To not be vulnerable to this issue, simply validate the data being put into queries; an object should never be treated like a string anyway. Overall, I really enjoyed this post and diving into the root cause of the problem on my own :)