Nmap NSE 库分析 >>> stdnse

时间:2022-07-23
本文章向大家介绍Nmap NSE 库分析 >>> stdnse,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

https://nmap.org/nsedoc/lib/stdnse.html

0x00 简介

这个模块中都是一些小而有用的功能,他们不足以独立成模块,所以集成在这个标准库里

0x01 方法

函数名

功能介绍

clock_ms ()

返回自纪元以来的当前时间(以毫秒为单位)

clock_us ()

返回自纪元以来的当前时间(以微秒为单位)

debug (level, fmt, ...)

如果当前调试级别大于或等于给定级别,则打印格式化的调试消息。

format_mac (mac)

将MAC地址格式化为以冒号分隔的十六进制字节。

format_output(status, data, indent)

被弃用了

fromhex (hex)

将十六进制字符串解码为原始字节

get_hostname(host)

获取给定主机最可能的主机名。它可以是命令行中指定的目标,反向dns名称或简单的ip地址

get_script_args (..., Arguments)

用来解析 --script-args 选项

get_timeout (host, max_timeout, min_timeout)

返回主机的超时时间

make_buffer(socket, sep)

这个就是一个迭代器,将socket接收到值根据 sep 进行分块,每次调用返回一块

module (name, ...)

模拟Lua 5.1模块功能某些行为的模块功能

new_thread (main, ...)

此功能使您可以创建工作线程,这些工作线程可以与脚本线程并行执行网络任务

output_table ()

返回一个表,该表按插入顺序保留元素,就是最后的输出表

parse_timespec(timespec)

解析持续时间规范,该规范是一个数字,后跟一个单位,并返回秒数

pretty_printer (obj, printer)

漂亮的Lua对象打印机

print_debug (level, fmt, ...)

不推荐使用的debug()版本,目前保留该版本,以防止脚本ID被打印两次。脚本应使用debug()且不要传递SCRIPT_NAME

print_verbose (level, fmt, ...)

verbose()的不推荐使用的版本,保留至今,以防止脚本ID被打印两次。脚本应使用verbose()且不传递SCRIPT_NAME

registry_add_array(subkeys, value, allow_duplicates)

将一个值添加到registry的array中,并在必要时创建所有子项

registry_add_table(subkeys, key, value, allow_duplicates)

这个是向 registry 的 table 中添加一个键值对

registry_get(subkeys)

检查一个子键列表中每一层子键是不是都存在,如果不存在返回 nil

seeall (env)

更改环境以加载全局变量

silent_require ()

将 require 函数报错隐藏的执行

sleep (t)

睡眠一段时间

string_or_blank(string, blank)

判断如果是字符串为返回本身,不是返回 或者给定的参数

tobinary (n)

转化成二进制数

tohex (s, options)

转化成十六进制数

tooctal (n)

转化成八进制数

verbose (level, fmt, ...)

如果当前详细级别大于或等于给定级别,则打印格式化的详细消息

0x02 方法实战

0x001 debug

debug 函数为不定长参数,第一个参数为打印等级

如果当前调试级别大于或等于给定级别,则打印格式化的调试消息。这个函数是 nmap.log_write 的一个简单封装,第一个参数是打印等级,其余参数都是由 string.format 函数进行处理

如果已知,则输出包括一些基于上下文的信息:脚本标识符和目标ip /端口(如果有)。如果调试级别至少为2,则还将打印基本线程标识符以及它是工作线程还是主线程

0x002 print_debug

不推荐使用

0x003 verbose

verbose 是不定长参数

如果当前详细级别大于或等于给定级别,则打印格式化的详细消息。

0x004 make_buffer

make_buffer 函数有两个参数:socket, sep ,socket 参数是一个socket变量,sep为分隔符,返回值为读取到的块的数据以及错误信息

在http的脚本中没有用到这个函数的,在所有的脚本中,这个函数也用的较少,我们看一下吧

可以看到大家调用的时候sep参数基本都是 "n"、"r"、"rn"、"r?n",可以看到是用作获取相关分隔符分隔后的数据块

0x005 tobinary

tobinary 只有一个参数,待转换的数字,返回值为二进制字符串

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local url = require "url"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local demo = 35
    local result = stdnse.tobinary(demo)
    output.restype = type(result)
    output.result = result
    return output
end

测试一下

0x006 tooctal

tooctal 只有一个参数,这个参数为一个数字,返回一个八进制格式数字的字符串

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local url = require "url"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local demo = 64
    local result = stdnse.tooctal(demo)
    output.restype = type(result)
    output.result = result
    return output
end

测试一下

可以看到将十进制的 64 转换为 八进制的 100

0x007 tohex

