0x01 赛题说明
赛题说明:
只能变电站通过 61850 规约进行监控层到间隔层的数据采集,请分析网络数据包,了解 MMS 规约,进一步发现数据中隐藏的 flag。
题目:question_1531222544_JYvFGmLP49PFC0R2.pcap.zip(下载见最下方的百度云传送门)
0x02 背景知识
MMS即制造报文规范,是ISO/IEC9506标准所定义的一套用于工业控制系统的通信协议。
此处不介绍关于 MMS 协议的具体内容,只做报文简单讲解。
如果有兴趣,可以阅读此文:MMS(见最下方的百度云传送门)
1、Initiate
MMSPdu Received ::=
A8 25 80 02 08 00 81 01 05 82 01 05 83 01 05 A4 16 80 01 01 81 03 05 F8 00 82 0C 03 EE 19 00 18 00 02 00 00 00 FD 18
Tag Length Value -- Tag definition
--------------------------------------------------------------------------------------------------
[8] A8 25 -- initiate-RequestPDU,
A8 = 1010 1000;
bit7,6 = Tag type, 00 = Universal tag, 10 = Context specific
bit5 = , 0 = Primitive, 1 = Contructed
(
00 0 = INTEGER, BITSTRING, BOOLEAN
00 1 = SEQUENCE, SEQUENCE OF
10 0 = IMPLICIT
10 1 = IMPLICIT SEQUENCE, IMPLICIT SEQUENCE OF
)
bit4-0 = Value, for primitive Universal tags, value defined in ASN.1, other use [x] in MMS.
{
[0] 80 02 08 00 - localDetailCalling (maxProposedMMSPduSize) = 2048 bytes
[1] 81 01 05 -- proposedMaxServOutstandingCalling
[2] 82 01 05 -- proposedMaxServOutstandingCalled
[3] 83 01 05 -- proposedDataStructureNestingLevel
[4] A4 16 -- mmsInitRequestDetail
{
[0] 80 01 01 -- proposedVersionNumber, MMS ISO IS 9506
[1] 81 03 05 F8 00 -- proposedParameterCBB,
BitString(11 bits used)
05 = indicate number of unused bit
F8 00
{
str1 (bit 0 / array support / MSB of F8) -- supported
str2 (bit 1 / structure support) -- supported
vnam (bit 2 / named variable support) -- supported
valt(bit 3 /alternate access support) -- supported
vadr(bit 4/ unnamed variable support) -- supported
viscera(bit 5/ scattered access support) -- not-supported
toy(bit 6/ third party operations support) -- not-supported
villas(bit 7/ named variable list support) -- not-supported
real(bit 8 / ASN.1 real data type support) -- not-supported
ache(bit 9/ acknowledge event conditionsupport) -- not-supported
chi(bit 10 / condition event support) -- not-supported
}
[2] 82 0C 03 EE 19 00 18 00 02 00 00 00 FD 18 -- servicesSupportedCalling, see ISO/IEC-9506
}
}
2、Initiate-Response
MMSPdu Received ::=
A9 25 80 02 08 00 81 01 05 82 01 05 83 01 05 A416 80 01 01 81 03 05 F8 00 82 0C 03 EE 19 00 18 00 02 00 00 00 FD 18
[9] A9 25 -- Initiate-ResponsePDU
{
[0] 80 02 08 00 -- localDetailCalled
[1] 81 01 05 -- negotiatedMaxServOutstandingCalling
[2] 82 01 05 -- negotiatedMaxServOutstandingCalled
[3] 83 01 05 -- negotiatedDataStructureNestingLevel
[4] A4 16
{
[0] 80 01 01 -- negotiatedVersionNumber
[1] 81 03 05 F8 00 -- negotiatedParameterCBB
[2] 82 0C 03 EE 19 00 18 00 02 00 00 00 FD 18 -- servicesSupportedCalled
}
}
3、Identify
MMSPdu Received ::=
A0 05 02 01 01 82 00
[0] A0 05 -- Confirmed-RequestPDU
{
02 01 01 -- invokeID
( 02 = 000 0 0010, Universal Primitive tag, Tag Value = 2 = Integer. )
[2] 82 00 -- ConfirmedServiceRequest, 82 = Request identify
}
where, invokeID ::=01
4、Identify-Response
MMSPdu Received ::=
A1 2A 02 01 01 A2 25 80 0B 53 49 53 43 4F 2C 2049 6E 63 2E 81 10 41 58 53 34 2D 4D 4D 53 2D 3133 32 2D 30 31 38 82 04 32 2E 30 30
[1] A1 2A -- Confirmed-ResponsePDU
{
02 01 01 -- invokeID, Integer
[2] A2 25 -- ConfirmedServiceResponse, A2 = Response identify.
{
[0] 80 0B 53 49 53 43 4F 2C 20 49 6E 63 2E -- vendorName
[1] 81 10 41 58 53 34 2D 4D 4D 53 2D 31 33 32 2D 30 31 38 -- modelName
[2] 82 04 32 2E 30 30 -- revision
}
}
where,
invokeID::=01 note: matching of response to is done by matching invokeID with response invokeID.
vendorName::="SISCO, Inc"
modelName::="AXS4-MMS-132-018"
revision::="2.00"
check [MMS and ASN.1 Encoding] page 10.
5、Read-Request
MMSPdu Received ::=
A0 1E 02 01 0A A4 19 A1 17 A0 15 30 13 A0 11 80 0F 66 65 65 64 65 72 31 5F 33 5F 70 68 61 73 65
[0] A0 1E -- ConfirmedRequestPDU
{
02 01 0A -- invokeID
[4] A4 19 -- ConfirmedServiceRequest, A4 = Read
{
[1] A1 17 -- variableAccessSpecification
{
[0] 30 13 -- listOfVariable
(30 = 00 1 10000, Universal Constructed)
{
[0] A0 11 -- variableSpecification
{
[0] 80 0F 66 65 65 64 65 72 31 5f 33 5f 70 68 61 73 65 -- name
}
}
}
}
}
where,
invokeID::=0A
Identifier (name of variable to read)::="feeder1_3_phase"
6、Read -Response
Assume
typedef struct var_def
{
int a;
int b;
} VAR_DEF;
VAR_DEF feeder1_3_phase;
MMS Data Production
Data ::= CHOICE
{
[1] IMPLICIT SEQUENCE OF,-- arrayed data
[2] IMPLICIT SEQUENCE OF,-- structured data
[3] IMPLICIT BOOLEAN,
[4] IMPLICIT BIT STRING,
[5] IMPLICIT INTEGER,-- signed int
[6] IMPLICIT INTEGER,-- unsigned int
[7] IMPLICIT Floating Point,
[9] IMPLICIT OCTET STRING,
[10] IMPLICIT VisibleString,
[11] IMPLICIT GeneralizedTime,
[12] IMPLICIT TimeofDay,
[13] IMPLICIT INTEGER,-- BCD
[14] IMPLICIT BIT STRING,-- boolean array
[15] IMPLICIT OBJECT IDENTIFIER
}
The encoded structure of the encoded data can be determined via VAR_DEF
VAR_DEF::= TAG
-------------------------------------------
struct { A2
inta; 85
intb; 85
}
MMSPdu Received ::=
A1 0F 02 01 0A A4 0A A1 08 A2 06 85 01 00 85 0100
[1] A1 0F -- ConfirmedResponsePDU
{
02 01 0A -- invokeID
[4] A4 0A -- ConfirmedServiceResponse, A4 = Read
{
[1] OF A1 08 -- listOfAccessResult
{
A2 06 -- success, Data of struct
{
85 01 00 -- int a;
85 01 00 -- int b;
}
}
}
}
where,
invokeID::=0A
value of a::=00, value of b::=00
这里例举了 3 种MMS 报文。
通过分析这三种报文得知MMS协议的规约中的一些结构,如下:
Initiate
initiate-RequestPD结构包含:
- localDetailCalling
- proposedMaxServOutstandingCalling
- proposedMaxServOutstandingCalled
- proposedDataStructureNestingLevel
- mmsInitRequestDetail
- proposedVersionNumber
- proposedParameterCBB
- servicesSupportedCalling等信息
Initiate-ResponsePDU结构包含:
- localDetailCalled
- negotiatedMaxServOutstandingCalling
- negotiatedMaxServOutstandingCalled
- negotiatedDataStructureNestingLevel
- negotiatedVersionNumber
- negotiatedParameterCBB
- servicesSupportedCalled等信息
Identify:
Confirmed-RequestPDU结构包含:
- invokeID
- tag
- ConfirmedServiceRequest等信息
Confirmed-ResponsePDU结构包含:
- invokeID
- ConfirmedServiceResponse
- vendorName
- modelName
- revision等信息
Read:
ConfirmedRequestPDU结构包含:
- invokeID
- ConfirmedServiceRequest
- variableAccessSpecification
- listOfVariable
- variableSpecification
- name等信息
ConfirmedResponsePDU结构包含:
- invokeID
- ConfirmedServiceResponse
- listOfAccessResult等信息
这里仅作简单说明,实际上以上的三种报文并非包含所有的MMS协议的协议数据单元,也没有包含所有的结构
如果有新兴趣研究具体内容,可以下载阅读此文件:GBT16720.2-2005_工业自动化系统_制造报文规范_第2部分_协议规范.pdf(下载见最下方的百度云传送门)
0x03 解题
对于数据包分析的 CTF 题目,第一件做的事情就是搜索关键字:flag
通过关键字的搜索可以发现存在 flag.txt,进一步查询此关键字:
在
1771 146.535714 192.168.2.112 192.168.2.53 MMS 110 513 confirmed-RequestPDU
发现:
如上图所示,这是一个RequestPDU,请求的内容如下:
MMS
confirmed-RequestPDU
invokeID: 513
confirmedServiceRequest: fileOpen (72)
fileOpen
fileName: 1 item
FileName item: flag.txt
initialPosition: 0
对应的是RequestPDU结构
那么根据上文中的知识,存在着对应的 ResponsePDU
但是注意,我们需要的并不是fileOpen(72) 的数据流
fileOpen 的数据告诉我们的仅仅是打开文件的名字
这些文件中存在一个文件名叫 flag.txt的文件,而我们需要的是 fileRead 或是 fileWrite
根据上图中,flag.txt所在行 为 1771,那么我们只需要用筛选器筛选出 fileRead 或者 fileWrite 的ResponsePDU
就应该在 1771 后 N 行找到 flag
根据 fileOpen 为 72 来看,73 应该为 fileRead,74 应该为 fileWrite,75 应该为 fileClose
但是发现
73 为 fileRead 没错,但是 74 为 fileClose ,75 返回的内容为空,即说明不存在 write 的操作。
那么简单明了,做过滤器mms.confirmedServiceRequest == 73,且在 1771 行后寻找读 flag.txt 的filedata
发现在 1771 行后最近的一行为 1800,这行为 RequestPDU,于是找到其对应 invokeID=527 的 ResponsePDU:
根据结果,发现其 fileData 内容为:363138353040313032
对应的 ASCII 内容为: 61850@102
即是最终的 flag
0x04 总结
此题大佬的解法很简单,只要知道对应的MMS协议的规约中Confirmed-RequestPDU和Confirmed-ResponsePDU的结构
然后找到对应的服务,直接一个脚本搞定。
如下:
import pyshark
def flag():
try:
captures = pyshark.FileCapture("question_1531222544_JYvFGmLP49PFC0R2.pcap")
flag_frsm = False
flag_frsm_id = None
flag_read = False
for capture in captures:
for pkt in capture:
if pkt.layer_name == "mms":
# file open
if hasattr(pkt, "confirmedservicerequest") and int(pkt.confirmedservicerequest) == 72:
if hasattr(pkt, "filename_item"):
filename_items = pkt.filename_item.fields
for f in filename_items:
file_name = str(f.get_default_value())
if file_name == "flag.txt":
flag_frsm = True
if hasattr(pkt, "confirmedserviceresponse") and int(pkt.confirmedserviceresponse) == 72 and flag_frsm:
# print(pkt.field_names)
if hasattr(pkt, "frsmid"):
flag_frsm_id = pkt.frsmid
flag_frsm = False
# file read
if hasattr(pkt, "confirmedservicerequest") and int(pkt.confirmedservicerequest) == 73 and flag_frsm_id:
if hasattr(pkt, "fileread"):
if str(pkt.fileread) == str(flag_frsm_id):
flag_read = True
flag_frsm_id = None
if hasattr(pkt, "confirmedserviceresponse") and int(pkt.confirmedserviceresponse) == 73 and flag_read:
if hasattr(pkt, "filedata"):
data = str(pkt.filedata).replace(":", "")
print(hex_to_ascii(data))
flag_read = False
except Exception as e:
print(e)
def hex_to_ascii(data):
data = data.decode("hex")
flags = []
for d in data:
_ord = ord(d)
if (_ord > 0) and (_ord < 128):
flags.append(chr(_ord))
return ''.join(flags)
if __name__ == '__main__':
flag()
工控协议有很多,代表性的除了 MMS 外还有Modbus、DNP3、Melsec-Q、S7、Ethernet/IP 等,
出题的方式也有很多,主要还是对这个协议比较熟悉,那么解什么题都很容易了
刚研究工控安全,如果文字描述有误,还望斧正,另外,如果有兴趣的小伙伴可以一起研究工控安全的内容~
0x04 参考
2018年工业信息安全技能大赛(东北赛区)解题报告——工业网络数据分析
=====================
传送门:
链接:百度网盘-链接不存在
密码:9mb8