首页
论坛
课程
招聘
[Reverse] [原创] CISCN2021 HMI 工控
2021-5-27 12:06 2331

[Reverse] [原创] CISCN2021 HMI 工控

2021-5-27 12:06
2331

CISCN2021 HMI

HMI

第一次玩工控…

 

.NET开发的软件,直接放dnSpy,题目给了hint:分析Modbus协议,找出触发flag的特殊事件

 

粗略看了下,知道这是一个客户端,所以需要整一个服务端来交互,找了个测试工具

 

https://github.com/study4coder/HslCommunicationDemo

 

开了服务端,就可以用WireShark抓包了,然而这并不是流量分析题,回到re上

 

下面找flag触发点

 

img

 

dnSpy反编译的代码并不是太好,换ILSpy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// AdvancedHMIControls.AnalogValueDisplay
using System;
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
private void UpdateText()
{
    string text = "";
    string text2 = "";
    double result;
    if (m_ShowValue)
    {
        text = m_Value;
        if (!string.IsNullOrEmpty(m_NumericFormat))
        {
            if (double.TryParse(Value, out result))
            {
                try
                {
                    text = result.ToString(m_NumericFormat);
                }
                catch (Exception ex)
                {
                    ProjectData.SetProjectError(ex);
                    Exception ex2 = ex;
                    text = "Check Numeric Format";
                    ProjectData.ClearProjectError();
                }
            }
        }
        else
        {
            text = Value;
        }
    }
    if (!string.IsNullOrEmpty(m_Prefix))
    {
        text = m_Prefix + text;
    }
    if (!string.IsNullOrEmpty(m_Suffix))
    {
        text += m_Suffix;
    }
    base.Text = text;
    if (double.TryParse(Value, out result))
    {
        if (result > m_ValueLimitUpper)
        {
            base.ForeColor = m_ForeColorOverLimit;
            return;
        }
        if (result < m_ValueLimitLower)
        {
            base.ForeColor = m_ForeColorUnderLimit;
            return;
        }
        base.ForeColor = m_ForeColorInLimits;
    }
    if (PLCAddressValue != null && int.TryParse(PLCAddressValue.PLCAddress, out var result2))
    {
        if ((result2 == 41049) & (Strings.Len(text) < 30))
        {
            combined[0] = text;
        }
        if (result2 == 41048)
        {
            combined[1] = text;
        }
        if (result2 == 41047)
        {
            combined[2] = text;
        }
        if (result2 == 41050)
        {
            combined[3] = text;
        }
        if (result2 == 41053)
        {
            combined[4] = text;
        }
        if (result2 == 41054)
        {
            combined[5] = text;
        }
        if (result2 == 41052)
        {
            combined[6] = text;
        }
        if (result2 == 41051)
        {
            combined[7] = text;
        }
    }
    int num = 0;
    int num2 = 0;
    do
    {
        if (string.IsNullOrEmpty(combined[num2]))
        {
            num = 1;
            break;
        }
        num2 = checked(num2 + 1);
    }
    while (num2 <= 7);
    if (num == 0)
    {
        text2 = GetHash(string.Join(",", combined));
        Console.WriteLine("Booooooooooooooooom!");
        if (Operators.CompareString(text2.Substring(0, 10), "F0B278CCB9", TextCompare: false) == 0)
        {
            Console.WriteLine("CISCN{" + text2 + "}");
        }
    }
}

逻辑很清楚,只要combined数组(大小为9)元素都不为NULL或空,就能触发flag事件。然而并不知道text是什么

 

用dnSpy动态调试下,可以看到text其实是每个**AnalogValueDisplay**控件的属性,并且有上下限

 

img

 

到这里我就没了思路…算是半吊子的工控逆向探索。到比赛结束这题都还是0,等官方出WP再补上吧..

填坑

2021-05-24

 

等了好久,还是没有官方的WP出来,也许是我太天真了把… 只好自己继续冲了。

 

