nmap之nse脚本简单学习
环境:centos8
nmap安装
yum -y install nmap
-- 版本
[root@qingchen /]# nmap -version
Nmap version 7.70 ( https://nmap.org )
脚本入门学习
cd /usr/share/nmap
[root@qingchen nmap]# ls
nmap.dtd nmap-mac-prefixes nmap-os-db nmap-payloads nmap-protocols nmap-rpc nmap-service-probes nmap-services nmap.xsl nselib nse_main.lua scripts
-- nselib scripts是脚本库和脚本都是lua语言编写的
cd /usr/share/nmap/scripts
nmap的脚本存放路径,自己写的脚本放进去就可以使用了,脚本语言是 lua
lua学习参考菜鸟或者https://blog.csdn.net/qq_40893942/article/details/127986572
NSE脚本基本格式
一个完整的NSE脚本通常都有这么几个部分的代码字段:
-
description 字段:本脚本的说明介绍。
-
categories 字段:本脚本的分类。Nmap执行脚本除了指定单个脚本外,还可以指定某一类脚本,比如default类,我们没有使用–script参数时,默认会加载这一类的脚本。
-
rule 字段:本脚本的执行规则,也即触发脚本执行的条件会在rule字段定义。一般执行规则是一个lua函数,返回值只有true和false两种。
-
action字段:脚本执行的具体内容。rule字段返回true时会执行action字段定义的函数。
local shortport = require "shortport"
description = [[demo]]
author = "qingchen"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default"}
portrule = function( host, port )
return true
end
action = function(host, port)
end
Demo中rule字段是portrule,NSE脚本的执行规则是和nmap的扫描相结合的,两者执行的先后顺序目前有如下4种。
- prerule():规则早于nmap的扫描,执行的顺序是先执行脚本,后nmap扫描。
- hostrule():nmap完成了主机发现之后运行脚本。
- portrule():nmap执行了端口扫描后运行脚本。
- postrule():nmap完成所有的扫描后才执行脚本。
编写脚本
在前面脚本demo代码的基础上,只需修改portrule函数的代码和让action函数来输出。
栗子:
编写文件名为qingchen-simple-test.nse
的脚本放在/usr/share/nmap/scripts
,判断80端口是不是tcp协议。内容如下:
-- 引用shortport脚本库
local shortport = require "shortport"
-- 描述
description = [[qingchen port simple test]]
-- 作者
author = "qingchen"
-- license
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
-- 类别
categories = {"default"}
portrule = function(host,port)
return port.protocol == "tcp" and port.number == 80
end
action = function(host, port)
print(host.ip)
return "qingchen-script-test"
end
nmap --script-updatedb
命令用来更新脚本库(貌似不更新也是可以直接在scripts文件夹下识别到自定义脚本的)
nmap -p 80 127.0.0.1 --script qingchen-simple-test.nse
运行脚本
[root@qingchen scripts]# vim qingchen-simple-test.nse
[root@qingchen scripts]# nmap --script-updatedb
[root@qingchen scripts]# nmap -p 80 127.0.0.1 --script qingchen-simple-test.nse
Starting Nmap 7.70 ( https://nmap.org ) at 2022-11-24 13:41 CST
127.0.0.1
Nmap scan report for VM-4-3-centos (127.0.0.1)
Host is up (0.000052s latency).
PORT STATE SERVICE
80/tcp open http
|_qingchen-simple-test: qingchen-script-test
Nmap done: 1 IP address (1 host up) scanned in 0.45
-- 换8080试试
[root@qingchen scripts]# nmap -p 8080 127.0.0.1 --script qingchen-simple-test.nse
Starting Nmap 7.70 ( https://nmap.org ) at 2022-11-24 13:44 CST
Nmap scan report for VM-4-3-centos (127.0.0.1)
Host is up (0.000046s latency).
PORT STATE SERVICE
8080/tcp closed http-proxy
Nmap done: 1 IP address (1 host up) scanned in 0.44 seconds
编写脚本库文件
在/nselib/
文件夹下新建一个名为qingchenlib.lua
的文件,填入如下内容:
function Porttest(port)
return string.format("The port '%s' is open",port)
end
作用是查看哪些端口是开放的
使用脚本库
编写脚本 qingchen-lib-test.nse
local shortport = require "shortport"
local qingchenlib = require "qingchenlib"
description = [[引用库文件测试]]
author = "qingchen"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default"}
portrule = function( host, port )
return true
end
action = function(host,port)
return Porttest(port.number)
end
引用库文件使用local,格式一般为: local 库文件名 = require “库文件名”,引用后可直接使用库里面的方法和属性值
Nmap 命令:nmap -Pn 127.0.0.1 --script qingchen-lib-test.nse(可以不带后缀)
[root@qingchen nselib]# nmap -Pn 127.0.0.1 --script qingchen-lib-test
Starting Nmap 7.70 ( https://nmap.org ) at 2022-11-24 13:58 CST
Nmap scan report for VM-4-3-centos (127.0.0.1)
Host is up (0.0000050s latency).
Not shown: 996 closed ports
PORT STATE SERVICE
22/tcp open ssh
|_qingchen-lib-test: The port '22' is open
80/tcp open http
|_qingchen-lib-test: The port '80' is open
3306/tcp open mysql
|_qingchen-lib-test: The port '3306' is open
9000/tcp open cslistener
|_qingchen-lib-test: The port '9000' is open
Nmap done: 1 IP address (1 host up) scanned in 1.80 seconds
自定义脚本对mysql数据库操作
涉及库文件
- mysql:用来进行数据库操作。
- nmap:通过nmap建立socket连接mysql。
- shortport:基本的port规则库。
创建数据库和存放结果的表
在你的MySql中建一个名为nmap的数据库,然后建立表和字段:
CREATE TABLE IF NOT EXISTS nmap.scanData (
date varchar(40),
hostos varchar(256),
hostname varchar(100),
ip varchar(16),
port integer(5),
protocol varchar(3),
state varchar(20),
service varchar(256),
version varchar(256)
);
脚本
mysql-test.nse
local mysql = require "mysql"
local nmap = require "nmap"
local shortport = require "shortport"
-- 登陆mysql
local function mysqlLogin(socket, username, password)
local status, response = mysql.receiveGreeting( socket )
if ( not(status) ) then
return response
end
return mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt )
end
description = [[mysql test]]
author = "qingchen"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default"}
portrule = function () return true end
function portaction (host,port)
local host_local="127.0.0.1"
local port_local="3306"
local username="root"
local password="1234"
local hostos_str= host.os
local version = port.version
if (port.version.product~=nil) then
version = port.version.product
end
if (port.version.version~=nil) then
version = version .. port.version.version
end
local date=os.date("%Y-%m-%d %H:%M:%S")
local sql = string.format("INSERT INTO nmap.scanData (date,hostos,hostname,ip, port,protocol,state,service,version) VALUES ('%s','%s','%s', '%s', %d, '%s', '%s', '%s', '%s');select 1",date,hostos_str,host.name,host.ip, port.number,port.protocol,port.state,port.service,version)
local socket = nmap.new_socket()
if ( not(socket:connect(host_local, port_local)) ) then
return fail("Failed to connect to server")
end
local status, response = mysqlLogin(socket, username, password)
if ( status ) then
local status, rs = mysql.sqlQuery( socket, sql )
socket:close()
else
socket:close()
end
end
local ActionsTable = {
portrule = portaction
}
-- execute the action function corresponding to the current rule
action = function(...) return ActionsTable[SCRIPT_TYPE](...) end
执行 nmap -O 127.0.0.1 --script mysql-test
[root@qingchen nselib]# nmap -O 127.0.0.1 --script mysql-test
Starting Nmap 7.70 ( https://nmap.org ) at 2022-11-24 14:08 CST
Nmap scan report for VM-4-3-centos (127.0.0.1)
Host is up (0.0000050s latency).
Not shown: 996 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3306/tcp open mysql
9000/tcp open cslistener
Device type: general purpose
Running: Linux 3.X
OS CPE: cpe:/o:linux:linux_kernel:3
OS details: Linux 3.7 - 3.10
Network Distance: 0 hops
OS detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 4.11 seconds
查看数据库已经写入数据了
但是其中hostos和version是lua中table格式的数据这里没有转成string所以看不到具体数据
可以写一个lua库文件作用是:把table转成string
table2string.lua
放入nselib库中
function ToStringEx(value)
if type(value)=='table' then
return TableToStr(value)
elseif type(value)=='string' then
return "\\'"..value.."\\'"
else
return tostring(value)
end
end
function TableToStr(t)
if t == nil then return "" end
local retstr= "{"
local i = 1
for key,value in pairs(t) do
local signal = ","
if i==1 then
signal = ""
end
if key == i then
retstr = retstr..signal..ToStringEx(value)
else
if type(key)=='number' or type(key) == 'string' then
retstr = retstr..signal..'['..ToStringEx(key).."]="..ToStringEx(value)
else
if type(key)=='userdata' then
retstr = retstr..signal.."*s"..TableToStr(getmetatable(key)).."*e".."="..ToStringEx(value)
else
retstr = retstr..signal..key.."="..ToStringEx(value)
end
end
end
i = i+1
end
retstr = retstr.."}"
return retstr
end
注意return "\\'"..value.."\\'"
这里要加两个转义字符不然就会报错 like this
connect sucess
INSERT INTO nmap.scanData (date,hostos,hostname,ip, port,protocol,state,service,version) VALUES ('2022-11-24 14:51:45','nil','VM-4-3-centos', '127.0.0.1', 9000, 'tcp', 'open', 'cslistener', '{['cpe']={},['service_tunnel']='none',['service_dtype']='table',['name']='cslistener',['name_confidence']=3.0}');
false
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'cpe']={},['service_tunnel']='none',['service_dtype']='table',['name']='http',['n' at line 1
false
-- 插入数据库时也需要对单引号转义否则插入失败
写完库文件后,我们只需要引用一下我们写的库调用一下table2string
完整代码如下:
local mysql = require "mysql"
local nmap = require "nmap"
local shortport = require "shortport"
local table2string = require "table2string"
local function mysqlLogin(socket, username, password)
local status, response = mysql.receiveGreeting( socket )
if ( not(status) ) then
return response
end
return mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt )
end
description = [[mysql save test]]
author = "qingchen"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default"}
portrule = function () return true end
function portaction (host,port)
local host_local="127.0.0.1"
local port_local="3306"
local username="root"
local password="1234"
local hostos_str = ToStringEx(host.os)
local version = ToStringEx(port.version)
if (port.version.product~=nil) then
version = port.version.product
end
if (port.version.version~=nil) then
version = version .. port.version.version
end
local date=os.date("%Y-%m-%d %H:%M:%S")
local sql = string.format("INSERT INTO nmap.scanData (date,hostos,hostname,ip, port,protocol,state,service,version) VALUES ('%s','%s','%s', '%s', %d, '%s', '%s', '%s', '%s');",date,hostos_str,host.name,host.ip, port.number,port.protocol,port.state,port.service,version)
local socket = nmap.new_socket()
if ( not(socket:connect(host_local, port_local)) ) then
return fail("Failed to connect to server")
end
local status, response = mysqlLogin(socket, username, password)
-- 这里我打印了连接状态和sql语句,方便查看定位错误
if ( status ) then
print("connect sucess")
print(sql)
local status, rs = mysql.sqlQuery( socket, sql )
print(status)
print(rs)
socket:close()
else
socket:close()
end
end
local ActionsTable = {
portrule = portaction
}
-- execute the action function corresponding to the current rule
action = function(...) return ActionsTable[SCRIPT_TYPE](...) end
执行命令:nmap -O 127.0.0.1 --script mysql-save-test
可以看到打印出数据了
end
到此就是完成对nmap脚本nse的简单学习了