Hello,

aa_inet_connect_perm() returns success immediately for AF_UNSPEC before 
checking the active AppArmor profile.  AF_UNSPEC is the socket disconnect 
operation for TCP and UDP.  A process confined by a profile containing deny 
network can therefore disconnect inherited sockets even though ordinary socket 
creation and connect calls are denied.

HEAD commit:    fe2ec83746e501645709761605c2464a44fd2929
git tree:       upstream Linux checkout, local branch master
source version: Linux 7.3.0-rc4-70-gfe2ec83746e50
compiler:       gcc 11.4.0, GNU ld 2.38 (build details in reproduction notes)

console output:
https://gist.githubusercontent.com/shaos26/73b057c77b9ea22d5e6813c259dbf43c/raw/fab5c58d6f184f5fab88a3a31a80c2b919efecf0/console-output.txt
reproduction kernel config:
https://gist.githubusercontent.com/shaos26/73b057c77b9ea22d5e6813c259dbf43c/raw/b9192c97c92fdd3b59089704bad6fbd0a33ccf06/kernel-config.txt
reproducer:
https://gist.githubusercontent.com/shaos26/73b057c77b9ea22d5e6813c259dbf43c/raw/92c7dd098a227276fe3daeea45996e2848e66439/poc.c

Build the kernel from HEAD with the attached reproduction config, then make a 
one-file initramfs.  The PoC mounts sysfs and securityfs itself; DEVTMPFS_MOUNT 
supplies /dev/console.

    mkdir -p build rootfs
    cp kernel-config.txt build/.config
    make O=build olddefconfig
    make O=build -j"$(nproc)" bzImage
    cc -O2 -static -Wall -Wextra -o poc poc.c
    cp poc rootfs/poc
    (cd rootfs && find poc | cpio -o -H newc --owner=0:0) | gzip -9 > 
initramfs.cpio.gz
    timeout 900 qemu-system-x86_64 -kernel build/arch/x86/boot/bzImage \
        -initrd initramfs.cpio.gz -append "console=ttyS0 rdinit=/poc panic=-1 
loglevel=7 lsm=apparmor security=apparmor apparmor=1" \
        -nographic -m 1024 -smp 1 -no-reboot 2>&1 | tee console.log

Under the deny-network profile, socket() and connect(AF_INET) returned EACCES, 
but connect(fd, AF_UNSPEC) returned success and tore down the inherited 
connection and listener.  Two fresh boots matched.  A one-line control that 
routed AF_UNSPEC through aa_sk_perm returned EACCES and kept both sockets alive.

Impact and prerequisites:
A confined process that inherits a TCP/UDP socket can bypass the profile's 
network mediation for disconnect and disrupt that socket.  The finding does not 
permit socket creation, bind, or arbitrary peer connections; no privilege 
escalation is demonstrated.

Run the normal connect permission check for the AF_UNSPEC disconnect case:

diff --git a/security/apparmor/af_inet.c b/security/apparmor/af_inet.c
--- a/security/apparmor/af_inet.c
+++ b/security/apparmor/af_inet.c
@@ -425,7 +425,7 @@
        if (addrlen < offsetofend(struct sockaddr, sa_family))
                return -EINVAL;
        if (addr->sa_family == AF_UNSPEC)
-               return 0;
+               return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk);
 
        /* do we need early bailout for !family ... */
        return sk_has_perm2(sock->sk, OP_CONNECT, AA_MAY_CONNECT, profile, ad,

Fixes: 89ac6aa752ec8 ("apparmor: push inet mediation into profile callbacks, 
and improve auditing")
Signed-off-by: shaos <[email protected]>

Validation: Two fresh vulnerable boots showed socket teardown.  The one-line 
fixed-kernel control denied the call and preserved both sockets.

Thanks,

shaos


Reply via email to