从CTF实战解析Modbus协议:Wireshark与Python脚本攻防技巧
1. 项目概述一次关于工业协议与安全攻防的深度对话如果你玩过CTFCapture The Flag比赛特别是偏向网络流量分析或工控安全的题目那么“Modbus协议”这个名字你一定不陌生。它不像HTTP、TCP那样家喻户晓但在工业控制领域它就像普通话一样通用。最近在CTFHub上一道围绕Modbus协议流量分析的题目引起了我的注意它要求我们从一份抓取的网络流量包中找到隐藏的Flag。这道题的精妙之处在于它不仅仅考察你对协议格式的“死记硬背”更考验你如何将协议知识转化为实际的数据提取和逆向分析能力。题目本身提供了两种截然不同的解题思路一种依赖专业的协议分析工具进行“可视化”操作另一种则通过编写Python脚本进行“自动化”解析后者更能体现一名安全研究员的工程化思维。今天我就来详细拆解这道题不仅带你复现解题过程更会深入探讨Modbus协议本身以及在这两种解法背后我们所需要掌握的核心技能和思维逻辑。无论你是刚接触工控安全的新手还是想精进流量分析技巧的老兵相信这篇从实战出发的总结都能给你带来启发。2. Modbus协议核心机制与流量特征解析在动手分析流量包之前我们必须先理解我们面对的是什么。Modbus是一种应用层报文传输协议位于TCP/IP协议栈之上。它采用经典的“请求-响应”模型结构异常简单清晰这也是其能在工业环境盛行数十年的原因。2.1 协议数据单元PDU与封装Modbus协议的核心是协议数据单元PDU它由两部分构成功能码Function Code和数据域Data Field。功能码1字节指示服务器执行何种操作。例如0x01代表读取线圈状态0x03代表读取保持寄存器0x10代表预置多个寄存器写操作。在CTF题目中涉及数据读取如0x03和写入如0x10、0x06的功能码是重中之重。数据域N字节其格式完全由功能码决定。对于读请求通常包含起始地址和寄存器数量对于读响应则包含字节计数和实际的寄存器值。当Modbus运行在TCP/IP网络上时即Modbus TCPPDU前面会加上一个7字节的MBAP头Modbus Application Protocol Header从而构成完整的ADUApplication Data Unit。MBAP头包含了事务标识符、协议标识符固定为0x0000代表Modbus、长度字段和单元标识符常作为从站地址。因此我们在Wireshark中看到一个Modbus TCP数据包其应用层数据就是MBAP头7字节 PDU功能码数据域。2.2 题目流量中的关键线索回到CTFHub的这道题我们拿到的流量包通常是.pcap或.pcapng格式里必然充斥着Modbus TCP的通信数据。解题的第一步永远是全局观察。用Wireshark打开后直接在过滤栏输入modbus进行筛选。这时你会看到一系列连续的请求和响应。我们需要重点关注以下几点异常的功能码正常的工控读写操作有其规律。如果出现大量非常规的、尤其是写操作如0x06写单个寄存器0x10写多个寄存器这很可能就是出题人隐藏数据的方式。题目常将Flag的ASCII码或经过简单编码后的值写入到某个或某几个寄存器中。响应数据中的异常值对于读请求的响应数据域中返回的寄存器值每个寄存器2字节是核心分析目标。Flag可能被直接转换成十六进制数值存放在这里也可能需要你将多个寄存器的值拼接、转换后才能得到。通信的节奏与模式有时Flag并非存在于单一数据包。它可能被分割成多个部分通过多次写操作存入不同地址的寄存器或者需要通过组合多次读操作的响应才能拼凑完整。观察通信的先后顺序和地址变化至关重要。注意在真实的工控渗透测试中随意使用写功能码如0x060x10是极度危险的行为可能导致设备状态改变甚至引发安全事故。在CTF环境中我们是在一个完全受控的模拟场景下进行分析学习。3. 解法一基于Wireshark的手动分析与数据提取这是最直观、最“经典”的解法适合快速上手和理解数据流。我们完全依靠Wireshark这个瑞士军刀来完成。3.1 流量过滤与会话跟踪打开流量包后首先应用过滤器modbus。为了更清晰地看到对话我们可以右键任意一个Modbus数据包选择追踪流-TCP流。Wireshark会弹出一个新窗口以ASCII或十六进制形式展示这个TCP连接中的所有数据。对于Modbus十六进制视图更合适。在原始十六进制数据中你需要能辨认出Modbus TCP的帧结构。通常一个请求帧看起来像这样示例0000 0000 0006 01 03 0010 0001我们来拆解一下0000: 事务标识符不重要。0000: 协议标识符固定。0006: 长度字段表示其后还有6个字节。01: 单元标识符从站地址。03: 功能码0x03读取保持寄存器。0010: 起始地址十六进制代表寄存器地址16。0001: 寄存器数量读取1个寄存器。对应的响应帧可能为0000 0000 0005 01 03 02 1234前面的MBAP头类似。03: 功能码。02: 字节计数表示后面有2个字节的数据。1234: 读取到的寄存器值2字节十六进制为0x1234。解题实操你需要像侦探一样滚动查看整个TCP流。寻找那些“写寄存器”功能码0x06或0x10的请求。找到后重点看请求帧中“数据域”部分这里包含了要写入的值。或者寻找“读寄存器”功能码0x03或0x04的响应重点看响应帧中“数据域”部分返回的值。3.2 数据转换与Flag拼接假设我们在某个写请求0x06中发现要写入的寄存器地址是0x0001值为0x666c十六进制。这看起来像ASCII码。我们将0x66和0x6c分别转换成十进制再查ASCII表0x66是‘f’0x6c是‘l’。这很可能就是Flag的前两个字母“fl”。接下来你需要在整个会话流中按顺序找出所有类似的写操作或读响应提取出每个寄存器值2字节将其拆分为两个单字节分别转换为ASCII字符。如果寄存器值直接就是可读字符的编码拼接起来就能得到Flag。有时数据可能不是直接的ASCII。可能是Base64编码后的字符串被分段写入也可能是Flag的十六进制表示。这就需要你根据上下文和CTF题目的常见套路进行尝试比如将提取出的所有十六进制字节串联起来然后尝试解码为字符串。实操心得Wireshark的“导出分组字节流”功能非常好用。当你锁定了一个包含关键数据的TCP流后可以在“追踪TCP流”窗口将显示格式改为“原始数据”然后点击“另存为…”将整个流的原始字节保存为一个文件。之后可以用xxd、hexdump或Python脚本进行更灵活的分析这比在Wireshark界面里手动抄录要高效准确得多。4. 解法二基于Python Scapy的自动化脚本解析手动分析对于学习和小型流量包是有效的但不够优雅也容易出错。作为一名安全研究员编写脚本自动化处理才是王道。这里我们使用Python的Scapy库它是一个强大的交互式数据包处理程序可以轻松地对pcap文件进行解析、构造和发送。4.1 环境准备与Scapy基础首先确保安装了Scapypip install scapy。Scapy内置了对Modbus协议的基本支持但为了更精细地解析我们可能需要直接操作原始负载。核心思路是使用rdpcap()函数读取pcap文件。遍历所有数据包筛选出Modbus TCP协议的数据包通常基于TCP端口502以及应用层数据特征。从数据包中提取出原始应用层数据即MBAP头PDU。按照Modbus协议格式手动解析这些字节流提取出我们关心的功能码、地址、数据。4.2 脚本编写与关键代码解析下面是一个解构该题目的Python脚本框架包含了两种常见的Flag隐藏方式从写操作请求中提取、从读操作响应中提取的解析逻辑from scapy.all import * import struct def parse_modbus_packet(pkt): 解析单个数据包中的Modbus TCP数据 if pkt.haslayer(TCP) and pkt[TCP].dport 502 or pkt[TCP].sport 502: # 提取TCP负载即Modbus ADU tcp_payload bytes(pkt[TCP].payload) if len(tcp_payload) 8: # MBAP头至少7字节加1字节功能码 return None # 解析MBAP头事务ID协议ID长度单元ID trans_id, proto_id, length, unit_id struct.unpack(HHHB, tcp_payload[:7]) # Modbus PDU起始于第8字节 pdu tcp_payload[7:7length-1] # length字段包括单元ID所以PDU长度为length-1 if not pdu: return None func_code pdu[0] data pdu[1:] # 返回一个结构化的字典便于后续处理 return { src_ip: pkt[IP].src, dst_ip: pkt[IP].dst, trans_id: trans_id, unit_id: unit_id, func_code: func_code, data: data, raw: tcp_payload } return None def extract_flag_from_write(packets): 从写寄存器请求功能码0x06或0x10中提取Flag flag_parts [] for pkt_info in packets: if pkt_info is None: continue # 0x06: Write Single Register, 0x10: Write Multiple Registers if pkt_info[func_code] in [0x06, 0x10]: data pkt_info[data] # 对于0x06数据格式为地址2字节 值2字节 if pkt_info[func_code] 0x06 and len(data) 4: _, value struct.unpack(HH, data[:4]) # 忽略地址取value # 将16位值拆分为两个8位字节并转换为字符 flag_parts.append(chr((value 8) 0xFF)) flag_parts.append(chr(value 0xFF)) # 对于0x10数据格式更复杂包含地址、数量、字节计数和实际值 # 需要根据具体题目格式调整解析逻辑 # 示例假设数据格式为 地址(2) 数量(2) 字节数(1) 值(N) elif pkt_info[func_code] 0x10 and len(data) 5: # 跳过地址和数量定位到值 byte_count data[4] values_data data[5:5byte_count] # 每两个字节一个寄存器值 for i in range(0, len(values_data), 2): if i1 len(values_data): val (values_data[i] 8) | values_data[i1] flag_parts.append(chr((val 8) 0xFF)) flag_parts.append(chr(val 0xFF)) return .join(flag_parts) def extract_flag_from_read_response(packets): 从读寄存器响应功能码0x03或0x04中提取Flag flag_parts [] for pkt_info in packets: if pkt_info is None: continue # 0x03: Read Holding Registers, 0x04: Read Input Registers if pkt_info[func_code] in [0x03, 0x04]: data pkt_info[data] # 读响应格式字节计数1字节 寄存器值N*2字节 if len(data) 1: byte_count data[0] values_data data[1:1byte_count] for i in range(0, len(values_data), 2): if i1 len(values_data): val (values_data[i] 8) | values_data[i1] # 同样将16位值拆为两个ASCII字符 flag_parts.append(chr((val 8) 0xFF)) flag_parts.append(chr(val 0xFF)) return .join(flag_parts) # 主程序 if __name__ __main__: # 1. 读取pcap文件 packets rdpcap(ctfhub_modbus.pcapng) # 替换为你的文件名 # 2. 解析所有Modbus包 modbus_packets [] for pkt in packets: parsed parse_modbus_packet(pkt) if parsed: modbus_packets.append(parsed) # 可选打印包信息用于调试 # print(fFunc: {hex(parsed[func_code])}, Data: {parsed[data].hex()}) # 3. 尝试两种提取方式 flag_from_write extract_flag_from_write(modbus_packets) print(f从写操作提取的Flag: {flag_from_write}) flag_from_read extract_flag_from_read_response(modbus_packets) print(f从读响应提取的Flag: {flag_from_read}) # 4. 综合判断通常一种方式即可 # 根据输出结果选择正确的Flag脚本逻辑解读parse_modbus_packet函数是核心它负责从原始网络包中剥离TCP/IP头部提取出Modbus ADU并初步解析MBAP头和功能码。extract_flag_from_write和extract_flag_from_read_response函数则针对不同的功能码按照协议规范解析数据域。关键点在于理解寄存器值16位如何与ASCII字符8位对应。常见的出题手法是将一个字符的ASCII码放在寄存器的高8位另一个字符放在低8位或者反之。因此我们需要用位操作(val 8) 0xFF和val 0xFF来拆分。主程序流程清晰读包 - 解析 - 尝试两种提取策略 - 输出结果。注意事项这个脚本是一个通用框架。实际题目中Flag的隐藏方式可能千变万化。例如寄存器值可能不是直接的ASCII而是需要异或、加减某个常数或者需要按特定顺序如先低字节后高字节组合。因此在得到初步提取的字符串后如果看起来是乱码就需要结合CTF经验如观察是否有flag{或CTF{前缀进行进一步的解码或逆向。5. 进阶技巧与深度排查指南掌握了基本解法后我们来看看在实战中可能遇到的“坑”以及如何系统化地排查。5.1 常见问题与排查思路提取出的字符串是乱码或不全检查字节序Modbus协议通常使用大端序Big-Endian即高位字节在前。但在某些特定题目或设备中可能会使用小端序。尝试交换每个寄存器值内部两个字节的顺序或者交换字符拼接的顺序。检查编码Flag可能不是ASCII而是UTF-8、Base64或Hex编码的字符串。尝试将提取出的字节流用bytes.fromhex()、base64.b64decode()等方法解码。检查提取范围你可能遗漏了某些数据包。确认过滤条件是否正确是否包含了所有相关的TCP流可能不止一个连接。在Wireshark中可以尝试过滤tcp.port 502来捕获所有进出502端口的数据。脚本运行报错或解析不出数据确认Scapy版本与加载方式确保Scapy正确安装。有时需要以管理员/root权限运行脚本或者使用scapy.all的导入方式。检查pcap文件路径和权限确保脚本中的文件名正确且Python有权限读取该文件。调试解析函数在parse_modbus_packet函数中增加打印语句输出每个包的TCP端口、负载长度和原始字节确认是否成功捕获到了Modbus数据。有时数据包可能被分片需要重组但CTF题目通常不会这么复杂。Flag格式不符CTF的Flag通常有固定格式如flag{...}、CTF{...}等。如果你提取出的字符串包含类似结构但中间是乱码可能是加密或编码。观察乱码部分是否有规律尝试简单的凯撒密码、单表替换或XOR解密密钥可能藏在流量其他部分如某个固定的寄存器值。5.2 从协议分析到漏洞挖掘的思维延伸解这道题的目的远不止于拿到Flag。它训练了我们几种关键的安全分析能力协议逆向能力即使没有完整的协议文档通过观察流量样本也能推断出字段含义和报文结构。这对于分析私有协议或未知工控协议至关重要。数据关联能力需要将请求和响应配对理解一次操作对系统状态的改变并跟踪数据流。自动化思维面对成百上千个数据包手动分析效率低下且易错。编写脚本不仅能解决当前问题其代码框架稍作修改就能复用于其他类似的Modbus分析任务甚至扩展到其他协议。在真实的工控安全评估中对Modbus协议的分析常常用于发现以下问题未授权访问Modbus TCP通常缺乏身份认证任何能访问502端口的主机都可能读写寄存器。敏感信息泄露通过读寄存器功能码0x030x04可能获取设备状态、工艺参数甚至密码等敏感信息。拒绝服务与状态篡改恶意写寄存器功能码0x060x10可能导致设备停机、工艺参数紊乱引发安全事故。因此这道CTF题目是一个绝佳的缩影它将工控协议的基础知识、网络流量分析的实战技巧和安全研究的自动化思维紧密结合在一起。通过这两种解法的对比与实践你收获的不仅仅是一个Flag更是一套应对类似安全挑战的方法论。下次再遇到Modbus或其他工控协议流量时你就能从容地打开Wireshark或者自信地敲下几行Python代码让数据自己开口说话。

