• [工控安全][原创]西门子PLC固件逆向之定位s7comm协议的一个切入口


    [原创]西门子PLC固件逆向之定位s7comm协议的一个切入口

    mailto:wangkai0351@gmail.com

    【未经同意禁止转载】

    鉴于本博客涉及的信息安全技术具有破坏计算机信息系统的风险,建议读者在学习/研究/探讨之前,确保已经充分了解以下内容:

    本博客所讨论的技术仅限于研究和学习,旨在提高计算机信息系统的安全性,严禁用于不良动机,任何个人/团队/组织不得将其用于非法目的,否则后果自负,特此声明。

    Choice,the problem is choice!

    在西门子PLC CPU固件逆向工程的研究工作中[1],我常常找不到一个合适的切入点。

    众所周知,一个CPU芯片私有、嵌入式操作系统私有、文件系统私有、应用层协议栈私有,甚至开发以上软件的编译器都是私有,并且没有符号表symbol table时,逆向工作是多么的困难。这和常见的物联网设备逆向[2]的难度是不可同日而语的。

    那么,没有头绪的情况下,我并没有choice的空间,只有妄图依靠灰箱知识找到一个突破口。

    我们知道s7comm的protocol id是0x32,所以使用“== 0x32”这个条件去全局搜索反编译结果,搜索结果如下

    Line 112668:         if ((piParm1 == 0x32) && (piParm1[0x16] == 0x14)) {
    Line 391457:     if (bVar1 == 0x32) {
    Line 446366:   if (psParm1 == 0x32) {
    Line 491055:   if ((((pcParm1 == 0x32) && (pcParm1[1] == 0x01)) && (pcParm1[10] == 0xf0)) &&
    Line 492543:   if (pcVar4 == 0x32) {
    Line 530415:     if (uParm2 == 0x32) goto LAB_00241bd0;
    

    我很快定位到491055行这条语句,如下所示。

    undefined4 s7comm_job_setup_communication_judge(char *pcParm1)
    //当然,在没有符号表的情况下,这个函数名是我自己修改的
    {
      undefined4 uVar1;
      
      if ((((*pcParm1 == 0x32) && (pcParm1[1] == 0x01)) && (pcParm1[10] == 0xf0)) &&
         ((*(short *)(pcParm1 + 0xc) != 0 && (*(short *)(pcParm1 + 0xe) != 0)))) {
        uVar1 = 1;
      }
      else {
        uVar1 = 0;
      }
      return uVar1;
    }
    

    看到这个非常冗长的判断条件自然令人兴奋,它和我们熟悉的s7comm协议中建立应用层连接的报文似乎存在千丝万缕的关系。

    我们找到一个s7comm协议的数据包例子,对比s7comm_job_setup_communication_judge函数的判断条件和真实的setup communicaiton报文有什么关系?

    我以网络上公开的一个s7comm数据包集合为例[3],下图展示了我要分析的一条setup communicaiton报文。

    从上图中抽出s7comm应用层的字段描述和值,摆在下面。

    //job_setup_communication报文应用层payload
    0000   32 01 00 00 02 00 00 08 00 00 f0 00 00 01 00 01   2...............
    0010   01 e0                                             ..
    
    Header: (Job)
        Protocol Id: 0x32
        ROSCTR: Job (1)
        Redundancy Identification (Reserved): 0x0000
        Protocol Data Unit Reference: 512
        Parameter length: 8
        Data length: 0
    Parameter: (Setup communication)
        Function: Setup communication (0xf0)
        Reserved: 0x00
        Max AmQ (parallel jobs with ack) calling: 1
        Max AmQ (parallel jobs with ack) called: 1
        PDU length: 480(字节?)
    

    接着,我们细致分析固件中这条判断条件

      if ((((*pcParm1 == 0x32) && (pcParm1[1] == 0x01)) && (pcParm1[10] == 0xf0)) &&
         ((*(short *)(pcParm1 + 0xc) != 0 && (*(short *)(pcParm1 + 0xe) != 0)))) {
    

    分开来写,

    条件1)s7comm.header.protid == 0x32,s7comm

    条件2)s7comm.header.rosctr == 0x01,Job

    条件3)s7comm.param.func == 0xf0,Setup communication

    条件4)s7comm.param.maxamq_calling != 0,Max AmQ calling

    条件5)s7comm.param.maxamq_called != 0,Max AmQ called

    可以看出,这五个判断条件和s7comm数据包实例中字段是可以一一对应上的,且符合我们对s7comm协议的灰盒知识。

    由此,印证了我们之前的推论,s7comm_job_setup_communication_judge函数是判断PLC设备接收到的某条报文是不是符合条件的s7comm.header.rosctr为job&&s7comm.param.func为setup communication报文。

    我们找到了s7comm_job_setup_communication_judge作为西门子PLC CPU设备固件逆向的一个突破口,围绕这个突破口,我们展开下一阶段的工作设想。

    从s7comm_job_setup_communication_judge函数向前看

    1. 函数判断条件中那没有涉及到的字段有

      1)s7comm.header.redid

      2)s7comm.header.pduref

      3)s7comm.header.parlg

      4)s7comm.header.datlg

      5)s7comm.param.setup_reserved1

      6)s7comm.param.pdu_length

      为什么没有判断,job_setup_communication中各个字段有什么作用?

    2. s7comm_job_setup_communication_judge函数的输入极有可能是socket_recv的报文队列,它的地址是什么?

    3. socket_recv函数和这个队列的关系?

    从s7comm_job_setup_communication_judge函数向后看

    1. 如果s7comm_job_setup_communication_judge函数输出等于1,下面PLC应该构造ack_data_setup_communication报文,构造函数是什么?
    2. 构造好的ack_data_setup_communication报文,如何通过socket_send函数发送出去?

    以上问题,我们日后将一一尝试解答。

    致谢

    感谢众多安全研究员公开发表自己的研究成果,并热情地回复我的疑问,这些人包括但不限于Dillon Beresford/Ali Abbasi/Thomas Weber/Ralf Ramsauer/Gene blue。


    1. 我手上under test的西门子PLC CPU模块属于早期的S7-1200系列(十分抱歉,我不能公开该设备的订货号、固件版本等指纹信息),它既支持s7comm协议,也支持s7comm-plus协议;因为产品硬件版本和固件版本太低,极大概率不支持s7comm-plus-plus协议。我使用IDA Pro和Ghidra等工具反汇编/反编译了该设备的固件。 ↩︎

    2. 《揭秘家用路由器0day漏洞挖掘技术》 ↩︎

    3. https://github.com/gymgit/s7-pcaps/blob/master/snap7_s300_setupCommunication.pcapng ↩︎

  • 相关阅读:
    一次关于聚合根的激烈讨论
    基于 abp vNext 和 .NET Core 开发博客项目
    PYTHON 学习笔记1 PYTHON 入门 搭建环境与基本类型
    DOCKER 学习笔记9 Kubernetes (K8s) 弹性伸缩容器 下
    DOCKER 学习笔记8 Docker Swarm 集群搭建
    DOCKER 学习笔记7 Docker Machine 建立虚拟机实战,以及错误总结
    DOCKER 学习笔记6 WINDOWS版尝鲜
    DOCKER 学习笔记5 Springboot+nginx+mysql 容器编排
    DOCKER 学习笔记4 认识DockerCompose 多容器编排
    DOCKER 学习笔记3 使用Docker部署SpringBoot
  • 原文地址:https://www.cnblogs.com/bianmu-dadan/p/12684571.html
Copyright © 2020-2023  润新知