Headline
CVE-2019-5010: TALOS-2019-0758 || Cisco Talos Intelligence Group
An exploitable denial-of-service vulnerability exists in the X509 certificate parser of Python.org Python 2.7.11 / 3.6.6. A specially crafted X509 certificate can cause a NULL pointer dereference, resulting in a denial of service. An attacker can initiate or accept TLS connections using crafted certificates to trigger this vulnerability.
Summary
An exploitable denial-of-service vulnerability exists in the X509 certificate parser of Python.org Python 2.7.11 / 3.6.6. A specially crafted X509 certificate can cause a NULL pointer dereference, resulting in a denial of service. An attacker can initiate or accept TLS connections using crafted certificates to trigger this vulnerability.
Tested Versions
Python.org CPython 2.7.11 Python.org CPython 3.6.6 Python.org CPython 3.5.2 Python.org CPython 3 master at 480833808e918a1dcebbbcfd07d5a8de3c5c2a66
Product URLs
https://github.com/python/cpython
CVSSv3 Score
5.9 - CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE
CWE-476: NULL Pointer Dereference
Details
Python can crash if getpeercert() is called on a TLS connection which uses a certificate with invalid DistributionPoint in its extension.
According to RFC 5280:
A DistributionPoint consists of three fields,
each of which is optional: distributionPoint, reasons, and cRLIssuer.
While each of these fields is optional, a DistributionPoint MUST NOT
consist of only the reasons field; either distributionPoint or
cRLIssuer MUST be present. If the certificate issuer is not the CRL
issuer, then the cRLIssuer field MUST be present and contain the Name
of the CRL issuer. If the certificate issuer is also the CRL issuer,
then conforming CAs MUST omit the cRLIssuer field and MUST include
the distributionPoint field.
Python assumes a valid distpoint, so a crafted certificate extension containing a DistributionPoint with both a blank distributionPoint and cRLIssuer causes a NULL pointer dereference:
static PyObject *
_get_crl_dp(X509 *certificate) {
STACK_OF(DIST_POINT) *dps;
int i, j;
PyObject *lst, *res = NULL;
dps = X509_get_ext_d2i(certificate, NID_crl_distribution_points, NULL, NULL);
if (dps == NULL)
return Py_None;
lst = PyList_New(0);
if (lst == NULL)
goto done;
for (i=0; i < sk_DIST_POINT_num(dps); i++) {
DIST_POINT *dp;
STACK_OF(GENERAL_NAME) *gns;
dp = sk_DIST_POINT_value(dps, i);
gns = dp->distpoint->name.fullname;
This vulnerability is present in TLS clients and servers that call getpeercert() to perform certificate verification. Clients typically call getpeercert() as part of the TLS handshake:
def do_handshake(self):
"""Start the SSL/TLS handshake."""
self._sslobj.do_handshake()
if self.context.check_hostname:
if not self.server_hostname:
raise ValueError("check_hostname needs server_hostname "
"argument")
match_hostname(self.getpeercert(), self.server_hostname)
Note that since the handshake completes before certificate verification, the crafted certificate has to be trusted by the remote party (victim). Handshake failures (for example, due to untrusted CA) result in skipping the call to getpeercert().
Crash Information
Backtrace for python 3.5.2
#0 0x00007fa24f04ee1e in _get_crl_dp (certificate=certificate@entry=0x1f4bce0) at /build/python3.5-IEzsnH/python3.5-3.5.2/Modules/_ssl.c:1096
#1 0x00007fa24f04f763 in _decode_certificate (certificate=0x1f4bce0) at /build/python3.5-IEzsnH/python3.5-3.5.2/Modules/_ssl.c:1275
#2 0x00007fa24f050e68 in _ssl__SSLSocket_peer_certificate_impl (self=self@entry=0x7fa24f35ace8, binary_mode=<optimised out>) at /build/python3.5-IEzsnH/python3.5-3.5.2/Modules/_ssl.c:1398
#3 0x00007fa24f050eae in _ssl__SSLSocket_peer_certificate (self=0x7fa24f35ace8, args=<optimised out>) at /build/python3.5-IEzsnH/python3.5-3.5.2/Modules/clinic/_ssl.c.h:76
#4 0x00000000004a2912 in PyCFunction_Call (func=func@entry=0x7fa24e95be68, args=args@entry=0x7fa24f450d58, kwds=kwds@entry=0x0) at ../Objects/methodobject.c:109
#5 0x000000000054ac7e in call_function (pp_stack=pp_stack@entry=0x7fff8a9f2b90, oparg=oparg@entry=1) at ../Python/ceval.c:4705
#6 0x0000000000547f43 in PyEval_EvalFrameEx (f=f@entry=0x7fa24f2a9db0, throwflag=throwflag@entry=0) at ../Python/ceval.c:3236
#7 0x000000000054a520 in _PyEval_EvalCodeWithName (_co=0x7fa24f324a00, globals=<optimised out>, locals=locals@entry=0x0, args=<optimised out>, argcount=argcount@entry=2, kws=0x7fa24f2a9d70, kwcount=0, defs=0x7fa24e98fb10, defcount=1, kwdefs=0x0, closure=0x0, name=0x7fa24f4162e0,
qualname=0x7fa24f2e3298) at ../Python/ceval.c:4018
#8 0x000000000054a800 in fast_function (func=func@entry=0x7fa24e96d4a8, pp_stack=pp_stack@entry=0x7fff8a9f2e00, n=n@entry=2, na=na@entry=2, nk=nk@entry=0) at ../Python/ceval.c:4813
#9 0x000000000054ada5 in call_function (pp_stack=pp_stack@entry=0x7fff8a9f2e00, oparg=oparg@entry=1) at ../Python/ceval.c:4730
#10 0x0000000000547f43 in PyEval_EvalFrameEx (f=f@entry=0x7fa24f2a9bc8, throwflag=throwflag@entry=0) at ../Python/ceval.c:3236
#11 0x000000000054a520 in _PyEval_EvalCodeWithName (_co=0x7fa24f346d00, globals=<optimised out>, locals=locals@entry=0x0, args=<optimised out>, argcount=argcount@entry=1, kws=0x1e29338, kwcount=0, defs=0x7fa24e98fd18, defcount=1, kwdefs=0x0, closure=0x0, name=0x7fa24f4162e0,
qualname=0x7fa24f2e3bf8) at ../Python/ceval.c:4018
#12 0x000000000054a800 in fast_function (func=func@entry=0x7fa24e96e280, pp_stack=pp_stack@entry=0x7fff8a9f3070, n=n@entry=1, na=na@entry=1, nk=nk@entry=0) at ../Python/ceval.c:4813
#13 0x000000000054ada5 in call_function (pp_stack=pp_stack@entry=0x7fff8a9f3070, oparg=oparg@entry=0) at ../Python/ceval.c:4730
#14 0x0000000000547f43 in PyEval_EvalFrameEx (f=f@entry=0x1e29198, throwflag=throwflag@entry=0) at ../Python/ceval.c:3236
#15 0x000000000054a520 in _PyEval_EvalCodeWithName (_co=_co@entry=0x7fa25065b400, globals=globals@entry=0x7fa2506efc98, locals=locals@entry=0x7fa2506efc98, args=args@entry=0x0, argcount=argcount@entry=0, kws=kws@entry=0x0, kwcount=0, defs=0x0, defcount=0, kwdefs=0x0, closure=0x0,
name=0x0, qualname=0x0) at ../Python/ceval.c:4018
#16 0x000000000054a610 in PyEval_EvalCodeEx (_co=_co@entry=0x7fa25065b400, globals=globals@entry=0x7fa2506efc98, locals=locals@entry=0x7fa2506efc98, args=args@entry=0x0, argcount=argcount@entry=0, kws=kws@entry=0x0, kwcount=0, defs=0x0, defcount=0, kwdefs=0x0, closure=0x0)
at ../Python/ceval.c:4039
#17 0x000000000054a639 in PyEval_EvalCode (co=co@entry=0x7fa25065b400, globals=globals@entry=0x7fa2506efc98, locals=locals@entry=0x7fa2506efc98) at ../Python/ceval.c:777
#18 0x0000000000424ee8 in run_mod (mod=mod@entry=0x1dfba68, filename=filename@entry=0x7fa24f3ed890, globals=globals@entry=0x7fa2506efc98, locals=locals@entry=0x7fa2506efc98, flags=flags@entry=0x7fff8a9f3350, arena=arena@entry=0x1e5af70) at ../Python/pythonrun.c:976
#19 0x000000000042797a in PyRun_FileExFlags (fp=fp@entry=0x1dffb20, filename_str=filename_str@entry=0x7fa250684130 "server.py", start=start@entry=257, globals=globals@entry=0x7fa2506efc98, locals=locals@entry=0x7fa2506efc98, closeit=closeit@entry=1, flags=0x7fff8a9f3350)
at ../Python/pythonrun.c:929
#20 0x0000000000427d17 in PyRun_SimpleFileExFlags (fp=fp@entry=0x1dffb20, filename=<optimised out>, filename@entry=0x7fa250684130 "server.py", closeit=closeit@entry=1, flags=flags@entry=0x7fff8a9f3350) at ../Python/pythonrun.c:396
#21 0x0000000000427e7a in PyRun_AnyFileExFlags (fp=fp@entry=0x1dffb20, filename=0x7fa250684130 "server.py", closeit=closeit@entry=1, flags=flags@entry=0x7fff8a9f3350) at ../Python/pythonrun.c:80
#22 0x00000000004370dc in run_file (fp=fp@entry=0x1dffb20, filename=filename@entry=0x1d95150 L"server.py", p_cf=p_cf@entry=0x7fff8a9f3350) at ../Modules/main.c:318
#23 0x0000000000437c0b in Py_Main (argc=argc@entry=2, argv=argv@entry=0x1d95020) at ../Modules/main.c:768
#24 0x000000000041f319 in main (argc=2, argv=0x7fff8a9f3568) at ../Programs/python.c:65
Exploit Proof-of-Concept
The following code decodes a supplied certificate, and crashes if the certificate has an invalid distribution point extension.
import ssl
ssl._ssl._test_decode_cert('bad-cert.pem')
Timeline
2019-01-15 - Vendor Disclosure
2019-01-15 - Vendor patched
2019-01-28 - Public Release
Discovered by Colin Read and Nicolas Edet of Cisco.
Related news
Ubuntu Security Notice 6891-1 - It was discovered that Python incorrectly handled certain inputs. An attacker could possibly use this issue to execute arbitrary code. This issue only affected Ubuntu 14.04 LTS and Ubuntu 18.04 LTS. It was discovered that Python incorrectly used regular expressions vulnerable to catastrophic backtracking. A remote attacker could possibly use this issue to cause a denial of service. This issue only affected Ubuntu 14.04 LTS.
Dell EMC Metro node, Version(s) prior to 7.1, contain a Code Injection Vulnerability. An authenticated nonprivileged attacker could potentially exploit this vulnerability, leading to the execution of arbitrary OS commands on the application.