A denial of service flaw was found in the way Simple XML-RPC Server module of Python processed client connections, that were closed prior the complete request body has been received. A remote attacker could use this flaw to cause Python Simple XML-RPC based server process to consume excessive amount of CPU.
Credit:
Issue reported by Daniel Callaghan
References:
[1] https://bugzilla.redhat.com/show_bug.cgi?id=789790
Steps to reproduce:
------------------
A) for v3.2.2 version:
1) start server:
cat s.py
#!/usr/local/bin/python3
from xmlrpc.server import SimpleXMLRPCServer
server = SimpleXMLRPCServer(('127.0.0.1', 12345))
server.serve_forever()
2) # top
3) issue request from client:
echo -e 'POST /RPC2 HTTP/1.0\r\nContent-Length: 100\r\n\r\nlol bye' | nc localhost 12345
Return to 'top' screen and see, how CPU consumption on particular host quickly moves to 100%.
B) for v2.7.2 version:
1) start server:
cat s.py
#!/usr/bin/python
from SimpleXMLRPCServer import SimpleXMLRPCServer
server = SimpleXMLRPCServer(('127.0.0.1', 12345))
server.serve_forever()
Steps 2) and 3) for v2.7.2 version are identical to
those for v3.2.2 version.
With test.
test_xmlrpc has a timeout detection code which is simply broken (and it's actually documented): I just removed it, so if the server loops, the test will block. I think it's acceptable since other tests behave in the same way, and those days we have faulthandler that can be used to pinpoint such deadlocks/loops easily. Also, I've noticed that people are more inclined to fix tests that block than mere failing tests :-)
The test fails on 2.6 and 2.7, because of a EPIPE, which is normal in
this case (well, at least expected):
"""
test_partial_post (test.test_xmlrpc.SimpleServerTestCase) ...
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 47844)
Traceback (most recent call last):
File "/home/cf/python/cpython/Lib/SocketServer.py", line 283, in
_handle_request_noblock
self.process_request(request, client_address)
File "/home/cf/python/cpython/Lib/SocketServer.py", line 309, in
process_request
self.finish_request(request, client_address)
File "/home/cf/python/cpython/Lib/SocketServer.py", line 322, in
finish_request
self.RequestHandlerClass(request, client_address, self)
File "/home/cf/python/cpython/Lib/SocketServer.py", line 617, in __init__
self.handle()
File "/home/cf/python/cpython/Lib/BaseHTTPServer.py", line 329, in handle
self.handle_one_request()
File "/home/cf/python/cpython/Lib/BaseHTTPServer.py", line 323, in
handle_one_request
method()
File "/home/cf/python/cpython/Lib/SimpleXMLRPCServer.py", line 490, in do_POST
self.send_response(200)
File "/home/cf/python/cpython/Lib/BaseHTTPServer.py", line 384, in
send_response
self.send_header('Server', self.version_string())
File "/home/cf/python/cpython/Lib/BaseHTTPServer.py", line 390, in send_header
self.wfile.write("%s: %s\r\n" % (keyword, value))
File "/home/cf/python/cpython/Lib/socket.py", line 318, in write
self.flush()
File "/home/cf/python/cpython/Lib/socket.py", line 297, in flush
self._sock.sendall(buffer(data, write_offset, buffer_size))
error: [Errno 32] Broken pipe
"""
What should I do? Remove the test?