diff --git a/CHANGES b/CHANGES index e00ab793e7..265f674115 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +2.6-589 | 2019-07-11 13:14:52 -0700 + + * GH-421: fix bugs/regressions in DNP3 analyzer (Hui Lin) + 2.6-587 | 2019-07-11 12:13:48 -0700 * Fix a sign-compare compiler warning (Jon Siwek, Corelight) diff --git a/VERSION b/VERSION index 92fb39a4f0..2e1ccaed09 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6-587 +2.6-589 diff --git a/src/analyzer/protocol/dnp3/dnp3-objects.pac b/src/analyzer/protocol/dnp3/dnp3-objects.pac index 3061500307..3aabe07e2a 100644 --- a/src/analyzer/protocol/dnp3/dnp3-objects.pac +++ b/src/analyzer/protocol/dnp3/dnp3-objects.pac @@ -215,10 +215,10 @@ type Request_Data_Object(function_code: uint8, qualifier_field: uint8, object_ty 0x2804 -> aos_dp: empty; # analog ouput g41 - 0x2901 -> ao_32: AnaOut32; - 0x2902 -> ao_16: AnaOut16; - 0x2903 -> ao_sp: AnaOutSP; - 0x2904 -> ao_dp: AnaOutDP; + 0x2901 -> ao_32: empty; + 0x2902 -> ao_16: empty; + 0x2903 -> ao_sp: empty; + 0x2904 -> ao_dp: empty; # analog output event g42 0x2a00 -> aoe_default: empty; @@ -258,7 +258,6 @@ type Request_Data_Object(function_code: uint8, qualifier_field: uint8, object_ty # class objects g60 0x3C01 -> class0data: empty; # &check(qualifier_field == 0x06); - #0x3C02 -> class1data: uint8; # &check(qualifier_field == 0x06); 0x3C02 -> class1data: empty; # &check(qualifier_field == 0x06 || qualifier_field == 0x07 || qualifier_field == 0x08); 0x3C03 -> class2data: empty; # &check(qualifier_field == 0x06 || qualifier_field == 0x07 || qualifier_field == 0x08); 0x3C04 -> class3data: empty; # &check(qualifier_field == 0x06 || qualifier_field == 0x07 || qualifier_field == 0x08); @@ -266,11 +265,9 @@ type Request_Data_Object(function_code: uint8, qualifier_field: uint8, object_ty 0x4601 -> file_control_id: File_Control_ID; 0x4602 -> file_control_auth: File_Control_Auth_Wrap(function_code); 0x4603 -> file_control_cmd: File_Control_Cmd; # &check( file_control_cmd.op_mode == 0 || file_control_cmd.op_mode == 1 || file_control_cmd.op_mode == 2 || file_control_cmd.op_mode == 3 ); - #0x4604 -> file_control_cmd_status: File_Control_Cmd_Status_Wrap(function_code, prefix.prefix_value); # example shown in P66 0x4604 -> file_control_cmd_status: File_Control_Cmd_Status(prefix.prefix_value); # example shown in P66 0x4605 -> file_trans: File_Transport(prefix.prefix_value); 0x4606 -> file_trans_status: File_Transport_Status(prefix.prefix_value); - #0x4607 -> file_desc: File_Desc_Wrap(function_code); 0x4607 -> file_desc: File_Desc; # internal indication g80 @@ -318,13 +315,20 @@ type Request_Data_Object(function_code: uint8, qualifier_field: uint8, object_ty # authentication challenge g120 0x7801 -> challenge: AuthChallenge(prefix.prefix_value); - 0x7802 -> reply: AuthRely(prefix.prefix_value); + 0x7802 -> reply: AuthReply(prefix.prefix_value); 0x7803 -> aggrRequest: AuthAggrRequest(prefix.prefix_value); - 0x7804 -> seesionKeyRequest: uint8; + 0x7804 -> seesionKeyRequest: uint16; 0x7805 -> status: AuthSessionKeyStatus(prefix.prefix_value); 0x7806 -> keyChange: AuthSessionKeyChange(prefix.prefix_value); 0x7807 -> error: AuthError(prefix.prefix_value); - + 0x7808 -> user_cert: UserCert(prefix.prefix_value); + 0x7809 -> mac: MAC(prefix.prefix_value); + 0x780A -> user_status_change: UserStatusChange(prefix.prefix_value); + 0x780B -> update_key_req: UpdateKeyReq(prefix.prefix_value); + 0x780C -> update_key_rep: UpdateKeyRep(prefix.prefix_value); + 0x780D -> update_key: UpdateKey(prefix.prefix_value); + 0x780E -> update_key_sig: UpdateKeySig(prefix.prefix_value); + 0x780F -> update_key_con: UpdateKeyCon(prefix.prefix_value); default -> unmatched: Default_Wrap(object_type_field); }; }; @@ -468,10 +472,10 @@ type Response_Data_Object(function_code: uint8, qualifier_field: uint8, object_t 0x1f02 -> f_ai_16_wflag: FrozenAnalogInput16wFlag; 0x1f03 -> f_ai_32_wtime: FrozenAnalogInput32wTime; 0x1f04 -> f_ai_16_wtime: FrozenAnalogInput16wTime; - 0x1f05 -> f_ai_32_woflag: AnalogInput32woFlag; - 0x1f06 -> f_ai_16_woflag: AnalogInput16woFlag; - 0x1f07 -> f_ai_sp_wflag: AnalogInputSPwFlag; - 0x1f08 -> f_ai_dp_wflag: AnalogInputDPwFlag; + 0x1f05 -> f_ai_32_woflag: FrozenAnalogInput32woFlag; + 0x1f06 -> f_ai_16_woflag: FrozenAnalogInput16woFlag; + 0x1f07 -> f_ai_sp_wflag: FrozenAnalogInputSPwFlag; + 0x1f08 -> f_ai_dp_wflag: FrozenAnalogInputDPwFlag; # analog input event g32 0x2001 -> ai32wotime: AnalogInput32woTime; @@ -592,12 +596,20 @@ type Response_Data_Object(function_code: uint8, qualifier_field: uint8, object_t # authentication challenge g120 0x7801 -> challenge: AuthChallenge(prefix.prefix_value); - 0x7802 -> reply: AuthRely(prefix.prefix_value); + 0x7802 -> reply: AuthReply(prefix.prefix_value); 0x7803 -> aggrRequest: AuthAggrRequest(prefix.prefix_value); - 0x7804 -> seesionKeyRequest: uint8; + 0x7804 -> seesionKeyRequest: uint16; 0x7805 -> status: AuthSessionKeyStatus(prefix.prefix_value); 0x7806 -> keyChange: AuthSessionKeyChange(prefix.prefix_value); 0x7807 -> error: AuthError(prefix.prefix_value); + 0x7808 -> user_cert: UserCert(prefix.prefix_value); + 0x7809 -> mac: MAC(prefix.prefix_value); + 0x780A -> user_status_change: UserStatusChange(prefix.prefix_value); + 0x780B -> update_key_req: UpdateKeyReq(prefix.prefix_value); + 0x780C -> update_key_rep: UpdateKeyRep(prefix.prefix_value); + 0x780D -> update_key: UpdateKey(prefix.prefix_value); + 0x780E -> update_key_sig: UpdateKeySig(prefix.prefix_value); + 0x780F -> update_key_con: UpdateKeyCon(prefix.prefix_value); #default -> unkonwndata: Debug_Byte; # &check( T ); default -> unmatched: Default_Wrap(object_type_field); @@ -1381,41 +1393,115 @@ type BCD_Large = record { # g120v1 type AuthChallenge(prefix: uint16) = record { - csqUsr: uint32; - hal: uint8; + cha_seq_num: uint32; + user_num: uint16; + mac_alg: uint8; reason: uint8; - chan_data: bytestring &length = (prefix - 10); + chan_data: bytestring &length = (prefix - 8); } &byteorder = littleendian; # g120v2 -type AuthRely(prefix: uint16) = record { - csqUsr: uint32; - chan_data: bytestring &length = (prefix - 4); +type AuthReply(prefix: uint16) = record { + cha_seq_num: uint32; + user_num : uint16; + mac: bytestring &length = (prefix - 6); } &byteorder = littleendian; # g120v3 type AuthAggrRequest(prefix: uint16) = record { - csqUsr: uint32; - chan_data: bytestring &length = (prefix - 4); + cha_seq_num: uint32; + user_num: uint16; } &byteorder = littleendian; # g120v5 type AuthSessionKeyStatus(prefix: uint16) = record { - csqUsr: uint32; + cha_seq_num: uint32; + user_num: uint16; key_alg: uint8; key_status: uint8; - chan_data: bytestring &length = (prefix - 10); + mac_alg: uint8; + cha_data_len : uint16; + chan_data: bytestring &length = cha_data_len; + mac: bytestring &length = (prefix - 11 - cha_data_len); } &byteorder = littleendian; # g120v6 type AuthSessionKeyChange(prefix: uint16) = record { - csqUsr: uint32; - key_wrap_data: bytestring &length = (prefix - 5); + key_change_num: uint32; + user_num: uint16; + key_wrap_data: bytestring &length = (prefix - 6); } &byteorder = littleendian; # g120v7 type AuthError(prefix: uint16) = record { - csqUsr: uint32; + cha_seq_num: uint32; + user_num: uint16; + id: uint16; error_code: uint8; - key_wrap_data: bytestring &length = (prefix - 6); + time_error: bytestring &length = 6; + error_text: bytestring &length = (prefix - 15); } &byteorder = littleendian; + +# g120v8 +type UserCert(prefix: uint16) = record { + method: uint8; + cert_type: uint8; + cert_text: bytestring &length = (prefix - 2); +} &byteorder = littleendian; + +# g120v9 +type MAC(prefix: uint16) = record { + mac_text: bytestring &length = prefix; +} &byteorder = littleendian; + +# g120v10 +type UserStatusChange(prefix: uint16) = record { + method: uint8; + operation: uint8; + seq_num: uint32; + user_role: uint16; + user_role_exp: uint16; + user_name_len: uint16; + user_pubkey_len: uint16; + cert_data_len: uint16; + user_name: bytestring &length = user_name_len; + user_pubkey: bytestring &length = user_pubkey_len; + cert_data: bytestring &length = cert_data_len; +} &byteorder = littleendian; + +# g120v11 +type UpdateKeyReq(prefix: uint16) = record { + method: uint8; + user_name_len: uint16; + master_cha_data_len: uint16; + user_name: bytestring &length = user_name_len; + master_cha_data: bytestring &length = master_cha_data_len; +} &byteorder = littleendian; + +# g120v12 +type UpdateKeyRep(prefix: uint16) = record { + seq_num: uint32; + user_num: uint16; + user_name_len: uint16; + outstation_cha_data_len: uint16; + outstation_cha_data: bytestring &length = outstation_cha_data_len; +} &byteorder = littleendian; + +# g120v13 +type UpdateKey(prefix: uint16) = record { + seq_num: uint32; + user_num: uint16; + update_key_len: uint16; + update_key_data: bytestring &length = update_key_len; +} &byteorder = littleendian; + +# g120v14 +type UpdateKeySig(prefix: uint16) = record { + digital_sig: bytestring &length = prefix; +} &byteorder = littleendian; + +# g120v15 +type UpdateKeyCon(prefix: uint16) = record { + mac: bytestring &length = prefix; +} &byteorder = littleendian; + diff --git a/src/analyzer/protocol/dnp3/dnp3-protocol.pac b/src/analyzer/protocol/dnp3/dnp3-protocol.pac index 154fdc8be3..5c20379c8b 100644 --- a/src/analyzer/protocol/dnp3/dnp3-protocol.pac +++ b/src/analyzer/protocol/dnp3/dnp3-protocol.pac @@ -91,10 +91,57 @@ type DNP3_Application_Response_Header = record { type Request_Objects(function_code: uint8) = record { object_header: Object_Header(function_code); data: case (object_header.object_type_field) of { + # binary output command g12 + 0x0c01 -> g12v1_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x0c02 -> g12v2_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; 0x0c03 -> bocmd_PM: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1*( object_header.number_of_item > ( (object_header.number_of_item / 8)*8 ) ) ]; - 0x3202 -> time_interval_ojbects: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + + # time data interval data object g50 + 0x3201 -> g50v1_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + #0x3202 -> time_interval_ojbects: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; # &check( object_header.qualifier_field == 0x0f && object_header.number_of_item == 0x01); - default -> ojbects: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x3202 -> g50v2_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x3203 -> g50v3_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + + # Time and Date Common Time-of-Occurrence g51 + 0x3301 -> g51v1_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x3302 -> g51v2_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + + # time delay g52 + 0x3401 -> g52v1_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x3402 -> g52v2_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + + # file control g70 + 0x4601 -> g70v1_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x4602 -> g70v2_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x4603 -> g70v3_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x4604 -> g70v4_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x4605 -> g70v5_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x4606 -> g70v6_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x4607 -> g70v7_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + + # internal indication g80 + 0x5001 -> g80v1_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + + # authentication challenge g120 + 0x7801 -> g120v1_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x7802 -> g120v2_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x7803 -> g120v3_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x7804 -> g120v4_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x7805 -> g120v5_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x7806 -> g120v6_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x7807 -> g120v7_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x7808 -> g120v8_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x7809 -> g120v9_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x780A -> g120v10_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x780B -> g120v11_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x780C -> g120v12_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x780D -> g120v13_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x780E -> g120v14_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + 0x780F -> g120v15_objs: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + + # default -> ojbects: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; + default -> objects: empty; }; # dump_data is always empty; I intend to use it for checking some conditions; # However, in the current binpac implementation, &check is not implemented