DianfengGeek-baby_forenics

今天蹭着学弟的队伍,玩了一下CTF,看到了一个有趣的Misc题目,这里记录一下

巅峰极客题解 Baby Forenics

初步分析

题目只给了一个流量包,打开之后里面基本上都看不懂。。。不过发现里面有一个协议的名字很扎眼,叫做IEC 60870-5 104:

这是个啥玩意儿???不过misc向来都是学习新的协议的,于是一通搜索,搜出来写奇奇怪怪的文章,不过好像都提到说,这个协议是一个用于电力行业采用的应用通信协议。那只能假设出题人真的再考我们这个的知识点了,于是找了几个文档:
比较宽泛的文档
讲的比较细致
这里有格式图

从那篇格式图的博客中,我们能知道,这种协议被称为APDU(Application Protocol Data Unit 应用程序信息单元),结构如下:

可以看到它的魔数为0x68。看到我们的流量包,好像还真的出现过这个数字:

那看来考点没找错,就是考我们这个协议的事情

IEC 60870-5 104 学习笔记

由于是为了做题目,所以是飞快的过了一遍这个协议的内容。

协议种类 ---- I, S, U

大致看了一下,协议分成三种类型的

  • I类型:编号的信息传输帧(通俗点来说就是主要传数据的)
  • S类型:编号的监视功能(个人理解就是校验帧一类的)
  • U类型:控制功能(也就是用于控制是否发送帧之类的)

协议传输与内部计数器

假设有A,B两个站使用这个协议进行通信,双方在通信过程中,内部都会维持三个计数器,分别是

  • V(S):send,发送端的计数器(也就是自己发出去的帧数),每次发送一个数据包自增1
  • V®:recive,接收的计数器(也就是自己收到的帧数),每次接受一个数据包自增1
  • Ack:接收到的数据包中的V®值

用一个实例来说明一下:
假设A向B发送了一个I类型的数据包,那么发送包的当下,因为是第一个发送的包,所有的值都为初始值,所以A中的三个计数器为

  • V(S)=0
  • V®=0
  • ACK=0

所以整个发送过程就类似

1
2
3
A                   B
I(0,0)
-------->

其中,I中记录的内容为I(V(S), V®),具体的格式后面会提到

那假设A又往B发送了一个I类型的数据包,那么当发送的时候,A中的V(S)已经记录过第一次发送的数据了。状态变成了

  • V(S)=1
  • V®=0
  • ACK=0

那么发送过程会变成

1
2
3
A                   B
I(1,0)
-------->

假设之后B要回复一个数据包,那么我们来看一下当前B中的数据包的形式:

  • V(S)=0
  • V®=2
  • ACK=0

于是就变成了

1
2
3
A                   B
I(0,2)
<--------

再之后,A要回复B一个数据包,此时A中的状态为

  • V(S)=2
  • V®=1
  • ACK=2

那么发送的时候数据包中内容就为:

1
2
3
A                   B
I(2,1)
-------->

协议组成

这个协议APDU是由两部分组成的:

  • APCI = Application Protocol Control Information 应用协议控制信息
  • ASDU = Application Service Data Unit 应用服务数据单元

APCI

前面这个APCI其实就是相当于一些常见通信协议中的协议头,用来记录一些基本信息的:

如上,我们结合一下我们实际的数据包一起看:

  • 68:开头数据
  • 0e:表示当前ASDU数据包的长度
  • 最低bit用于表示当前数据包为I类型

然后的这个Control Field控制域,会因为数据包种类不同而不同:

  • I: 这里的4个字节分别表示发送者和接收者
  • S: 这里的4个字节只有后两个字节在使用,表示接收者
  • U: 这里只有第一个字节使用,用于表示当前的控制状态

然后回到我们前面的示例中,我们看的是一个I种类的数据包

  • 0400和0200:表示发送者和接收者(小端)。但是根据协议,它们的最低bit是未被使用的,所以其其实表示的是发送者和接收者为2和1,正好就是I(2,1)

我们可以看一下别的数据类型的包:

  • 0100:当前数据包为S类型,并且除了最低的两个bit其余bit并未使用
  • 0400:表示接收者。这里表示的是接收者的计数器为2

43中的低量比特表示当前包类型为U,而高6bit表示当前控制为TESTER act。这个数据包之后需要跟随一个TESTER con的U类型包作为回应,用来确保当前传输的稳定性

ASDU

之后的数据表示的是ASDU,也就是数据信息,我猜测可以将这里理解成,从这里开始表示的是传输的内容:

  • 01:表示当前信息的类型,显示是M_SP_NA_1(1),也就是遥信单点信息的意思
  • 下一个01:表示的是信息的个数,也即是之后1个
  • 03:表示传输原因,这里的原因是突发传输
  • 00:表示传输发起人的地址,这里就是0
  • 0100:表示通用ASDU的地址,同样是小段,这里是0001的意思
  • 070000:表示当前对象地址,这里是7
  • 01:表示当前传输的品质(不知道为啥是这么个翻译),这里可以看wireshark的解析:

    总的来说,就是当前的SPI(状态)属于合(?)
    反正总的来说,这个状态表示的是1,那么可以猜测如果没有信息传输的话,默认的信息应该为0

题解

大致学习了一下整个协议,发现这个协议本质上是一个信息传输协议。我猜测这个协议应该是用来传输一些非常底层的数据的,所以很可能每次tcp都只会传输一个bit的信息,而选择对象的下表,应该就是当前对象的地址。那么我们就只需要将这个传输过程中提到的对象地址取出来,将其设为1,其余比特设为0,那就能得到整个传输的数据了!

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
content = ['0']*100
content[2] = '1'
content[3] = '1'
content[6] = '1'
content[7] = '1'
content[0xA] = '1'
content[0xb] = '1'
content[0xd] = '1'
content[0xe] = '1'
content[0x13] = '1'
content[0x14] = '1'
content[0x16] = '1'
content[0x1a] = '1'
content[0x1b] = '1'
content[0x1f] = '1'
content[0x20] = '1'
content[0x1e] = '1'
content[0x28] = '1'
content[0x26] = '1'
content[0x27] = '1'
content[0x25] = '1'
content[0x22] = '1'
content[0x24] = '1'
content[0x2a] = '1'
content[0x2f] = '1'
content[0x2e] = '1'
content[0x32] = '1'
content[0x36] = '1'
content[0x35] = '1'
content[0x40] = '1'
content[0x3a] = '1'
content[0x42] = '1'
content[0x46] = '1'
content[0x48] = '1'
content[0x47] = '1'
print(content)

def getrevnum(bits):
re =''.join(reversed(bits))
return int(re, 2)

def getnum(bits):
return int(bits, 2)

for i in range(1,len(content), 8):
if i + 8 > len(content):
continue
each_content = ''.join(content[i:i+8])
print(chr(getnum(each_content)),end='')

后记

这次比赛除了这个题目其实还有一些比较标准的CTF题,有空的话再整理到博客上吧。