2024江西省振兴杯工控CTF

协议分析

被攻击的电机

思路:

过滤一下 modbus 协议,看 func_code == 5 时,Data 的 10 进制数据是大于 3000 的,但是提交 hex 数据错误
modbus.func_code == 5

image-20241011151125-7gprwj3

然后看 func_code == 6 刚好,Data 为 5000 很可疑,提交一下正常,其实就是硬找

image-20241011151415-gwbqc57

flag:flag{c90a00000006030600721388}

omron

思路:

过滤 omron 协议。然后追踪一下 tcp 流

image-20241011151717-i53h75u

image-20241011151731-51ays6h

然后在第二个流发现 base64 字符,尝试解码,就是 flag

image-20241011151813-fkbhlt0

image-20241011151859-fj562i8

flag: flag{guoqingcdgl923}

应急处置

工程被加密了

思路:

下载 组态王7.5SP1软件,打开后发现要密码

image-20241011122112-iia3rrr

猜测为弱密码,123456,进去

image-20241011122136-ip0mygu

然后点击操作记录就能看到 flag

image-20241011122205-60moo2q

flag: flag{fujka8899}

时间炸弹

题目:

产线中运行的PLC程序中被供应商埋入了“时间炸弹”,时间条件满足后触发,PLC的自动运行功能被锁定从而致使生产停摆,只能通过手动进行简单操作,生产效率将大幅降低,需要输入密码进行程序解锁。请分析找出解锁密码,密码即为flag。提交格式:flag{xxxxxxxx}。

固件分析

固件后门分析

思路:

解压文件后找到 tmp 目录打开

image-20241011152317-bliuuex

看到了 runs.py 和 ce.pyc,将 ce.pyc 反编译一下

image-20241011152435-gtz47pt

flag: flag{www.1sdfa4sdfdsgbnm098d8342kflgb.com:38209}

组态编程

西门子组态分析

题目:

请对西门子组态程序进行分析:按照原来的设计思路,按下启动按钮A后设备运行指示灯C应该点亮,但是C并没有亮,请根据程序,判断出还需要按下X(按钮)持续D(秒)以上得到F(点位)=G(数值)才能使得C亮。flag格式为flag{X_D_F_G}。题目同时提供了组态源程序的附件和程序代码段的截图。

思路:

prog-20251113115534-tvxn2zt

prog.png可以看到完整的PLC梯形图程序,包含4个程序段,如下

程序段1:主控逻辑

 %M10.0 ——|  |—— %M10.4 ——|  |—— ( %M10.3 )
    A              D              C
  • 逻辑:A AND D → C
  • 含义:启动按钮A 且 条件D 同时满足时,指示灯C点亮

程序段2:备用控制

 %M10.1 ——|  |—— ( %M10.3 )
    B              C
  • 逻辑:B → C
  • 含义:按钮B可以直接控制指示灯C

程序段3:定时器1

 %M10.1 ——|  |—— [TON] —— ( %M10.4 )
    B         %DB2 T#12S      D
  • 逻辑:B触发12秒定时器,输出到D
  • 含义:按下B按钮12秒后,D条件满足

程序段4:定时器2(关键)

 %M10.3 ——|/|—— %M10.2 ——|  |—— [TON] —— ( %M10.4 )
    C              E         %DB1 T#10S      D
  • 逻辑:NOT C AND E 触发10秒定时器,输出到D
  • 含义:当C未点亮且E按钮按下时,10秒后D条件满足

问题分析如下:

题目描述中提到"按下启动按钮A后设备运行指示灯C应该点亮,但是C并没有亮"。

从程序段1可以看出,要使C点亮需要满足:A AND D

  • A = %M10.0(启动按钮)✓ 已按下
  • D = %M10.4(某个条件)✗ 未满足

问题核心:D条件不满足,导致C无法点亮。

payload:

要使D (%M10.4) 为真,有两种方式:

  1. 方式1(程序段3) :按下B按钮12秒
  2. 方式2(程序段4) :按下E按钮10秒(推荐)

选择方式2的原因

  • 初始状态下C=0,所以NOT C = 1(条件已满足)
  • 只需要按下E按钮即可触发定时器
  • 定时时间更短(10秒 vs 12秒)