tohex 函数有两个参数:s, options,返回值为16进制形式的字符串

  • s 待转换的字符串或者数字
  • options 这个是一个格式选项的表,表中字段 separator 为转化后的分隔符,最后返回值中会体现;字段 group 为分隔符之间每组数字的大小。默认值为2,但如果未同时提供 separator 则无效。
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local url = require "url"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local demo1 = "abc"
    local demo2 = 123456
    output.result_abc_sep_null_group_null = stdnse.tohex(demo1)
    output.result_abc_sep_colon_group_null = stdnse.tohex(demo1, {separator = ":"})
    output.result_abc_sep_colon_group_3 = stdnse.tohex(demo1, {separator = ":", group = 3})
    output.result_123456_sep_null_group_null = stdnse.tohex(demo2)
    output.result_123456_sep_colon_group_null = stdnse.tohex(demo2, {separator = ":"})
    output.result_123456_sep_colon_group_3 = stdnse.tohex(demo2, {separator = ":", group = 3})
    output.restype = type(output.result_123456_sep_colon_group_3)
    return output
end

测试一下

可以看到, abc 被转换为 616263, 添加了分隔符后会按照默认的每两个字符来进行按照分隔符进行分隔返回,如果设置了分隔尺寸,则按照相关尺寸进行分隔。

0x008 fromhex

fromhex 只有一个参数,为16进制的字符串,该字符串可以包含任意数量的空格和大写或小写十六进制数字。十六进制数字必须是偶数,因为要用2个十六进制数字来构成一个字节。

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local url = require "url"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local demo = "534364"
    local result = stdnse.fromhex(demo)
    output.restype = type(result)
    output.result = result
    return output
end

测试一下

0x009 format_mac

format_mac 只有一个参数,参数为MAC地址二进制的字符串,比如 host.mac_addr, 返回值为 XX:XX:XX:XX:XX:XX 形式的字符串

上面这是 Nmap官方库的注释里写的,但是经过我的测试并不是这样,其实参数 MAC 是一个十进制数,假如我想得到一个正确的mac,我们需要将这个不带冒号的mac地址转换为10进制数,之后作为参数提交

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local url = require "url"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local demo = 20526534579951
    local result = stdnse.format_mac(demo)
    output.restype = type(result)
    output.rawrest = demo
    output.result = result
    return output
end

测试一下

0x0010 string_or_blank

string_or_blank 有两个参数,string, blank,这个函数就是用来判断第一个参数是否为nil或者"" ,如果不是空则返回字符串本身,是空则返回第二个参数,第二个参数没有设置的时候返回 "" 字符串

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local url = require "url"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local demo1 = "test"
    local result1 = stdnse.string_or_blank(demo1)
    local demo2 = ""
    local result2 = stdnse.string_or_blank(demo2, "this is blank")
    output.restype = type(result1)
    output.demo1 = demo1
    output.result1 = result1
    output.demo2 = demo2
    output.result2 = result2
    return output
end

测试一下

0x0011 parse_timespec

解析持续时间规范,该规范是一个数字,后跟一个单位,并返回秒数。

parse_timespec函数只有一个参数 timespec,这个参数是一个规范字符串,可以是以下格式:

  • 10 --> 10
  • 10ms --> 0.01
  • 10s --> 10
  • 10m --> 600
  • 10h --> 36000

如果给定参数不是规范字符串,则返回 nil

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local url = require "url"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local res_10 = stdnse.parse_timespec("10")
    local res_10ms = stdnse.parse_timespec("10ms")
    local res_10s = stdnse.parse_timespec("10s")
    local res_10m = stdnse.parse_timespec("10m")
    local res_10h = stdnse.parse_timespec("10h")
    output.restype = type(res_10h)
    output.res_10 = res_10
    output.res_10ms = res_10ms
    output.res_10s = res_10s
    output.res_10m = res_10m
    output.res_10h = res_10h
    return output
end

测试一下

0x0012 clock_ms

返回自纪元以来的当前时间(以毫秒为单位),无参数

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local url = require "url"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local result = stdnse.clock_ms()
    output.restype = type(result)
    output.result = result
    return output
end

测试一下

0x0013 clock_us

返回自纪元以来的当前时间(以微秒为单位), 无参数

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local url = require "url"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local result = stdnse.clock_us()
    output.restype = type(result)
    output.result = result
    return output
end

测试一下

0x0014 get_script_args

get_script_args 函数用来解析 --script-args ,不定长参数