花了近一天时间(中途上课去了),终于找到了规律,花了大半个小时爆出了flag….(题是真的….)

 

讲一下规律把,之前以为他是0.0001一点点加上去的,想了想不可能,时间复杂度算下来,就算是超算也算不出来。

 

于是再次上dnSpy动态调试了一番,看到调用堆栈,瞬间找到了突破点

 

img

 

反复分析一下AdvancedHMIControls.dll!AdvancedHMIControls.SubscriptionHandler.SubscribedDataReturned就能发现它的规律:0.00305和0.0153交替出现,而且分别对应控件的PLCAddress

 

img

 

一番调试过后,总结出以下规律

No Name Range Address StringIndex Step
1 AnalogValueDisplay3 [52.8, 52.9] 41047 2 0.00305
2 AnalogValueDisplay2 [25, 25.1] 41048 1 0.0153
3 AnalogValueDisplay1 [62.1, 62.2] 41049 0 0.00305
4 AnalogValueDisplay4 [406.6, 406.7] 41050 3 0.0153
5 AnalogValueDisplay8 [54, 54.1] 41051 7 0.00305
6 AnalogValueDisplay7 [158, 158.1] 41052 6 0.0153
7 AnalogValueDisplay5 [22, 22.1] 41053 4 0.00305
8 AnalogValueDisplay6 [13.1, 13.2] 41054 5 0.0153
  • No:序号
  • Name:控件名
  • Range:取值范围
  • Address:((AdvancedHMIControls.AnalogValueDisplay)this.m_Parent).m_PLCAddressValue.m_PLCAddress
  • StringIndex:文本排列顺序
  • Step:步长

这里需要注意的是,num就是PLC中的寄存器的值,而且是乘法,所以需要取到范围内能够被Step整除的最小值

 

AnalogValueDisplay3Math.Ceil(52.8 / 0.00305) * 0.00305 即 52.8016

 

如此,时间复杂度大幅度降低,可以考虑爆破了

 

但是,用什么语言实现是一个头疼的问题,试了试使用Python爆破,慢的离谱….

 

写了份Go来爆破。事实证明,Go真香(花的时间依旧在40分钟左右,CPU环境:Intel i5 -10200H)

 

