CVE-2017-15112 (Other, published 2018-01-20T00:29Z)
Description: value: keycloak-httpd-client-install versions before 0.8 allow users to insecurely pass password through command line, leaking it via command history and process info to other local users.
Severity: LOW (CVSS v3 base score: 7.8)
Weakness: CWE-200 \u2014 Exposure of Sensitive Information to an Unauthorized Actor
The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
Repository: https://github.com/jdennis/keycloak-httpd-client-install
Files changed:
bin/keycloak-httpd-client-install
doc/keycloak-httpd-client-install.8
keycloak_httpd_client/keycloak_cli.py
Commit message: CVE-2017-15112 unsafe use of -p/--admin-password on command line

It is unsafe to pass a password as part of the command line args
because it appears in the process info and is recorded in the
shell command history.

The -p --keycloak-admin-password arg has been replaced with -P
--keycloak-admin-password-file which reads the password from a file or
stdin if given a hyphen. The now deprecated --keycloak-admin-password
also accepted stdin with a hyphen, this was retained for backward
compatibility during a transition period, it will issue a deprecation
warning. Trying to use --keycloak-admin-password with anything other
than a hyphen will result in an error and explanation.

The man page was updated to include a section on password passing and
includes documention on using the KEYCLOAK_ADMIN_PASSWORD environment
variable (which was always supported but never documented).

The --admin-password command line arg in keycloak_cli.py was also
updated in a similar manner and now also includes support for
KEYCLOAK_ADMIN_PASSWORD environment variable.


Vulnerable code:
                       help='admin password (use - to read from stdin)')
            else:
                options.keycloak_admin_password = getpass.getpass(
                    '%s password: ' % (options.keycloak_admin_username))
        elif options.keycloak_admin_password == '-':
            options.keycloak_admin_password = sys.stdin.readline().rstrip('\n')
admin password (use - to read from stdin)
A session is established with the Keycloak server. OAuth2 is used to log in as the admin user using the \fB\-\-keycloak\-admin\-username\fR and \fB\-\-keycloak\-admin\-password\fR options. The Keycloak server is identified by the \fB\-keycloak\-server\-url\fR option. This step is performed first to assure the remaining steps can complete successfully. A session is maintained for efficiency reasons. You may also need to specify \fB\-\-keycloak\-admin\-role\fR and \fB\-\-keycloak\-admin\-realm\fR to indicate the privilege level you are authenticating with. An anonymous auth role connects to the Keycloak service without any authentication.
{prog_name} -s http://example.com:8080 -p password client delete -r ABC -c XYZ
    group.add_argument('-p', '--admin-password',
                       required=True,
                       help='admin password')

Fixed code:
class DeprecatedPasswordAction(argparse.Action):
    def __init__(self, option_strings, dest, nargs=None, **kwargs):
        super(DeprecatedPasswordAction, self).__init__(option_strings, dest,
                                                       **kwargs)

    def __call__(self, parser, namespace, values, option_string=None):
        msg1 = ('%s Deprecated. Use --keycloak-admin-password-file instead '
                '(which also accepts - to read from stdin) '
                'or the KEYCLOAK_ADMIN_PASSWORD environment variable. '
                'Using "-" to read the password from stdin '
                'will continue to work for the new few releases for backward '
                'compatibility, then will be removed. '
                'See the man page.' % self.option_strings)
        msg2 = ('Deprecated. It is insecure to pass a password on the '
                'command line. Use --keycloak-admin-password-file or the '
                'KEYCLOAK_ADMIN_PASSWORD environment variable instead. '
                'See the man page.')
        if values == '-':
            print(msg1, file=sys.stderr)
            setattr(namespace, self.dest, values)
        else:
            raise argparse.ArgumentError(self, msg2)

    group.add_argument('-P', '--keycloak-admin-password-file',
                       type=argparse.FileType('rb'),
                       help=('file containing admin password '
                             '(or use a hyphen "-" to read the password '
                             'from stdin)'))

                       action=DeprecatedPasswordAction,
                       dest='deprecated_keycloak_admin_password',
                       help=('Deprecated. Use  --keycloak-admin-password-file '
                             'instead. During transition perion this argument '
                             'will continue to accept a hyphen to indicate '
                             'the password will be read read from stdin)'))
    options.keycloak_admin_password = None

        # We need the admin password for this role

        # 1. Try password file
        if options.keycloak_admin_password_file is not None:
            options.keycloak_admin_password = options.keycloak_admin_password_file.readline().strip()
            options.keycloak_admin_password_file.close()

        # 2. Try KEYCLOAK_ADMIN_PASSWORD environment variable

        # 3. Try deprecated keycloak-admin-password, accept only hyphen for stdin
        try:
            if options.deprecated_keycloak_admin_password == '-':
                options.keycloak_admin_password = sys.stdin.readline().strip()
        except AttributeError:
            pass

        # 4. Try prompting for the password from the terminal
        if options.keycloak_admin_password is None:
            options.keycloak_admin_password = getpass.getpass(
                'enter %s password: ' % (options.keycloak_admin_username))