操作步骤:

  1. 按下E按钮 (%M10.2) 并保持
  2. 等待10秒 定时器完成
  3. D条件满足 (%M10.4 = 1)
  4. 按下A按钮 (%M10.0)
  5. 指示灯C点亮 (%M10.3 = 1)

逻辑验证:

  1. 初始状态:A=0, B=0, C=0, D=0, E=0
  2. 按下E按钮:E=1, 触发程序段4条件 (NOT C AND E = 1 AND 1 = 1)
  3. 定时器运行:%DB1定时器开始计时10秒
  4. 10秒后:D=1 (%M10.4 = 1)
  5. 按下A按钮:A=1, 满足程序段1条件 (A AND D = 1 AND 1 = 1)
  6. 结果:C=1 (%M10.3 = 1) 指示灯点亮

flag:flag{E_10_D_1}

Smart

题目:

黑客在桌面上留下一个工程文件,经检查发现是西门子200smart plc程序,打开发现可能是一个计算程序,经分析,需要置位v22.0寄存器,获取最终VD716数值,flag为{VW502_VW600_VD716}的最终值。

思路:

使用 STEP7MicroWINSMARTV2.7.0.0 打开发现流程图,

奇怪的文件

题目:

某企业的SCADA系统主机被黑客入侵,管理员小申发现黑客上传了一个文件,我们需要帮助小申分析文件内的蛛丝马迹,找到FLAG,flag形式为 flag{}。

思路:

使用 binwalk 123.jpg 发现图片里面有个压缩包,使用 foremost 分离出来即可,观察压缩包很明显利用 crc32 进行工具,

image-20251114151944-e1dkkyz

然后使用 20251114_151839.dic 生成的字典进行字典爆破

image-20251114152104-bo6jty6

爆破得到密码 kfoudgclum5r9gx0Sv

image-20251114152246-pj4p270

解压得到一个 123 文件,然后 010 观察发现是一个 zip 文件,改完后缀后,观察一下,是一个力控的文件格式,可以先解压出来只有下面那个 xml 文件被加密了

image-20251114152420-h7bcamz

然后搜索 flag 可以发现 flag

image-20251114152551-1xcs0i2

然后使用力控软件(forcecontrol v7.2)恢复一下

image-20251114152835-e10j1h7

然后选择恢复的工程,运行一下,点击用户管理

image-20251114153120-zhhav0k

然后使用 Admin/123 登入一下,在点击修改用户

image-20251114153150-suh5m9m

可以发现有个 flag 用户,估计后半段 flag 就这里

image-20251114153225-vc2yfuh

可以使用 AsteriskPassword-星号密码查看器 查看一下密码,得到后半段 flag

image-20251114153334-745024s

flag:flag{ES8-A05A-CB78-XST78-dgk8tvm9}

梯形图分析1

题目:

小朱在进行设备调试时,编写了一段模拟量转换的程序,已知IW2的值为2100,V10为2.23606801,V27为6429,请计算出V100和V200的值帮助小朱进一步分析,flag为V100与V200之和。flag格式为flag{}

思路:

给了 11.jpg ,使用 010 打开一下,发现图片尾部有个 rar 文件,

image-20251114161153-ne0p3b9

搜索一下发现是一个 AutoThink 项目文件,使用 FA-AutoThinkV3.1.9Beta1和利时PLC 这个工具打开即可

image-20251114161216-cmq2apa

现在就是求V100与V200之和,已知IW2的值为2100,V10为2.23606801,V27为6429

image-20251114161329-cdgc424

不会计算,后面都改 ai 分析的不知道对不对

网络 0001 - 基础运算与模运算

功能: 进行基础的算术运算,为后续计算准备中间变量

运算流程:

 第一个SUB块: V2 = 88 - 23 = 65
 第二个SUB块: V3 = V3 - V2 (这里V3的初值影响结果,但不影响最终目标)
 第三个SUB块: V4 = %IW2 - 56 = 2100 - 56 = 2044
 MUL块: V5 = %IW2 × V4 = 2100 × 2044 = 4,292,400
 MOD块: V6 = V5 MOD V4 = 4,292,400 MOD 2044 = 0

关键分析:

  • V4 = 2044
  • V5 = 4,292,400
  • V6 = 0 (重要!因为V5 = %IW2 × V4,所以V5是V4的整数倍,取模结果为0)

网络 0002 - 除法与开方运算

功能: 进行除法运算、开方运算和数据类型转换