这个函数非常重要且复杂,可能是我们以后经常用到的

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local url = require "url"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local functest_arg1, functest_arg2, functest_arg3, functest_arg4, functest_arg5 = stdnse.get_script_args("functest.arg1", "functest.arg2", "functest.arg3", "functest.arg4", "functest.arg5")
    output.functest_arg1_type = type(functest_arg1)
    output.functest_arg1_result = functest_arg1
    output.functest_arg2_type = type(functest_arg2)
    output.functest_arg2_result = functest_arg2
    output.functest_arg3_type = type(functest_arg3)
    output.functest_arg3_result = functest_arg3
    output.functest_arg4_type = type(functest_arg4)
    output.functest_arg4_result = functest_arg4
    output.functest_arg4_type = type(functest_arg5)
    output.functest_arg4_result = functest_arg5
    return output
end

这里我定义了functest有五个参数,之后分别进行不同情况的测试

我们通过 --script-args 来进行参数传递,相关参数传递的值分别为

  • functest.arg1 字符串 test1
  • functest.arg2 一个表
  • functest.arg3 未传递这个参数
  • functest.arg4 传递了这个参数,未传递值
  • functest.arg5 一个数字 2

得到的结果如下:

  • functest.arg1 字符串 test1
  • functest.arg2 一个表,{demo1,demo2}
  • functest.arg3 nil
  • functest.arg4 数字 1
  • functest.arg5 字符串 2

可以得出结论

  • 无论传递的是数字还是字符串都会被认定为字符串
  • 如果传递的是一个表,得到结果也是一个表
  • 如果要获取的参数没有传递,那么返回 nil
  • 如果要获取的参数传递了,但是没有赋值,那么返回 1

通过以上结果可以事先做好类型定义以及异常处理

0x0015 get_hostname

获取给定主机的最可能的主机名。它可以是命令行中指定的目标,反向dns解析的名称或就是ip地址。

get_hostname 只有一个参数,参数为默认的 host, 返回值为最佳的 hostname 值

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local http = require "http"
local stdnse = require "stdnse"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local options = {header={}}
    options["header"]["User-Agent"] = "function post test "
    --local data = http.get(host, port, "/", options)
    --local contain, content = http.response_contains(data,'[a-z](.+?)',false)

    local result = stdnse.get_hostname(host)
    output.restype = type(result)
    output.result = result
    return output
end

使用百度测试一下

可以看到返回值为字符串类型的hostname

0x0016 registry_get

registry_get 函数只有一个参数 subkeys ,是一个表,即子键

这个表中存放着要查询的子键,如果所有层子键都存在则返回最下层的值,如果有一个不存在就返回 nil

0x0017 registry_exists

registry_exists 函数有三个参数,subkeys, key, value

  • subkeys 子键列表
  • key 要查询的key
  • value 要查询的 value

如果key,value键值对存在,那么返回 true,否则返回false

0x0018 registry_add_array

registry_add_array有三个参数:subkeys, value, allow_duplicates ,没有返回值

  • subkeys 子键
  • value 存储的值
  • allow_duplicates 允许重复
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local http = require "http"
local stdnse = require "stdnse"
local nmap = require "nmap"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local result = stdnse.registry_add_array({'192.168.1.100', 'www', '80', 'pages'}, 'index.html')
    output.restype = type(result)
    output.result = result
    output.reg = nmap.registry
    return output
end

这里我们将返回值打印,将运行后的 nmap.registry 打印

从结果可以看到,这个函数并没有返回值,运行后的 nmap.registry 按照 subkeys的层级关系建立了一个数组,并将 value 的值存储到了其中

0x0019 registry_add_table

registry_add_table 与 registry_add_array 相似,有四个参数 subkeys, key, value, allow_duplicates,这个函数是向 nmap.registry 中的表中插入键值对

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local http = require "http"
local stdnse = require "stdnse"
local nmap = require "nmap"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local result = stdnse.registry_add_table({'192.168.1.100', 'www', '80', 'pages'}, 'index_page','index.html')
    output.restype = type(result)
    output.result = result
    output.reg = nmap.registry
    return output
end

测试一下

0x0020 output_table

这个函数应该就不用说了,咱们的每个代码都有这个,这就是一个输出表,会按照插入数据的顺序进行显示

0x0021 pretty_printer

pretty_printer 这个函数在nse中用的极少,可以使用下面命令进行查询

可以看到没有script 使用了这个函数

0x0022 get_timeout

get_timeout 函数有三个参数:host, max_timeout, min_timeout ,返回一个以毫秒为单位的适合传递给 set_timeout 的数字

  • host 默认参数
  • max_timeout 默认最大超时时间,默认 8000ms
  • min_timeout 默认最小超时时间,默认 1000ms
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---

local http = require "http"
local stdnse = require "stdnse"
local nmap = require "nmap"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("functest running")
end
portrule = function () return true end --shortport.http

action = function(host, port)
    local output = stdnse.output_table()
    local result = stdnse.get_timeout(host, 4000, 1000)
    output.restype = type(result)
    output.result = result
    output.host_timeout = host.times.timeout
    return output
end

找一个美国的主机测试一下