OpenSC 0.18.0 Buffer Overflow / Out Of Bounds Read

Multiple issues have been identified in OpenSC, ranging from stack based buffer overflows to out of bounds reads and writes on the heap. They can be triggered by malicious smartcards sending malformed responses to APDU commands. Additionally to those fixes reported here, a lot of minor issues (eg. OOB reads and similar) have been reported and fixed. Version 0.18.0 is affected.


MD5 | a2dd502bfe24ba28f95e8149df61a905

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

X41 D-Sec GmbH Security Advisory: X41-2018-002

Multiple Vulnerabilities in OpenSC
==================================


Overview
- --------
Confirmed Affected Versions: 0.18.0
Confirmed Patched Versions: possibly 0.19.0
Vendor: OpenSC
Vendor URL: https://github.com/OpenSC/OpenSC
Credit: X41 D-Sec GmbH, Eric Sesterhenn
Status: Public
Advisory-URL: https://www.x41-dsec.de/lab/advisories/x41-2018-002-OpenSC/


Summary and Impact
- ------------------
Multiple issues have been identified in OpenSC, ranging from stack
based buffer overflows to out of bounds reads and writes on the heap.
They can be triggered by malicious smartcards sending malformed
responses to APDU commands. Additionally to those fixes reported here,
a lot of minor issues (eg. OOB reads and similar) have been reported
and fixed. The OpenSC team (especially Frank Morgner) did an excellent
job on identifying and fixing further issues.
Due to the large amount of issues, no individual issues have been
rated with CVSS / CVE ID yet.
X41 did not perform a full test or audit on the software, but tried to
help identifying as many bugs as possible in over the course of a year.


Product Description
- -------------------
OpenSC provides a set of libraries and utilities to work with smart
cards. Its main focus is on cards that support cryptographic
operations, and facilitate their use in security applications such as
authentication, mail encryption and digital signatures.

OOB Write in musclelistfiles()
==============================
In function muscle_list_files() in file src/libopensc/card-muscle.c an
out of bounds write might occur, since bufLen is not checked.

{% highlight c %}
static int musclelistfiles(sccardt card, u8 *buf, sizet bufLen)
{
muscleprivate_t priv = MUSCLEDATA(card);
mscfst fs = priv->fs;
int x;
int count = 0;
mscfscheckcache(priv->fs);
for(x = 0; x < fs->cache.size; x++) {
u8 oid= fs->cache.array[x].objectId.id;
scdebug(card->ctx, SCLOGDEBUGNORMAL,
"FILE: %02X%02X%02X%02X\n",
oid[0],oid[1],oid[2],oid[3]);
if(0 == memcmp(fs->currentPath, oid, 2)) {
buf[0] = oid[2];
buf[1] = oid[3];
if(buf[0] == 0x00 && buf[1] == 0x00) continue;
/* No directories/null names outside of root */
buf += 2;
count+=2;
}
}
return count;
}
{% endhighlight %}


OOB Write in tcosselectfile()
=============================
In function tcos_select_file) in file src/libopensc/card-tcos.c a
filename is extracted from an APDU response and written into the
internal file->name variable.

{% highlight c %}
case 0x84:
memcpy(file->name, d, len);
file->namelen = len;
break;
{% endhighlight %}

No check is performed whether the string retrieved from the card fits
into the buffer, which could trigger an OOB write.

OOB Write in pivvalidategeneral_authentication()
================================================
In case piv_validate_general_authentication()in
src/libopensc/card-piv.c is called with a datalen parameter greater
than 4096, an out of bound write occurs. Currently no caller seems to
do this.

OOB Write in gemsafegetcert_len()
=================================
The function gemsafe_get_cert_len() in file
src/libopensc/pkcs15-gemsafeV1.c might write beyond the gemsafe_prkeys
and gemsafe_cert arrays in case more than 12 containers are stored on
the card.

