EFF secure messaging scorecard review
Part 2 - libotr
Part 2 - libotr
Back in February we published part one of our EFF Secure IM Scorecard review. In the first post we introduced the scorecard and took a look at an application called RetroShare, which after a short audit revealed a number of high-impact vulnerabilities, illustrating a number of areas of improvement needed for the scorecard.
Since then, we followed up on other clients and libraries, and presented at the BSides Canberra conference here in Australia. Shortly after, the EFF took down their scorecard and announced a new version is in the works.
… hazzah! - without sugar coating it too much, this initial scorecard was broken and quite frankly, dangerous. As it’s being revised now, we’ll shift the focus of this post to look at software security outside of just a static snapshot of security bugs, based on some time reviewing libotr.
One of the reasons we wanted to look at libotr was due to the following thread related to the EFF Scorecard that explicitly calls it out with a bet on its security:
Matthew Green and I had a bet for the last year, which just ended, over libotr’s security; I bet him that nobody would find a sev:hi flaw in it all year, and, of course, won, because at this point all the low-hanging fruit in libotr has been shaken out. That bet was a reaction to the release of the EFF scorecard, which at the time gave Cryptocat(†) a perfect score but dinged ChatSecure, which is a libotr client, for not having an audit done.
Libotr is a library (C, Java) used by a number of clients to speak the OTR protocol, including Adium, ChatSecure and Jitsi natively and then Irssi, Miranda, and Pidgin via plug-in. The OTR protocol was first presented in 2004 as an improvement over OpenPGP and S/MIME, supporting forward-secrecy and deniable authentication. The protocol has gone through a few revisions after published research, most notably the papers “Secure Off-the-Record Messaging” and “Finite-State Security Analysis of OTR Version 2”.
Libotr itself has a small code-base and it is approximately 7000 lines of C and C/C++ header code (we are not focussing on the Java library in our review).
We considered the attack surface related to:
In late February 2016 we quickly audited the libotr library and several consumers of the library using manual code review.
Fairly early on we stumbled across a number of bad constructs related to memory allocation and reallocation. Unfortunately for us, there were usually mitigating circumstances that limited the exploitability of issues and could only be described as “insecure coding practice”. For example, see the following construct which includes a few of our audit notes:
We spotted a number of concerns along these lines, took note, and moved on to other areas planning to come back. A few days later though, the following patch was issued:
In several places in proto.c, the sizes of portions of incoming messages were stored in variables of type int or unsigned int instead of size_t. If a message arrives with very large sizes (for example unsigned int datalen = UINT_MAX), then constructions like malloc(datalen+1) will turn into malloc(0), which on some architectures returns a non-NULL pointer, but UINT_MAX bytes will get written to that pointer.
This ended up patching at least one remotely triggerable vector. In the post-vulnerability Twitter haze, we found the following in the discussion:
Twitter bestof: @4Dgifts had seen the libotr bug before the bet deadline but no one verified https://t.co/ciRyEHEXbb https://t.co/VLDYzoNMGW
— Filippo Valsorda (@FiloSottile) March 9, 2016
Crazily, Ivan had pointed out the vanilla allocation overflow construct several months before.
The next thing we stumbled across was a potential overflow in otrl_message_symkey()
via the usedatalen parameter.
We ended up Googling this function and came across the following patch dating back to late 2014:
Interestingly, this patch never appeared to make it into the main branch, so we shot over an email to the developers which they promptly replied to.
This parameter is only supplied by the users client and not directly over the network (like the previous finding), and so a patch would only be defense in depth - nonetheless it was planned for merging.
For a break we decided to spend a bit of time glancing over some clients using the library. While looking at irssi-otr, we came across the following in ennqueue_otr_fragment
:
In this instance, [1] would need to be 2^31 (2GB), and then [2] would wrap past the 2^32 width of 4GB and result in an integer overflow. The developers acknowledged this as a possible issue and said a patch would be issued (although low priority due to new irssi branches supporting OTR natively).
Before we closed our time looking at this project - we came across possible issues when libotr is used by multithreaded clients (e.g. ChatSecure - although it does use synchronous queues). We raised the question “is libotr threadsafe?” in one of our reports, and one developer replied it should be.
In the code the easiest way to spot multithreading issues is looking for cases where the library allocates and uses a shared control structure, such as ConnContext
, and where no locking mechanisms are available. This would result in race conditions such as the following in proto.c
:
The following shows triggering an access violation callstack of this multihreaded issue:
After we raised this, we had clarification from another developer that everything (other than key generation) needs to be called from a single thread.
No, that’s not true at all. There’s one bit of functionality (key
generation, carefully documented) that allows you to do that one thing
in a separate thread, but everything else in libotr needs to be called
from one thread.
Software security really cannot be gauged via what vulnerabilities have been publicised, regardless of bets or bounties.
While focusing on the susceptibility to various vulnerabilities is important, another important element to software security is the readiness for responding to potential security issues.
The following are some general recommendations for those running open-source software projects:
Libotr is a library, and like all libraries, there are edge-cases and behaviours that can have security consequences. It is inevitable for there to be quirks to the developers when interfacing with API’s, and as such clear documentation is particularly important.
For example, man pages covering libc functions detail not only the appropriate usage of the function, but any edge-cases that can lead to potential security issues, such as the following for the *nprintf()
family:
The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte (‘\0’)). If the output was truncated due to this limit then the return value is the number of characters (excluding the terminating null byte) which would have been written to the final string if enough space had been available. Thus, a return value of size or more means that the output was truncated. (See also below under NOTES.)
If an output error is encountered, a negative value is returned.
The following are some general recommendations for those running open-source software projects:
While libotr is quite small, it’s built to be used by IM clients, some of which support an immense range of functionality with extensive attack surface. Without a security conscious design, any vulnerabilities and weaknesses can have a lower effort to attack with a higher impact.
As such, the concept of incorporating security mitigations by default is quite important for any software with high security requirements.
In the Chromium sandbox FAQ they have the following:
This is very neat. Can I use the sandbox in my own programs?
Yes. The sandbox does not have any hard dependencies on the Chromium browser and was designed to be used with other Internet-facing applications. The main hurdle is that you have to split your application into at least two interacting processes. One of the processes is privileged and does I/O and interacts with the user; the other is not privileged at all and does untrusted data processing.
Isn’t that a lot of work?
Possibly. But it’s worth the trouble, especially if your application processes arbitrary untrusted data. Any buffer overflow or format decoding flaw that your code might have won’t automatically result in malicious code compromising the whole computer. The sandbox is not a security silver bullet, but it is a strong last defense against nasty exploits.
The following are some general recommendations for those running open-source software projects:
As the EFF scorecard is offline and being revised, this will be our last post on the subject for now. There are many, many considerations for gauging software security, and we hope future scorecards take the time to properly consider the breadth and depth of factors for it to be reasonable.
EFF secure messaging scorecard review
October 2024 - A Monocle on Chronicles
August 2024 - DUCTF 2024 ESPecially Secure Boot Writeup
July 2024 - plORMbing your Prisma ORM with Time-based Attacks
June 2024 - plORMbing your Django ORM
January 2024 - Keeping up with the Pwnses
October 2023 - Exploring the STSAFE-A110
elttam is a globally recognised, independent information security company, renowned for our advanced technical security assessments.
Read more about our services at elttam.com
Connect with us on LinkedIn
Follow us at @elttam