AppArmor

Basic Information

AppArmor is a kernel enhancement to confine programs to a limited set of resources. It’s a Mandatory Access Control or MAC that binds access control attributes to programs rather than to users.
AppArmor confinement is provided via profiles loaded into the kernel, typically on boot.
AppArmor profiles can be in one of two modes:

  • Enforcement: Profiles loaded in enforcement mode will result in enforcement of the policy defined in the profile as well as reporting policy violation attempts (either via syslog or auditd).
  • Complain: Profiles in complain mode will not enforce policy but instead report policy violation attempts.

AppArmor differs from some other MAC systems on Linux: it is path-based, it allows mixing of enforcement and complain mode profiles, it uses include files to ease development, and it has a far lower barrier to entry than other popular MAC systems.

Parts of AppArmor

  • Kernel module: Does the actual work
  • Policies: Defines the behaviour and containment
  • Parser: Loads the policies into kernel
  • Utilities: Usermode programs to interact with apparmor

Profiles path

Apparmor profiles are usually saved in /etc/apparmor.d/
With sudo aa-status you will be able to list the binaries that are restricted by some profile. If you can change the char “/” for a dot of the path of each listed binary and you will obtain the name of the apparmor profile inside the mentioned folder.

For example, a apparmor profile for /usr/bin/man will be located in /etc/apparmor.d/usr.bin.man

Commands

aa-status     #check the current status 
aa-enforce    #set profile to enforce mode (from disable or complain)
aa-complain   #set profile to complain mode (from diable or enforcement)
apparmor_parser #to load/reload an altered policy
aa-genprof    #generate a new profile
aa-logprof    #used to change the policy when the binary/program is changed
aa-mergeprof  #used to merge the policies

