nmap脚本写作教程

原文:Script Writing Tutorial

事实上,在你看这篇译文之前,我必须要解释两点

  1. 按照这篇译文写出的代码并没有发挥作用
  2. 你可以通过这篇文章了解到script的框架

假设您想要从identification服务器提取信息以确定侦听TCP端口的进程的所有者。 这不是identd的目的(它是为了查询传出连接的所有者,而不是监听守护进程),但许多identd服务器都允许它。Nmap曾经拥有这种功能(称为ident扫描),但在过渡到新的扫描引擎架构时被删除。 identd使用的协议非常简单,但仍然太复杂,无法处理Nmap的版本检测语言。 首先,连接到标识服务器并发送<port-on-server>,<port-on-client>格式的查询,并以换行符结尾。 然后,服务器应该使用包含服务器端口,客户端端口,响应类型和地址信息的字符串进行响应。 如果出现错误,地址信息将被省略。 更多细节可在RFC 1413中找到,但这种描述对我们的目的来说已经足够了。该协议不能用Nmap的版本检测语言建模,原因有两个。 首先,您需要知道连接的本地端口和远程端口。 版本检测不提供此数据。 第二个更严重的障碍是,您需要两个打开的连接到目标 - 一个连接到识别服务器,一个连接到您希望查询的侦听端口。 NSE很容易克服这两种障碍。

脚本的解剖部分在脚本格式一节中进行了描述。 在本节中,我们将介绍如何使用所描述的结构。

The Head

脚本的head本质上是它的元信息。包含下列字段:description,categories,dependencies,author,license以及初始NSEDoc信息,例如用法,参数和输出标签参考编写脚本文档一节

description应包含一段或更多段描述脚本的功能。 如果有关脚本结果的任何信息可能会混淆或误导用户,并且您无法通过改进脚本或结果文本来消除此问题,则应将其记录在description中。 如果有多个段落,第一个在必要时用作简短摘要。 确保第一段可以作为一个独立的摘要。 这个描述很简短,因为它是一个如此简单的脚本:

description = [[
Attempts to find the owner of an open TCP port by querying an auth
(identd - port 113) daemon which must also be open on the target system.
]]

接下来是NSEDoc信息。 此脚本缺少常见的@usage和@args标记,因为它非常简单,但它确实有一个NSEDoc @output标记:

---
--@output
-- 21/tcp   open     ftp       ProFTPD 1.3.1
-- |_ auth-owners: nobody
-- 22/tcp   open     ssh       OpenSSH 4.3p2 Debian 9etch2 (protocol 2.0)
-- |_ auth-owners: root
-- 25/tcp   open     smtp      Postfix smtpd
-- |_ auth-owners: postfix
-- 80/tcp   open     http      Apache httpd 2.0.61 ((Unix) PHP/4.4.7 ...)
-- |_ auth-owners: dhapache
-- 113/tcp  open     auth?
-- |_ auth-owners: nobody
-- 587/tcp  open     submission Postfix smtpd
-- |_ auth-owners: postfix
-- 5666/tcp open     unknown
-- |_ auth-owners: root

接下来是author,license,categories标签。 此脚本属于safe,因为我们没有将该服务用于任何不适用的内容。 由于此脚本是默认运行的脚本,因此它也位于default类别中。 以下是上下文中的变量:

author = "Diman Todorov"

license = "Same as Nmap--See https://nmap.org/book/man-legal.html"

categories = {"default", "safe"}

The Rule

rule是一个Lua方法,决定跳过还是执行脚本动作。 该决定通常基于规则的类型以及传递给它的主机和端口信息。 prerule或postrule将始终评估为true。 在识别脚本的情况下,它比这稍微复杂一些。 要决定是否针对给定端口运行识别脚本,我们需要知道目标机器上是否存在运行auth服务器。 换句话说,只有在当前扫描的TCP端口打开并且TCP端口113也打开的情况下,脚本才能运行。 现在我们将依赖识别服务器在TCP端口113上侦听的事实。不幸的是,NSE只给我们提供关于当前扫描端口的信息。

要知道端口113是否打开,我们使用nmap.get_port_state函数。 如果未扫描auth端口,则get_port_state函数返回nil。 所以我们检查表不是零。 我们还检查两个端口是否处于打开状态。 如果是这种情况,则执行该动作,否则我们跳过该动作。

portrule = function(host, port)
    local auth_port = { number=113, protocol="tcp" }
    local identd = nmap.get_port_state(host, auth_port)

    return identd ~= nil
        and identd.state == "open"
        and port.protocol == "tcp"
        and port.state == "open"
end

The Action

最后我们实现了实际的功能! 该脚本首先连接到我们期望找到标识服务器的端口,然后它将连接到我们想要获取信息的端口。 这样做首先通过调用nmap.new_socket创建两个套接字选项。 接下来我们定义一个错误处理catch函数,如果检测到失败,它会关闭这些套接字。 在这一点上,我们可以安全地使用诸如打开,关闭,发送和接收之类的对象方法来在网络套接字上操作。 在这种情况下,我们称connect为连接。 NSE的异常处理机制用于避免过多的错误处理代码。 我们只需在尝试调用中打包网络调用,如果出现任何问题,我们会调用catch函数。

如果两个连接成功,我们构造一个查询字符串并解析响应。 如果我们收到满意的答复,我们会返回检索到的信息。

action = function(host, port)
        local owner = ""

        local client_ident = nmap.new_socket()
        local client_service = nmap.new_socket()

        local catch = function()
                client_ident:close()
                client_service:close()
        end

        local try = nmap.new_try(catch)

        try(client_ident:connect(host.ip, 113))
        try(client_service:connect(host.ip, port.number))

        local localip, localport, remoteip, remoteport =
                try(client_service:get_info())

        local request = port.number .. ", " .. localport .. "\r\n"

        try(client_ident:send(request))

        owner = try(client_ident:receive_lines(1))

        if string.match(owner, "ERROR") then 
                owner = nil
        else
                owner = string.match(owner,
                        "%d+%s*,%s*%d+%s*:%s*USERID%s*:%s*.+%s*:%s*(.+)\r?\n")
        end

        try(client_ident:close())
        try(client_service:close())

        return owner
end

请注意,因为我们知道远程端口存储在port.number中,所以我们可以忽略client_service的最后两个返回值:get_info(),如下所示:

local localip, localport = try(client_service:get_info())

在这个例子中,如果服务响应错误,我们会安静地退出。 这是通过将nil分配给将返回的所有者变量来完成的。 NSE脚本通常只在成功时才会返回消息,因此它们不会用无意义的警报泛滥用户。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容