挂上代码(tips:强迫症,挂上了自己写的控制台美化模块,可能会造成性能损失,换成普通的fmt速度应该还能更快)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package main
import (
    "crypto/md5"
    "encoding/hex"
    "fmt"
    "github.com/shopspring/decimal"
    "math"
    // "runtime"
    "strings"
    "time"
    "github.com/Mas0nShi/goConsole/console"
)
var pCount, maxCount int64
var isOK = make(chan bool, 1)
var startTimeStamp int64
func getHashMD5(s string) string {
    md5obj := md5.New()
    md5obj.Write([]byte(s))
    md5Str := hex.EncodeToString(md5obj.Sum(nil))
    return md5Str
}
func dequePush(channel chan string)  {
    str := <- channel
    hash := getHashMD5(str)
    if hash[:10] == "f0b278ccb9" {
        fmt.Println("Success, rawText: " + str)
        fmt.Println("your flag is: " + hash)
        isOK <- true
    }
    pCount++
    close(channel)
}
func floatRange(start float64, end float64, step float64) []string {
    dstart := decimal.NewFromFloat(start)
    dend := decimal.NewFromFloat(end)
    dstep := decimal.NewFromFloat(step)
    var dslice []string
    for !dstart.GreaterThan(dend) {
        dslice = append(dslice, dstart.String())
        dstart = dstart.Add(dstep)
    }
    return dslice
}
func int64toString(n int64) string {
    buf := [11]byte{}
    pos := len(buf)
    i := n
    signed := i < 0
    if signed {
        i = -i
    }
    for {
        pos--
        buf[pos], i = '0'+byte(i%10), i/10
        if i == 0 {
            if signed {
                pos--
                buf[pos] = '-'
            }
            return string(buf[pos:])
        }
    }
}
func backEcho() {
    time.Sleep(3 * time.Second)
    console.Log("Time: " + int64toString(time.Now().UnixNano() / 1e6 - startTimeStamp) + " ms " + "Progress: " +decimal.NewFromFloat(float64(pCount)/float64(maxCount) * 100).Round(2).String() + "% Counts: " + int64toString(maxCount) + "" + "/" + int64toString(pCount))
    select {
    case <-isOK:
        return
    default:
        backEcho()
    }
}
func main() {
    console.Info("Initialization params")
    AnalogValueDisplay1range := floatRange(math.Ceil(62.1 / 0.00305) * 0.00305, 62.2, 0.00305)
    AnalogValueDisplay2range := floatRange(math.Ceil(25 / 0.0153) * 0.0153, 25.1, 0.0153)
    AnalogValueDisplay3range := floatRange(math.Ceil(52.8 / 0.00305) * 0.00305, 52.9, 0.00305)
    AnalogValueDisplay4range := floatRange(math.Ceil(406.6 / 0.0153) * 0.0153, 406.7, 0.0153)
    AnalogValueDisplay5range := floatRange(math.Ceil(22 / 0.00305) * 0.00305, 22.1, 0.00305)
    AnalogValueDisplay6range := floatRange(math.Ceil(13.1 / 0.0153) * 0.0153, 13.2, 0.0153)
    AnalogValueDisplay7range := floatRange(math.Ceil(158 / 0.0153) * 0.0153, 158.1, 0.0153)
    AnalogValueDisplay8range := floatRange(math.Ceil(54 / 0.00305) * 0.00305, 54.1, 0.00305)
    pCount = 0
    maxCount = int64(len(AnalogValueDisplay1range) * len(AnalogValueDisplay2range) * len(AnalogValueDisplay3range) * len(AnalogValueDisplay4range) * len(AnalogValueDisplay5range) * len(AnalogValueDisplay6range) * len(AnalogValueDisplay7range) * len(AnalogValueDisplay8range))
    // runtime.GOMAXPROCS(8)
    console.Info("Start Run")
    go backEcho()
    startTimeStamp = time.Now().UnixNano() / 1e6
    for _, value1 := range AnalogValueDisplay1range {
        for _, value2 := range AnalogValueDisplay2range {
            for _, value3 := range AnalogValueDisplay3range {
                for _, value4 := range AnalogValueDisplay4range {
                    for _, value5 := range AnalogValueDisplay5range {
                        for _, value6 := range AnalogValueDisplay6range {
                            for _, value7 := range AnalogValueDisplay7range {
                                for _, value8 := range AnalogValueDisplay8range {
                                    select {
                                    case <-isOK:
                                        console.Info("End Run")
                                        return
                                    default:
                                        strSilce := []string {value1, value2, value3, value4, value5, value6, value7, value8, ""}
                                        str := strings.Join(strSilce, ",")
                                        channel := make(chan string, 80)
                                        channel <- str
                                        go dequePush(channel)
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Go接触的时间不多,可能会有效率更高的方法,或是代码存在缺陷或BUG,如果您有什么更好的方式或意见,欢迎师傅们和我交流。

 

最后挂个爆出来的图?

 

img


[看雪官方培训] Unicorn Trace还原Ollvm算法!《安卓高级研修班》2021年秋季班火热招生!!

收藏
点赞1
打赏
分享
最新回复 (2)
雪    币: 39
活跃值: 活跃值 (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
王二二 活跃值 2021-5-27 18:08
2
1
师傅我是https://myts2.cn/2021/05/16/ciscn2021/ 博客的博主,方便来交流一下吗?
雪    币: 642
活跃值: 活跃值 (541)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
sunfishi 活跃值 2021-5-27 20:39
3
0
王二二 师傅我是https://myts2.cn/2021/05/16/ciscn2021/ 博客的博主,方便来交流一下吗?

可以吖,QQ:1281814306

最后于 2021-5-27 20:41 被sunfishi编辑 ,原因:
游客
登录 | 注册 方可回帖
返回