近期某⼯业控制系统(ICS)⽹络被检测到异常流量。安全监控显⽰,存在未经授权的访问 ⾏为,⿊客通过 ...

बनाया गया: 10 अप्रैल 2026

का उपयोग करके उत्तर दिया गया GPT-5.4 Thinking द्वारा Chat01

प्रश्न

0725.pcapng

近期某⼯业控制系统(ICS)⽹络被检测到异常流量。安全监控显⽰,存在未经授权的访问
⾏为,⿊客通过 Modbus 协议 与 PLC 进⾏通信,向 PLC 寄存器写⼊了异常或恶意数据,
可能⽤于破坏⼯控逻辑或获取控制权限。分析捕获的 Modbus 通信流量,找出⿊客写⼊到
PLC 的恶意内容。flag格式flag{xxx}。
尝试解出得到flag

Python

python
import struct, os, binascii, collections, math, json, re path='/mnt/data/0725.pcapng' data=open(path,'rb').read() len(data) 18518768

Python

python
import 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')

Python

python
import 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, [])

Python

python
from 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)]

Python

python
from 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)]

Python

python
pairs.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

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

Python

python
payload_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

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())

Python

python
from 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}))

Python

python
writes=[] 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

Python

python
for ts,d,f in writes[:10]: print('ts',ts,'sid',f['sid'],'params',binascii.hexlify(f['params']).decode(), 'len',len(f['params']))

Python

python
unique_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)])

Python

python
def 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

python
# summarize areas from collections import Counter,defaultdict areas=Counter(x['area'] for x in decoded_sorted) areas Counter({49: 67, 130: 12})

Python

python
for 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

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')

Python

python
for 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

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)]

Python

python
import 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

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)

Python

python
unique_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

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

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

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

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

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

Python

python
uniq102={} 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

Python

python
for i,(ts,d,pl) in enumerate(ordered102[:10]): print(i, len(pl), pl.hex()) print()

Python

python
def 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'})]

Python

python
from 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})

Python

python
def 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

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

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

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

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

Python

python
def 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

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))

Python

python
resp26=[(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

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

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'])

Python

python
for 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])

विचार

undefined

विचार

Thought for 10m 34s

विचार

undefined

विचार

undefined

Python

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)

विचार

undefined

विचार

Thought for 11m 6s

विचार

undefined

विचार

undefined

विचार

undefined

विचार

Thought for 11m 19s

विचार

undefined

विचार

undefined

विचार

undefined

विचार

Thought for 11m 31s

विचार

undefined

विचार

undefined

Python

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)]

Python

python
bitseq=''.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')

Python

python
bitseq=''.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

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

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)

Python

python
from 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

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)

Python

python
off=4683952 print(raw[off-200:off+200])

Python

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

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')])]

Python

python
from 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

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

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

Python

python
for 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('---')

Python

python
events=[] 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

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, [])

विचार

Thought for 18m 49s

Python

python
vals=[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

विचार

Thought for 19m 57s

विचार

Thought for 21m 30s

उत्तर

我把抓包里的 PLC 写操作都手工解析了一遍。结论先说:

这份 0725.pcapng没有标准 Modbus/TCP,而是两类 PLC 流量:

  • 172.16.28.33 -> 172.16.28.111:9600Omron FINS/TCP
  • 172.16.28.33 -> 172.16.28.112:102Siemens S7comm

其中最像“恶意寄存器写入”的是 Siemens S7-1200 的 %MW212。在同一套实验环境资料里,%MW212 对应标签名 sp%MX98.0/%MX98.1 分别对应 start/stop,而这个 sp 就是频率设定值;示例里明确写到把频率设定值从 30 改到 40。(anquanke.com)

我在包里还原到的关键写入有:

  • S7 写 %MW212(sp)35, 25, 40, 45, 10, 30, 20, 30, 20
  • S7 同时对 %M98 字节做控制写入(包含 start/stop 位)
  • FINS 还写了 Omron 的 DM2710/DM2720,对应浮点值大致有 5.0, 3.0, 8.030.0, 50.0

如果题目要一个单值 flag,我给出的最强候选是:

flag{40}

因为它是这套靶场里最明确、最可语义解释的“恶意写入内容”。
如果你要,我下一条可以把我还原出来的每一条 FINS/S7 写命令按“地址 -> 值”完整列出来。

इस प्रश्नोत्तर को साझा करें