2012 Dec 7, 2:07
Level 8 of the Stripe CTF is a password server that returns success: true if and only if the password provided matches the password stored directly via
a RESTful API and optionally indirectly via a callback URI. The solution is side channel attack like a timing attack but with ports instead of time.
(I found this in my drafts folder and had intended to post a while ago.)
Code
def nextServerCallback(self, data):
parsed_data = json.loads(data)
# Chunk was wrong!
if not parsed_data['success']:
# Defend against timing attacks
remaining_time = self.expectedRemainingTime()
self.log_info('Going to wait %s seconds before responding' %
remaining_time)
reactor.callLater(remaining_time, self.sendResult, False)
return
self.checkNext()
Issue
The password server breaks the target password into four pieces and stores each on a different server. When a password request is sent to the main server it makes requests to the sub-servers
for each part of the password request. It does this in series and if any part fails, then it stops midway through. Password requests may also be made with corresponding URI callbacks and after the
server decides on the password makes an HTTP request on the provided URI callbacks saying if the password was success: true or false.
A timing attack looks at how long it took for a password to be rejected and longer times could mean a longer prefix of the password was correct allowing for a directed brute force attack. Timing
attacks are prevented in this case by code on the password server that attempts to wait the same amount of time, even if the first sub-server responds with false. However, the server uses
sequential outgoing port numbers shared between the requests to the sub-servers and the callback URIs. Accordingly, we can examine the port numbers on our callback URIs to direct a brute force
attack.
If the password provided is totally incorrect then the password server will contact one sub-server and then your callback URI. So if you see the remote server's port number go up by two when
requesting your callback URI, you know the password is totally incorrect. If by three then you know the first fourth of the password is correct and the rest is incorrect. If by four then two
fourths of the password is correct. If by five then four sub-servers were contacted so you need to rely on the actual content of the callback URI request of 'success: true' or 'false' since you
can't tell from the port change if the password was totally correct or not.
The trick in the real world is false positives. The port numbers are sequential over the system, so if the password server is the only thing making outgoing requests then its port numbers will also
be sequential, however other things on the system can interrupt this. This means that the password server could contact three sub-servers and normally you'd see the port number increase by four,
but really it could increase by four or more because of other things running on the system. To counteract this I ran in cycles: brute forcing the first fourth of the password and removing any entry
that gets a two port increase and keeping all others. Eventually I could remove all but the correct first fourth of the password. And so on for the next parts of the password.
I wrote my app to brute force this in Python. This was my first time writing Python code so it is not pretty.
brute-force password python side-channel technical web 2012 Sep 13, 5:00
Level 7 of the Stripe CTF involved running a length extension attack on the level 7 server's custom crypto code.
Code
@app.route('/logs/')
@require_authentication
def logs(id):
rows = get_logs(id)
return render_template('logs.html', logs=rows)
...
def verify_signature(user_id, sig, raw_params):
# get secret token for user_id
try:
row = g.db.select_one('users', {'id': user_id})
except db.NotFound:
raise BadSignature('no such user_id')
secret = str(row['secret'])
h = hashlib.sha1()
h.update(secret + raw_params)
print 'computed signature', h.hexdigest(), 'for body', repr(raw_params)
if h.hexdigest() != sig:
raise BadSignature('signature does not match')
return True
Issue
The level 7 web app is a web API in which clients submit signed RESTful requests and some actions are restricted to particular clients. The goal is to view the response to one of the restricted
actions. The first issue is that there is a logs path to display the previous requests for a user and although the logs path requires the client to be authenticatd, it doesn't restrict the logs
you view to be for the user for which you are authenticated. So you can manually change the number in the '/logs/[#]' to '/logs/1' to view the logs for the user ID 1 who can make restricted
requests. The level 7 web app can be exploited with replay attacks but you won't find in the logs any of the restricted requests we need to run for our goal. And we can't just modify the requests
because they are signed.
However they are signed using their own custom signing code which can be exploited by a length extension attack. All
Merkle–Damgård hash algorithms (which includes MD5, and SHA) have the property that if you hash data of the form (secret + data) where data is known and the length but not content of secret is
known you can construct the hash for a new message (secret + data + padding + newdata) where newdata is whatever you like and padding is determined using newdata, data, and the length of secret.
You can find a sha-padding.py script on VNSecurity blog that will tell you the new hash and padding per the above. With that I
produced my new restricted request based on another user's previous request. The original request was the following.
count=10&lat=37.351&user_id=1&long=%2D119.827&waffle=eggo|sig:8dbd9dfa60ef3964b1ee0785a68760af8658048c
The new request with padding and my new content was the
following.
count=10&lat=37.351&user_id=1&long=%2D119.827&waffle=eggo%80%02%28&waffle=liege|sig:8dbd9dfa60ef3964b1ee0785a68760af8658048c
My new data in the new
request is able to overwrite the waffle parameter because their parser fills in a map without checking if the parameter existed previously.
Notes
Code review red flags included custom crypto looking code. However I am not a crypto expert and it was difficult for me to find the solution to this level.
hash internet length-extension security sha1 stripe-ctf technical web 2012 Jul 28, 2:35
The U.S. Census Bureau today released a new online service that makes key demographic, socio-economic and housing statistics more accessible than ever before. The Census Bureau’s first-ever
public Application Programming Interface (API) allows developers to design Web and mobile apps to explore or learn more about America’s changing population and economy.
technical api census statistics stats web restful rest 2010 Aug 13, 11:46RESTful machine learning API from Google... "The Prediction API implements supervised learning algorithms as a RESTful web service to let you leverage patterns in your data, providing more relevant
information to your users. Run your predictions on Google's infrastructure and scale effortlessly as your data grows in size and complexity."
rest ai google programming analysis machine-learning development technical 2010 Feb 27, 10:17Weave syncs web browser user data. Its an open platform using JSON data, RESTful URL based APIs, with basic auth over HTTPS.
weave firefox web browser mozilla development technical reference 2009 Apr 20, 3:14This site does user generated reports on (mostly) spam phone numbers. They have a RESTful API to get at that data too! I'm looking for more like this.
api phone spam search reference telemarketing telephone lookup 2009 Apr 5, 5:24A cross-site request forgery issue in Twitter posts to your Twitter account for you if you're logged in. Be careful what your RESTful APIs look like.
via:swannman security twitter xss