Bug 1116485 (CVE-2014-3539) - CVE-2014-3539 python-rope: pickle.load of remotely supplied data with no authentication required
Summary: CVE-2014-3539 python-rope: pickle.load of remotely supplied data with no auth...
Keywords:
Status: CLOSED DEFERRED
Alias: CVE-2014-3539
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Red Hat Product Security
QA Contact:
URL:
Whiteboard:
Depends On: 1240804
Blocks:
TreeView+ depends on / blocked
 
Reported: 2014-07-05 02:22 UTC by Kurt Seifried
Modified: 2019-09-29 13:19 UTC (History)
4 users (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Environment:
Last Closed: 2015-07-07 19:38:35 UTC
Embargoed:


Attachments (Terms of Use)
suggested patch (10.63 KB, patch)
2015-02-11 21:39 UTC, Matěj Cepl
no flags Details | Diff

Description Kurt Seifried 2014-07-05 02:22:42 UTC
Kurt Seifried and Vasyl Kaigorodov of Red Hat Product Security report:

So while I was looking through the source I spotted a flaw, Vasyl confirmed it

============================================================
python rope
http://rope.sourceforge.net/
https://pypi.python.org/pypi/rope
229 downloads in the last day
1455 downloads in the last week
5361 downloads in the last month
============================================================

pickle.load of remotely supplied data with no auth, RCE

============================================================
rope/base/oi/doa.py
==============================
class _SocketReceiver(_MessageReceiver):
def receive_data(self):
conn, addr = self.server_socket.accept()
self.server_socket.close()
my_file = conn.makefile('r')
while True:
try:
yield pickle.load(my_file)
except EOFError:
break
my_file.close()
conn.close()


class PythonFileRunner(object):
def _init_data_receiving(self):
if self.analyze_data is None:
return
Show quoted text
if True or os.name == 'nt':
self.receiver = _SocketReceiver()

============================================================
/rope/base/pycore.py
==============================
class PyCore(object):
def run_module(self, resource, args=None, stdin=None, stdout=None):
"""Run `resource` module

Returns a `rope.base.oi.doa.PythonFileRunner` object for
controlling the process.

"""
perform_doa = self.project.prefs.get('perform_doi', True)
perform_doa = self.project.prefs.get('perform_doa', perform_doa)
receiver = self.object_info.doa_data_received
if not perform_doa:
receiver = None
runner = rope.base.oi.doa.PythonFileRunner(
self, resource, args, stdin, stdout, receiver)
runner.add_finishing_observer(self.module_cache.forget_all_data)
runner.run()
return runner
============================================================

Vasyl Kaigorodov (coworker) then confirmed it was network reachable:

Confirmed:

...
import rope.base.pycore
import rope.base.project

myproject = rope.base.project.Project('.')
res = myproject.get_resource("mod2.py")
myproject.pycore.run_module(res)
...

When run_module() executed - rope really opens a port:
...
tcp        0      0 0.0.0.0:3037            0.0.0.0:*
LISTEN      31234/python
...
and I was able to easily send some "pickled" python code to it (using
nc), which then was executed.
The window of opportunity is really small though (around 0.8 second
for a 2kb file):
...
17:00:53.432052 IP localhost.59128 > localhost.hp-san-mgmt: Flags [S],
seq 3302699683, win 43690, options [mss 65495,sackOK,TS val 533783316
ecr 0,nop,wscale 7], length 0
17:00:53.432075 IP localhost.hp-san-mgmt > localhost.59128: Flags
[S.], seq 3571033229, ack 3302699684, win 43690, options [mss
65495,sackOK,TS val 533783316 ecr 533783316,nop,wscale 7], length 0
17:00:53.432088 IP localhost.59128 > localhost.hp-san-mgmt: Flags [.],
ack 1, win 342, options [nop,nop,TS val 533783316 ecr 533783316], length 0
17:00:54.198028 IP localhost.59128 > localhost.hp-san-mgmt: Flags
[P.], seq 1:2230, ack 1, win 342, options [nop,nop,TS val 533784082
ecr 533783316], length 2229
17:00:54.198055 IP localhost.59128 > localhost.hp-san-mgmt: Flags
[F.], seq 2230, ack 1, win 342, options [nop,nop,TS val 533784082 ecr
533783316], length 0
17:00:54.198055 IP localhost.hp-san-mgmt > localhost.59128: Flags [.],
ack 2230, win 1012, options [nop,nop,TS val 533784082 ecr 533784082],
length 0
17:00:54.211353 IP localhost.hp-san-mgmt > localhost.59128: Flags
[F.], seq 1, ack 2231, win 1024, options [nop,nop,TS val 533784096 ecr
533784082], length 0
17:00:54.211378 IP localhost.59128 > localhost.hp-san-mgmt: Flags [.],
ack 2, win 342, options [nop,nop,TS val 533784096 ecr 533784096], length 0
...
Also according to the rope code (base/oi/doa.py, lines 113-117) , if
port 3037 is taken already - rope will try to bind to 3038, 3039 and
so on - so there's even no 100% guarantee that the port will be static.

Comment 1 Matěj Cepl 2015-02-11 21:38:23 UTC
At least some fix for this was suggested in https://github.com/python-rope/rope/pull/107 upstream.

It doesn't resolve the issue, but at least users who want to use DOA should be intentional about it and they should be warned against possible security risks.

Comment 2 Matěj Cepl 2015-02-11 21:39:21 UTC
Created attachment 990653 [details]
suggested patch

Comment 3 Kurt Seifried 2015-07-07 19:38:04 UTC
Created python-rope tracking bugs for this issue:

Affects: fedora-all [bug 1240804]

Comment 4 nicks 2019-06-28 10:47:46 UTC
This issue was fixed as of rope 0.11.0.

https://github.com/python-rope/rope/pull/251


Note You need to log in before you can comment on or make changes to this bug.