Bug 11224 - multiple vulnerabilities in python-oauth2 (CVE-2013-4346, CVE-2013-4347)
: multiple vulnerabilities in python-oauth2 (CVE-2013-4346, CVE-2013-4347)
Status: RESOLVED FIXED
Product: Mageia
Classification: Unclassified
Component: Security
: 3
: i586 Linux
: Normal Severity: normal
: ---
Assigned To: QA Team
: Sec team
: http://lwn.net/Vulnerabilities/571988/
: MGA2TOO mga2-32-ok mga2-64-ok mga3-32...
: validated_update
:
:
  Show dependency treegraph
 
Reported: 2013-09-13 11:59 CEST by Oden Eriksson
Modified: 2013-10-28 22:26 CET (History)
3 users (show)

See Also:
Source RPM: python-oauth2
CVE:


Attachments
server.py (10.12 KB, text/x-python)
2013-10-26 12:41 CEST, claire robinson
Details
client.py (6.68 KB, text/x-python)
2013-10-26 12:42 CEST, claire robinson
Details

Description Oden Eriksson 2013-09-13 11:59:08 CEST
http://www.openwall.com/lists/oss-security/2013/09/12/7

"Date: Thu, 12 Sep 2013 13:55:51 -0600
From: Kurt Seifried <kseifried@...hat.com>
To: oss-security@...ts.openwall.com, security@...ntu.com,
        chuck.short@...onical.com
Subject: Re: cve requests for python-oauth2

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I have some clarifying questions, see below