运算流程:

 DIV块: V8 = V6 ÷ V7 = 0 ÷ V7 = 0 (只要V7≠0)
 SQRT块: V10 = √V8 = √0 = 0 (理论计算)
 REAL_TO_INT块: V30 = INT(V10) = INT(2.23606801) = 2

关键分析:

  • V8 = 0 (因为V6=0,所以无论V7为何值,V8都等于0)
  • 题目给出V10 = 2.23606801,这可能是程序运行中的实际值
  • V30 = 2 (对V10进行向零取整)

网络 0003 - 计算V100

功能: 通过减法、开方和幂运算计算目标变量V100

运算流程:

 SUB块: V28 = V7 - V8 = V7 - 0 = V7
 SQRT块: V29 = √V28 = √V7
 EXPT块: V100 = V29^V8 = (√V7)^0 = 1

关键分析:

  • V28 = V7 (但V7的具体值不影响最终结果)
  • V29 = √V7
  • V100 = 1 (任何非零数的0次方都等于1,0^0在大多数PLC中也定义为1)

网络 0004 - 计算V200

功能: 通过模运算、幂运算和三角函数计算目标变量V200

运算流程:

 MOD块: V36 = V27 MOD V30 = 6429 MOD 2 = 1
 SUB块: V31 = V8 - V7 = 0 - V7 = -V7
 EXPT块: V37 = V31^V36 = (-V7)^1 = -V7
 COS块: V200 = COS(V37) = COS(-V7) = COS(V7)

关键分析:

  • V36 = 6429 MOD 2 = 1 (6429是奇数)
  • V31 = -V7
  • V37 = -V7 (因为指数为1)
  • 由于题目未给出V7的值,且程序中未对V7进行赋值,按PLC变量初始化规则,V7 = 0
  • V200 = COS(0) = 1

数学计算验证

关键计算步骤

  1. 模运算验证:

     V5 = 2100 × 2044 = 4,292,400
     V6 = 4,292,400 MOD 2044 = 0
     证明: 4,292,400 ÷ 2044 = 2100 (整除)
    
  2. 实数取整验证:

     V10 = 2.23606801
     V30 = REAL_TO_INT(2.23606801) = 2
    
  3. 幂运算验证:

     V100 = (任意数)^0 = 1
     V200 = COS(0) = 1
    

最终结果计算

  • V100 = 1
  • V200 = 1
  • Flag = V100 + V200 = 1 + 1 = 2

exp:

import math

def solve_plc_problem():
    # 已知条件
    IW2 = 2100
    V10 = 2.23606801
    V27 = 6429
    V7 = 0  # 默认初始值
    
    print("=== 工控梯形图分析求解过程 ===")
    print(f"已知: IW2={IW2}, V10={V10}, V27={V27}")
    
    # 网络0001
    print("\n--- 网络0001 ---")
    V4 = IW2 - 56
    V5 = IW2 * V4
    V6 = V5 % V4
    print(f"V4 = {IW2} - 56 = {V4}")
    print(f"V5 = {IW2} × {V4} = {V5}")
    print(f"V6 = {V5} MOD {V4} = {V6}")
    
    # 网络0002
    print("\n--- 网络0002 ---")
    V8 = V6 // V7 if V7 != 0 else 0  # 避免除零
    V30 = int(V10)
    print(f"V8 = {V6} ÷ {V7} = {V8}")
    print(f"V30 = INT({V10}) = {V30}")
    
    # 网络0003
    print("\n--- 网络0003 ---")
    V28 = V7 - V8
    V29 = math.sqrt(abs(V28)) if V28 >= 0 else 0
    V100 = V29 ** V8 if V8 != 0 else 1
    print(f"V28 = {V7} - {V8} = {V28}")
    print(f"V29 = √{V28} = {V29}")
    print(f"V100 = {V29}^{V8} = {V100}")
    
    # 网络0004
    print("\n--- 网络0004 ---")
    V36 = V27 % V30
    V31 = V8 - V7
    V37 = V31 ** V36
    V200 = math.cos(V37)
    print(f"V36 = {V27} MOD {V30} = {V36}")
    print(f"V31 = {V8} - {V7} = {V31}")
    print(f"V37 = {V31}^{V36} = {V37}")
    print(f"V200 = COS({V37}) = {V200}")
    
    # 最终结果
    print("\n=== 最终结果 ===")
    print(f"V100 = {V100}")
    print(f"V200 = {V200}")
    flag_value = V100 + V200
    print(f"Flag = V100 + V200 = {flag_value}")
    print(f"答案: flag{{{int(flag_value)}}}")
    
    return int(flag_value)

