False Positives I: MitM on an RDP Connection?


This is the first POST concerning “False Positives” under GNU/Linux systems and derivatives. These new series of technical discussions will show how BUGS, misinterpretations of log files and paranoia, all mixed together, can make a sysadmin think about being p0wned … when, in fact, he has not! There are thousands of articles concerning attack vectors and how to protect systems against them. Now it is turn to focus on real cases where a false positive can be explained by means of analysing logs or running a debugger. Let’s start!

The first issue

A Windows-RDP based server, with a valid certificate, was put on a network to allow GNU/Linux clients using Remmina to connect remotely to it. So, the sysadmin distributed a .freerdp/know_hosts file among all the GNU/Linux boxes with the Windows RDP server certificate’s fingerprint, to make sure that no one would be connecting to the wrong RDP server without noticing it. That is, to avoid Man in the Middle attacks (MitM). To do that, the sysadmin got the server’s certificate from inside the RDP server by means of running the Microsoft Management Console, read its fingerprint, and then prepared and distributed the know_hosts file via any available method. The server’s certificate fingerprint was the one shown next:

The Windows RDP Server's certificate fingerprint, obtained by running the MMC.

The Windows RDP Server’s certificate fingerprint, obtained by running the MMC.

So, the know_hosts file was created accordingly:

rdp_server fa:b2:36:96:ac:1a:43:a0:57:65:d9:1d:04:59:de:10:a4:e6:5a:bc

After a while, he received a phone call from a GNU/Linux user trying to connect to the RDP server, telling him that he could not connect at all. Instead, a popup showing an error message appeared, as shown below:

Unable to connect to the RDP server error message from a GNU/Linux user.

Unable to connect to the RDP server error message from a GNU/Linux user.

So the sysadmin connected to this GNU/Linux box remotely by means of the ssh command, and tested the Remmina software from the cli. He got an error message concerning the RDP server’s certificate fingerprint along with the popup error message previously reported by the user:

The host key for rdp_server has changed
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the host key sent by the remote host is
Please contact your system administrator.
Add correct host key in ~/.freerdp/known_hosts to get rid of this message.
Host key for rdp_server has changed and you have requested strict checking.
Host key verification failed.
SSL_write: Failure in SSL library (protocol error?)
Authentication failure, check credentials.
If credentials are valid, the NTLMSSP implementation may be to blame.

The sysadmin paled. A MitM was underway?

The first byte is missing

In this scenario, the first thing to notice is  that the RDP server’s certificate fingerprint is exactly the same but the first byte:

know_hosts file:    fa:b2:36:96:ac:1a:43:a0:57:65:d9:1d:04:59:de:10:a4:e6:5a:bc

remmina reported:     b2:36:96:ac:1a:43:a0:57:65:d9:1d:04:59:de:10:a4:e6:5a:bc

So, could it be a software bug? Let’s find out: we install the Remmina sources on the remote GNU/Linux box, then recompile it with its debugging symbols:

# apt-get install remmina

# cd remmina-1.0.0/

# CFLAGS=”-ggdb” ./configure && make

Once we have the new remmina binary with its debugging symbols, we execute it inside a gdb session, making sure to push the “connect button” from the GUI so that the error can arise. After that, we press <ctrl+c> inside the gdb session to interrupt and trace the program. What we are looking for here is just the backtrace:

(gdb) bt

#5 0x000000000042af36 in remmina_init_dialog_certificate (dialog=0x694ec0,
subject=0x7fffe864d9d0 “CN = rdp_server”,
issuer=0x7fffe864c4f0 “CN = rdp_server”,
fingerprint=0x7fffe864cf90 “b2:36:96:ac:1a:43:a0:57:65:d9:1d:04:59:de:10:a4:e6:5a:bc”) at /remmina-1.0.0/remmina/src/remmina_init_dialog.c:412

#12 0x00007fffede79b24 in freerdp_connect ()

According to the previous backtrace, the function that connects to the remote RDP server is freerdp_connect. This is an external function that does not belong to the remmina program, but to the libfreerdp library instead:

(gdb) frame 12
#12 0x00007fffede79b24 in freerdp_connect ()
from /usr/lib/x86_64-linux-gnu/libfreerdp-core.so.1.0

Therefore, the remmina software does not seem to be the culprit. To make sure that the BUG is inside the libfreerdp library, we need to get its sources and analyse the freerdp_connect function. We can use the cscope utility to look for this particular function.

# apt-get source libfreerdp1

# cd freerdp-1.0.1

# cscope -R

Then, we go to the freerdp_connect function in libfreerdp-core/freerdp.c.  Inside this function there’s a call to rdp_client_connect, that tries to negotiate a TLS channel with the remote RDP server. Among other things, the TLS connection needs to read and parse the RDP Server’s certificate, where apparently our first byte is lost. Following the rdp_client_connect function, we end up here:

rdpCertificateData* crypto_get_certificate_data(X509* xcert, char* hostname)
    char* fp; 
    rdpCertificateData* certdata;
    fp = crypto_cert_fingerprint(xcert);
    certdata = certificate_data_new(hostname, fp);
    return certdata;

Seems quite obvious that the RDP server’s certificate fingerprint is returned as a char pointer after calling the crypto_cert_fingerprint function, and stored in the fp variable. Another look at the crypto_cert_fingerprint function show us where the bug lies:

char* crypto_cert_fingerprint(X509* xcert)
    int i = 0;
    char* p;
    char* fp_buffer;
    uint32 fp_len;
    uint8 fp[EVP_MAX_MD_SIZE];
    X509_digest(xcert, EVP_sha1(), fp, &fp_len);
    fp_buffer = (char*) xzalloc(3 * fp_len);
    p = fp_buffer;
    for (i = 0; i < (int) (fp_len - 1); i++)
        sprintf(p, "%02x:", fp[i]);
        p = &fp_buffer[i * 3]; 
    sprintf(p, "%02x", fp[i]);
    return fp_buffer;

As reported here, the problem is inside the looping; the value for i is initially 0, so we’ve got after the first iteration this:

p = &fp_buffer[i * 3] => p = &fp_buffer[0 * 3] => p = &fp_buffer[0]

Therefore, the second byte is written in &fp_buffer[0] instead of being written in &fp_buffer[1], overwriting the first byte in doing so.

Fixing the false positive

As already described here, all we need to do is to fix this looping like this:

 p = &fp_buffer[(i+1)* 3];

Then, we recompile the library and replace the /usr/lib/x86_64-linux-gnu/libfreerdp-core.so with this new one. After that, trying to connect to the remote RDP server has no more certificate fingerprint warnings, solving our apparently security issue!