Kernel Hardening - security-misc

Luckily the bash test built-in can check for any of these.

The following script sets the following variables:

  • suid
  • sgid
  • stickybit

To either true or false.

#!/bin/bash

set -e

while read -r line ; do
   if ! read -r file_name existing_mode ; then
      continue
   fi

   string_length_existing_mode="${#existing_mode}"

   suid=false
   sgid=false
   stickybit=false

   if [ "$string_length_existing_mode" = "4" ]; then

      if test -u "$file_name" ; then
         suid=true
         echo "suid - file_name: '$file_name' | existing_mode: '$existing_mode'"
      fi
      if test -g "$file_name" ; then
         sgid=true
         echo "gid - file_name: '$file_name' | existing_mode: '$existing_mode'"
      fi
      if test -k "$file_name" ; then
         stickybit=true
         echo "sticky - file_name: '$file_name' | existing_mode: '$existing_mode'"
      fi

   fi
   if [ "$string_length_existing_mode" -gt "4" ]; then
      error_code=2
      echo "error 2..." >&2
      continue
   fi
   if [ "$string_length_existing_mode" -lt "3" ]; then
      error_code=3
      echo "error 3..." >&2
      continue
   fi

done < <( stat -c "%n %a" /usr/bin/* )

suid - file_name: ‘/usr/bin/at’ | existing_mode: ‘6755’
gid - file_name: ‘/usr/bin/at’ | existing_mode: ‘6755’
gid - file_name: ‘/usr/bin/bsd-write’ | existing_mode: ‘2755’
gid - file_name: ‘/usr/bin/chage’ | existing_mode: ‘2755’
gid - file_name: ‘/usr/bin/crontab’ | existing_mode: ‘2755’
gid - file_name: ‘/usr/bin/dotlockfile’ | existing_mode: ‘2755’
gid - file_name: ‘/usr/bin/expiry’ | existing_mode: ‘2755’
suid - file_name: ‘/usr/bin/newgidmap’ | existing_mode: ‘4755’
suid - file_name: ‘/usr/bin/newuidmap’ | existing_mode: ‘4755’
suid - file_name: ‘/usr/bin/passwd’ | existing_mode: ‘4755’
suid - file_name: ‘/usr/bin/pkexec.security-misc-orig’ | existing_mode: ‘4755’
suid - file_name: ‘/usr/bin/sudo’ | existing_mode: ‘4755’

Does that help?

1 Like

Yes. This should be added to permission-hardening to remove all SUID/SGID bits.

Maybe instead of using

/bin u-s root root -R

we can add a nosuid argument e.g.

/bin nosuid

which would use the script above to find and remove all SUID bits and add entires to dpkg-statoverride.

1 Like

I’ve tried implementing this. What do you think of Debian paste error?

I’ve simplified your script above and used it in this. I use stat -c "%n %a %U %G" $(find ${file} -type f) instead of stat -c "%n %a %U %G" ${file}/* as the * won’t find files like /usr/bin/dir/file. It will only look for /usr/bin/file. $(find ...) probably isn’t the correct way to do this though.

I’ve also used --update for dpkg-statoverride instead of chmod/chown.

The while read -r line part in set_file_perms is still breaking due to it parsing empty lines and I don’t know how to fix that.

If you want match sub folders one option is bash’s:

shopt -s globstar

And then use ** instead of *, I think. Gotta test. More comments soon.

1 Like

There’s a global variable

nosuid=true

and once it’s set it is never reset/unset/set to something else as far as I can see.

1 Like

file globstartest

#!/bin/bash

set -x

shopt -s globstar

for file_name in /usr/share/** ; do
   true "$file_name"
done

./globstartest

excerpt:

  • for file_name in /usr/share/**
  • true /usr/share/zuluCrypt/translations/zuluCrypt-gui/de_DE.qm
  • for file_name in /usr/share/**
  • true /usr/share/zuluCrypt/translations/zuluCrypt-gui/en_US.qm
  • for file_name in /usr/share/**
  • true /usr/share/zuluCrypt/translations/zuluCrypt-gui/fr_FR.qm
  • for file_name in /usr/share/**
  • true /usr/share/zuluCrypt/zuluCrypt.pdf
1 Like

I made some fixes: Debian paste error

It seems to work. Need to test it more though.

Example of a config file: Debian paste error

1 Like

This doesn’t work properly for some reason. Some SUID binaries still exist.

root@host:/usr/lib/security-misc# find / -perm /4000 -user root 2>/dev/null
/bin/umount
/bin/mount
/bin/su
/usr/bin/sudo
/usr/bin/firejail
/usr/bin/gpasswd
/usr/bin/bwrap
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/pkexec.security-misc-orig
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
root@host:/usr/lib/security-misc#

Some directories like /usr/local/lib/python2.7/site-packages are SGID and removing that might break things.

When executing the script I also get some errors but they don’t seem important.

root@host:/usr/lib/security-misc# ./permission-hardening 
dpkg-statoverride: warning: stripping trailing /
dpkg-statoverride: warning: no override present
dpkg-statoverride: warning: stripping trailing /
dpkg-statoverride: warning: no override present
suid - file_name: '/usr/bin/sudo' | existing_mode: '4755'
dpkg-statoverride: warning: stripping trailing /
dpkg-statoverride: warning: no override present
dpkg-statoverride: warning: stripping trailing /
dpkg-statoverride: warning: no override present
ERROR: File '/lib32/' does not exist!
dpkg-statoverride: warning: stripping trailing /
dpkg-statoverride: warning: no override present
ERROR: File '/usr/lib32/' does not exist!
ERROR: File '/usr/lib64/' does not exist!
dpkg-statoverride: warning: stripping trailing /
dpkg-statoverride: warning: no override present
ERROR: File '/usr/local/lib32/' does not exist!
ERROR: File '/usr/local/lib64/' does not exist!
stat: cannot stat '/usr/bin/bwrap/**': Not a directory
stat: cannot stat '/usr/lib/policykit-1/polkit-agent-helper-1/**': Not a directory
stat: cannot stat '/usr/lib/dbus-1.0/dbus-daemon-launch-helper/**': Not a directory
root@host:/usr/lib/security-misc#
1 Like

There is still a code path where unset nosuid might not be set.

if [ "${mode}" = "nosuid" ]; then
  nosuid=true

I’d suggest to add nosuid="" or nosuid=false on top. I.e.

nosuid=""
if [ "${mode}" = "nosuid" ]; then
  nosuid=true

Then we could be sure it is reset in any case.

Could you please add the license header?

Need more debugging. I recocommend to set -x at the top or to echo the dpkg-statoverwrite command before executing it.

Because it’s a folder.

if ! [ -e "${file}" ]; then

will return non-zero, i.e. throw that error message. To check for folder

if [ -d "${file}" ]; then

Also we might not call it file but file_system_object since it could be anything (file, folder, device, socket). Also file is a non-ideal name since file is a standard command line tool.

It is probably running:

stat -c "%n %a %U %G" /usr/bin/bwrap/**

stat: cannot stat ‘/usr/bin/bwrap/**’: Not a directory

This won’t work. add_statoverride_entry needs to check if it is a file or a folder.

1 Like

Alternatively the config file could declare if it is a file or folder. But then we still need to check this in case this changes?

1 Like

No, it actually doesn’t exist. That error doesn’t matter. Those lines are just for systems that do use things like /lib32/ instead of just /lib/.

No, -e checks if anything exists there (so files and folders). -f is the one that checks for just files.

It shouldn’t be running that at all. It should be running

stat -c "%n %a %U %G" /usr/bin/**

There is no bwrap nosuid rule so stat shouldn’t even be used on bwrap.

1 Like

Adding nosuid="" fixed this.

1 Like

There’s a mention of an updated grsec kernel being used in this new SecureDrop release. Anything useful in this tarball?

2 Likes

That’s only if you’ve already paid for grsecurity.

You must have a grsecurity subscription in order to fetch the patches for use in building.

They just maintain a build system.

2 Likes

I’m working on enabling hardening features in security-misc for newer kernel versions (e.g. init_on_{,free,alloc} or lockdown) but I’m having trouble figuring out how to check if the current kernel version is greater or equal to the version the feature was introduced in.

e.g. for lockdown I have

kver="$(uname -r)"

if [ "${kver}" -ge "5.4" ]; then
  GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX lockdown=confidentiality"
fi

but this fails with an error saying “integer expression expected”.

I think the errors are because the numbers both have dots in them. Removing the dots makes it work fine.

Do you know how to solve this?

1 Like

script:

#!/bin/sh

set -x

kver="$(uname -r)"

if [ "${kver}" -ge "5.4" ]; then
  GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX lockdown=confidentiality"
fi

xtrace:

  • uname -r
  • kver=4.19.79-1.pvops.qubes.x86_64
  • [ 4.19.79-1.pvops.qubes.x86_64 -ge 5.4 ]
    ./x: 7: [: Illegal number: 4.19.79-1.pvops.qubes.x86_64

Thinking…

dpkg --compare-versions

?

(Whonix source code uses this in some places.)

Is this required? What happens when enabling lockdown=confidentiality on a too old kernel? It’s simply ignored? If the answer is yes, then that is not so bad. Or does something break? Well, then the “if” needs to be sorted indeed.

2 Likes

That seems to work.

Lockdown specifically won’t break, the parameter will just be ignored, but we do need to compare versions to figure out whether to use init_on_alloc=1 init_on_free=1 or page_poison=1 slub_debug=P.

1 Like

Also, I need someone to test if init_on_free=1 breaks in Qubes like page_poison=1 does.

1 Like

Could you please add license header and add the permission hardening script and config to git?

1 Like

9 posts were split to a new topic: permission hardening