Nmap NSE开发之三菱Q系列PLC以太网识别

工控安全 源码 工控 Nmap NSE 开发

简介

三菱Q系列PLC以太网模块系统默认开放了TCP的5007端口和UDP的5006端口用于与GX软件进行通信,通过对通讯协议的分析,利用基于NMAP的脚本可以更方便快捷的实现对设备的识别和发现。

NSE源码

对源码进行了更新优化,并对关键语句进行了注释,便于阅读

--载入指定的扩展库
local bin = require "bin"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"

--用法:
--分析 MELSEC-Q 系列 PLC CPU型号
--nmap -script melsec-q-discover -sT -p 5007 <host>


--输出样例:
-- PORT     STATE SERVICE
-- 5007/tcp open  MelsoftTCP
-- | melsec-q-discover: 
-- |_  CPUINFO: Q03UDECPU       

--插件描述
description = [[
discovery Mitsubishi Electric Q Series PLC 
    GET CPUINFO
]]
--插件作者
author = "ICS"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
--插件分类
categories = {"discovery","intrusive"}

--prerule: 在Nmap扫描之前触发脚本执行,扫描之前结束(初始化脚本)
--prerule = function() return true end

--portrule: 在Nmap执行端口扫描或版本探测时触发(探测脚本)
portrule = shortport.port_or_service(5007, "MelsoftTCP", "tcp")

--postrule: 在Nmap扫描结束用于扫描结果提取和整理(归纳总结)
--postrule = function() return true end

--自定义函数: 端口信息
function set_nmap(host, port)
    port.state = "open" --端口状态
    port.version.name = "MelsoftTCP"  --版本名称
    port.version.product = "Mitsubishi Q PLC"  --版本产品
    nmap.set_port_version(host, port)
    nmap.set_port_state(host, port, "open")

 end

--自定义函数:发包和收包--
function send_receive(socket, query) 
    
    local sendstatus, senderr = socket:send(query)
    if(sendstatus == false) then
    return "Error Sending getcpuinfopack"
    end

    local rcvstatus,response = socket:receive()
    if(rcvstatus == false) then
    return "Error Reading getcpuinfopack"
    end

    return response
    end

--执行
action = function(host,port)
    --stdnse.fromhex(): 将十六进制字符串解码为原始字节
    local getcpuinfopack = stdnse.fromhex("57000000001111070000ffff030000fe03000014001c080a080000000000000004" .. "0101" .. "010000000001")
    local response
    --stdnse.output_table(): 内置格式化输出
    local output = stdnse.output_table()
    --nmap.new_socket(): 创建Nmap socket对象
    local socket = nmap.new_socket()
    --socket:connect(): 内置建立socket连接,返回状态和错误码
    local constatus,conerr = socket:connect(host,port)
    if not constatus then
    stdnse.print_debug(1,
      'Error establishing connection for %s - %s', host,conerr
      )
    return nil
    end

    response  = send_receive(socket, getcpuinfopack)
    --bin.unpack(format,date,int): 从二进制数据中解包读取数值,int表示解包起始位置
    --返回 解包停止位置和 所有解包出的数值
    local mel, pack_head = bin.unpack("C", response, 1)
    --如果解包数值为"\xd7\x00\x21\x00\x00\x11.....\x02"
    if ( pack_head == 0xd7) then
        --从第42位置解包,数值就是CPU型号
        local mel, cpuinfo = bin.unpack("z", response, 42)
        --string.sub: Lua内置函数用于截取从i到j之间的字符串
        output["CPUINFO"] = string.sub(cpuinfo, 1, 16)
        set_nmap(host, port)
        socket:close()
        return output    
    else
    socket:close()
    return nil
    end
end

使用方法

nmap -script melsec-q-discover -sT -p 5007 <host>

参考:http://plcscan.org/blog/2014/08/melsecq-plc-discover-tools-releases/

新评论

称呼不能为空
邮箱格式不合法
网站格式不合法
内容不能为空