{% highlight c %}
ind = 2; /* skip length */
while (ibuf[ind] == 0x01) {
if (ibuf[ind+1] == 0xFE) {
gemsafeprkeys[i].ref = ibuf[ind+4];
sclog(card->ctx, "Key container %d is
allocated and uses keyref %d",
i+1, gemsafeprkeys[i].ref);
ind += 9;
}
else {
gemsafeprkeys[i].label = NULL;
gemsafecert[i].label = NULL;
sc_log(card->ctx, "Key container %d is
unallocated", i+1);
ind += 8;
}
i++;
}
{% endhighlight %}


OOB Write in utilaclto_str()
============================
In function util_acl_to_str() in file src/tools/util.c no checks are
performed whether the string put together fits into line, which could
be abused to trigger limited out of bounds writes.

OOB Write in readpublickey() and readprivatekey()
=================================================
In function read_public_key() in file src/tools/cryptoflex-tool.c the
bufsize variable is overwritten with file->size retrieved from the
smartcard. This could be bigger than 2048, allowing for a stack based
buffer overflow in the sc_read_binary() call.

{% highlight c %}
u8 buf[2048], *p = buf;
sizet bufsize, keysize;
r = selectappdf();
if (r)
return 1;
scformatpath("I1012", &path);
r = scselectfile(card, &path, &file);
if (r) {
fprintf(stderr, "Unable to select public key file:
%s\n", scstrerror(r));
return 2;
}
bufsize = file->size;
scfilefree(file);
r = screadbinary(card, 0, buf, bufsize, 0);
{% endhighlight %}

The same issue can be found in read_private_key()

{% highlight c %}
bufsize = file->size;
scfilefree(file);
r = screadbinary(card, 0, buf, bufsize, 0);
{% endhighlight %}


OOB Write in decrypt_response()
===============================
In function decrypt_response() in file src/libopensc/card-epass2003.c
an out of bounds overwrite can occur. No check is performed if the
plaintext buffer fits into the out buffer before copying, leading to a
memory overwrite.

{% highlight c %}
memcpy(out, plaintext, inlen - 2);
*outlen = in_len - 2;
return 0;
{% endhighlight %}


OOB Write in cacgetserialnrfrom_CUID()
======================================
In function cac_get_serial_nr_from_CUID() in file
src/libopensc/card-cac.c a serial number is copied into serial->value.
The length argument of the memcpy() is the length of the source, not
the destination, which can lead to an out of bounds memory write.

{% highlight c %}
if (priv->cacidlen) {
serial->len = MIN(priv->cacidlen, SCMAXSERIALNR);
memcpy(serial->value, priv->cacid, priv->cacidlen);
SCFUNCRETURN(card->ctx, SCLOGDEBUGNORMAL, SC_SUCCESS);
}
{% endhighlight %}


Off by One Write in scpkcs15emuesteid_init()
============================================
In function sc_pkcs15emu_esteid_init() in file
src/libopensc/pkcs15-esteid.c an off by one write with a \x00 occurs
in case the sc_read_record() functions returns sizeof(buf) read bytes.

{% highlight c %}
/* read the serial (document number) */
r = screadrecord (card, SCESTEIDPDDOCUMENTNR, buff,
sizeof(buff), SCRECORDBYRECNR);
SCTESTRET(card->ctx, SCLOGDEBUG_NORMAL, r, "read document
number failed");
buff[r] = '\0';
{% endhighlight %}


Double Free in scfilesetsecattr()
=================================
In function sc_file_set_sec_attr() in file src/libopensc/sc.c a double
free occurs in case sec_attr_len is equal to 0, since the call to
realloc() will free file->sec_attr and return NULL. The variable is
then freed again in the error handling path.

{% highlight c %}
tmp = (u8 *) realloc(file->secattr, secattrlen);
if (!tmp) {
if (file->secattr)
free(file->secattr);
file->secattr = NULL;
file->secattrlen = 0;
return SCERROROUTOFMEMORY;
}
{% endhighlight %}


Double Free in read_file()
==========================
In function read_file() in file src/tools/egk-tool.c a double free can
be triggered in case two calls to sc_select_file() return a file->size
of zero. The first call to realloc() frees the memory, the second
frees it again.

{% highlight c %}
len = file ? file->size : 4096;
p = realloc(*data, len);
if (!p) {
goto err;
}
{% endhighlight %}


Double free in scpkcs15emuschsminit()
=====================================
In function sc_pkcs15emu_sc_hsm_init() in file
src/libopensc/pkcs15-sc-hsm.c a double free can occur, since this
function can be called twice. The call to realloc() with a size of 0
would free priv->EF_C_DevAut with a second call freeing the already
freed memory.

{% highlight c %}
/* save EFCDevAut for further use */
ptr = realloc(priv->EFCDevAut, len);
if (ptr) {
memcpy(ptr, efbin, len);
priv->EFCDevAut = ptr;
priv->EFCDevAut_len = len;
}
ptr = efbin;
{% endhighlight %}


Unbound Recursion in iaseccselectmf()/iaseccselectfile()
========================================================
Function iasecc_select_file() in file src/libopensc/card-iasecc.c
calls iasecc_select_mf() in the same file, which calls
iasecc_select_file() again. This can lead to an infinite recursion
exhausting the stack.

Timeline
========
2018-02-03 Issues found
2018-04-18 Vendor contacted
2018-04-18 Vendor reply
2018-05-18 Technical details provided
2018-05-24 Private git branch created, fixing started
2018-08-11 Patched version released: https://github.com/x41sec/OpenSC
2018-08-11 Advisory released
- --
X41 D-SEC GmbH, Dennewartstr. 25-27, D-52068 Aachen
T: +49 241 9809418-0, Fax: -9
Unternehmenssitz: Aachen, Amtsgericht Aachen: HRB19989
GeschA$?ftsfA1/4hrer: Markus Vervier
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEpwxVTgxAIcUvTugIo5Klpg50CxAFAlty3FkACgkQo5Klpg50
CxCAsA//alpu0RpyO1ar9wyKSVFKAYjVEtqrxKyhejQVX74dWV9oC6NB8iRM5hPE
uQC9CHSI7XS/1SsPlDExazDBVsgIW5i6aq6T1nlLAYjIQ9+osAyM5eV/K/ls9l1A
MfjGuGK+jRY98uu00Zgux6Zzv8F9bouoG5B6qs/cvlp1kh7pB0pptBJtHGguB/GH
kQtAWMi+dNaqPEGlBGs0b1UJVDK+NlVPEkGO7qx/hInYRz1u8fFmmSM28CDTst/S
+dM9tbgSrPig36VcgBBn1wqXzYPKStRV0epDM+30S/ABvrHglyVKZUt+CM8Yatvo
jtOONMpVLAhPemyrHyEIL0rj1BX+LmzhwSe0Hgd3MhxXKLTPz+ejK+Z2VskzXN3e
wWgpdATNJYmWRqpQ6BQrS7h/9mCexU5ZZMWoONPYgRNUfEYWMgEkInJBcCbLL+zT
fUmmG6g0Fe3iqgzAcwdbN91q22CBBMb8rSn2DeJzFCeqoxnn3VurKMV5XtKhlVyX
HNWiOjHI87XfwypL1rcPrLtweVb8PTA0Cv47GYBkoshW1xJ6HM+FDWFOnf8C7aSD
kjdfxBP0owRNXb76fu1xJ+f5r3tHBtLTBsnecTJCI1B4Zzv4ePa/R/9E0dAS7SnC
MzHuoOVaNpAGbbSbseZJP4fO3WOAtKG5iFvj5KjGK0TzqxKl5v0=
=/aWX
-----END PGP SIGNATURE-----


Related Posts