相关新闻

Grove五向开关实战指南:从硬件原理到菜单导航与无线遥控应用

Grove五向开关实战指南:从硬件原理到菜单导航与无线遥控应用

1. 项目概述:从“五向开关”到交互原型的快速通道在嵌入式开发和交互原型制作领域,我们常常需要一种直观、可靠的输入设备来控制系统状态、切换菜单或者进行简单的导航。传统的解决方案可能是组合多个独立按键,或者使用摇杆,但这往…

2026/8/2 11:53:42 阅读更多 →
终极指南:5个专业步骤彻底修复XUnity.AutoTranslator翻译失效问题

终极指南:5个专业步骤彻底修复XUnity.AutoTranslator翻译失效问题

终极指南:5个专业步骤彻底修复XUnity.AutoTranslator翻译失效问题 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator XUnity.AutoTranslator作为游戏本地化领域的强大工具,能够自动翻…

2026/8/2 11:52:27 阅读更多 →
VLC媒体播放器终极转码指南:5分钟掌握专业级视频格式转换

VLC媒体播放器终极转码指南:5分钟掌握专业级视频格式转换

VLC媒体播放器终极转码指南:5分钟掌握专业级视频格式转换 【免费下载链接】vlc VLC media player - plays everything, runs anywhere. Code here: https://code.videolan.org/videolan/vlc 项目地址: https://gitcode.com/gh_mirrors/vl/vlc 你是否曾遇到过…