\fB\-P\fR \fIKEYCLOAK_ADMIN_PASSWORD_FILE\fR
.BR \-P ", " \-\-keycloak\-admin\-password\-file " " \fIKEYCLOAK_ADMIN_PASSWORD_FILE\fR
file containing the admin password (or use a hyphen "-" to indicate the
password will be read from stdin)
(default: None)
.TP
admin password (or use a hyphen "-" to to indicate the password will read
from stdin)
\fB Deprecated.\fR It is insecure to pass a password on the command line.
Use \fI\-\-keycloak\-admin\-password\-file\fR instead. \fBIt is no longer
possible to use this argument to pass a password.\fR During a transition
period this argument will continue to accept a hyphen to indicate the
password should be read from stdin in a manner identical to
\fI\-\-keycloak\-admin\-password\-file\fR.
.PP
.B How to pass the Keycloak admin password

.PP
The Keycloak admin password may be passed via one of the possible ways listed
here in the order the tool looks for the password.

.PP
\fB1.\fR Try the \fI\-\-keycloak\-admin\-password\-file\fR argument.
If it's a hyphen read the password from stdin, otherwise treat the argument
as the name of a file, open the file and read the password from the file.

.PP
\fB2.\fR Test for the existence of the \fBKEYCLOAK_ADMIN_PASSWORD\fR
environment variable. If the \fBKEYCLOAK_ADMIN_PASSWORD\fR is defined
read the password from it.

.PP
\fB3.\fR Try the \fBdeprecated\fR \fI\-\-keycloak\-admin\-password\fR argument.
If it's a hyphen read the password from stdin, otherwise return an error
because passwords should never be passed on the command line.

.PP
\fB4.\fR Prompt for the password from the terminal.

A session is established with the Keycloak server. OAuth2 is used to log in as the admin user using the \fB\-\-keycloak\-admin\-username\fR and \fB\-\-keycloak\-admin\-password\-file\fR options. The Keycloak server is identified by the \fB\-keycloak\-server\-url\fR option. This step is performed first to assure the remaining steps can complete successfully. A session is maintained for efficiency reasons. You may also need to specify \fB\-\-keycloak\-admin\-role\fR and \fB\-\-keycloak\-admin\-realm\fR to indicate the privilege level you are authenticating with. An anonymous auth role connects to the Keycloak service without any authentication.
echo password | {prog_name} -s http://example.com:8080 -P - client delete -r ABC -c XYZ
    group.add_argument('-P', '--admin-password-file',
                       type=argparse.FileType('rb'),
                       help=('file containing admin password '
                             '(or use a hyphen "-" to read the password '
                             'from stdin)'))
    # Get admin password
    options.admin_password = None

    # 1. Try password file
    if options.admin_password_file is not None:
        options.admin_password = options.keycloak_admin_password_file.readline().strip()
        options.keycloak_admin_password_file.close()

    # 2. Try KEYCLOAK_ADMIN_PASSWORD environment variable
    if options.admin_password is None:
        if (('KEYCLOAK_ADMIN_PASSWORD' in os.environ) and
            (os.environ['KEYCLOAK_ADMIN_PASSWORD'])):
            options.admin_password = os.environ['KEYCLOAK_ADMIN_PASSWORD']