使用 Python 打造基础 Web 漏洞扫描器 (一):网站爬虫 + SQL 注入检测实现
使用Python打造基本WEB漏洞扫描器(一) 网站爬虫SQL注入检测一、实验介绍扫描器需要实现功能的思维导图1.1 实验内容编写一个简单的多线程爬虫用于对网站地址进行爬取编写一个简单的sql注入工具用于对网站地址进行sql注入的检测。1.2 实验知识点多线程的使用网站爬虫的基本知识SQL注入的基本原理SQL检测工具编写多参数URL的sql注入检测正则表达式的基本知识1.3 实验环境Python 2.7Win10PyCharm二、实验原理简单的扫描器雏形编写爬虫sql判断程序后续优化以此雏形为基础。2.1 爬虫编写思路爬虫的作用是收集网站的链接我们需要记录一下已经爬取的链接和待爬取的链接并且去重复去重复使用python的set()就可以解决大概流程是输入url下载解析出urlurl去重判断是否为本站加入到待爬去列表重复循环即可2.2 SQL判断思路通过在url 后面加上AND %d%d或者OR NOT (%d%d)%d后门的数字是随机可变的然后搜索网页中特殊关键词,比如mysql中是 SQL syntax.*MySQL Microsoft SQL Server是 Warning.*mssql_ Microsoft Access 是 Microsoft Access Driver Oracle 是 Oracle error IBM DB2 是 DB2 SQL error SQLite 是 SQLite.Exception 等等....通过这些关键词就可以判断出所用的数据库我们还要判断一下waf之类的东西有这种东西就直接停止简单的方法就是用特定的url访问如果出现了像 ip banned ,firewall之类的关键词可以判断出是waf了具体的正则表达式是(?i)(/A|/b)IP/b.*/b(banned|blocked|bl(a|o)ck/s?list|firewall)当然我们只是简单的来判断是否有注入用这个思路写个脚本非常简单三、开发准备在线环境已经安装好了这些库如果不是用的在线环境请先安装这些库pip install requests pip install beautifulsoup4requests是一个友好可靠的网络访问库beautifulsoup4是用于解析html标签的库。 同时创建个wgdscan文件夹里面的目录结构如下/wgd.py //项目启动主文件 /lib/core //核心文件存放目录 /lib/core/config.py //配置文件 /script //插件存放 /exp //exp和poc存放四、实验步骤4.1 sql检测脚本编写用一个字典存储数据库特征DBMS_ERRORS { # regular expressions used for DBMS recognition based on error message response MySQL: (rSQL syntax.*MySQL, rWarning.*mysql_.*, rvalid MySQL result, rMySqlClient/.), PostgreSQL: (rPostgreSQL.*ERROR, rWarning.*/Wpg_.*, rvalid PostgreSQL result, rNpgsql/.), Microsoft SQL Server: (rDriver.* SQL[/-/_/ ]*Server, rOLE DB.* SQL Server, r(/W|/A)SQL Server.*Driver, rWarning.*mssql_.*, r(/W|/A)SQL Server.*[0-9a-fA-F]{8}, r(?s)Exception.*/WSystem/.Data/.SqlClient/., r(?s)Exception.*/WRoadhouse/.Cms/.), Microsoft Access: (rMicrosoft Access Driver, rJET Database Engine, rAccess Database Engine), Oracle: (r/bORA-[0-9][0-9][0-9][0-9], rOracle error, rOracle.*Driver, rWarning.*/Woci_.*, rWarning.*/Wora_.*), IBM DB2: (rCLI Driver.*DB2, rDB2 SQL error, r/bdb2_/w/(), SQLite: (rSQLite/JDBCDriver, rSQLite.Exception, rSystem.Data.SQLite.SQLiteException, rWarning.*sqlite_.*, rWarning.*SQLite3::, r/[SQLITE_ERROR/]), Sybase: (r(?i)Warning.*sybase.*, rSybase message, rSybase.*Server message.*), }通过正则如果发现我们的正则语句就可以判断出是哪个数据库了。for (dbms, regex) in ((dbms, regex) for dbms in DBMS_ERRORS for regex in DBMS_ERRORS[dbms]): if(re.search(regex,_content)): return True这个是我们的测试语句[payload]。BOOLEAN_TESTS ( AND %d%d, OR NOT (%d%d))用报错语句返回正确的内容和错误的内容进行对比。for test_payload in BOOLEAN_TESTS: #正确的网页 RANDINT random.randint(1, 255) _url url test_payload%(RANDINT,RANDINT) content[true] Downloader.get(_url) _url url test_payload%(RANDINT,RANDINT1) content[false] Downloader.get(_url) if content[origin]content[true]!content[false]: return sql fonud: %%url这一句content[origin]content[true]!content[false]意思就是当原始的网页等于正确的网页不等于错误的网页内容时就可以判定这个地址存在注入漏洞。完整代码import re,random from lib.core import Download def sqlcheck(url): if(not url.find(?)): return False Downloader Download.Downloader() BOOLEAN_TESTS ( AND %d%d, OR NOT (%d%d)) DBMS_ERRORS {# regular expressions used for DBMS recognition based on error message response MySQL: (rSQL syntax.*MySQL, rWarning.*mysql_.*, rvalid MySQL result, rMySqlClient/.), PostgreSQL: (rPostgreSQL.*ERROR, rWarning.*/Wpg_.*, rvalid PostgreSQL result, rNpgsql/.), Microsoft SQL Server: (rDriver.* SQL[/-/_/ ]*Server, rOLE DB.* SQL Server, r(/W|/A)SQL Server.*Driver, rWarning.*mssql_.*, r(/W|/A)SQL Server.*[0-9a-fA-F]{8}, r(?s)Exception.*/WSystem/.Data/.SqlClient/., r(?s)Exception.*/WRoadhouse/.Cms/.), Microsoft Access: (rMicrosoft Access Driver, rJET Database Engine, rAccess Database Engine), Oracle: (r/bORA-[0-9][0-9][0-9][0-9], rOracle error, rOracle.*Driver, rWarning.*/Woci_.*, rWarning.*/Wora_.*), IBM DB2: (rCLI Driver.*DB2, rDB2 SQL error, r/bdb2_/w/(), SQLite: (rSQLite/JDBCDriver, rSQLite.Exception, rSystem.Data.SQLite.SQLiteException, rWarning.*sqlite_.*, rWarning.*SQLite3::, r/[SQLITE_ERROR/]), Sybase: (r(?i)Warning.*sybase.*, rSybase message, rSybase.*Server message.*), } _url url %29%28%22%27 _content Downloader.get(_url) for (dbms, regex) in ((dbms, regex) for dbms in DBMS_ERRORS for regex in DBMS_ERRORS[dbms]): if(re.search(regex,_content)): return True content {} content[origin] Downloader.get(_url) for test_payload in BOOLEAN_TESTS: RANDINT random.randint(1, 255) _url url test_payload%(RANDINT,RANDINT) content[true] Downloader.get(_url) _url url test_payload%(RANDINT,RANDINT1) content[false] Downloader.get(_url) if content[origin]content[true]!content[false]: return sql fonud: %%url我们在/script目录中创建这个文件命名为sqlcheck.py。 暂时我们可以把他作为一个模块单独的进行调用等以后写完插件系统后可由插件系统自动的调用这些模块。有些url地址是我们不需要测试的比如.html结尾的地址我们可以过滤掉他们这里我直接find(?)查找?来判断url是否符合我们的标准。4.2 爬虫的编写爬虫的思路我们上面已经讲过了先完成url的管理我们单独将他作为一个类 文件保存在lib/core/UrlManager.py。#!/usr/bin/env python #-*- coding:utf-8 -*- class UrlManager(object): def __init__(self): self.new_urls set() self.old_urls set() def add_new_url(self, url): if url is None: return if url not in self.new_urls and url not in self.old_urls: self.new_urls.add(url) def add_new_urls(self, urls): if urls is None or len(urls) 0: return for url in urls: self.add_new_url(url) def has_new_url(self): return len(self.new_urls) ! 0 def get_new_url(self): new_url self.new_urls.pop() self.old_urls.add(new_url) return new_url同时为了方便我们也可以将下载功能单独的作为一个类使用,文件保存在lib/core/Downloader.py简单写一下get/post方法即可。#!/usr/bin/env python # -*- coding:utf-8 -*- import requests class Downloader(object): def get(self, url): r requests.get(url, timeout10) if r.status_code ! 200: return None _str r.text return _str def post(self, url, data): r requests.post(url, data) _str r.text return _str def download(self, url, htmls): if url is None: return None _str {} _str[url] url try: r requests.get(url, timeout10) if r.status_code ! 200: return None _str[html] r.text except Exception as e: return None htmls.append(_str)特别说明下因为我们爬虫会是多线程的所以类中有个download方法是专门为多线程下载用的。在lib/core/Spider.py创建爬虫。爬虫代码如下#!/usr/bin/env python #-*- coding:utf-8 -*- from lib.core import Download,UrlManager import threading from urlparse import urljoin from bs4 import BeautifulSoup from script import sqlcheck class SpiderMain(object): def __init__(self,root,threadNum): self.urls UrlManager.UrlManager() self.download Download.Downloader() self.root root self.threadNum threadNum def _judge(self, domain, url): if (url.find(domain) ! -1): return True else: return False def _parse(self,page_url,content): if content is None: return soup BeautifulSoup(content, html.parser) _news self._get_new_urls(page_url,soup) return _news def _get_new_urls(self, page_url,soup): new_urls set() links soup.find_all(a) for link in links: new_url link.get(href) new_full_url urljoin(page_url, new_url) if(self._judge(self.root,new_full_url)): new_urls.add(new_full_url) return new_urls def craw(self): self.urls.add_new_url(self.root) while self.urls.has_new_url(): _content [] th [] for i in list(range(self.threadNum)): if self.urls.has_new_url() is False: break new_url self.urls.get_new_url() ##sql check try: if(sqlcheck.sqlcheck(new_url)): print(url:%s sqlcheck is valueable%new_url) except: pass print(craw: new_url) t threading.Thread(targetself.download.download,args(new_url,_content)) t.start() th.append(t) for t in th: t.join() for _str in _content: if _str is None: continue new_urls self._parse(new_url,_str[html]) self.urls.add_new_urls(new_urls)爬虫通过调用craw()方法传入一个网址进行爬行然后采用多线程的方法下载待爬行的网站下载后的源码用_parse方法调用BeautifulSoup进行解析之后将解析出的url列表丢入url管理器中这样循环最后只要爬完了网页爬虫就会停止 我们使用了threading库进行多线程编写本项目中可以自定义需要开启的线程数线程开启后每个线程会得到一个url进行下载然后线程会阻塞阻塞完毕后线程放行继续运行。4.3 爬虫和SQL检查的结合在lib/core/Spider.py文件引用一下from script import sqlcheck等下节我们开发出了插件系统后就不需要这样引用了爬虫会自动调用但这节课为了测试我们还是引用一下。在craw()方法中取出新url地方调用一下。##sql check try: if(sqlcheck.sqlcheck(new_url)): print(url:%s sqlcheck is valueable%new_url) except: pass用try检测可能出现的异常绕过它在文件wgdscan.py中我们可以进行测试了。#!/usr/bin/env python #-*- coding:utf-8 -*- Name:wgdScan Author:wanggangdan Copyright (c) 2019 import sys from lib.core.Spider import SpiderMain def main(): root https://www.shiyanlou.com/ threadNum 10 #spider wgd SpiderMain(root,threadNum) wgd.craw() if __name__ __main__: main()很重要的一点为了使得lib和script文件夹中的.py文件可以被认作是模块请在liblib/core和script文件夹中创建__init__.py文件init前后是两个_下划线文件中什么都不需要加。五、总结及回顾SQL注入检测通过一些payload使页面报错判断原始网页正确网页错误网页即可检测出是否存在SQL注入漏洞通过匹配出sql报错出来的信息可以正则判断出所用的数据库扫描器目前是通过一个爬虫扫描来进行漏洞检测以后会从各个方面进行检测GitHub源码地址https://github.com/ldz0/Python-wgdscan/tree/master/一网站爬虫开发以及SQL注入检测插件/wgdscan实验来自实验楼《网络安全从零到精通全套学习大礼包》网络安全学习路线学习资源对于从来没有接触过网络安全的同学我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线大家跟着这个大的方向学习准没问题。**读者福利 |**【CSDN大礼包】最新网络安全/网安技术资料包~282G无偿分享**安全链接放心点击**!网络安全的知识多而杂怎么科学合理安排下面给大家总结了一套适用于网安零基础的学习路线应届生和转行人员都适用学完保底6k就算你底子差如果能趁着网安良好的发展势头不断学习日后跳槽大厂、拿到百万年薪也不是不可能初级网工1、网络安全理论知识2天①了解行业相关背景前景确定发展方向。②学习网络安全相关法律法规。③网络安全运营的概念。④等保简介、等保规定、流程和规范。非常重要2、渗透测试基础一周①渗透测试的流程、分类、标准②信息收集技术主动/被动信息搜集、Nmap工具、Google Hacking③漏洞扫描、漏洞利用、原理利用方法、工具MSF、绕过IDS和反病毒侦察④主机攻防演练MS17-010、MS08-067、MS10-046、MS12-20等3、操作系统基础一周①Windows系统常见功能和命令②Kali Linux系统常见功能和命令③操作系统安全系统入侵排查/系统加固基础4、计算机网络基础一周①计算机网络基础、协议和架构②网络通信原理、OSI模型、数据转发流程③常见协议解析HTTP、TCP/IP、ARP等④网络攻击技术与网络安全防御技术⑤Web漏洞原理与防御主动/被动攻击、DDOS攻击、CVE漏洞复现5、数据库基础操作2天①数据库基础②SQL语言基础③数据库安全加固6、Web渗透1周①HTML、CSS和JavaScript简介②OWASP Top10③Web漏洞扫描工具④Web渗透工具Nmap、BurpSuite、SQLMap、其他菜刀、漏扫等恭喜你如果学到这里你基本可以从事一份网络安全相关的工作比如渗透测试、Web 渗透、安全服务、安全分析等岗位如果等保模块学的好还可以从事等保工程师。薪资区间6k-15k到此为止大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗【“脚本小子”成长进阶资源领取】7、脚本编程初级/中级/高级在网络安全领域。是否具备编程能力是“脚本小子”和真正黑客的本质区别。在实际的渗透测试过程中面对复杂多变的网络环境当常用工具不能满足实际需求的时候往往需要对现有工具进行扩展或者编写符合我们要求的工具、自动化脚本这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中想要高效地使用自制的脚本工具来实现各种目的更是需要拥有编程能力.零基础入门建议选择脚本语言Python/PHP/Go/Java中的一种对常用库进行编程学习 搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP IDE强烈推荐Sublime ·Python编程学习学习内容包含语法、正则、文件、 网络、多线程等常用库推荐《Python核心编程》不要看完 ·用Python编写漏洞的exp,然后写一个简单的网络爬虫 ·PHP基本语法学习并书写一个简单的博客系统 熟悉MVC架构并试着学习一个PHP框架或者Python框架 (可选) ·了解Bootstrap的布局或者CSS。8、超级网工这部分内容对零基础的同学来说还比较遥远就不展开细说了贴一个大概的路线。感兴趣的童鞋可以研究一下不懂得地方可以【点这里】加我耗油跟我学习交流一下。网络安全工程师企业级学习路线如图片过大被平台压缩导致看不清的话可以【点这里】加我耗油发给你大家也可以一起学习交流一下。一些我自己买的、其他平台白嫖不到的视频教程需要的话可以扫描下方卡片加我耗油发给你都是无偿分享的大家也可以一起学习交流一下。网络安全学习路线学习资源结语网络安全产业就像一个江湖各色人等聚集。相对于欧美国家基础扎实懂加密、会防护、能挖洞、擅工程的众多名门正派我国的人才更多的属于旁门左道很多白帽子可能会不服气因此在未来的人才培养和建设上需要调整结构鼓励更多的人去做“正向”的、结合“业务”与“数据”、“自动化”的“体系、建设”才能解人才之渴真正的为社会全面互联网化提供安全保障。特别声明此教程为纯技术分享本书的目的决不是为那些怀有不良动机的人提供及技术支持也不承担因为技术被滥用所产生的连带责任本书的目的在于最大限度地唤醒大家对网络安全的重视并采取相应的安全措施从而减少由网络安全而带来的经济损失