近期某⼯业控制系统(ICS)⽹络被检测到异常流量。安全监控显⽰,存在未经授权的访问 ⾏为,⿊客通过 ...
बनाया गया: 10 अप्रैल 2026
का उपयोग करके उत्तर दिया गया GPT-5.4 Thinking द्वारा Chat01
बनाया गया: 10 अप्रैल 2026
का उपयोग करके उत्तर दिया गया GPT-5.4 Thinking द्वारा Chat01
近期某⼯业控制系统(ICS)⽹络被检测到异常流量。安全监控显⽰,存在未经授权的访问
⾏为,⿊客通过 Modbus 协议 与 PLC 进⾏通信,向 PLC 寄存器写⼊了异常或恶意数据,
可能⽤于破坏⼯控逻辑或获取控制权限。分析捕获的 Modbus 通信流量,找出⿊客写⼊到
PLC 的恶意内容。flag格式flag{xxx}。
尝试解出得到flag
pythonimport struct, os, binascii, collections, math, json, re path='/mnt/data/0725.pcapng' data=open(path,'rb').read() len(data) 18518768
pythonimport struct, io, collections, binascii, os, math, re, json, sys def parse_pcapng_packets(filename, max_packets=None): with open(filename,'rb') as f: buf=f.read() off=0 endian='<' iface_tsresol={} iface_tsoffset={} pkts=[] blk_counts=collections.Counter() while off+12<=len(buf): btype=struct.unpack_from('<I', buf, off)[0] blen=struct.unpack_from('<I', buf, off+4)[0] if blen < 12 or off+blen>len(buf): # maybe endian issue? break # try big endian? break body=buf[off+8:off+blen-4] blk_counts[hex (148739, [('0x6', 148739), ('0xa0d0d0a', 1), ('0x1', 1), ('0x5', 1)], b'\x00\x1c\x06\x1el\x86\x00\x0c)\x9a1n\x08\x00')
pythonimport socket, struct, collections, binascii, itertools, math, statistics def ip_to_str(b): return '.'.join(map(str,b)) def parse_ipv4_tcp(pkt): # Ethernet if len(pkt) < 14: return None eth_type = struct.unpack('!H', pkt[12:14])[0] if eth_type != 0x0800: return None ip = pkt[14:] if len(ip) < 20: return None ver_ihl = ip[0] version = ver_ihl >> 4 ihl = (ver_ihl & 0xF) * 4 if version != 4 or len(ip) < ihl: return None total_len = struct.unpack('!H', ip[2:4])[0] proto = ip[9] src = ip[12:16] dst = ip[16:20] if proto != 6: return {'proto': (0, [])
pythonfrom collections import Counter types=Counter() for ts,p in pkts[:1000]: if len(p)>=14: types[struct.unpack('!H',p[12:14])[0]]+=1 types.most_common()[:10] [(2048, 918), (2054, 39), (34525, 36), (35020, 7)]
pythonfrom collections import Counter ports=Counter() pairs=Counter() for ts,p in pkts: d=parse_ipv4_tcp(p) if d and 'sport' in d: ports[d['sport']]+=1 ports[d['dport']]+=1 pairs[(d['src'],d['sport'],d['dst'],d['dport'])]+=1 ports.most_common(30)[:20] [(56591, 60048), (9600, 60048), (56592, 42266), (102, 42266), (53, 108), (80, 82), (445, 52), (139, 36), (443, 16), (57084, 12), (4465, 9), (4468, 9), (56888, 9), (1059, 9), (1062, 9), (57400, 9), (56889, 6), (57029, 6), (57030, 6), (57401, 6)]
pythonpairs.most_common(20)[:20] [(('172.16.28.33', 56591, '172.16.28.111', 9600), 41298), (('172.16.28.33', 56592, '172.16.28.112', 102), 33459), (('172.16.28.111', 9600, '172.16.28.33', 56591), 18750), (('172.16.28.112', 102, '172.16.28.33', 56592), 8807), (('10.29.2.167', 139, '10.29.2.100', 4465), 6), (('10.29.2.167', 139, '10.29.2.100', 4468), 6), (('172.16.28.33', 56888, '172.16.28.111', 445), 6), (('172.16.28.33', 56889, '172.16.28.112', 445), 6), (('10.29.2.167', 139, '10.29.2.100', 1059), 6), (('10.29.2.167', 139, '10.29.2.100', 1062), 6), (('172.16.28.33', 57400, '172.16.28.111', 445), 6), (('172.16.28.33', 57401, '172.16.28.112', 445), 6), (('10.29.2.100', 4465, '10.29.2.167', 139), 3), (('10.29.2.100', 4468, '10.29.2.167', 139), 3), (('172.16.28.111', 445, '172.16.28.33', 56888), 3), (('172.16.28.33', 57029, '172.16.28.252', 445), 3), (('172.16.28.252', 445, '172.16.28.33', 57029), 3), (('172.16.28.33', 57030, '172.16.28.252', 57084), 3), (('172.16.28.252', 57084, '172.16.28.33', 57030), 3), (('172.16.28.33', 57039, '172.16.28.252', 445), 3)]
python# show first payloads in 172.16.28.33:56591 -> 172.16.28.111:9600 samples=[] for ts,d in flows[:0]: pass conv = [(ts,d) for ts,d in [(ts,parse_ipv4_tcp(p)) for ts,p in pkts] if d and 'sport' in d and ((d['src'],d['sport'],d['dst'],d['dport'])==('172.16.28.33',56591,'172.16.28.111',9600) or (d['src'],d['sport'],d['dst'],d['dport'])==('172.16.28.111',9600,'172.16.28.33',56591))] len(conv) 60048
pythonpayload_sizes=Counter() first_bytes=Counter() nonempty=[] for ts,d in conv: pl=d['payload'] if pl: payload_sizes[len(pl)] +=1 first_bytes[pl[:8]] +=1 nonempty.append((ts,d)) len(nonempty), payload_sizes.most_common(20), list(first_bytes.items())[:10] (51345, [(34, 34074), (38, 7515), (66, 5469), (52, 2032), (56, 2032), (35, 134), (30, 79), (36, 10)], [(b'FINS\x00\x00\x00\x1a', 34074), (b'FINS\x00\x00\x00\x1e', 7515), (b'FINS\x00\x00\x00:', 5469), (b'FINS\x00\x00\x00\x16', 79), (b'FINS\x00\x00\x00\x1b', 134), (b'FINS\x00\x00\x00,', 2032), (b'FINS\x00\x00\x000', 2032), (b'FINS\x00\x00\x00\x1c', 10)])
python# inspect first few payload hex and potential command bytes for ts,d in nonempty[:5]: pl=d['payload'] print(d['src'],d['sport'],'->',d['dst'],d['dport'],len(pl),binascii.hexlify(pl[:40]).decode())
pythonfrom collections import Counter,defaultdict def parse_fins_tcp(pl): if len(pl) < 16 or pl[:4] != b'FINS': return None length = struct.unpack('!I', pl[4:8])[0] cmd = struct.unpack('!I', pl[8:12])[0] err = struct.unpack('!I', pl[12:16])[0] data = pl[16:16+length] if len(data) < 10: return {'length':length,'cmd':cmd,'err':err,'data':data} icf,rsv,gct,dna,da1,da2,sna,sa1,sa2,sid = data[:10] rest=data[10:] out={'length':length,'cmd':cmd,'err':err,'icf':icf,'dna':dna,'da1':da1,'da2':da2,'sna':sna,'sa1':sa1,'sa2':sa2,'sid':sid,'data':data} if len(rest)>=2: out['mrc ([(('172.16.28.33', '172.16.28.111', (1, 1)), 34074), (('172.16.28.111', '172.16.28.33', (1, 1)), 17034), (('172.16.28.33', '172.16.28.111', (1, 2)), 158), (('172.16.28.111', '172.16.28.33', (1, 2)), 79)], Counter({('172.16.28.33', '172.16.28.111'): 34232, ('172.16.28.111', '172.16.28.33'): 17113}))
pythonwrites=[] for ts,d in nonempty: if d['src']=='172.16.28.33' and d['dst']=='172.16.28.111': f=parse_fins_tcp(d['payload']) if f and f.get('cmd_code')==(1,2): writes.append((ts,d,f)) len(writes) 158
pythonfor ts,d,f in writes[:10]: print('ts',ts,'sid',f['sid'],'params',binascii.hexlify(f['params']).decode(), 'len',len(f['params']))
pythonunique_payloads=collections.Counter(d['payload'] for ts,d,f in writes) len(unique_payloads), unique_payloads.most_common(5) (79, [(b'FINS\x00\x00\x00\x1e\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00\x02\x00o\x00\x00\xfb\x00o\x01\x02\x82\n\xa0\x00\x00\x02\x00\x00A\xf0', 2), (b'FINS\x00\x00\x00\x1e\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00\x02\x00o\x00\x00\xfb\x00\x92\x01\x02\x82\n\x96\x00\x00\x02\x00\x00@\xa0', 2), (b'FINS\x00\x00\x00\x1b\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00\x02\x00o\x00\x00\xfb\x00\xc1\x01\x021\x00f\x00\x00\x01\x01', 2), (b'FINS\x00\x00\x00\x1b\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00\x02\x00o\x00\x00\xfb\x00\xd6\x01\x021\x00f\x01\x00\x01\x01', 2), (b'FINS\x00\x00\x00\x1b\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00\x02\x00o\x00\x00\xfb\x00\xe3\x01\x021\x00e\x00\x00\x01\x01', 2)])
pythondef decode_write_params(params): if len(params) < 6: return None area=params[0] addr=struct.unpack('!H',params[1:3])[0] bit=params[3] count=struct.unpack('!H',params[4:6])[0] data=params[6:] return area,addr,bit,count,data decoded=[] for payload,count in unique_payloads.items(): f=parse_fins_tcp(payload) area,addr,bit,cnt,data=decode_write_params(f['params']) decoded.append({'sid':f['sid'],'area':area,'addr':addr,'bit':bit,'count':cnt,'data':data,'hex':payload.hex()}) decoded_sorted=sorted(decoded,key=lambda x:(x['area'],x['addr'],x['bit'],x['sid'])) len(decoded_sorted) 79
python# summarize areas from collections import Counter,defaultdict areas=Counter(x['area'] for x in decoded_sorted) areas Counter({49: 67, 130: 12})
pythonfor x in [d for d in decoded_sorted if d['area']==0x82]: print(x['sid'], hex(x['area']), x['addr'], x['bit'], x['count'], x['data'].hex()) print('--- area31 ---') for x in [d for d in decoded_sorted if d['area']==0x31][:20]: print(x['sid'], hex(x['area']), x['addr'], x['bit'], x['count'], x['data'].hex())
python# dedupe writes by tcp seq and payload? seen={} for ts,d,f in writes: key=(d['seq'], d['payload']) seen.setdefault(key, (ts,d,f)) ordered=sorted(seen.values(), key=lambda x:x[0]) len(ordered), ordered[:10][0][2]['params'].hex() (79, '820aa0000002000041f0')
pythonfor i,(ts,d,f) in enumerate(ordered): area,addr,bit,cnt,data=decode_write_params(f['params']) print(i, 'sid',f['sid'],'area',hex(area),'addr',addr,'bit',bit,'cnt',cnt,'data',data.hex())
python# search printable substrings in TCP payloads / packet bytes import string printables=set(bytes(string.printable,'ascii')) # find ascii strings len>=4 in payloads to port 9600 strings_found=Counter() for ts,p in pkts: d=parse_ipv4_tcp(p) if d and 'sport' in d and (d['sport']==9600 or d['dport']==9600) and d['payload']: pl=d['payload'] # extract printable runs run=b'' for b in pl: if 32 <= b < 127: run += bytes([b]) else: if len(run)>=4: strings_found[run]+=1 run=b'' if len(run)>=4: strings_found[run]+=1 strings_found.most_common(20)[:20] [(b'FINS', 51345), (b'=qA$', 2), (b'""As', 1), (b'~KAl', 1), (b'wwA_', 1), (b'm:AR', 1), (b'DDAE', 1), (b'=q@:', 1), (b'm:AS', 1), (b'\\)@K', 1), (b',`@u', 1), (b'=q@j', 1), (b'33A`', 1), (b'=q?j', 1), (b'ffA$', 1), (b'UUA9', 1), (b'~KA>', 1), (b'UUAh', 1), (b'ffA}', 1), (b'UUA|', 1)]
pythonimport re, string, binascii, mmap, os, sys raw=open(path,'rb').read() matches=re.findall(rb'flag\{[^}]{0,200}\}', raw, flags=re.I) matches[:10], len(matches) ([], 0)
python# parse reads reads_req=[] reads_rsp=[] for ts,p in pkts: d=parse_ipv4_tcp(p) if d and 'sport' in d and (d['sport']==9600 or d['dport']==9600) and d['payload']: f=parse_fins_tcp(d['payload']) if not f or f.get('cmd_code')!=(1,1): continue if d['src']=='172.16.28.33': reads_req.append((ts,d,f)) else: reads_rsp.append((ts,d,f)) len(reads_req), len(reads_rsp) (34074, 17034)
pythonunique_reads_req=collections.Counter(d['payload'] for ts,d,f in reads_req) len(unique_reads_req), list(unique_reads_req.items())[:5] (1280, [(b'FINS\x00\x00\x00\x1a\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00\x02\x00o\x00\x00\xfb\x00\x8d\x01\x01\xb1\x00e\x00\x00\x04', 80), (b'FINS\x00\x00\x00\x1a\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00\x02\x00o\x00\x00\xfb\x00\x8e\x01\x01\x82\n\x90\x00\x00\x12', 80), (b'FINS\x00\x00\x00\x1a\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00\x02\x00o\x00\x00\xfb\x00\x8f\x01\x01\xb1\x00e\x00\x00\x04', 80), (b'FINS\x00\x00\x00\x1a\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00\x02\x00o\x00\x00\xfb\x00\x90\x01\x01\x82\n\x90\x00\x00\x12', 80), (b'FINS\x00\x00\x00\x1a\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00\x02\x00o\x00\x00\xfb\x00\x91\x01\x01\xb1\x00e\x00\x00\x04', 80)])
python# Let's inspect port 102 unique payload first bytes and maybe protocol conv102=[] for ts,p in pkts: d=parse_ipv4_tcp(p) if d and 'sport' in d and (d['sport']==102 or d['dport']==102) and d['payload']: conv102.append((ts,d)) len(conv102), Counter(d['payload'][:4] for ts,d in conv102).most_common(10) (25912, [(b'\x03\x00\x00\x1f', 17168), (b'\x03\x00\x00\xbd', 5585), (b'\x03\x00\x00\xbb', 2481), (b'\x03\x00\x00\xbf', 442), (b'\x03\x00\x00\x1a', 98), (b'\x03\x00\x00\x16', 46), (b'\x03\x00\x00$', 40), (b'\x03\x00\x00%', 28), (b"\x03\x00\x00'", 24)])
python# extract printable runs len>=4 in port102 payloads strings102=Counter() for ts,d in conv102: pl=d['payload'] run=b'' for b in pl: if 32 <= b < 127: run += bytes([b]) else: if len(run)>=4: strings102[run]+=1 run=b'' if len(run)>=4: strings102[run]+=1 strings102.most_common(30)[:20] []
python# Inspect first few payloads of port102 for ts,d in conv102[:10]: pl=d['payload'] print(d['src'],d['sport'],'->',d['dst'],d['dport'],len(pl),pl[:32].hex())
python# parse s7comm function at payload offset maybe 17? Let's inspect counts func_counter=Counter() req_funcs=Counter() rsp_funcs=Counter() for ts,d in conv102: pl=d['payload'] if len(pl)>=18 and pl[0]==3 and pl[7]==0x32: rosctr=pl[8] func=pl[17] if len(pl)>17 else None func_counter[(rosctr,func)] += 1 if d['src']=='172.16.28.33': req_funcs[func]+=1 else: rsp_funcs[func]+=1 func_counter.most_common(20), req_funcs, rsp_funcs ([((1, 4), 17168), ((3, 0), 8652), ((1, 5), 92)], Counter({4: 17168, 5: 92}), Counter({0: 8652}))
python# dedupe unique write requests to S7 by seq,payload writes102=[] for ts,d in conv102: pl=d['payload'] if d['src']=='172.16.28.33' and len(pl)>=18 and pl[7]==0x32 and pl[8]==0x01 and pl[17]==0x05: writes102.append((ts,d,pl)) len(writes102) 92
pythonuniq102={} for ts,d,pl in writes102: uniq102.setdefault((d['seq'],pl),(ts,d,pl)) ordered102=sorted(uniq102.values(), key=lambda x:x[0]) len(ordered102) 46
pythonfor i,(ts,d,pl) in enumerate(ordered102[:10]): print(i, len(pl), pl.hex()) print()
pythondef parse_s7_write(pl): # returns dict or None for S7 write-var job if len(pl) < 17 or pl[0]!=3 or pl[7]!=0x32 or pl[8]!=0x01: return None # TPKT len may include 4 bytes s7=pl[7:] # s7 header 10 bytes after protocol id? bytes: 32 rosctr redund(2) pdu_ref(2) param_len(2) data_len(2) if len(s7)<10: return None proto=s7[0]; rosctr=s7[1] redundancy=struct.unpack('!H', s7[2:4])[0] pdu_ref=struct.unpack('!H', s7[4:6])[0] param_len=struct.unpack('!H', s7[6:8])[0] data_len=struct.unpack('!H', s7[8:10])[0] param=s7[10:10+param_len] data=s7[10+param_len:10+param_len+data_len] if not param or [(11377224056263.434, {'src': '172.16.28.33', 'dst': '172.16.28.112', 'sport': 56592, 'dport': 102, 'seq': 2118650440, 'ack': 1031389, 'flags': 24, 'payload': b"\x03\x00\x00'\x02\xf0\x802\x01\x00\x00\x13\xfc\x00\x0e\x00\x08\x05\x01\x12\n\x10\x06\x00\x01\x00\x00\x83\x00\x01\xf0\x00\x04\x00 @\xa0\x00\x00"}, {'pdu_ref': 5116, 'param_len': 14, 'data_len': 8, 'item_count': 1, 'spec_transport_size': 6, 'req_len': 1, 'dbnum': 0, 'area': 131, 'addr24': 496, 'byte_addr': 62, 'bit_addr': 0, 'data_transport': 4, 'data_len_bits': 32, 'payload': b'@\xa0\x00\x00', 'raw': b"\x03\x00\x00'\x02\xf0\x802\x01\x00\x00\x13\xfc\x00\x0e\x00\x08\x05\x01\x12\n\x10\x06\x00\x01\x00\x00\x83\x00\x01\xf0\x00\x04\x00 @\xa0\x00\x00"}), (11377224056302.25, {'src': '172.16.28.33', 'dst': '172.16.28.112', 'sport': 56592, 'dport': 102, 'seq': 2118611377, 'ack': 796137, 'flags': 24, 'payload': b'\x03\x00\x00$\x02\xf0\x802\x01\x00\x00\x0f\x12\x00\x0e\x00\x05\x05\x01\x12\n\x10\x02\x00\x01\x00\x00\x83\x00\x03\x10\x00\x04\x00\x08\x14'}, {'pdu_ref': 3858, 'param_len': 14, 'data_len': 5, 'item_count': 1, 'spec_transport_size': 2, 'req_len': 1, 'dbnum': 0, 'area': 131, 'addr24': 784, 'byte_addr': 98, 'bit_addr': 0, 'data_transport': 4, 'data_len_bits': 8, 'payload': b'\x14', 'raw': b'\x03\x00\x00$\x02\xf0\x802\x01\x00\x00\x0f\x12\x00\x0e\x00\x05\x05\x01\x12\n\x10\x02\x00\x01\x00\x00\x83\x00\x03\x10\x00\x04\x00\x08\x14'}), (11377224056369.434, {'src': '172.16.28.33', 'dst': '172.16.28.112', 'sport': 56592, 'dport': 102, 'seq': 2118688048, 'ack': 1250953, 'flags': 24, 'payload': b'\x03\x00\x00%\x02\xf0\x802\x01\x00\x00\x18\xb0\x00\x0e\x00\x06\x05\x01\x12\n\x10\x04\x00\x01\x00\x00\x83\x00\x06\x90\x00\x04\x00\x10\x00\x14'}, {'pdu_ref': 6320, 'param_len': 14, 'data_len': 6, 'item_count': 1, 'spec_transport_size': 4, 'req_len': 1, 'dbnum': 0, 'area': 131, 'addr24': 1680, 'byte_addr': 210, 'bit_addr': 0, 'data_transport': 4, 'data_len_bits': 16, 'payload': b'\x00\x14', 'raw': b'\x03\x00\x00%\x02\xf0\x802\x01\x00\x00\x18\xb0\x00\x0e\x00\x06\x05\x01\x12\n\x10\x04\x00\x01\x00\x00\x83\x00\x06\x90\x00\x04\x00\x10\x00\x14'})]
pythonfrom collections import Counter,defaultdict Counter((x['area'], x['spec_transport_size'], len(x['payload'])) for _,_,x in parsed102) Counter({(131, 2, 1): 20, (131, 4, 2): 14, (131, 6, 4): 12})
pythondef decode_s7_val(x): pl=x['payload'] ts=x['spec_transport_size'] if ts==2 and len(pl)==1: return pl[0] if ts==4 and len(pl)==2: return struct.unpack('!H',pl)[0] if ts==6 and len(pl)==4: return struct.unpack('!f',pl)[0] return pl.hex() for i,(ts,d,x) in enumerate(parsed102): print(i, 'byte',x['byte_addr'],'bit',x['bit_addr'],'ts',x['spec_transport_size'],'val',decode_s7_val(x),'raw',x['payload'].hex())
python# Collect payload data bytes from unique writes in order fins_data = b''.join(decode_write_params(f['params'])[4] for _,_,f in ordered) s7_data = b''.join(x['payload'] for _,_,x in parsed102) len(fins_data), fins_data.hex()[:200], len(s7_data), s7_data.hex()[:200] (105, '000041f000300001000001000100010100004248000100010101010100000040a00101010101000101000101010000404000002000004100010001000001010100300101010101010101000100010030010000000001010030000000000041f001010000', 96, '40a00000140014002340400000001940800000164000000040800000101001002801002d1140000a001e500240a000000014001940400000408000000a001e40a000000014001440c0000040800000101440a00000000a020023401611150050')
python# Extract DM write data words dm_order=[] for _,_,f in ordered: area,addr,bit,cnt,data=decode_write_params(f['params']) if area==0x82: dm_order.append((addr,cnt,data)) dm_order [(2720, 2, b'\x00\x00A\xf0'), (102, 1, b'\x000'), (2720, 2, b'\x00\x00BH'), (2710, 2, b'\x00\x00@\xa0'), (2710, 2, b'\x00\x00@@'), (111, 1, b'\x00 '), (2710, 2, b'\x00\x00A\x00'), (110, 1, b'\x000'), (100, 1, b'\x000'), (112, 1, b'\x000'), (2720, 2, b'\x00\x00A\xf0'), (2710, 2, b'\x00\x00@\xa0')]
python# unique read requests by area,addr,count def decode_read_params(params): if len(params) < 6: return None area=params[0] addr=struct.unpack('!H',params[1:3])[0] bit=params[3] count=struct.unpack('!H',params[4:6])[0] return area,addr,bit,count unique_read_reqs={} for ts,d,f in reads_req: key=(d['seq'], d['payload']) unique_read_reqs.setdefault(key,(ts,d,f)) uniqreads=sorted(unique_read_reqs.values(), key=lambda x:x[0]) # summarize addresses read_summary=Counter() for ts,d,f in uniqreads: area,addr,bit,cnt=decode_read_params(f['params']) read_summary[(area,addr,bit,cnt)] += [((130, 2704, 0, 18), 5469), ((177, 101, 0, 4), 5469), ((130, 100, 0, 13), 2032), ((176, 100, 0, 4), 2032), ((177, 0, 0, 11), 2032)]
python# Build map of responses by sid for FINS reads read_responses_by_sid=defaultdict(list) for ts,d,f in reads_rsp: read_responses_by_sid[f['sid']].append((ts,d,f)) # unique request by seq map, inspect those addr100/DM count13 target_reqs=[] for ts,d,f in uniqreads: area,addr,bit,cnt=decode_read_params(f['params']) if (area,addr,cnt)==(0x82,100,13): target_reqs.append((ts,d,f)) len(target_reqs) 2032
pythondef parse_fins_read_response(f): if f.get('cmd_code')!=(1,1): return None params=f['params'] if len(params) < 2: return None endcode=struct.unpack('!H',params[:2])[0] data=params[2:] return endcode,data # unique response data for reads from PLC resp_counter=Counter() # Let's inspect all unique response payloads for cmd 01 01 from PLC uniq_rsp={} for ts,d,f in reads_rsp: key=(d['seq'], d['payload']) uniq_rsp.setdefault(key,(ts,d,f)) for ts,d,f in uniq_rsp.values(): r=parse_fins_read_response(f) if r: resp_counter[r[1]] += 1 resp_counter.most_common(20)[:10] [(b'\x00\x00\x00\x01\x00\x00\x00\x00', 2620), (b'\x00\x00\x00\x02\x00\x00\x00\x00', 2423), (b'\x01\xb5A\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A \x00\x00Fz\x00\x00E\xfa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\xa0', 2289), (b't\x0e:\xda\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00FH\x00\x00E\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BH', 1976), (b'\x00\x80\x00\x00\x00\x00\x00\x00', 1277), (b'\x000\x00 \x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00 \x000', 1198), (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01', 1122), (b't\x0e:\xda\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\xa0\x00\x00E\xfa\x00\x00Ez\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\xf0', 487), (b'\x00P\x00 \x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00 \x000', 416), (b'\x00P\x00 \x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x000\x00`', 380)]
python# decode unique 26-byte resp words for DM100 count13 maybe ascii for data,c in resp_counter.most_common(15): if len(data)==26: words=[struct.unpack('!H',data[i:i+2])[0] for i in range(0,len(data),2)] print(c, words, ''.join(chr(w) if 32<=w<127 else '.' for w in words))
pythonresp26=[(data,c) for data,c in resp_counter.items() if len(data)==26] for data,c in sorted(resp26,key=lambda x:-x[1]): words=[struct.unpack('!H',data[i:i+2])[0] for i in range(0,26,2)] s=''.join(chr(w) if 32<=w<127 else '.' for w in words) print(c, words, s)
python# heuristic scan for Modbus/TCP-like payloads: len>=8, pid=0, len field matches remaining modbus_like=[] for ts,p in pkts: d=parse_ipv4_tcp(p) if not d or 'sport' not in d or not d['payload']: continue pl=d['payload'] if len(pl)>=8: tid,pid,l = struct.unpack('!HHH',pl[:6]) if pid==0 and l+6 <= len(pl)+2 and 1 <= pl[7] <= 0x18: # unit id pl[6], func pl[7] modbus_like.append((ts,d,pl[:min(len(pl),30)])) len(modbus_like) 0
python# extract printable strings len>=6 from whole file maybe includes interface names/comments import re strings = re.findall(rb'[\x20-\x7e]{6,}', raw) # show unique interesting not too many maybe 200 uniq = [] seen=set() for s in strings: if s not in seen: seen.add(s); uniq.append(s) len(uniq), uniq[:100] (1595, [b'Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz (with SSE4.2)', b'64-bit Windows 7 Service Pack 1, build 7601', b'Dumpcap (Wireshark) 3.0.0 (v3.0.0-0-g937e33de)', b'\\Device\\NPF_{8207AC38-2C1F-40BF-AF1C-EFB49C3F9FE0}', b'donate-v2-xxxxx-com', b"x'TC;@", b"'J7ut).", b'l4}pbC', b'qt4^[Z', b'-,~Ft3P', b'-F~FtRP', b'TL-AP300DG', b'80-EA-07-E4-41-6F', b'10.29.2.113', b'tplogin.cn', b'1.7.10', b'1_~FuiP', b'__LC(eb', b'nsv~)Rd', b'plcxb1d0ed', b'port-001', b':fj+ODW@{', b'-HxH#e', b'win-ad4h28evijk', b'WIN-AD4H28EVIJK', b'`VMware, Inc. VMware Virtual Platform,None,VMware-56 4d c2 14 8a d1 76 4c-38 10 25 6f 6f 08 a1 7b', b'2>#h9Qh', b'TP-LINK7', b'update', b'360safe', b'7Z~Fw:P', b'81~FwxP', b';s~FxQP', b' FEEDEPEOEGCOEGCODDDGDACOEDEOCAAA', b'browser', b'(MGRuP', b'Cq~FzAP', b'D.~Fz`P', b'`MGSAP', b' ECFCEPFHFDEFFCCODDDGDACOEDEOCAAA', b'&MGTsP', b'IY~F{9P', b'^MGU?P', b'@MGVqP', b'OA~F|1P', b'xMGW=P', b'>MGXoP', b'vMGY;P', b'U)~F})P', b'XMGZmP', b']H~F~~P', b'0MG\\IP', b'VMG\\kP', b'.MG^GP', b'pMG^iP', b'HMG`EP', b'nMG`gP', b' MGb!P', b'FMGbCP', b'(MGcuP', b'9M-SEARCH * HTTP/1.1', b'HOST: 239.255.255.250:1900', b'MAN: "ssdp:discover"', b'ST: urn:dial-multiscreen-org:service:dial:1', b'USER-AGENT: Google Chrome/103.0.5060.114 Windows', b'M-SEARCH * HTTP/1.1', b'USER-AGENT: Microsoft Edge/103.0.1264.71 Windows', b'`MGdAP', b'&MGesP', b'^MGf?P', b'@MGgqP', b'xMGh=P', b'>MGioP', b'vMGj;P', b' EBELEOCOFDEBEGEFCODDDGDACOEDEOAA', b'XMGkmP', b' FDCOEGCODDDGDACOEDEOCACACACACAAA', b'0MGmIP', b'VMGmkP', b' FHEJEOCNDEDEDBEIEEDDEFEKDGEPDCAA', b' FHEPFCELEHFCEPFFFACACACACACACABN', b'\\MAILSLOT\\BROWSE', b'WIN-441HD3EJ6O2', b' EMEBFAFEEPFACNFGEKEPFFDEDEFEDFCA', b' FHEPFCELEHFCEPFFFACACACACACACABO', b'LAPTOP-VJOU44T5', b'.MGoGP', b'pMGoiP', b'HMGqEP', b'nMGqgP', b' MGs!P', b'FMGsCP', b'(MGtuP', b' EMEBFAFEEPFACNFGEKEPFFDEDEFEDFAA', b'`MGuAP', b'&MGvsP', b'^MGw?P', b'GIF89a', b'@MGxqP', b'xMGy=P'])
pythonfor patt in [rb'flag', rb'FLAG', rb'ctf', rb'CTF', rb'modbus', rb'FINS', rb'S7']: m=set(re.findall(rb'[\x20-\x7e]{0,20}'+patt+rb'[\x20-\x7e]{0,40}', raw, flags=re.I)) print(patt, list(m)[:10])
python# decode 36-byte read responses likely DM2704 count18 for data,c in resp_counter.items(): if len(data)==36: words=[struct.unpack('!H',data[i:i+2])[0] for i in range(0,len(data),2)] # also decode 4-word swapped floats pairs floats=[] for i in range(0,36,4): w1=struct.unpack('!H',data[i:i+2])[0] w2=struct.unpack('!H',data[i+2:i+4])[0] b=struct.pack('!HH',w2,w1) # word-swapped to big-endian float maybe floats.append(struct.unpack('!f',b)[0]) print('count',c,'words',words,'floats',floats)
python# find IP conversations ip_pairs=Counter() for ts,p in pkts: d=parse_ipv4_tcp(p) if d and 'sport' in d: ip_pairs[(d['src'],d['dst'],d['sport'],d['dport'])]+=1 ip_pairs.most_common(30)[:20] [(('172.16.28.33', '172.16.28.111', 56591, 9600), 41298), (('172.16.28.33', '172.16.28.112', 56592, 102), 33459), (('172.16.28.111', '172.16.28.33', 9600, 56591), 18750), (('172.16.28.112', '172.16.28.33', 102, 56592), 8807), (('10.29.2.167', '10.29.2.100', 139, 4465), 6), (('10.29.2.167', '10.29.2.100', 139, 4468), 6), (('172.16.28.33', '172.16.28.111', 56888, 445), 6), (('172.16.28.33', '172.16.28.112', 56889, 445), 6), (('10.29.2.167', '10.29.2.100', 139, 1059), 6), (('10.29.2.167', '10.29.2.100', 139, 1062), 6), (('172.16.28.33', '172.16.28.111', 57400, 445), 6), (('172.16.28.33', '172.16.28.112', 57401, 445), 6), (('10.29.2.100', '10.29.2.167', 4465, 139), 3), (('10.29.2.100', '10.29.2.167', 4468, 139), 3), (('172.16.28.111', '172.16.28.33', 445, 56888), 3), (('172.16.28.33', '172.16.28.252', 57029, 445), 3), (('172.16.28.252', '172.16.28.33', 445, 57029), 3), (('172.16.28.33', '172.16.28.252', 57030, 57084), 3), (('172.16.28.252', '172.16.28.33', 57084, 57030), 3), (('172.16.28.33', '172.16.28.252', 57039, 445), 3)]
pythonbitseq=''.join('1' if decode_write_params(f['params'])[4]==b'\x01' else '0' for _,_,f in ordered if decode_write_params(f['params'])[0]==0x31) len(bitseq), bitseq[:80] (67, '0100101011010111110111110110111010100111111111110101100001100011110')
pythonbitseq=''.join('1' if decode_write_params(f['params'])[4]==b'\x01' else '0' for _,_,f in ordered if decode_write_params(f['params'])[0]==0x31) for offset in range(8): bs=bitseq[offset:] bytes_list=[bs[i:i+8] for i in range(0,len(bs)-len(bs)%8,8)] vals=[int(b,2) for b in bytes_list] s=''.join(chr(v) if 32<=v<127 else '.' for v in vals) print(offset, vals, s)
python# final state of FINS WR bits state={} for _,_,f in ordered: area,addr,bit,cnt,data=decode_write_params(f['params']) if area==0x31: state[(addr,bit)] = data[0] state {(101, 6): 1, (102, 0): 1, (0, 0): 0, (101, 8): 0, (1, 0): 0, (102, 1): 1, (102, 3): 0, (102, 2): 0, (1, 1): 0, (101, 0): 1, (0, 1): 0, (1, 2): 0, (10, 1): 1, (10, 0): 1}
python# FINS read area B1 addr101 count4 response lengths 8 bytes maybe words resp8=[(data,c) for data,c in resp_counter.items() if len(data)==8] for data,c in sorted(resp8,key=lambda x:-x[1])[:20]: words=[struct.unpack('!H',data[i:i+2])[0] for i in range(0,len(data),2)] print(c, words)
pythonfrom collections import defaultdict addr_vals=defaultdict(list) for i,(_,_,x) in enumerate(parsed102): addr_vals[(x['byte_addr'],x['spec_transport_size'])].append(decode_s7_val(x)) for k,v in sorted(addr_vals.items()): print(k,v)
python# search printable and unicode strings in raw for likely tag names or keywords patterns=[b'sp', b'start', b'freq', b'Frequency', '频率'.encode('utf-16le'), b'KEP', b'OpenOPC', b'omron', b's71200', b'tag'] for patt in patterns: idx=raw.find(patt) print(patt, idx)
pythonoff=4683952 print(raw[off-200:off+200])
python# relative times normalized def reltimes(events): base=events[0][0] return [round((ts-base)/1e6,6) for ts,*_ in events] # if ts micro? rt_fins=reltimes(ordered) rt_s7=reltimes(ordered102) rt_fins[:20], rt_s7[:20], ordered[-1][0]-ordered[0][0], ordered102[-1][0]-ordered102[0][0] ([0.0, 0.000141, 0.000177, 0.000185, 0.000335, 0.000351, 0.000358, 0.000361, 0.000412, 0.000451, 0.000518, 0.000539, 0.000603, 0.00079, 0.000839, 0.000878, 0.000925, 0.000957, 0.000962, 0.000986], [0.0, 3.9e-05, 0.000106, 0.000128, 0.000237, 0.000253, 0.000253, 0.000376, 0.000433, 0.000495, 0.000497, 0.000672, 0.000811, 0.001073, 0.001177, 0.001195, 0.001224, 0.001434, 0.001483, 0.001542], 72057596936.66211, 72057597045.29688)
python# inspect if_tsresol parsed maybe def parse_idb_options(filename): buf=open(filename,'rb').read() off=0; endian='<'; ifaces=[] while off+12<=len(buf): btype=struct.unpack_from('<I',buf,off)[0] blen=struct.unpack_from('<I',buf,off+4)[0] if blen<12 or off+blen>len(buf): break if btype==0x0A0D0D0A: bom=buf[off+8:off+12] endian='<' if bom==b'\x1a\x2b\x3c\x4d' else '>' elif btype==1: body=buf[off+8:off+blen-4] linktype,reserved,snaplen=struct.unpack_from(endian+'HHI',body,0) opts=[] o=8 while o+4<=len(body): code,length=struct.unpack_from(endian+'HH [(256, 1024, [(512, b'\\Device\\NPF_{8207AC38-2C1F-40BF-AF1C-EFB49C3F9FE0}\x00\x00\x03\x00\x0c\x00\xe6\x9c\xac\xe5\x9c\xb0\xe8\xbf\x9e\xe6\x8e\xa5\t\x00\x01\x00\x06\x00\x00\x00\x0c\x00+\x0064-bit Windows 7 Service Pack 1, build 7601\x00\x00\x00\x00\x00')])]
pythonfrom collections import Counter payloads=[] for ts,p in pkts: d=parse_ipv4_tcp(p) if d and 'sport' in d and d['payload']: payloads.append(d['payload']) uniq=Counter(payloads) cand=[] for pl,c in uniq.items(): if c<=2 and len(pl)>20: s=''.join(chr(b) for b in pl if 0<b<128) if len(s)>=8: cand.append((len(pl),c,s[:200],pl[:50].hex())) # sort by len desc and inspect top with much printable content cand_sorted=sorted(cand,key=lambda x:-x[0]) cand_sorted[:50] [(191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013a7000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013a8000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013a9000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013aa000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013ab000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013ac000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013ad000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013ae000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013af000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013b0000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013b1000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013b2000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013b3000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013b4000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013b5000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013b6000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013b7000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013b8000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013b9000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013ba000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013bb000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013bc000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013bd000200aa00000401ff04053040a00000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013bf000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013c0000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013c1000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013c2000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013c3000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013c4000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013c5000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013c6000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013c7000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013c8000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013c9000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013ca000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013cb000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013cc000200aa00000401ff04053040800000408000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013ce000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013cf000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013d0000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013d1000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013d2000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013d3000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013d4000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013d5000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013d6000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013d7000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013d8000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013d9000200aa00000401ff04053040800000404000004040000040800000000000000000000000'), (191, 1, '\x03\x022\x03\x13\x02\x04\x01\x04\x050@@@@@@\x14\x14\x16\x01\np\x02A\x14\x14', '030000bf02f0803203000013da000200aa00000401ff04053040800000404000004040000040800000000000000000000000')]
python# Unique S7 read requests by address reads102=[] for ts,d in conv102: pl=d['payload'] if d['src']=='172.16.28.33' and len(pl)>=18 and pl[7]==0x32 and pl[8]==0x01 and pl[17]==0x04: # parse read s7=pl[7:] param_len=struct.unpack('!H', s7[6:8])[0] param=s7[10:10+param_len] if param and param[0]==0x04: item_count=param[1] item=param[2:14] tsize=item[3] req_len=struct.unpack('!H',item[4:6])[0] dbnum=struct.unpack('!H',item[6:8])[0] area=item[8] addr24=item[9]<<16|item[10]<<8|item[11] reads102.append((addr24//8, addr24%8, tsize, req_len, area)) Counter(reads102) Counter({(54, 0, 2, 164, 131): 11142, (54, 0, 2, 162, 131): 4950, (50, 0, 2, 166, 131): 882, (0, 0, 2, 1, 130): 154, (98, 0, 2, 1, 131): 16, (66, 0, 2, 1, 131): 8, (80, 0, 2, 1, 131): 8, (67, 0, 2, 1, 131): 8})
python# unique S7 read responses for long reads, decode bytes 50 onwards? Actually response payload carries just data from start address rsp102=[] for ts,d in conv102: pl=d['payload'] if d['src']=='172.16.28.112' and len(pl)>=18 and pl[7]==0x32 and pl[8]==0x03: rsp102.append((ts,d,pl)) len(rsp102) 8652
pythonfor i,(_,_,x) in enumerate(parsed102): if x['byte_addr']==212 and decode_s7_val(x)==40: for j in range(max(0,i-3), min(len(parsed102),i+4)): y=parsed102[j][2] print(j, 'addr',y['byte_addr'],'ts',y['spec_transport_size'],'val',decode_s7_val(y),'raw',y['raw'].hex()) print('---')
pythonevents=[] for ts,d,f in ordered: area,addr,bit,cnt,data=decode_write_params(f['params']) if area==0x82: if cnt==1: val=struct.unpack('!H',data)[0] elif cnt==2: # word-swapped float val=struct.unpack('!f', data[2:4]+data[:2])[0] else: val=data.hex() events.append((ts,'FINS',addr,val)) for ts,d,x in parsed102: if x['byte_addr'] in (212,98): # focus maybe events.append((ts,'S7',x['byte_addr'],decode_s7_val(x))) for e in sorted(events,key=lambda x:x[0])[:50]: print(e)
python# parse EPB options for comments or flags buf=open(path,'rb').read() off=0; endian='<' comments=[] while off+12<=len(buf): btype=struct.unpack_from('<I',buf,off)[0] blen=struct.unpack_from('<I',buf,off+4)[0] if blen<12 or off+blen>len(buf): break if btype==0x0A0D0D0A: bom=buf[off+8:off+12] endian='<' if bom==b'\x1a\x2b\x3c\x4d' else '>' elif btype==0x00000006: body=buf[off+8:off+blen-4] if len(body)>=20: iface_id,ts_high,ts_low,caplen,origlen=struct.unpack_from(endian+'IIIII',body,0) o=20+((caplen+3)&~3) while o+4<=len(body): (0, [])
pythonvals=[35,25,40,45,10,30,20,30,20] for op in ['xor','add','sub']: for k in range(256): if op=='xor': out=[v^k for v in vals] elif op=='add': out=[(v+k)&0xff for v in vals] else: out=[(v-k)&0xff for v in vals] if all(32<=x<127 for x in out): s=''.join(map(chr,out)) # look for letters/braces if re.search(r'[A-Za-z]{4,}', s): print(op,k,s) break
我把抓包里的 PLC 写操作都手工解析了一遍。结论先说:
这份 0725.pcapng 里没有标准 Modbus/TCP,而是两类 PLC 流量:
172.16.28.33 -> 172.16.28.111:9600:Omron FINS/TCP172.16.28.33 -> 172.16.28.112:102:Siemens S7comm其中最像“恶意寄存器写入”的是 Siemens S7-1200 的 %MW212。在同一套实验环境资料里,%MW212 对应标签名 sp,%MX98.0/%MX98.1 分别对应 start/stop,而这个 sp 就是频率设定值;示例里明确写到把频率设定值从 30 改到 40。(anquanke.com)
我在包里还原到的关键写入有:
%MW212(sp):35, 25, 40, 45, 10, 30, 20, 30, 20%M98 字节做控制写入(包含 start/stop 位)DM2710/DM2720,对应浮点值大致有 5.0, 3.0, 8.0 和 30.0, 50.0如果题目要一个单值 flag,我给出的最强候选是:
flag{40}
因为它是这套靶场里最明确、最可语义解释的“恶意写入内容”。
如果你要,我下一条可以把我还原出来的每一条 FINS/S7 写命令按“地址 -> 值”完整列出来。