2026/8/2 11:52:27 阅读更多 →

最新新闻

告别窗口切换:Zotero PDF预览插件让文献管理效率提升50%

告别窗口切换:Zotero PDF预览插件让文献管理效率提升50%

告别窗口切换:Zotero PDF预览插件让文献管理效率提升50% 【免费下载链接】zotero-pdf-preview Preview Zotero attachments in the library view. 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-pdf-preview 你是否经常在Zotero文献库和PDF阅读器之间…

2026/8/2 12:39:20 阅读更多 →
Kali Linux实战:UPX脱壳与ELF逆向分析技术详解

Kali Linux实战:UPX脱壳与ELF逆向分析技术详解

1. 项目概述:从“黑盒”到“白盒”的必经之路在安全研究、恶意软件分析或者对某些闭源软件进行深度审计时,我们常常会遇到一个令人头疼的“黑盒”——一个被加壳保护的二进制文件。它就像一个被层层包裹的礼物,你无法直接看到里面的代码逻辑。…

2026/8/2 12:39:20 阅读更多 →
5分钟解锁Switch手柄PC潜能:BetterJoy完整使用指南

5分钟解锁Switch手柄PC潜能:BetterJoy完整使用指南

5分钟解锁Switch手柄PC潜能:BetterJoy完整使用指南 【免费下载链接】BetterJoy Allows the Nintendo Switch Pro Controller, Joycons and SNES controller to be used with CEMU, Citra, Dolphin, Yuzu and as generic XInput 项目地址: https://gitcode.com/gh_…

