Bugzilla – Full Text Bug Listing |
Summary: | VUL-0: CVE-2016-2779: util-linux: runuser tty hijacking via TIOCSTI ioctl | ||
---|---|---|---|
Product: | [Novell Products] SUSE Security Incidents | Reporter: | Alexander Bergmann <abergmann> |
Component: | Incidents | Assignee: | Stanislav Brabec <sbrabec> |
Status: | RESOLVED FIXED | QA Contact: | Security Team bot <security-team> |
Severity: | Normal | ||
Priority: | P3 - Medium | CC: | abergmann, jkosina, jslaby, meissner, mkubecek, mmachova, pth, rfrohl, sbrabec, smash_bz, vcizek |
Version: | unspecified | ||
Target Milestone: | --- | ||
Hardware: | Other | ||
OS: | Other | ||
URL: | https://smash.suse.de/issue/162294/ | ||
Whiteboard: | CVSSv2:SUSE:CVE-2016-2779:6.2:(AV:L/AC:H/Au:N/C:C/I:C/A:C) CVSSv3.1:SUSE:CVE-2016-2779:8.6:(AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H) maint:planned:update | ||
Found By: | Security Response Team | Services Priority: | |
Business Priority: | Blocker: | --- | |
Marketing QA Status: | --- | IT Deployment: | --- |
Bug Depends on: | |||
Bug Blocks: | 968375, 968675, 1018892 | ||
Attachments: | test_tiocsti.c TIOCSTI terminal injection exploit |
Description
Alexander Bergmann
2016-02-29 12:53:34 UTC
I don't see any fix in the util-linux git yet. No upstream response yet. Karel Žák seems to be offline. There is an easy reproducer. However it is not clean to me, what is the correct fix. The proposed setsid() causes change of behavior. The current implementation calls setsid() only sometimes: if (request_same_session || !command || !pw->pw_uid) same_session = 1; ... if (!same_session) setsid (); i. e. not in the reported case, because of !command. setsid() prevents TIOCSTI attack described in the report (easy to reproduce), but it has side effects: It disconnects the task from job control. With setsid(), ^Z cannot be used for sending the application to background any more (easy to reproduce by calling setsid() unconditionally in the same place). Additionally, https://bugzilla.redhat.com/show_bug.cgi?id=173008 says, that even it does not handle all possible attacks, because attacker can still write to the terminal. Here is an example. With a modified runuser that always calls setsid(), it is still possible to steal input characters after returning back to the session. ==== steal.sh ==== #!/bin/sh ( sleep 3 exec 0>&1 echo "Hallo" >/dev/stdout cat >/tmp/nobody-savefile ) & ================== ~/util-linux # ./runuser -u sbrabec ./stral.sh ~/util-linux # Hallo Debian developers suggest to fix the TIOCSTI issue in kernel: restrict use of this ioctl(): https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=815922#40 Regarding the unwanted reading from the console, I can imagine, that the application will kill all tasks that have the console open, once it exits. Or, if such syscall exists, refuse them to access. Adding kernel developers to Cc:. (In reply to Stanislav Brabec from comment #3) > Debian developers suggest to fix the TIOCSTI issue in kernel: restrict use > of this ioctl(): > > https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=815922#40 Adding Jiří Slabý to CC. Regarding the access to the console after returning: Should we consider it as a security issue? Or should we suppose, that everybody who wants to prevent it, has to redirect stdin, stdout and stderr? It is hard to prevent it otherwise without killing the process that has stdin, stdout or stderr opened. There were several attempts to implement revoke() syscall, but nothing similar exists in the kernel yet. Looking at su in util-linux and comparing it with https://bugzilla.redhat.com/attachment.cgi?id=120976&action=diff, the signal logic is the same for a long time, but setsid() logic uses the same code in comment 2. SLE11 uses su from coreutils. Adding maintainer of coreutils to check for SLE11. And sudo is affected as well: util-linux # sudo -u nobody ./test_tiocsti id -u -n util-linux # id -u -n root The root executed "id -u -n" was injected to the terminal by a non-privileged ./test_tiocsti. According to https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=815922#5, sudo was fixed by use_pty. I guess they mean CD_USE_PTY. And here is a result for util-linux su: If -s is not used, it is not affected: util-linux # su nobody -c ./test_tiocsti util-linux # su - nobody -c $PWD/test_tiocsti util-linux # No injected command. But for -s, the injection happens as well: util-linux # su - nobody -s $PWD/test_tiocsti id -u -n util-linux # id -u -n root util-linux # For su command without -s, the first part of the code in the comment 2 is not executed, so the second part falls back to the default, which is not same session. Created attachment 667514 [details]
test_tiocsti.c TIOCSTI terminal injection exploit
This exploit injects "id -u -n\n" to the terminal.
What can happen:
Nothing, no exploit: pty is not accessible, sedsid() disconnected the task from pty, TIOCSTI failed.
The command is injected to the unprivileged environment pty, and you see e. g. "nobody": This is acceptable.
The command is injected to the caller (privileged) pty, and you see "root" (or caller uid name): This is not acceptable.
util-linux su is also affected if --session-command is used: util-linux # su - nobody --session-command $PWD/test_tiocsti id -u -n util-linux # id -u -n root util-linux # I just opened a discussion on the upstream util-linux list: http://marc.info/?t=145694748900001&r=1&w=2 Discussion there proposes to fix it in the kernel: Disallow the use of TIOCSTI to unprivileged users unless the caller has CAP_SYS_ADMIN. It will fix util-linux issues, but not chroot. For reference, the grsecurity approach can be seen here: https://gitweb.gentoo.org/proj/hardened-patchset.git/commit/?id=d47ea9080b76a7445e8a36545c539b2a62c31faa ++int gr_handle_tiocsti(struct tty_struct *tty) ++{ ++#ifdef CONFIG_GRKERNSEC_HARDEN_TTY ++ if (grsec_enable_harden_tty && (current->signal->tty == tty) && ++ !capable(CAP_SYS_ADMIN)) { ++ gr_log_noargs(GR_DONT_AUDIT, GR_TIOCSTI_MSG); ++ return 1; ++ } ++#endif ++ return 0; ++} It looks like it disables TIOCSTI for non-root unconditionally? we could supply a sysctl for the check, defaulting to off FYI: a similar problem was handled earlier for su utility, see bsc#697897 (see also a follow-up issue starting with comment 28). We had a talk about this bug, and we found, that there is no quick and 100% safe fix. Here are possibilities: 1) Quick kernel fix disabling TIOCSTI ioctl() for non-root, if the PID of the terminal owner is not equal to PID of the calling process, eventually use capabilities for the same. Pros: + Fix in one place. + Fix all possible future abuses. Cons: - Many utilities are potentially affected and need testing. - Some custom code could be affected. (I can imagine for example bar code reader running with a dedicated UID, and pushing bar code to the terminal. Such code will break for sure.) 2) Per utility fix using setsid(). Pros: + Prevents the exploit without uncertain side effects. Cons: - Each affected utility needs fix. - Loss of job control will affect working style of many people. Conclusion: We need a different solution: 3) Introduce new terminal ioctl() or flag in the kernel. This flag will block TIOCSTI (and possibly other dangerous actions). It will allow to implement something like setsid(), but without side effects of job control loss. Pros: + No unwanted side effects at all. Cons: - Each affected utility needs fix. We think, that only 3 will be safe and have no side effects. Note: Fixing character stealing described in comment 2 is not covered by any of these solutions. This could be possible safely only with a new syscall revoke(), which was not yet accepted to the kernel. Michal Kubeček (comment 14): As I look into the bug, it also changed su behavior, and introduced su --command=foo x so --session-command=foo schism. In case su, losing the job control is more acceptable than for sudo. Michal pointed to a simple use case that will break: osc build press ^Z, send build to background type next command It will be no more possible with fix type 2) in sudo. ust for curiosity, I just ran grep for TIOCSTI ioctl() over all openSUSE sources. I got about 60 matches. I analyzed use of some cases: util-linux: used in agetty in wait_for_term_input() kbd: contrib utility sti equal to tiocsti utility. irda: Used by handle_scancode() to emulate input. tcsh: Used in ed mode and in pushback(). emacs: Used in stuff_char() (putting char to be read from terminal) ... It seems that TIOCSTI is used for: - Read character, and if it does not match, put it back. - Wait for character, than put it back for processing. - Implementing a simple line editing. If irda daemon will run with a different UID, then it is a candidate for breakage by 1). It's perfectly fine to add a new and safer ioctl (for these purposes) and optionally disable TIOCSTI per process. So I agree that 3) is perhaps the best way to go. So would be sufficient to add an ioctl to allow {en,dis}abling "current->signal->tty != tty" check, where only CAP_SYS_ADMIN can do so? As TIOCSTI does not need a special privileges, I guess that this new ioctl() could be called without any special privileges as well. Maybe re-enabling TIOCSTI (if we allow it) would require CAP_SYS_ADMIN. This bug is reported for chroot as well, and there the inner task still has root privileges. But as we discussed on the call, it's questionable, whether it is a security issue there at all, because root inside chroot still can do anything. And regarding the second issue, is there a way to disconnect running task from the opened terminal without killing it? Something like revoke()? yes, that would be the setsid() call I think, this detaches the tty control from the task. util-linux-2.31 introduces new su option: --pty. It finally allows to fully separate privileged and unprivileged shells. This is an autogenerated message for OBS integration: This bug (968674) was mentioned in https://build.opensuse.org/request/show/546087 Factory / util-linux hmm, can we backport this to older codestreams? I guess it is possible to backport --pty. But we cannot make this default, as many things could break. Security team, what do you think about this issue? I haven't looked into the details, but would it possible to have --pty optional, so that nothing breaks but the feature is available? Here is the main part of the --pty patch: https://git.kernel.org/pub/scm/utils/util-linux/util-linux.git/commit/?id=eb7d0ad0fed520ecf66ef39075f404e1c7e37a51 It would probably be possible to backport to older version in SLE12. In case of SLE11 and older, it would be more problematic, as su and runuser there comes from shadow, and it is a different implementation. I am trying to apply this patch to SLE12SP4. The code structure has changed too much (for example su has become a struct in the meantime), so we have to apply some more upstream patches before this one. What I wanted to say: it is not smooth. Do we really want to backport it? done |