简介
三菱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/