Creating a profile

  • In order to indicate the affected executable, absolute paths and wildcards are allowed (for file globbing) for specifying files.
  • To indicate the access the binary will have over files the following access controls can be used:
    • r (read)
    • w (write)
    • m (memory map as executable)
    • k (file locking)
    • l (creation hard links)
    • ix (to execute another program with the new program inheriting policy)
    • Px (execute under another profile, after cleaning the environment)
    • Cx (execute under a child profile, after cleaning the environment)
    • Ux (execute unconfined, after cleaning the environment)
  • Variables can be defined in the profiles and can be manipulated from outside the profile. For example: @{PROC} and @{HOME} (add #include <tunables/global> to the profile file)
  • Deny rules are supported to override allow rules.

aa-genprof

To easily start creating a profile apparmor can help you. It’s possible to make apparmor inspect the actions performed by a binary and then let you decide which actions you want to allow or deny.
You just need to run:

sudo aa-genprof /path/to/binary

Then, in a different console perform all the actions that the binary will usually perform:

/path/to/binary -a dosomething

Then, in the first console press “s” and then in the recorded actions indicate if you want to ignore, allow, or whatever. When you have finished press “f” and the new profile will be created in /etc/apparmor.d/path.to.binary

{% hint style=“info” %} Using the arrow keys you can select what you want to allow/deny/whatever {% endhint %}

aa-easyprof

You can also create a template of an apparmor profile of a binary with:

sudo aa-easyprof /path/to/binary
# vim:syntax=apparmor
# AppArmor policy for binary
# ###AUTHOR###
# ###COPYRIGHT###
# ###COMMENT###

#include <tunables/global>

# No template variables specified

"/path/to/binary" {
  #include <abstractions/base>

  # No abstractions specified

  # No policy groups specified

  # No read paths specified

  # No write paths specified
}

{% hint style=“info” %} Note that by default in a created profile nothing is allowed, so everything is denied. You will need to add lines like /etc/passwd r, to allow the binary read /etc/passwd for example. {% endhint %}

You can then enforce the new profile with

sudo apparmor_parser -a /etc/apparmor.d/path.to.binary

Modifying a profile from logs

The following tool will read the logs and ask the user if he wants to permit some of the detected forbidden actions:

sudo aa-logprof

{% hint style=“info” %} Using the arrow keys you can select what you want to allow/deny/whatever {% endhint %}

Managing a Profile

#Main profile management commands
apparmor_parser -a /etc/apparmor.d/profile.name #Load a new profile in enforce mode
apparmor_parser -C /etc/apparmor.d/profile.name #Load a new profile in complain mode
apparmor_parser -r /etc/apparmor.d/profile.name #Replace existing profile
apparmor_parser -R /etc/apparmor.d/profile.name #Remove profile

Logs

Example of AUDIT and DENIED logs from /var/log/audit/audit.log of the executable service_bin:

type=AVC msg=audit(1610061880.392:286): apparmor="AUDIT" operation="getattr" profile="/bin/rcat" name="/dev/pts/1" pid=954 comm="service_bin" requested_mask="r" fsuid=1000 ouid=1000
type=AVC msg=audit(1610061880.392:287): apparmor="DENIED" operation="open" profile="/bin/rcat" name="/etc/hosts" pid=954 comm="service_bin" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0

You can also get this information using:

sudo aa-notify -s 1 -v
Profile: /bin/service_bin
Operation: open
Name: /etc/passwd
Denied: r
Logfile: /var/log/audit/audit.log

Profile: /bin/service_bin
Operation: open
Name: /etc/hosts
Denied: r
Logfile: /var/log/audit/audit.log

AppArmor denials: 2 (since Wed Jan  6 23:51:08 2021)
For more information, please see: https://wiki.ubuntu.com/DebuggingApparmor

Apparmor in Docker

Note how the profile docker-profile of docker is loaded by default:

sudo aa-status
apparmor module is loaded.
50 profiles are loaded.
13 profiles are in enforce mode.
   /sbin/dhclient
   /usr/bin/lxc-start
   /usr/lib/NetworkManager/nm-dhcp-client.action
   /usr/lib/NetworkManager/nm-dhcp-helper
   /usr/lib/chromium-browser/chromium-browser//browser_java
   /usr/lib/chromium-browser/chromium-browser//browser_openjdk
   /usr/lib/chromium-browser/chromium-browser//sanitized_helper
   /usr/lib/connman/scripts/dhclient-script
   docker-default

By default Apparmor docker-default profile is generated from https://github.com/moby/moby/blob/master/profiles/apparmor/template.go

docker-default profile Summary:

  • Access to all networking
  • No capability is defined (However, some capabilities will come from including basic base rules i.e. #include <abstractions/base> )
  • Writing to any /proc file is not allowed
  • Other subdirectories/files of /proc and /sys are denied read/write/lock/link/execute access
  • Mount is not allowed
  • Ptrace can only be run on a process that is confined by same apparmor profile

Once you run a docker container you should see the following output:

1 processes are in enforce mode.
   docker-default (825) 

Note that apparmor will even block capabilities privileges granted to the container by default. For example, it will be able to block permission to write inside /proc even if the SYS_ADMIN capability is granted because by default docker apparmor profile denies this access:

docker run -it --cap-add SYS_ADMIN --security-opt seccomp=unconfined ubuntu /bin/bash
echo "" > /proc/stat
sh: 1: cannot create /proc/stat: Permission denied

You need to disable apparmor to bypass its restrictions:

docker run -it --cap-add SYS_ADMIN --security-opt seccomp=unconfined --security-opt apparmor=unconfined ubuntu /bin/bash

Note that by default AppArmor will also forbid the container to mount folders from the inside even with SYS_ADMIN capability.

{% hint style=“info” %} Usually, when you find that you have a privileged capability available inside a docker container but some part of the exploit isn’t working, this will be because docker apparmor will be preventing it. {% endhint %}

AppArmor Docker breakout

You can find which apparmor profile is running a container using:

docker inspect 9d622d73a614 | grep lowpriv
        "AppArmorProfile": "lowpriv",
                "apparmor=lowpriv"

Then, you can run the following line to find the exact profile being used:

find /etc/apparmor.d/ -name "*lowpriv*" -maxdepth 1 2>/dev/null

In the weird case you can modify the apparmor docker profile and reload it. You could remove the restrictions and “bypass” them.