On 09/12/2013 11:34 AM, Seth Arnold wrote:
> Hello Kurt, all, I recently gave python-oauth2 a quick audit and
> believe it needs three CVE entries:
> 
> - _check_signature() ignores the nonce value when validating signed
> urls
> 
> def _check_signature(self, request, consumer, token): timestamp,
> nonce = request._get_timestamp_nonce() 
> self._check_timestamp(timestamp) signature_method =
> self._get_signature_method(request)
> 
> try: signature = request.get_parameter('oauth_signature') except: 
> raise MissingSignature('Missing oauth_signature.')
> 
> # Validate the signature. valid = signature_method.check(request,
> consumer, token, signature)
> 
> if not valid: key, base = signature_method.signing_base(request,
> consumer, token)
> 
> raise Error('Invalid signature. Expected signature base ' 'string:
> %s' % base)
> 
> Ignoring the nonce value enables replay attacks.
> 
> This appears to already be known (ignoring the misleading title): 
> https://github.com/simplegeo/python-oauth2/issues/129

Yeah ignoring nonces is not good. Oddly enough CWE only has CWE-323
"Reusing a Nonce, Key Pair in Encryption" there is nothing for
"completely ignored nonce". So this gets a CVE. Please use
CVE-2013-4346 for this issue.

> - _check_timestamp() does not constrain how far into the future
> times may be, (also does not prevent negative times, but probably
> not relevant for a CVE)
> 
> def _check_timestamp(self, timestamp): """Verify that timestamp is
> recentish.""" timestamp = int(timestamp) now = int(time.time()) 
> lapsed = now - timestamp if lapsed > self.timestamp_threshold: 
> raise Error('Expired timestamp: given %d and now %s has a ' 
> 'greater difference than threshold %d' % (timestamp, now, 
> self.timestamp_threshold))
> 
> The timestamps are probably most useful to limit the number of
> nonces that must be stored and compared but it seems generally
> useful to prevent timestamps from the distant future from being
> allowed.

I see how this can be a problem, but with proper nonces it shouldn't
be an issue on it on, correct? As such I'm leaning towards classifying
this one as security hardening.

> - make_nonce(), generate_nonce(), and generate_verifier() use a
> poor prng:
> 
> @classmethod def make_nonce(cls): """Generate pseudorandom
> number.""" return str(random.randint(0, 100000000))
> 
> 
> def generate_nonce(length=8): """Generate pseudorandom number.""" 
> return ''.join([str(random.randint(0, 9)) for i in range(length)])
> 
> 
> def generate_verifier(length=8): """Generate pseudorandom
> number.""" return ''.join([str(random.randint(0, 9)) for i in
> range(length)])
> 
> Nonces may not need full-blown /dev/urandom but the Python
> 'random' documentation clearly states the results are repeatable.
> The lack of seeding in this module makes me think this is too weak
> for this use.
> 
> The safety of oauth depends upon the verifier being unguessable,
> and this is both too short, with too few character choices, and
> probably does need full-blown /dev/urandom style randomness.
> 
> The poor PRNG for the nonce has been known since 2010-04-24 (silly
> github, hover your _mouse pointer_ over the "3 years ago" text in
> the bug report): 
> https://github.com/simplegeo/python-oauth2/issues/9

Yeah to quote Python random():

However, being completely deterministic, it is not suitable for all
purposes, and is completely unsuitable for cryptographic purposes.

so even with a 'random' seed can attacker could conceivably take a
sample and then brute force the original seed allowing them to predict
future values. So this would go under insufficient randomness and get
a CVE. Please use CVE-2013-4347 for this issue.

> 
> 
> Thanks
> 


- -- 
Kurt Seifried Red Hat Security Response Team (SRT)
PGP: 0x5E267993 A90B F995 7350 148F 66BF 7554 160D 4553 5E26 7993
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.14 (GNU/Linux)

iQIcBAEBAgAGBQJSMhxHAAoJEBYNRVNeJnmTo1MQALHLps+XJ37Nfge/ZO0lNh0F
/CbiL9rkwYRdM61kKi13H995HcGeCcjiPI+v/6GlIEWJIrtHbf8bkYA1aqQF5cvi
yb1cYQ0Q8h51q6mC57IPfx3EIRLbcHws+WzWrHYBrGz3yHzfLy9ZJs5D1CV8UHve
X18QTmU654vEIZ4GhdWQnvktzCHlZ32tigP+PD+utXPQ/sjd0C3GGVicsh1BNVyG
GB006iRrwlA1xgyyJxLXVcz24zeXFTDpHq8NQVXfj4LILAVGzyS53RWh8+FocdR0
HoUSPZfzg5N2+vt8HjjWJNdlaAW2uKijR6eNSNDF5HH3I+LuCY01sww+40IN6I+U
ncVuo1U8c4F+Nt8EjrxMJAa1DCdy0voee1yn8fQt7/doAN7yZdElaispQ0YkWqJC
HQVdz8+1k/NxrLyJQ3vKZPh6qiPIoYykRFkTUjZQJ/I5yJ2ffRP76/mNkIMsWZy2
YVMCeeZbHe7g7y56FWE6poCI7JYRGXPLWGOTioOI2RSVYGVfsyEEZAAdISKIpS9z
xw9aPZCd3IYf9ouUV+Ra85egWWvZf7VC9sESa5CCqEWX1XO0lXKR3TadzVYtK9+q
dY3ZhlaYlmZbZWlJagesCxp9KL+LYbRkw/Q+YoboLxJMaHBw2AaQsbEpK4mPAeXe
JzSDqUqrfiG4W4qpITFb
=hOZj
-----END PGP SIGNATURE-----

"

Reproducible: 

Steps to Reproduce:
Comment 1 Philippe Makowski 2013-10-09 09:12:27 CEST
Colin, for what other packages you needed this one ?
I'm looking with Debian and Fedora how to fix these, but a more long term solution would certainly be to drop this package and use for example https://pypi.python.org/pypi/oauthlib
Comment 2 Philippe Makowski 2013-10-09 10:06:04 CEST
we have this package only in Mga3 and Cauldron and only for python-twitter for what I know

so I'm in favor to update python-twitter as the most recent version of python-twitter (v1.1) no longer uses that library - they switched to using Requests and it's oauth helper lib  (https://code.google.com/p/python-twitter/issues/detail?id=257) and drop this buggy and not maintained package (python-oauth2)

do you aggree ?
Comment 3 Colin Guthrie 2013-10-09 10:24:24 CEST
Yup, I agree. I only used it for python-twitter to make a supybot twitter plugin (or rather update an existing one). Happy to go with the new version of python-twitter.

Would be happy enough to push the new version to mga3 too if that's easier. I doubt many people use it and they may as well use the new one if they do!
Comment 4 Philippe Makowski 2013-10-09 19:25:08 CEST
ok
so I need to add Requests-OAuthlib as a new package (python-requests-oauthlib),
update python-twitter to latest (1.1) that work with Twitter API v1.1 (python-twitter 0.8.2 that we have now only work with v1.0 API)
remove python-oauth2
and doing that for Cauldron, Mga3 and Mga2

I every one agree I do it this way
Today neither Debian, Ubuntu, Fedora have a patch for the two CVE and the mainstream seems dead. In fact they were discovered in Ubuntu when they investigate to get it including into main for what I saw in September and since then no patch (https://bugs.launchpad.net/ubuntu/+source/python-oauth2/+bug/1213934).
Comment 5 David Walser 2013-10-09 19:31:52 CEST
Sounds like a good plan.  Thanks for looking at this Philippe!

I wonder what other packages need updated because of the Twitter API.  Probably one of the telepathy ones at least.  Oh well, that's a problem for another day.
Comment 6 Philippe Makowski 2013-10-14 15:04:41 CEST
done in cauldron
Comment 7 Philippe Makowski 2013-10-18 17:06:21 CEST
There are more dependencies than only python-twitter, openstack-keystone  and twixer need it too
So I think I will submit a patch to fix these two CVE and re import python-oauth2
after looking at the code, I think I can do it.
Comment 8 David Walser 2013-10-18 21:28:27 CEST
We don't have openstack keystone in Mageia 3, so don't worry about that.  If there isn't a newer twixer that doesn't use it, I guess we're stuck with patching it though.  Thanks Philippe.
Comment 9 Philippe Makowski 2013-10-21 13:45:52 CEST
python-oauth2-1.5.170-1.2.mga2.noarch
python-oauth2-1.5.170-1.2.mga2.src
python-oauth2-1.5.170-2.2.mga3.noarch
python-oauth2-1.5.170-2.2.mga3.src

Are available

By the way, these CVE (and even more CVE-2013-4346) are strange  since python-oauth2 Server is only :

    """A skeletal implementation of a service provider, providing protected
resources to requests from authorized consumers.


It don't intend to be a full service provider

And for Mageia, no packages use the server part of python-oauth2 (only a contrib part of openstack keystone potentially use it)
Comment 10 David Walser 2013-10-21 19:12:10 CEST
Thanks Philippe!

Do you or Oden think you could come up with intelligible descriptions for these security issues that we could use for the advisory?  Somehow I don't think talking about a "nonce" is going to be helpful for anyone.
Comment 11 claire robinson 2013-10-24 13:16:03 CEST
Testing mga3 64 - some issues with the update..

Testing with the examples here
https://github.com/simplegeo/python-oauth2/pull/64

server.py can be found here https://github.com/jace/python-oauth2/blob/261388117c7228c2e0d5bbf45e6b30c6dfe81da8/example/server.py

client.py can be found here https://github.com/jace/python-oauth2/blob/261388117c7228c2e0d5bbf45e6b30c6dfe81da8/example/client.py

These are not yet merged apparently and use oath2 rather then oauth. The examples in the simplegeo repository fail.

Before
------
Starting the server in one terminal tab and then the client in another terminal tab..

$ python server.py
Test server running...
127.0.0.1 - - [24/Oct/2013 12:10:39] "GET https://photos.example.net/request_token HTTP/1.1" 200 -
127.0.0.1 - - [24/Oct/2013 12:10:42] "GET https://photos.example.net/authorize?oauth_token=requestkey HTTP/1.1" 200 -
127.0.0.1 - - [24/Oct/2013 12:10:45] "GET https://photos.example.net/access_token HTTP/1.1" 200 -
127.0.0.1 - - [24/Oct/2013 12:10:48] "POST http://photos.example.net/photos HTTP/1.1" 200 -

$ python client.py
** OAuth Python Library Example **

* Obtain a request token ...

REQUEST (via headers)
parameters: {'oauth_body_hash': '2jmj7l5rSw0yVb/vlWAYkK/YBwk=', u'oauth_nonce': u'3730088', u'oauth_timestamp': u'1382613038', u'oauth_consumer_key': u'key', 'oauth_signature_method': 'PLAINTEXT', u'oauth_version': u'1.0', 'oauth_signature': 'secret&', u'oauth_callback': u'http://printer.example.com/request_token_ready'}

GOT
key: requestkey
secret: requestsecret
callback confirmed? true

* Authorize the request token ...

REQUEST (via url query string)
parameters: {u'oauth_token': u'requestkey'}

GOT
http://printer.example.com/request_token_ready?oauth_verifier=verifier
verifier: verifier

* Obtain an access token ...

REQUEST (via headers)
parameters: {'oauth_body_hash': '2jmj7l5rSw0yVb/vlWAYkK/YBwk=', u'oauth_nonce': u'2470446', u'oauth_timestamp': u'1382613044', 'oauth_signature_method': 'PLAINTEXT', u'oauth_consumer_key': u'key', u'oauth_verifier': u'verifier', u'oauth_version': u'1.0', u'oauth_token': u'requestkey', 'oauth_signature': 'secret&requestsecret'}

GOT
key: accesskey
secret: accesssecret

* Access protected resources ...

REQUEST (via post body)
parameters: {'oauth_body_hash': '2jmj7l5rSw0yVb/vlWAYkK/YBwk=', u'oauth_nonce': u'73312455', u'oauth_timestamp': u'1382613047', u'oauth_consumer_key': u'key', 'oauth_signature_method': 'HMAC-SHA1', u'oauth_version': u'1.0', u'oauth_token': u'accesskey', 'oauth_signature': 'LomNizxXVT+Rmx6RE6IXUU562iM='}

GOT
non-oauth parameters: {u'file': u'vacation.jpg', u'size': u'original'}

Done.

Kill the server with ctrl-c


After
-----
Some regression here..

$ python server.py
Test server running...
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 56857)
Traceback (most recent call last):
  File "/usr/lib64/python2.7/SocketServer.py", line 295, in _handle_request_noblock
    self.process_request(request, client_address)
  File "/usr/lib64/python2.7/SocketServer.py", line 321, in process_request
    self.finish_request(request, client_address)
  File "/usr/lib64/python2.7/SocketServer.py", line 334, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "server.py", line 165, in __init__
    BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
  File "/usr/lib64/python2.7/SocketServer.py", line 649, in __init__
    self.handle()
  File "/usr/lib64/python2.7/BaseHTTPServer.py", line 340, in handle
    self.handle_one_request()
  File "/usr/lib64/python2.7/BaseHTTPServer.py", line 328, in handle_one_request
    method()
  File "server.py", line 197, in do_GET
    token = self.oauth_server.fetch_request_token(oauth_request)
  File "server.py", line 108, in fetch_request_token
    consumer = self._get_consumer(oauth_request)
  File "server.py", line 144, in _get_consumer
    consumer = self.data_store.lookup_consumer(consumer_key)
AttributeError: 'NoneType' object has no attribute 'lookup_consumer'
----------------------------------------


$ python client.py
** OAuth Python Library Example **

* Obtain a request token ...

REQUEST (via headers)
parameters: {'oauth_body_hash': '2jmj7l5rSw0yVb/vlWAYkK/YBwk=', u'oauth_nonce': u'94334183', u'oauth_timestamp': u'1382613291', u'oauth_consumer_key': u'key', 'oauth_signature_method': 'PLAINTEXT', u'oauth_version': u'1.0', 'oauth_signature': 'secret&', u'oauth_callback': u'http://printer.example.com/request_token_ready'}

Traceback (most recent call last):
  File "client.py", line 169, in <module>
    run_example()
  File "client.py", line 107, in run_example
    token = client.fetch_request_token(oauth_request)
  File "client.py", line 63, in fetch_request_token
    response = self.connection.getresponse()
  File "/usr/lib64/python2.7/httplib.py", line 1045, in getresponse
    response.begin()
  File "/usr/lib64/python2.7/httplib.py", line 409, in begin
    version, status, reason = self._read_status()
  File "/usr/lib64/python2.7/httplib.py", line 373, in _read_status
    raise BadStatusLine(line)
httplib.BadStatusLine: ''
Comment 12 Philippe Makowski 2013-10-24 21:28:23 CEST
may be I need to rewrite the second patch (oauth2-1.5.170-mga-nonce.patch) to be more exhaustive
thanks for the feed back
Comment 13 Philippe Makowski 2013-10-25 16:01:36 CEST
python-oauth2-1.5.170-1.3.mga2.noarch
python-oauth2-1.5.170-1.3.mga2.src
python-oauth2-1.5.170-2.3.mga3.noarch
python-oauth2-1.5.170-2.3.mga3.src

Are available

They fix CVE-2013-4347

for CVE-2013-4346 I don't have solution 
as I said python-oauth2 Server is only :
    """A skeletal implementation of a service provider, providing protected
resources to requests from authorized consumers.


It don't intend to be a full service provider
if someone want to use this skeletal implementation, he have to be aware of CVE-2013-4346 and take care of this in his own code.
Comment 14 claire robinson 2013-10-25 18:08:29 CEST
Testing complete mga3 64

Well done Philippe, it completes successfully this time.
Comment 15 claire robinson 2013-10-25 18:10:17 CEST
Could somebody give an advisory for this please. Testing the others shortly.
Comment 16 claire robinson 2013-10-25 18:24:49 CEST
Testing complete mga2 32 & 64 and mga3 32
Comment 17 David Walser 2013-10-25 18:54:49 CEST
All I know is the issue CVE-2013-4347 is about insufficient randomness, but I don't totally grasp what the random numbers are used for or the implications of insufficient randomness here.  I'm hoping Oden or Philippe understand the issue well enough to write a description for it.
Comment 18 Philippe Makowski 2013-10-25 19:27:23 CEST
Something like that ?


It was found that in python-oauth2, an application for authorizing flows for web application,  the nonce value generated isn't random enough, because while doing bulk operations, nonce might get repeated, so there is a chance of predictability. This could allow MITM attackers to conduct replay attacks.
Comment 19 claire robinson 2013-10-25 19:36:04 CEST
Thanks Philippe.

Advisory uploaded as..

+  It was found that in python-oauth2, an application for authorizing flows for
+  web applications, the nonce value generated isn't sufficiently random.
+  While doing bulk operations the nonce might be repeated, so there is a chance
+  of predictability. This could allow MITM attackers to conduct replay attacks.
+  (CVE-2013-4347)

Could sysadmin please push from 2&3 core/updates_testing to updates

Thanks!
Comment 20 claire robinson 2013-10-25 19:38:47 CEST
Altered to 'authorization flows'
Comment 21 Thomas Backlund 2013-10-25 23:19:20 CEST
Update pushed:
http://advisories.mageia.org/MGASA-2013-0314.html
Comment 22 claire robinson 2013-10-26 12:41:54 CEST
Created attachment 4458 [details]
server.py

Attaching test scripts for next time this is updated.
Comment 23 claire robinson 2013-10-26 12:42:15 CEST
Created attachment 4459 [details]
client.py

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