[{"data":1,"prerenderedAt":454},["ShallowReactive",2],{"content:\u002F5%E6%9C%8821%E6%97%A5%E5%88%B7%E9%A2%98":3,"surround:\u002F5%E6%9C%8821%E6%97%A5%E5%88%B7%E9%A2%98":453},{"id":4,"title":5,"body":6,"categories":429,"date":431,"description":432,"draft":433,"extension":434,"image":435,"meta":436,"navigation":438,"path":439,"permalink":439,"published":435,"readingTime":440,"recommend":435,"references":435,"seo":445,"sitemap":446,"stem":447,"tags":448,"type":451,"updated":431,"__hash__":452},"content\u002Fposts\u002F2026\u002F5月21日刷题.md","5 月 21 日刷题记录",{"type":7,"value":8,"toc":414},"minimark",[9,13,23,28,35,39,66,130,133,137,142,149,163,167,170,179,186,189,199,203,206,228,231,234,240,243,245,251,255,257,263,266,271,274,281,291,299,318,321,327,339,345,348,354,360,366,369,377,380,386,389,395,398,404,408],[10,11,12],"h2",{"id":12},"今日靶场",[14,15,16],"p",{},[17,18,22],"a",{"href":19,"rel":20},"https:\u002F\u002Fplatform.cyclens.tech\u002F#\u002F",[21],"nofollow","循镜",[24,25,27],"h1",{"id":26},"公交车司机-misc工控流量分析","公交车司机 (Misc\u002F工控流量分析)",[14,29,30],{},[31,32],"img",{"alt":33,"src":34},"靶场题目","\u002Fimages\u002Fctf-modbus-1.png",[10,36,38],{"id":37},"_1-题目背景与分析","1. 题目背景与分析",[40,41,42,50,56],"ul",{},[43,44,45,49],"li",{},[46,47,48],"strong",{},"题目名称","：公交车司机",[43,51,52,55],{},[46,53,54],{},"题目描述","：有一名黑客入侵了公交车交通系统，我们需要分析流量中的蛛丝马迹，找到 FLAG。",[43,57,58,61,62],{},[46,59,60],{},"文件名称","：",[63,64,65],"code",{"code":65},"modbus.pcapng",[14,67,68,69,72,73,125,126,129],{},"通过 Wireshark 的",[63,70,71],{"code":71},"统计 (Statistics) -> 协议分级 (Protocol Hierarchy)","功能观察，发现该流量包中 ",[74,75,78,106],"span",{"className":76},[77],"katex",[74,79,82],{"className":80},[81],"katex-mathml",[83,84,86],"math",{"xmlns":85},"http:\u002F\u002Fwww.w3.org\u002F1998\u002FMath\u002FMathML",[87,88,89,101],"semantics",{},[90,91,92,96],"mrow",{},[93,94,95],"mn",{},"100",[97,98,100],"mi",{"mathvariant":99},"normal","%",[102,103,105],"annotation",{"encoding":104},"application\u002Fx-tex","100\\%",[74,107,111],{"className":108,"ariaHidden":110},[109],"katex-html","true",[74,112,115,120],{"className":113},[114],"base",[74,116],{"className":117,"style":119},[118],"strut","height:0.8056em;vertical-align:-0.0556em;",[74,121,124],{"className":122},[123],"mord","100%"," 均为 ",[46,127,128],{},"Modbus\u002FTCP"," 协议流量，总共包含 150 个数据包。",[14,131,132],{},"结合题目公交车交通系统的背景，判定这是一道典型的工控安全流量分析题。黑客通过 Modbus 协议向 PLC（可编程逻辑控制器）发送了指令，Flag 极有可能隐藏在黑客写入的数据中。",[10,134,136],{"id":135},"_2-解题步骤","2. 解题步骤",[138,139,141],"h3",{"id":140},"步骤一流量宏观观察","步骤一：流量宏观观察",[14,143,144,145,148],{},"使用 Wireshark 打开流量包，在主界面过滤器中输入 ",[63,146,147],{"code":147},"modbus"," 进行筛选。",[14,150,151,152,155,156,162],{},"观察发现，所有的 ",[63,153,154],{"code":154},"Query"," 请求功能码均为 ",[46,157,158,161],{},[63,159,160],{"code":160},"Func: 16: Write Multiple Registers","（写多个保持寄存器）","，且每一条报文的长度均为固定的 69 字节。这表明黑客正在连续、有规律地向工控设备写入特定数据。",[138,164,166],{"id":165},"步骤二数据提取","步骤二：数据提取",[14,168,169],{},"由于数据包较多（150个），逐个手动查看效率较低。我们利用 Wireshark 的导出功能将流量包转换为结构化的 JSON 文件：",[40,171,172],{},[43,173,174,175,178],{},"点击 ",[63,176,177],{"code":177},"文件 (File) -> 导出分组解析结果 (Export Packet Dissections) -> 作为 JSON... (As JSON...)","。",[14,180,181,182,185],{},"随后编写 Python 脚本，批量提取每个 Modbus 报文中的寄存器写入值（",[63,183,184],{"code":184},"modbus.regval_uint16"," 字段）：",[14,187,188],{},"Python",[190,191,196],"pre",{"className":192,"code":194,"language":195},[193],"language-text","import json\n\n# 读取导出的 JSON 流量数据\nwith open('modbus.json', 'r', encoding='utf-8') as f:\n    packets = json.load(f)\n\nextracted_values = []\nfor p in packets:\n    layers = p.get('_source', {}).get('layers', {})\n    mb = layers.get('modbus', {})\n    \n    # 提取寄存器写入值\n    for k, v in mb.items():\n        if isinstance(v, dict) and 'modbus.regval_uint16' in v:\n            extracted_values.append(int(v['modbus.regval_uint16']))\n            break\n","text",[63,197,194],{"__ignoreMap":198},"",[138,200,202],{"id":201},"步骤三数据分析与解码","步骤三：数据分析与解码",[14,204,205],{},"对提取出的寄存器数值进行分析发现，前 66 个数据包的值较小，属于干扰或探测流量。",[14,207,208,209,212,213,216,217,220,221,216,224,227],{},"从",[46,210,211],{},"第 67 个数据包","开始，写入的数值对应的 ASCII 码呈现明显的十六进制字符特征（如 ",[63,214,215],{"code":215},"54"," 对应字符 ",[63,218,219],{"code":219},"'6'","，",[63,222,223],{"code":223},"99",[63,225,226],{"code":226},"'c'","）。",[14,229,230],{},"将第 67 包之后的所有数值转换成对应的字符并拼接，得到如下十六进制字符串：",[14,232,233],{},"Plaintext",[190,235,238],{"className":236,"code":237,"language":195},[193],"666c61677b31343234303135392d376633362d346333362d623436612d3734613661366465643834617d\n",[63,239,237],{"__ignoreMap":198},[14,241,242],{},"使用 Python 或 CyberChef 将该 Hex 字符串转换为明文字符串：",[14,244,188],{},[190,246,249],{"className":247,"code":248,"language":195},[193],"hex_str = \"666c61677b31343234303135392d376633362d346333362d623436612d3734613661366465643834617d\"\nflag = bytes.fromhex(hex_str).decode('utf-8')\nprint(flag)\n",[63,250,248],{"__ignoreMap":198},[10,252,254],{"id":253},"_3-最终-flag","3. 最终 FLAG",[14,256,233],{},[190,258,261],{"className":259,"code":260,"language":195},[193],"flag{14240159-7f36-4c36-b46a-74a6a6ded84a}\n",[63,262,260],{"__ignoreMap":198},[24,264,265],{"id":265},"老乡鸡sign",[14,267,268],{},[31,269],{"alt":33,"src":270},"\u002Fimages\u002Fctf-modbus-2.png",[10,272,273],{"id":273},"解题过程",[14,275,276,277,280],{},"拿到图片后，先查看文件结构。PNG 正常结束于 ",[63,278,279],{"code":279},"IEND","，但文件尾部仍然存在多余数据，说明图片后面可能拼接了其他文件。",[14,282,283,284,286,287,290],{},"手动解析 PNG chunk，发现 ",[63,285,279],{"code":279}," 之后还有 ",[63,288,289],{"code":289},"358"," 字节数据：",[190,292,297],{"className":293,"code":295,"language":296,"meta":198},[294],"language-python","\nimport struct\n\n  \n\npath = \"老乡鸡.png\"\n\ndata = open(path, \"rb\").read()\n\n  \n\noffset = 8\n\nwhile offset \u003C len(data):\n\n    length = struct.unpack(\">I\", data[offset:offset + 4])[0]\n\n    chunk_type = data[offset + 4:offset + 8]\n\n    offset += 12 + length\n\n    if chunk_type == b\"IEND\":\n\n        break\n\n  \n\nprint(\"IEND end:\", offset)\n\nprint(\"trailing bytes:\", len(data) - offset)\n\n","python",[63,298,295],{"__ignoreMap":198},[14,300,301,302,305,306,309,310,313,314,317],{},"查看尾部数据时可以看到明显的 ZIP 目录结构特征，例如 ",[63,303,304],{"code":304},"504B0102"," 和 ",[63,307,308],{"code":308},"504B0506","，并且还出现了 ",[63,311,312],{"code":312},".txt"," 文件名特征。但是文件头缺失了 ",[63,315,316],{"code":316},"504B0304","，所以普通解压工具无法直接解压。",[14,319,320],{},"尾部数据开头类似：",[190,322,325],{"className":323,"code":324,"language":195,"meta":198},[193],"\n33 00 01 00 63 00 ...\n\n...\n\n50 4B 01 02 ...\n\n...\n\n50 4B 05 06 ...\n\n",[63,326,324],{"__ignoreMap":198},[14,328,329,330,332,333,336,337,178],{},"因此将 ",[63,331,279],{"code":279}," 后面的数据取出，并在前面补上 ZIP 本地文件头 ",[63,334,335],{"code":335},"PK\\x03\\x04","，也就是十六进制的 ",[63,338,316],{"code":316},[190,340,343],{"className":341,"code":342,"language":296,"meta":198},[294],"\nimport struct\n\nsrc = \"老乡鸡.png\"\ndata = open(src, \"rb\").read()\n\noffset = 8\nwhile offset \u003C len(data):\n    length = struct.unpack(\">I\", data[offset:offset + 4])[0]\n    chunk_type = data[offset + 4:offset + 8]\n    offset += 12 + length\n    if chunk_type == b\"IEND\":\n        break\n\ntail = data[offset:]\n\nopen(\"recovered_tail_fixed.zip\", \"wb\").write(b\"PK\\x03\\x04\" + tail)\n\n",[63,344,342],{"__ignoreMap":198},[14,346,347],{},"修复后可以识别为 ZIP，里面有一个文件。文件名使用 GBK 解码后为：",[14,349,350],{},[351,352,353],"em",{},"老乡鸡秘诀.txt",[14,355,356,357,359],{},"继续查看 ZIP 信息，发现压缩方式为 ",[63,358,223],{"code":223},"，这是 WinZip AES 加密。",[190,361,364],{"className":362,"code":363,"language":296,"meta":198},[294],"\nimport zipfile\n\nwith zipfile.ZipFile(\"recovered_tail_fixed.zip\") as z:\n    for info in z.infolist():\n        print(info.filename)\n        print(info.compress_type)\n        print(info.flag_bits)\n\n",[63,365,363],{"__ignoreMap":198},[14,367,368],{},"图片元数据中存在异常时间戳提示：",[190,370,375],{"className":371,"code":373,"language":374,"meta":198},[372],"language-bash","\nexiftool 老乡鸡.png\n\n","bash",[63,376,373],{"__ignoreMap":198},[14,378,379],{},"可以看到时间相关字段异常，对应时间戳：",[190,381,384],{"className":382,"code":383,"language":195,"meta":198},[193],"\n1065054630\n\n",[63,385,383],{"__ignoreMap":198},[14,387,388],{},"用该时间戳作为密码解压 AES ZIP。",[190,390,393],{"className":391,"code":392,"language":296,"meta":198},[294],"\nimport pyzipper\n\npassword = b\"1065054630\"\n\nwith pyzipper.AESZipFile(\"recovered_tail_fixed.zip\") as z:\n\n    name = z.namelist()[0]\n    z.setpassword(password)\n    data = z.read(name)\n    print(data.decode(\"utf-8\"))\n\n",[63,394,392],{"__ignoreMap":198},[14,396,397],{},"得到内容：",[190,399,402],{"className":400,"code":401,"language":195,"meta":198},[193],"\nhocctf {老乡鸡_中式快餐_老母鸡汤_香飘全国}\n\n干净卫生，比家里还放心\n\n",[63,403,401],{"__ignoreMap":198},[10,405,407],{"id":406},"flag","Flag",[190,409,412],{"className":410,"code":411,"language":195,"meta":198},[193],"\nhocctf {老乡鸡_中式快餐_老母鸡汤_香飘全国}\n\n",[63,413,411],{"__ignoreMap":198},{"title":198,"searchDepth":415,"depth":415,"links":416},4,[417,419,420,426,427,428],{"id":12,"depth":418,"text":12},2,{"id":37,"depth":418,"text":38},{"id":135,"depth":418,"text":136,"children":421},[422,424,425],{"id":140,"depth":423,"text":141},3,{"id":165,"depth":423,"text":166},{"id":201,"depth":423,"text":202},{"id":253,"depth":418,"text":254},{"id":273,"depth":418,"text":273},{"id":406,"depth":418,"text":407},[430],"Practice","2026-05-21","整理当天刷题过程里的关键步骤、脚本片段和切入点。",false,"md",null,{"slots":437},{},true,"\u002F5月21日刷题",{"text":441,"minutes":442,"time":443,"words":444},"6 min read",5.38,322800,1076,{"title":5,"description":432},{"loc":439},"posts\u002F2026\u002F5月21日刷题",[449,430,450],"CTF","Notes","tech","7EUpHyfdxRKuxWyABH9EHHmsr2ZWNmE4ByT8ijijBXI",[435,435],1786294718122]