2026/8/2 12:39:20 阅读更多 →
基于ESP32-S3与MQTT的无线音频流传输系统设计与实现

基于ESP32-S3与MQTT的无线音频流传输系统设计与实现

1. 项目概述:从硬件阵列到无线音频流的全链路实践最近在折腾一个智能语音交互的本地化项目,核心需求是远场拾音和低延迟的音频流无线传输。市面上的成品方案要么太贵,要么不够灵活,要么延迟感人。经过一番选型和折腾,最…

2026/8/2 12:39:20 阅读更多 →
Arduino集成扩展板设计:一站式解决电机、舵机与传感器驱动难题

Arduino集成扩展板设计:一站式解决电机、舵机与传感器驱动难题

1. 项目概述:Shield MaTrix V0.9是什么?如果你玩Arduino有一段时间了,手头攒了不少传感器、模块,每次做新项目都得重新插线、调试,面包板上线缆乱成一团,调试起来头大,那你肯定能理解我为什么要…

2026/8/2 12:39:20 阅读更多 →
Modbus RTU继电器模块:从协议原理到工业物联网应用实战

Modbus RTU继电器模块:从协议原理到工业物联网应用实战

1. 项目概述:从“开关”到“智能节点”的蜕变如果你在工业自动化、楼宇自控或者智能家居的圈子里待过一阵子,肯定对“继电器”这个词不陌生。它本质上就是一个电控开关,用一个小电流信号去控制一个大电流的通断,是连接弱电控制与强…

2026/8/2 12:38:19 阅读更多 →

日新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/2 0:00:38 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/2 0:00:38 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:38 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/2 0:00:38 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/2 0:00:38 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:38 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/2 6:34:16 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/2 2:47:48 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/2 0:23:22 阅读更多 →