title: Mitmproxy精华笔记
copyright: true
top: 0
date: 2019-05-12 22:41:37
tags:
categories: 爬虫笔记
permalink:
password:
keywords:
description: Mitmproxy一些常用的方法实践
是啊,世界那么残酷,无论你怎么反抗它,它都沉默无声地运转着,根本不管你会怎么想。你在大使的沙拉里放入了鱼胆,苦得他落荒而逃,可他选中的小羊还是被宰杀了,剥了皮泡在胡椒和香叶汤里;你吓得那些红男绿女落荒而逃,可不久之后他们又会聚在你家的舞厅里,就着靡靡之音跳贴面舞,喝醉的男男女女搂在一起,在午夜里高声调笑;你吓走了种马老爹带回来的女明星,可是几天之后卧室里换了新的画作,又有新的女人从老爹的豪车上下来,袅袅婷婷地踏入你家的房门,袅袅婷婷地跟着他走向卧室,流水般的裸女在老爹的大床上滚过。那么多年过去了你还是那么弱小,你自以为足够叛逆了,可你根本不曾改变这个世界,你只是躲开不去看它那残酷的一面。现在你回想起来了吧?你那被愤怒和不甘支配的童年。
介绍
中间代理抓包处理库~类似与burpsuite,fidder,wireshark
安装方法:
pip3 install mitmproxy
安装好后,会有mitmproxy、mitmdump、mitmweb三个使用方法,在cmd命令下输入:
mitmdump --version
出现如下结果表示安装成功:
Mitmproxy: 4.0.4
Python: 3.6.3
OpenSSL: OpenSSL 1.1.0g 2 Nov 2017
Platform: Windows-10-10.0.16299-SP0
注意mitmproxy是不支持windos的噢~mitmweb是web界面的管理器。分别介绍他们的功能:
mitmproxy:提供一个命令行界面,实时显示发生的请求
mitmweb:web页面的获取发生的请求,可视乎查看过滤内容
mitmdump:没有界面,程序默默运行,所以 mitmdump 无法提供过滤请求、查看数据的功能,只能结合自定义脚本,默默工作。
基础使用
首先要开启抓包,使用命令:
mitmdump -p 1080
# 流量走本地代理的1080端口
如果发现需证书有问题,我们还需要安装 mitmproxy 提供的证书,要不抓包失败。打开网址 http://mitm.it , 选择匹配的平台,下载 HTTPS 证书。并按照对应的步骤进行安装
注意不要有端口冲突!!
当启动mitmproxy后,会在根目录下生成证书
C:\Users\langzi\.mitmproxy
这个时候安装证书即可mitmproxy-ca-cert.p12
资料来自 碳基体
要捕获https证书,就得解决证书认证的问题,因此需要在通信发生的客户端安装证书,并且设置为受信任的根证书颁布机构。下面介绍6种客户端的安装方法。
当我们初次运行mitmproxy或mitmdump时,
会在当前目录下生成 ~/.mitmproxy文件夹,其中该文件下包含4个文件,这就是我们要的证书了。
mitmproxy-ca.pem 私钥
mitmproxy-ca-cert.pem 非windows平台使用
mitmproxy-ca-cert.p12 windows上使用
mitmproxy-ca-cert.cer 与mitmproxy-ca-cert.pem相同,android上使用
1. Firefox上安装
preferences-Advanced-Encryption-View Certificates-Import (mitmproxy-ca-cert.pem)-trust this CA to identify web sites
2. chrome上安装
设置-高级设置-HTTPS/SSL-管理证书-受信任的根证书颁发机构-导入mitmproxy-ca-cert.pem
2. osx上安装
双击mitmproxy-ca-cert.pem - always trust
3.windows7上安装
双击mitmproxy-ca-cert.p12-next-next-将所有的证书放入下列存储-受信任的根证书发布机构
4.iOS上安装
将mitmproxy-ca-cert.pem发送到iphone邮箱里,通过浏览器访问/邮件附件
我将证书放在了vps上以供下载
http://tanjiti.com/crt/mitmproxy-ca-cert.pem mitmproxy iOS
http://tanjiti.com/crt/mitmproxy-ca-cert.cer mitmproxy android
http://tanjiti.com/crt/mitmproxy-ca-cert.p12 windows
http://tanjiti.com/crt/PortSwigger.cer BurpSuite (burpsuite的证书,随便附上)
5.iOS模拟器上安装
git clone https://github.com/ADVTOOLS/ADVTrustStore.gitcd ADVTrustStore/
DANI-LEE-2:ADVTrustStore danqingdani$ python iosCertTrustManager.py -a ~/iostools/mitmproxy-ca-cert.pem
subject= CN = mitmproxy, O = mitmproxyImport certificate to iPhone/iPad simulator v5.1 [y/N] yImporting to /Users/danqingdani/Library/Application Support/iPhone Simulator/5.1/Library/Keychains/TrustStore.sqlite3 Certificate added
实际上上面的操作就是给 ~/Library/Application\ Support/iPhone\ Simulator/5.1/Library/Keychains/TrustStore.sqlite3 数据库中表tsettings表中插入证书数据
6.Android上安装
将mitmproxy-ca-cert.cer 放到sdcard根目录下
选择设置-安全和隐私-从存储设备安装证书
然后再浏览器中设置本地代理,方法如下:
这个时候浏览器的请求都会被mitmproxy捕获噢~
mitmdump 命令最大的特点就是可以自定义脚本,你可以在脚本中对请求或者响应内容通过编程的方式来控制,实现数据的解析、修改、存储等工作
使用方法为:
mitmdump -p 1080 -s script.py
你的script.py 的文件内容为:
import mitmproxy.http
from mitmproxy import ctx
class Counter:
def __init__(self):
self.num = 0
def request(self, flow: mitmproxy.http.HTTPFlow):
self.num = self.num + 1
ctx.log.info("We've seen %d flows" % self.num)
addons = [
Counter()
]
然后在浏览器打开网站,浏览页面,这个时候显示数据如下:!
说明捕获到数据了噢~
还能这样这样写script.py,获取每次请求头:
def request(flow):
print(flow.request.headers) # 打印请求头
这里我会重点说说自定义脚本的使用方法与功能
生命周期
即发起一次http请求的全部过程,从发起连接http_connect到获取到发起连接的响应头requestheaders,然后获取到想要发起连接的内容request。
发起连接后,得到获取内容的响应头responseheaders,然后获取到返回相应的内容response。
上面的script文件中的函数名,request都是mitm定义好的了函数名,我们每次要写对应功能的脚本时候,需要找到相关功能提供的函数名即可。
http_connect
def http_connect(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 收到了来自客户端的 HTTP CONNECT 请求。在 flow 上设置非 2xx 响应将返回该响应并断开连接。CONNECT 不是常用的 HTTP 请求方法,目的是与服务器建立代理连接,仅是 client 与 proxy 的之间的交流,所以 CONNECT 请求不会触发 request、response 等其他常规的 HTTP 事件。
requestheaders
def requestheaders(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 来自客户端的 HTTP 请求的头部被成功读取。此时 flow 中的 request 的 body 是空的。
request
def request(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 来自客户端的 HTTP 请求被成功完整读取。
responseheaders
def responseheaders(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 来自服务端的 HTTP 响应的头部被成功读取。此时 flow 中的 response 的 body 是空的。
response
def response(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 来自服务端端的 HTTP 响应被成功完整读取。
通用周期
即每次发起连接或者收到相应内容,都可以使用下面的函数
error
def error(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 发生了一个 HTTP 错误。比如无效的服务端响应、连接断开等。注意与“有效的 HTTP 错误返回”不是一回事,后者是一个正确的服务端响应,只是 HTTP code 表示错误而已。
configure
def configure(self, updated: typing.Set[str]):
(Called when) 配置发生变化。updated 参数是一个类似集合的对象,包含了所有变化了的选项。在 mitmproxy 启动时,该事件也会触发,且 updated 包含所有选项。
done
def done(self):
(Called when) addon 关闭或被移除,又或者 mitmproxy 本身关闭。由于会先等事件循环终止后再触发该事件,所以这是一个 addon 可以看见的最后一个事件。由于此时 log 也已经关闭,所以此时调用 log 函数没有任何输出。
load
def load(self, entry: mitmproxy.addonmanager.Loader):
(Called when) addon 第一次加载时。entry 参数是一个 Loader 对象,包含有添加选项、命令的方法。这里是 addon 配置它自己的地方。
log
def log(self, entry: mitmproxy.log.LogEntry):
(Called when) 通过 mitmproxy.ctx.log 产生了一条新日志。小心不要在这个事件内打日志,否则会造成死循环。
running
def running(self):
(Called when) mitmproxy 完全启动并开始运行。此时,mitmproxy 已经绑定了端口,所有的 addon 都被加载了。
update
def update(self, flows: typing.Sequence[mitmproxy.flow.Flow]):
(Called when) 一个或多个 flow 对象被修改了,通常是来自一个不同的 addon。
功能使用
response 1
获取返回信息
from mitmproxy import http
def response(flow:http.HTTPResponse)->None:
print('-'*30)
print(flow.request)
# Request(GET langzi.fun:80/upload/TIM%E6%88%AA%E5%9B%BE20190422162029.png)
# 请求发送 请求的url
print(flow.response)
# Response(200 OK, image/png, 61.29k)
# 返回状态码 返回的信息类型 返回的文件大小
print(flow.client_conn)
# <ClientConnection: 127.0.0.1:28055>
# 本地发起连接
print(flow.server_conn)
# <ServerConnection: langzi.fun:80>
# 服务器端口
print('-'*30)
response 2
获取请求头,主机,查询
import mitmproxy
def response(flow: mitmproxy.http.HTTPFlow)->None:
print(flow.request.headers)
# Headers[(b'Host', b'www.syztfj.com'), (b'User-Agent', b'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0'), (b'Accept', b'*/*'), (b'Accept-Language', b'zh-CN,zh;q=0.8,en-US;q=0.5,en;q
# =0.3'), (b'Accept-Encoding', b'gzip, deflate'), (b'Referer', b'http://www.syztfj.com/css/stylelunbo_nei.css'), (b'Cookie', b'online_service_status=1; ASPSESSIONIDCQTTRCBC=HDPJBKAAAFJOACLEDEGCNDAF'), (b'DNT',
# b'1'), (b'X-Forwarded-For', b'8.8.8.8'), (b'Connection', b'keep-alive')]
print(flow.request.host)
# www.syztfj.com
print(flow.request.query)
# 访问网址 http://c.cnzz.com/core.php?web_id=1271804123&t=z ,返回的结果如下:
# MultiDictView[('web_id', '1271804123'), ('t', 'z')]
# 可以使用flow.request.query.keys()获取所有的键
resnose 3
获取url动态链接
from mitmproxy import http
import re
from urllib.parse import urljoin
def response(flow:http.HTTPFlow):
if "Content-Type" in flow.response.headers and flow.response.headers["Content-Type"].find("text/html") != -1:
pageUrl = flow.request.url
pageText = flow.response.text
pattern = (r"<a\s+(?:[^>]*?\s+)?href=(?P<delimiter>[\"'])"
r"(?P<link>(?!https?:\/\/|ftps?:\/\/|\/\/|#|javascript:|mailto:).*?)(?P=delimiter)")
rel_matcher = re.compile(pattern, flags=re.IGNORECASE)
rel_matches = rel_matcher.finditer(pageText)
for match_num, match in enumerate(rel_matches):
(delimiter, rel_link) = match.group("delimiter", "link")
abs_link = urljoin(pageUrl, rel_link)
print('LINKS:'+abs_link)
# LINKS:http://bbs.kjj.com/space.php?username=%BB%F9%BD%F0%CA%D6
# LINKS:http://bbs.kjj.com/redirect.php?tid=655201&goto=lastpost#lastpost
# LINKS:http://bbs.kjj.com/space-username-gtthc.html
# LINKS:http://bbs.kjj.com/forum-154-1.html
# LINKS:http://bbs.kjj.com/forum-154-1.html
# LINKS:http://bbs.kjj.com/space.php?username=%B4%BA%C8%D5%D4%D8%D1%F4
# LINKS:http://bbs.kjj.com/redirect.php?tid=968702&goto=lastpost#lastpost
# LINKS:http://bbs.kjj.com/space-username-%C0%B6%CC%EC%B7%E3.html
# LINKS:http://bbs.kjj.com/forum-144-1.html
# LINKS:http://bbs.kjj.com/forum-144-1.html
# LINKS:http://bbs.kjj.com/space.php?username=%D4%C2%D7%ED%BB%A8%D2%F5
response 4
获取相关结果
def parser_data(self):
result = dict()
result['url'] = self.flow.request.url
# 当前网址
result['path'] = '/{}'.format('/'.join(self.flow.request.path_components))
# 当前路径
result['host'] = self.flow.request.host
# 当前主机
result['port'] = self.flow.request.port
# 主机端口
result['scheme'] = self.flow.request.scheme
# http or https
result['method'] = self.flow.request.method
# 请求方式
result['status_code'] = self.flow.response.status_code
# 状态码
result['content_length'] = int(self.flow.response.headers.get('Content-Length', 0))
# 返回内容长度
result['request_header'] = self.parser_header(self.flow.request.headers)
# 请求头
result['request_content'] = self.flow.request.content
# 返回内容
result['query'] = self.flow.request.query
# 查询参数
return result
重定向
from mitmproxy import http
def request(flow: http.HTTPFlow) -> None:
if flow.request.pretty_host == "example.org":
flow.request.host = "mitmproxy.org"
# 在这里把提交的主机地址修改成Mitmproxy.org
使用日志
使用日志的时候,不能再print,因为log.xxx自动输出内容
from mitmproxy import ctx
def load(l):
ctx.log.info("This is some informative text.")
ctx.log.warn("This is a warning.")
ctx.log.error("This is an error.")
写入文件
import random
import sys
from mitmproxy import io, http
import typing # noqa
class Writer:
def __init__(self, path: str) -> None:
self.f: typing.IO[bytes] = open(path, "wb")
self.w = io.FlowWriter(self.f)
def response(self, flow: http.HTTPFlow) -> None:
if random.choice([True, False]):
self.w.add(flow)
def done(self):
self.f.close()
addons = [Writer(sys.argv[1])]
读取文件
from mitmproxy import io
from mitmproxy.exceptions import FlowReadException
import pprint
import sys
with open(sys.argv[1], "rb") as logfile:
freader = io.FlowReader(logfile)
pp = pprint.PrettyPrinter(indent=4)
try:
for f in freader.stream():
print(f)
print(f.request.host)
pp.pprint(f.get_state())
print("")
except FlowReadException as e:
print("Flow file corrupted: {}".format(e))
获取路径
from pathod import pathoc
p = pathoc.Pathoc(("google.com", 80))
p.connect()
print(p.request("get:/"))
print(p.request("get:/foo"))
欢迎关注公众号:【安全研发】获取更多相关工具,课程,资料分享哦~