- A+
注意
Bugscan平台是基于Python2.7.X开发,所以为了实现良好的兼容,编写插件的时候所使用的平台也应该为Python 2.7.X。
Python 2.7.X 下载地址:
https://www.python.org/downloads/
这里对于Python如何使用不再赘述,请自己查找相关资料
不会Python怎么办
如果不会Python的话,可以去看看相关的资料/视频,这里给出一些资料
廖雪峰的Python教程:
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000
简明 Python 教程:
http://www.kuqin.com/abyteofpython_cn/
中谷教育Python教学视频:
http://pan.baidu.com/s/1c02HoZm 密码: 03va
我自己学Python的笔记:
http://blog.l1n3.net/tag/python-2/
编写前的准备
首先去下载BugScan的SDK,放置到Python目录中的lib中,方便插件的调试
SDK链接地址:https://www.bugscan.net/sdk.zip
默认安装路径: C:\Python27
建议SDK存放位置: C:\Python27\Lib\
仔细阅读过官方的SDK文档:https://www.bugscan.net/#!/i/
前置技能(不绝对)
如果需要为BugScan编写插件,需要以下前置技能
1.熟练运用Python中的数字,列表,字典等数据类型 2.熟练运用Python的函数 3.熟练运用Python的流程控制 4.熟练运用Python的正则表达式(re模块)
如果想学习BugScan插件,需要以下前置技能
1.最少能够熟练的运用Python类以下的所有函数/类型/方法 2.了解漏洞利用方法,最好也能了解漏洞成因 3.了解大多数网站的架构Windows/Linux +IIS/Apache/Nginx + SQL Server/Mysql/Oracle + ASP/PHP/JSP/ASPX 4.了解大部分漏洞的原理,注入,XSS,CSRF,远程命令执行,上传截断,改包等 如果想自主的去发现漏洞 5.了解HTTP协议通信过程,GET方式和POST方式提交,cookie的作用等 6.熟练运用BurpSuite,SQLMAP,Chrome开发者工具,FireBug等工具(Fuzz) 7.能看懂ASP/PHP等代码(开源程序审计) 8.和大牛不断交(Gao)流(Ji)
BugScan插件实现原理
一个标准的插件的代码如下
#!/usr/bin/env python # -*- coding: utf-8 -*- #__Author__ = LinE #_PlugName_ = Plugin Format #_Function_ = 插件格式 #_FileName_ = Plugin_Format.py ######建议把之上的东西都带上,以便于辨认###### def assign(service, arg): if service =="service": # 1. service字符串 return True, arg def audit(arg): print"hello, world!" # 2.主体攻击代码部分 if __name__ =='__main__': from dummy import* audit(assign('service', 'http://www.example.com/')[1]) # 3. service字符串 # 4.http://www.example.com/ 字符串
需要修改的地方主要为代码中编号的4处,其中1处和3处的服务类型须保持一致
服务类型判断
服务类型列表在https://www.bugscan.net/#!/n/7
请严格按照列表中的服务名称来书写
主体攻击代码这里书写主要用于实现的攻击代码,如何实现请看下文
服务类型提交这里也是服务类型,由框架传参进入,与1.处保持一致即可
测试URL
上传插件时,这里写成存在该漏洞测试网站,方便管理员审核
但插件在提交展示的时候会自动替换为http://www.example.com/
只有管理员可以看到漏洞测试网站
函数解说
if __name__ == '__main__'主体函数
#!/usr/bin/env python # -*- coding: utf-8 -*- #__Author__ = LinE #_PlugName_ = Plugin Format #_Function_ = 插件格式 #_FileName_ = Plugin_Format.py if __name__ =='__main__': from dummy import* audit(assign('service', 'http://www.example.com/')[1]) # 移交执行权至audit
当调用到一个插件的时候,主体函数()即得到执行,该函数执行过程为assign函数判断服务类型(即该插件是否可用于该网站),如果返回True的话,即调用audit函数来完成攻击/扫描的过程
assign函数
#!/usr/bin/env python # -*- coding: utf-8 -*- #__Author__ = LinE #_PlugName_ = Plugin Format #_Function_ = 插件格式 #_FileName_ = Plugin_Format.py def assign(service, arg): if service =="service": return True, arg # 服务类型判断
assign函数判断BugScan框架传入的服务类型,如果服务类型与插件中定义(即service字符串)的一致,则表明该插件可用于该网站,返回True和网站的URL(arg字符串)
然后执行接下来的漏洞测试代码
audit函数
#!/usr/bin/env python # -*- coding: utf-8 -*- #__Author__ = LinE #_PlugName_ = Plugin Format #_Function_ = 插件格式 #_FileName_ = Plugin_Format.py def audit(arg): print"hello,world!" # 主体攻击代码部分
audit函数为主要实现攻击过程的函数,当扫描一个网站的时候大多数情况下需要调用curl.curl这个函数来完成扫描过程,随后会对curl进行单独解说
audit在执行的过程中会获取一个变量,变量名为arg,变量值为待扫描的URL,字符串类型
我们是如何访问网站的
众所周知,访问网站实际上也就是客户端HTTP请求和服务端HTTP返回的过程
请求一般最常用的是GET和POST方法,而有时候我们访问的网站是动态页面,这就需要客户端传入一些参数来实现同一个样式的网页返回不同的信息
GET提交
动态网页的URL一般为:http://www.xxoo.com/1.php?id=1这里的id即为我们传入的参数
而在W3C的规定里,以URL方式传入的参数为GET方式的提交,所以我们常见的大部分在URL上的注入起始都是GET方式的提交
GET提交一般常见于网页访问等
POST提交
POST提交中,带入的参数一般来说客户端不可见(隐性提交),我们需要通过抓包软件(如Burp)来获取传入的参数名和参数值
一个POST提交的数据包可能像以下的样子:
POST /CheckLogin.asp HTTP/1.1 Host: www.xxoo.com Proxy-Connection: keep-alive Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36 LBBROWSER Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8 If-None-Match: "4ded5b-199-4dc439c1e65c0" If-Modified-Since: Thu, 09 May 201307:00:47 GMT UserName=admin&PassWord=admin123
而这个底下的UserName和PassWord即为我们传入的参数值,POST提交一般常见于登陆,搜索,上传等
让插件像个访问者一样
在一些时候我们需要让插件像个正常访问者一样去请求网页
这就需要我们的插件具备HTTP通信的能力,而这里BugScan官方给出了curl类的curl函数,能够更加方便的取获取网页的信息
我们先看一下curl的帮助信息,中文版哦
curl是一个使用纯Python编写的工具,只支持HTTP协议
命令行格式:
curl [-I | -d DATA] [-A USER_AGENT] [-b COOKIE] [--connect-timeoutCONNECT_TIMEOUT] [-e REFERER] [-H HEADER][-i] [-m MAX_TIME] [--max-filesizeMAX_FILESIZE] [--mime-type MIME_TYPE] [-L][--max-redirs MAX_REDIRS] [-T] [--retry RETRY] [--retry-delayRETRY_DELAY] [-u USER] [-X REQUEST] <url>
–mime-type 参数表示指定一个MimeType,如果MimeType类型不对,curl将会抛出一个错误(找不到指定字符串类型)
curl函数执行以后会以元组形式返回5个值,我们可以使用5个变量去接收这5个返回值:
code :返回HTTP状态码 head :返回HTTP头 body :返回HTTP内容,即HTML errcode :返回curl的状态码,代码随后解释 final_url :返回提交的URL
errcode状态码解释:
0 : 返回正常 1 : 不能连接目标URL 2 : 连接超时 3 : 返回异常 4 : 发送异常 5 : 发送数据包过大 6 : 不能确定的主机 7 : 不支持的协议 8 : 参数错误 9 : MimeType错误
一些举例:
code, head,body, ecode, redirect_url = curl.curl('-L http://www.baidu.com')
GET提交:
curl.curl('http://www.abc.com/')
带HEAD提交:
curl.curl('-Hhttp://www.abc.com/')
POST提交:
curl.curl('-duser=abc&pass=ddd http://www.abc.com/')
PUT提交:
curl.curl('T -d"Content to put" http://www.abc.com/')
带Cookie提交:
curl.curl('-buser=abc&pass=ddd http://www.abc.com/')
带Referer提交:
curl.curl('-ehttp://www.google.com/ http://www.abc.com/')
重定向数据流:
curl.curl('-Lhttp://www.abc.com/')
curl会在获取网页内容的过程中自动去接收cookie,在进行第二次请求的时候将附加到HTTP头,如果想保持cookie为空,请使用curl.reset()
那么现在我们想让我们的插件去像个正常的访问者一样去访问百度,那么我们的代码可以这么写
#!/usr/bin/env python code, head, body, errcode, final_url = curl.curl('http://www.baidu.com')
什么,你告诉我执行完了啥都没有?
当然啥都没有了,这个只是一个获取并赋值的过程,你见过你定义一个变量之后就返回变量值么?
如果我要显示内容,我们可以用print打印出来
#!/usr/bin/env python code, head, body, errcode, final_url = curl.curl('http://www.baidu.com') # 以下显示HTTP状态码 print'---------------code---------------' print code # 以下显示HTTP头 print'---------------code---------------' print head # 以下显示HTTP主体内容(HTML) print'---------------code---------------' print body # 以下显示curl的状态码 print'-------------err code-------------' print errcode # 以下显示curl访问的URL print'------------final_url-------------' print final_url
这样我们就获取到了百度的主页内容,当然如果要像浏览器一样显示出来,我们还需要进行HTML解析和渲染,当然curl不具备此功能(一个命令行工具要求那么多作死啊)
那么接下来我们带着一些参数去访问
#!/usr/bin/env python # -*- coding: utf-8 -*- #__Author__ = LinE #_PlugName_ = Test Plugin #_FileName_ = Test_Plugint.py def assign(service, arg): if service =="www": return True, arg def audit(arg): payload ='readnews.asp?id=1 and 1=1' # 注意这里的payload部分不需要在之前加入/ # 否则接下来的curl没办法正确识别url target = arg + payload code1, head, body1, errcode, final_url = curl.curl('"%s"', % arg) # 使用格式化输入,保证我们带入的字符串不被当做指令来执行 code2, head, body2, errcode, final_url = curl.curl('"%s"', % target) #同上 if code1 ==200and code2 ==200and body1 == body2: # 判断加和不加payload的网页的HTTP状态码是否为200 # 同时判断加和不加payload的网页的内容是否一样 # (and 1=1返回是否与原网页一样) print'Find Vulnerability !'#打印信息 if __name__ =='__main__': from dummy import* audit(assign('www', 'http://www.test.com/')[1])
这样我们就完成了一个最基本的检测注入漏洞的插件,当然这个插件功能上有些弱
不过这个插件已经能说明一些问题了
木有错,给BugScan写插件就这么简单
这里我们的举例是GET方式的提交,那么POST方式的提交我们可以写成以下的样子
使用POST提交的时候curl的参数为-d
#!/usr/bin/env python # -*- coding: utf-8 -*- #__Author__ = LinE #_PlugName_ = Test Plugin #_FileName_ = Test_Plugint.py def assign(service, arg): if service =="www": return True, arg def audit(arg): URL ='check.asp' POST_Data ='username=admin&password=admin888' #正常的POST数据 payload ='username=admin and 1=1&password=admin888' #带有攻击语句的POST数据 target = arg + payload code1, head, body1, errcode, final_url = curl.curl('"-d %s %s"'% (POST_Data, target)) # 使用-d 参数带入POST数据 # 使用格式化输入,保证我们带入的字符串不被当做指令来执行 code2, head, body2, errcode, final_url = curl.curl('"-d %s %s"', % (payload, target)) #同上 if code1 ==200and code2 ==200and body1 == body2 : # 判断加和不加payload的网页的HTTP状态码是否为200 # 同时判断加和不加payload的网页的内容是否一样(and 1=1返回是否与原网页一样) print'Find Vulnerability !'#打印信息 if __name__ =='__main__': from dummy import* audit(assign('www', 'http://www.test.com/')[1])
我们也可以带着cookie提交
使用cookie提交的参数为-b
#!/usr/bin/env python # -*- coding: utf-8 -*- #__Author__ = LinE #_PlugName_ = Test Plugin #_FileName_ = Test_Plugint.py def assign(service, arg): if service =="www": return True, arg def audit(arg): URL ='main.asp' cookie ='sessionid=PHPSESSIONADMIN;username=admin;passcode=21232f297a57a5a743894a0e4a801fc3' #cookie值 target = arg + payload code, head, body, errcode, final_url = curl.curl('"-b %s %s"'% (POST_Data, target)) # 使用-d 参数带入POST数据 # 使用格式化输入,保证我们带入的字符串不被当做指令来执行 #同上 if code ==200 : print'Find Vulnerability !'#打印信息 if __name__ =='__main__': from dummy import* audit(assign('www', 'http://www.test.com/')[1])
我们也可以同时带着多个参数去请求
#!/usr/bin/env python # -*- coding: utf-8 -*- #__Author__ = LinE #_PlugName_ = Test Plugin #_FileName_ = Test_Plugint.py def assign(service, arg): if service =="www": return True, arg def audit(arg): URL ='main.asp' cookie ='sessionid=PHPSESSIONADMIN;username=admin;passcode=21232f297a57a5a743894a0e4a801fc3' #cookie值 POST ='SQL=select username,password from admin' target = arg + payload code, head, body, errcode, final_url = curl.curl('"-b %s -d %s %s"'% (cookie, POST, target)) # 使用-b 参数带入cookie数据 # 使用-d 参数带入POST数据 # 使用格式化输入,保证我们带入的字符串不被当做指令来执行 #同上 if code ==200 : print'Find Vulnerability !'#打印信息 if __name__ =='__main__': from dummy import* audit(assign('www', 'http://www.test.com/')[1])
OK就是这么简单
在BugScan框架中,print是不会有任何效果的,因为print是打印到控制台了,而我们在扫描的时候并不能去登录到控制台上查看输出信息,所以我们这里就要使用报警函数
报警函数操作指南
通知函数类型:
通知消息(仅是信息,用绿色表示) : security_note(str) 敏感信息(看做低危,用蓝色表示) : security_info(str) 需要注意(看做中危,用橙色表示) : security_warning(str) 特别注意(看做高危,用红色表示) : security_hole(str)
当我们需要让插件进行报警的时候,选用相应的通知函数即可实现在BugScan的Web端的通知
#!/usr/bin/env python # -*- coding: utf-8 -*- #__Author__ = LinE #_PlugName_ = Test Plugin #_FileName_ = Test_Plugint.py def assign(service, arg): if service =="www": return True, arg def audit(arg): payload ='readnews.asp?id=1 and 1=1' # 注意这里的payload部分不需要在之前加入/,否则接下来的curl没办法正确识别url target = arg + payload code1, head, body1, errcode, final_url = curl.curl('"%s"', % arg) # 使用格式化输入,保证我们带入的字符串不被当做指令来执行 code2, head, body2, errcode, final_url = curl.curl('"%s"', % target) #同上 if code1 ==200and code2 ==200and body1 == body2 : # 判断加和不加payload的网页的HTTP状态码是否为200 # 同时判断加和不加payload的网页的内容是否一样(and 1=1返回是否与原网页一样) security_hole(final_url) # 因为注入算是比较危险的漏洞,所以我们这里报警高危 if __name__ =='__main__': from dummy import* audit(assign('www', 'http://www.test.com/')[1])
一般来说,我们对一些信息的获取划分了等级
低危:绝对路径获取,反射型XSS等
中危:备份下载,存储型XSS等
高危:远程命令执行,SQL注入,任意文件下载,拒绝服务等
现在回过头来看看官方SDK给出的DZ检测插件
#!/usr/bin/env python import re # 引入re模块,来实现正则表达式相关操作 def assign(service, arg): if service =="discuz": return True, arg def audit(arg): code, head, res, errcode, _ = curl.curl(arg +'uc_server/control/admin/db.php') # 发出HTTP请求 if code ==200: # 判断返回状态码,以保证有信息返回 m = re.search('not found in ([^<]+) on line (\d+)', res) # 使用正则表达式来匹配返回HTML中的相关字符串 if m: # 如果正则匹配到了 security_info(m.group(1)) #报警 if __name__ =='__main__': from dummy import* audit(assign('discuz', 'http://www.test.com/')[1])
BugScan插件编写教程基本到这里就算是完了
小菜我也是刚刚学会的初学者,如文中有什么不对的地方欢迎指正!
[作者/LinE,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)]