if __name__ == "__main__":
    result = solve_plc_problem()

flag:flag{2}

扫雷

题目:

自动化专家老吴通过PLC编程开发了一款小游戏——扫雷,通关后可获得flag,flag格式为:flag{}。

思路:

想让我通关不可以的,解压出来是 扫雷_V16.ap16 文件,很明显的用S7-1200 博途软件V16 打开,然后 crtl + f 搜索 flag,发现如下

image-20251114193630-vtvxp0q

双击文件定位到关键位置,就能发现密文

image-20251114193811-axnvq1v

密文如下,根据 U2FsdG 头知道这是一个用 js 前端加密库解密的密文,一般都需要 key,看到这个文件名猜测就是密钥,jdgf

U2FsdGVkX1/MRLx94+KHoo9xb0iO9g2swJLVNYD38y7yMYhDpciH6pK0F5HHMY+Qj4XACALNZBY=

解密一下,得到 flag

image-20251114194518-mw2qgf5

在线Triple DES加密 | Triple DES解密- 在线工具 用在线的也许,就是要多试一下解密方式

image-20251114194601-nch4k8l

flag:flag{Revitalization_Cup@578790336}

恶意程序分析

勒索病毒分析

枢网智盾线上赛Writeup - FreeBuf网络安全行业门户

题目:

客团伙利用工控系统的RCE漏洞投递了一款勒索病毒,导致系统重大量重要文件被加密,管理员经过排查已修复相关漏洞,并找到了勒索病毒样本,但无法恢复被加密的文件。你能通过分析恶意样本,协助管理员解密被加密的文件吗?请解密flag.txt.encrypt文件,获取flag。

思路:

ida 反汇编报错

image-20251113130305-yfufu04

可以发现 401D48 这个位置失败,先双击跟进一下这个函数,然后按 f5 ,可以发现这个函数可以反汇编,然后再回到 mian 函数 f5 也能反汇编了

image-20251113130342-6sru3y4

image-20251113130500-ue3bk6a

exp:

# coding: utf-8
"""
勒索病毒解密脚本 - ChaCha20 算法
"""

from Crypto.Cipher import ChaCha20

# 加密数据(明文标记之前的部分)
data = bytes([
    0x0B, 0xB0, 0xB5, 0x4E, 0x89, 0x4E, 0x9D, 0x03, 0x93, 0x1D, 0x5B, 0xE5,
    0x10, 0xE2, 0xEA, 0x13, 0x10, 0xBE, 0x60, 0x09, 0xD8, 0x16, 0x4A, 0xF0,
    0x4B, 0xF8, 0xCB, 0xC6, 0x49, 0x39, 0x88, 0x10, 0x72, 0x00, 0x28, 0xB6,
    0x0D, 0x5A, 0xCB, 0x49, 0xFB, 0x3D
])

# ChaCha20 密钥和 Nonce
key = bytes([
    0x75, 0x2b, 0xcb, 0x44, 0x8b, 0xd1, 0x70, 0x53, 0xe6, 0xb5, 0x5c, 0xc4,
    0xe6, 0xba, 0x1b, 0xe8, 0x75, 0x6d, 0x2d, 0xbb, 0x03, 0x89, 0x9e, 0xb6,
    0x5e, 0xf5, 0xa7, 0xef, 0xf6, 0xde, 0x5e, 0xe7
])

nonce = bytes([
    0x4b, 0xf6, 0x5b, 0x44, 0xe2, 0x79, 0x81, 0x1c, 0x36, 0x7f, 0x94, 0xcc
])

# 初始化 ChaCha20 并设置计数器为 1(偏移 64 字节)
cipher = ChaCha20.new(key=key, nonce=nonce)
cipher.seek(64)

# 解密并输出
decrypted = cipher.decrypt(data)
print(decrypted.decode('utf-8'))

# FLAG: flag{e26e86af-bb71-4bf5-9dec-e845b4f7b1c3}

posted @ 2025-12-06 10:03  lpppp小公主  阅读(0)  评论(0)    收藏  举报