Exploiting CVE-2021-26708 (Linux kernel) with sshd March 1, 2022 | 6 min Read

Exploiting CVE-2021-26708 (Linux kernel) with sshd

Kernel vulnerability CVE-2021-26708

Alexander Popov has published an article Four Bytes of Power: Exploiting CVE-2021-26708 in the Linux kernel which use a four-byte overwrite vulnerability to do privilege escalation. It’s really an excellent article that you can find all the details of reproducing privilege escalation. In short, the exploit has several steps as follow:

  1. Alloc a good msg at a predictable address: win a race on vsock; Read heap address leaked from the /dev/kmsg and then use msgsnd() to alloc good msg at that address.
  2. Arbitary-free good msg: win a race on vsock; overwrite msg->security with the leaked address; use failed msgsnd alloc a corrupt msg and free msg->security. Plz noted that the good msg can be freed but it’s still in msg queue, which is the spot where UAF become feasible.
  3. Arbitary-write good msg: Arbitary read primitives could be forged by allocating the objects via setxattr at the address of good msg and then overwrite it.
  4. Arbitray-read from good msg: leak content of vsk structure by using msgrcv().
  5. Alloc a sk_buff with special flags in its buff.
  6. Similar to step-4: Search that special flags in the heap to get the address of sk_buff.
  7. Similar to step-2: Free sk_buff.
  8. Similar to step-3: Overwrite sk_buff function pointer, control-flow can be hyjacked.
  9. ROP to do privilege escalation.

This is the way: sshd

You might noticed that step-4 is able to get leaked address of owner_cred, while step-2 can achieve arbitary free. The cred structure is allocated in the special slab cache cred_jar. If multiple privileged processes are created and the cred pointer being freed occurs at the same time (or the privileged processes are created a bit little later than cred pointer being freed), then the cred of one of the processes can be located at the freed address, which make privilege escalation possible. sshd seems the privileged process we need. An ssh connection with login completed has two processes:

$ ps -axu|grep ssh
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
...
root         924  0.6  0.5  43876 10576 ?        Ss   01:47   0:00 sshd: victim [priv]
victim       939  0.0  0.3  43776  6196 ?        S    01:47   0:00 sshd: [email protected]/0
...

An ssh connection to the target without login would look like this:

$ ps -axu|grep ssh
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
sshd         988  0.0  0.2  30564  5676 ?        S    01:54   0:00 sshd: victim [net]

The privilege of client ssh won’t matter because sshd, whose user is root by default, will establish the connection. sshd will wait for the password input until timeout. These mechaism might make exploit writer’s job easier. The following steps should be:

  1. Alloc a good msg at a predictable address: win a race on vsock; Read heap address leaked from the /dev/kmsg and then use msgsnd() to alloc good msg at that address.
  2. Arbitary-free good msg: win a race on vsock; overwrite msg->security with the leaked address; use failed msgsnd alloc a corrupt msg and free msg->security. Plz noted that the good msg can be freed but it’s still in msg queue, which is the spot where UAF become feasible.
  3. Arbitary-write good msg: Arbitary read primitives could be forged by allocating the objects via setxattr at the address of good msg and then overwrite it.
  4. Arbitray-read from good msg: leak content of vsk structure by using msgrcv().
  5. Prepare 20 “ssh [email protected]&” on your host, but don’t run them.
  6. Similar to step-2, arbitrary free owner_cred. After the owner_cred was freed, run those ssh right now. One of those sshd’s cred would locate at owner_cred. The privilege escalation would be done!

We can see the result:

[[email protected] ~]$ uname -a
Linux hardenedvault 5.10.11-200.fc33.x86_64 #1 SMP Wed Jan 27 20:21:22 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

[[email protected] ~]$ id
uid=1001(victim) gid=1001(victim) groups=1001(victim) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[[email protected] ~]$ ./cve-2021-26708
[+] finish init userfaultfd:
        page fault addr: 5004000
[+] try to get addr of good msg_msg...
[+] 1598'th finished get addr of good msg_msg
[+] get the address of good msg after 1598 trys
[+] addr_of_good_msg: 0xffff977a85f71340
[+] addr_of_vsk: 0xffff977a8555e600
[+] adapt the msg_msg spraying payload:
        msg_ptr 0x5003fd8
        m_type 1337 at 0x5003fe8
        m_ts 6096 at 0x5003ff0
        msgseg next 0xffff977a8555e600 at 0x5003ff8
[+] prepare setxattr threads...
[+] start read userfault...
[+] Start free good msg...
[+] msgsnd failed: msg->security maybe freed
[+] intriguring userfaultfd page fault(msg)
        page fault address: 5004000 flags: 0
[+] try to read kernel leak...
[+] 8177'th finished read the leak
[+] read leak:
        sk_def_write_space: ffffffff939851b0
        owner_cred: ffff977a83e15600
        sk_memcg: ffff977a85549000
[+] Start free cred...
[+] msgsnd failed: owner cred maybe freed !!!
[+] 100'th free owner_cred, waitting for sshd
[[email protected] ~]# id
uid=0(root) gid=0(root) groups=0(root) context=system_u:system_r:unconfined_t:s0-s0:c0.c1023

Some details about sshd help cred-jar heap spraying

Is it possible to exploit in one single step?

Actually, it’s possible, but you need to craft a special process which create a process before arbitrary-free owner_cred and waiting until owner_cred is freed, starts ssh to localhost.

ssh with login or not

sshd may terminate because of timeout. ssh and login would be better. In this case, a kernel panic appears when sshd is terminated, but the time is enough to do lots of evil things! LoginGraceTime in /etc/ssh/sshd_config is for configuring it.

multiple ssh would be better

Since cred_jar cache may be affected by other processes. Running more than one ssh would be more stable.

Limitation

The ssh-server should be enabled by the target

Obviously, the ssh-server running on the target machine is necessary for the privilege escalation. We haven’t investigate if there is another way that an unprivileged process can intrigue creating privileged processes to help cred_jar heap spraying. If a process satisfies the following:

  1. Unprivileged process can let the target create a privileged process.
  2. One privileged process response to one unprivileged process.
  3. The privileged process has a long lifetime.

Then it can be used to do cred_jar heap spraying.

This may affect by other processes

If the target is a server with lots of processes created and terminated, the privilege may be affected by them.

PoC and defense

What GNU/Linux distor and kernel version has been tested?

Federa 33 and 5.10.11-200.fc33.x86_64.

Is PoC/Exploit open source?

Yes, you can find the PoC on project Vault Range PoC.

Any suggestion on mitigation/defense?

There are quite a lot of effective mitigations for this exploit but we’d like to point out that some mitigations are more important than others while data-only attack can be achieved by arbitrary writes, which is likely bypass most mitigation in two stages: exploitation and post-exploitation. Noted that PaX/GRsecurity’s KERNSEAL/AUTOSLAB is an exceptional outstanding mitigation because it’s targeting the Pre-exploitation stage.

Mitigation Pre-exploitation Exploitation Post-exploitation Bypassable
PaX RAP N Y N L4: Hardcore
PaX KERNSEAL/AUTOSLAB Y N N L5: Nightmare
VED wCFI N Y N L3: Hurt Me Plenty
Metadata integrity N N Y, AKO/LKRG/VED L2: Bring It On
VED self-protection N Y N L3: Hurt Me Plenty

With all due respect, the bypassable level of most fancy name security product (e.g: EDR/XDR, HIDS, etc) is only able to holding “L1: I can win” if the threats from platform security level according to our client’s report.

Demo

alter-text
an image caption