频道栏目
首页 > 资讯 > 工具软件 > 正文

TDD 方法开发网络安全测试工具:代理扫描器(第一集)

16-12-10        来源:[db:作者]  
收藏   我要投稿

本文讲述的是TDD 方法开发网络安全测试工具:代理扫描器(第一集),旨在服务社会,供安全研究人员学习使用,请勿用于其他非法用途,违者后果自负。

什么是 TDD 以及 我为什么推荐 TDD

做过一些软件开发的朋友们可能会知道这个东西 TDD(Test-Driven Development),诚然我自己之前的开发也并不是 TDD,而是传统的开发,写文档,开发,写文档的“瀑布”开发。当然自己也是开始尝试进行一些系统的 Web 开发训练的时候深深体会到了 TDD 开发的优势,想到了 红黑联盟这边还有一个坑没填,那就来结合现在的经验谈一下这个事情吧!Test-Driven Development 顾名思义,就是利用测试来驱动(督促)开发进展中文名也就是测试驱动开发,简称 TDD。是一种敏捷开发模式。当然这么说大家会觉得非常抽象。那我就通俗的来解释一下,可能有不恰当的地方,希望读者在评论区指出:众所周知瀑布式开发一般来说写代码-写文档-写代码-写文档-… 。每一个周期与周期之间唯一的衔接就是文档;但是 TDD 是测试驱动的,就是先有测试用例 (testCase),然后再有代码(听起来是不是非常的奇怪),其实想想非常的常见,不就是现有需求后代码么?没错啊,TDD 可以说是把这个大需求,直接反映在测试上,然后根据测试用例来开发代码,写出满足测试用例的最少的代码。

优势

这样做有什么好处呢?时刻检查自己的代码是不是符合自己的目标,如果符合要求可以通过,如果不符合,则不能通过。除此之外,仔细想想,在未完成开发的时候,你可以随时停下你的开发,下次只需要运行测试用例,查看哪里没有通过,就可以继续之前的代码进行开发。当然,这只是其中的两条显而易见的好处。

缺点

理所当然的,作为一个渗透测试人员,需要一些短平快的脚本的时候,是根本不需要使用 TDD 方法去写代码的,显然啊,TDD 方法开发的代码量其实要比原来的开发要大(主要体现在有时候需要写大量的测试代码),这算是 TDD 一个缺点。不过按照我个人的体验来说,TDD 方法去开发工具的时候,效率不止是提高了一点,就算写了大量的测试代码,但是仍然是效率高于原来。

渗透工具(HTTP 代理扫描)从 0 开始:

提到 HTTP 代理大家都还是挺熟悉的。那么既然要学习渗透工具开发,不妨就来做一个这样的实用的小玩意。在学习 Python 之余坚持完成了,还可以平时自己使用一波,还是挺不错的。那么关于 HTTP 代理呢?大家应该都并不是特别陌生,我们经常会使用到 HTTP/HTTPS 代理去完成隐藏自己的真实 IP(隐藏自己真实信息这个的可行性我们暂且不谈),那么大家会发现,我们经常上网寻找一些公开的代理网站,(比如快代理啊,xici之类的)

开发环境与重要模块

1. Python2.7+

2. Pylint 用于检查代码规范

3. unittest 用于 TDD 测试驱动开发

说实话,基本 Python 基础知识和 unittest 的基本使用方法我就不介绍了,如果你在阅读这篇文章,就说明你至少还有有一些 Python 基础的。

哦,除此之外,建议在写代码的时候使用 Google 开源项目代码规范 (Python) 。

0×00 Start Up!

(嗨呀,首先是不是要写 Hello World 啊?)当然,既然我们想要使用 TDD 的方法来开发,我们首先当然需要写一个测试用例。(暂且撇开测试套件什么的,我们就最简单的做一个测试用例)。那么要写测试用例了,我们首先得知道,我们想要做出的功能是怎么样的?嗯…既然是代理扫描器嘛,首先我们得知道怎么去扫描 HTTP 代理,其实并不用说的太玄乎,有个最简单的办法就是,我们就去把它当代理用,如果成功了,那么就说明这个 IP 的端口开启了代理模式,如果失败了,那就说明这个端口并不能作为代理来使用。那么事情就简单了,我们想要的第一个功能就是使用 Python 完成检测目标 IP:PORT 是否是可用代理。那么就动手吧!

当然我们需要先建立一个文件,根据功能,就叫 check_proxy.py 吧!在新建文件之后,我们需要开始编写测试用例了。(什么?为什么不是编写代码?)毕竟我们尝试的是 TDD 方法开发工具,显然首先写个函数什么的显然并不是符合我们的初衷。那么,我们就开始吧!

#!/usr/bin/env python

#coding:utf-8

"""

Author: --

Purpose: check proxy available

Created: 2016/10/31

"""

import unittest

########################################################################

class CheckProxyTest(unittest.case.TestCase):

"""Test CheckProxy"""

pass

if __name__ == '__main__':

unittest.main()

在创建了这个段代码之后,显然,大家看到我 import unittest 应该就知道接下来要编写测试用例了吧。那么 unittest 应该怎么使用我觉得我不用向大家解释太多,直接看代码更加直观。在有了测试文件类之后呢,我们并不着急编写我们的 CheckProxy 的功能代码。我们不妨设想一下我们这个类的功能:姑且就是我们输入一个 IP:PORT 这个形式,运行模块,如果可以用作代理,返回结果表明这个地址可以用作 HTTP 还是 HTTPS 代理,那么我们就规定一下返回的格式吧:

{

'result':True,

'proxy':{

'http':'xxx.xxx.xxx.xx:port',

'https':'xxx.xxx.xxx.xx:port'

}

}

如果没有 HTTPS 代理的功能的话那就是:

{

'result':True,

'proxy':{

'http':'xxx.xxx.xxx.xx:port',

}

}

当然这是理想情况,如果不是代理呢?

{

'result':False,

'proxy':None

}

嗯这样的话,我们就可以根据这个来写第一个测试用例了:我们删除 pass 然后添加 def test_xxxxx 方法:

def test_check_ip(self):

'''

result should be

{

'result':True,

{C} 'proxy':{

'http':'xxx.xxx.xxx.xx:port',

'https':'xxx.xxx.xxx.xx:port'

}

}

'''

#创建想要测试的实例

master = CheckProxy('78.6.5.45:8080')

result = master.test()

##对结果进行测试

#测试结果必须是个 dict 类型

self.assertIsInstance(result, dict)

#必须有一个 result 和 proxy 的键

self.assertTrue(result.has_key('result'))

self.assertTrue(result.has_key('proxy'))

#result 键对应的值必须是一个 bool 类型

self.assertIsInstance(result['result'], bool)

#断言测试 result['proxy'] 的类型

if result['result']:

self.assertIsInstance(result['proxy'], dict)

else:

self.assertIsNone(result['proxy'])
[page]

0×01 测试与修改

显然我们看到我们的测试代码,如果能跑通的话,也就一定是我们想要的结果了(至少接口是符合我们需求的)。那么我们切换到命令行来执行这个测试用例。

E

======================================================================

ERROR: test_check_ip (__main__.CheckProxyTest)

----------------------------------------------------------------------

Traceback (most recent call last):

File "check_proxy.py", line 26, in test_check_ip

master = CheckProxy('78.6.5.45:8080')

NameError: global name 'CheckProxy' is not defined

----------------------------------------------------------------------

Ran 1 test in 0.000s

FAILED (errors=1)

显然不用过多解释大家也知道这个是失败了,原因是什么呢?我们根本没有建立 CheckProxy 方法或者 CheckProxy 类啊,当然会失败,那么我们看到 Traceback 中告诉我们 CheckProxy 没有定义,那么我们肯定要去先解决这第一个问题了吧。 所以我们新建我们的类:

class CheckProxy(object):

"""Check Proxy

Attributes:

target: A str, the target you want to check.

Example: 44.4.4.4:44"""

#----------------------------------------------------------------------

def __init__(self, target):

"""Constructor"""

self.target = target

然后我们再来测试我们的代码 python check_proxy.py:

E

======================================================================

ERROR: test_check_ip (__main__.CheckProxyTest)

----------------------------------------------------------------------

Traceback (most recent call last):

File "check_proxy.py", line 43, in test_check_ip

result = master.test()

AttributeError: 'CheckProxy' object has no attribute 'test'

----------------------------------------------------------------------

Ran 1 test in 0.000s

FAILED (errors=1)****

显然又失败了,原因当然 unittest 他已经告诉我们了: 没有一个叫 test 的参数,当然啊我们毕竟之写了 CheckProxy 的构造器,我们并没有创建一个叫 test 的方法。所以我们又需要修改我们的代码… 我觉得到现在了,读者应该觉得挺累的也挺无聊的,不就是这个小东西么?我分分钟写出来啊。而且并不用写这么烦人的测试代码,而且很蠢的一次一次去测试。事实上我在实际做的时候也不会这么蠢,这么慢去写,当然熟练的话,可以根据测试用例直接写出符合规范的代码。嗯,确实是这样。但是既然是刚开始学习这项新的 “技术” 为了领会思想,还是乖乖照做吧。

0×02 第一次测试成功

经过上面各种各样循环,我们终于测试成功了,当然下面的代码可能并不是特别光荣。哈哈 但是至少我们知道成功是什么滋味了对吧?

.

----------------------------------------------------------------------

Ran 1 test in 0.000s

OK

我们的代码现在是什么样子呢?

class CheckProxy(object):

"""Check Proxy

Attributes:

target: A str, the target you want to check.

Example: 44.4.4.4:44"""

#----------------------------------------------------------------------

def __init__(self, target):

"""Constructor"""

self.target = target

def test(self):

'''Test the self.target whether is a http proxy addr

Returns:

A dict: if the result is True:

Example:

{

'result':True,

'proxy':{

'http':'xxx.xxx.xxx.xx:port',

'https':'xxx.xxx.xxx.xx:port'

}

}

if the target isn' t the http proxy addr,

the result['proxy'] will be None

'''

result = {}

result['result'] = False

result['proxy'] = None

return result

什么嘛!你这明明是欺骗 unittest 获得的通过测试,一点都不诚实。为什么这么做呢?简单来说我们先保证接口是统一的,然后再对细节进行一些填充,可以很理所当然的增加新的测试用例,完成更加强大的功能。因为很有可能我们不让这个测试通过的话,会干扰我们后面很多选择,让我们觉得,这个 CheckProxy 的每一个功能似乎都与 test 有关,实际上啊,我们第一个测试用例是在测试接口啊。所以大胆放心吧,我们后面当然会完善。

当然,我们先尽量让它通过,然后在来继续写一些测试用例来完成细节,那么我们可以开始下一个测试用例了。

0×03 正式开始了!

之前的算是熟悉我们需要用到的方法了,接下来我们要做的,肯定就是完成这个测试用例的功能了吧~那么接下来怎么做呢?继续完善 CheckProxy.test() 方法还是?当然我们要继续补充我们的测试用例啊。

那么问题就来了,我们如果想验证我们的代理检测工具是不是可以正常工作,我们当然需要找到一个可以使用的匿名代理对不对?那么这些东西我们去哪里找呢?当然,笔者自然不会打没有准备的仗,哈~其实这次讲的这个工具,是我之间就做过的一个代理搜集(扫描工具),当时做的有不成熟的地方,那么现在就准备重构一下,带领大家过一遍一个工具的开发流程。所以公开的代理,我就不用满大街去寻找了,就随便从我的旧版的 pr0xy 中寻找一些出来吧。

Pr0xy-shell # proxy show

proxy : {u'http': u'http://120.52.72.23:80'}

check_time : Wed Aug 03 23:31:44 2016

proxy : {u'http': u'http://103.27.24.238:80'}

check_time : Wed Aug 03 23:31:44 2016

proxy : {u'http': u'http://50.31.252.54:8080', u'https': u'https://50.31.252.54:8080'}

check_time : Wed Aug 03 23:31:44 2016

proxy : {u'http': u'http://82.196.10.29:80', u'https': u'https://82.196.10.29:80'}

check_time : Wed Aug 03 23:31:44 2016

proxy : {u'http': u'http://119.188.94.145:80', u'https':

proxy : {u'http': u'http://108.59.10.129:55555', u'https': u'https://108.59.10.129:55555'}

check_time : Wed Aug 03 23:31:44 2016

proxy : {u'http': u'http://14.161.21.170:8080', u'https': u'https://14.161.21.170:8080'}

check_time : Wed Aug 03 23:31:44 2016

proxy : {u'http': u'http://179.242.95.20:8080'}

当然笔者对上面的代理不保证永久的可用性,以上代理收集途径均为公共代理。很有可能在大家看到这篇文章的时候,上面代理已经没剩下几个活着的了。自然我也是有办法验证自己的 IP 是不是被很好的隐藏了,我们平时怎么做的呢?就是打开百度,输入 IP:

那么如果成功了的话

大家看到红色箭头了么?我们想要在程序中使用这项功能,不妨可以去 ip138 中寻找一下接口看能不能使用~所以经过一番查找,我们发现了接口是 http://1212.ip138.com/ic.asp, 至于这个网站是干什么的不妨大家自己去看一下。一切顺利,于是我们先添加测试代码吧!

def test_ip_check_function(self):

#首先测试一个正确的实例(已知一定是个代理)

addr = '108.59.10.129:55555'

{C} master = CheckProxy(target=addr)

result = master.test()

#在前一个例子中我们验证了接口

#那么在这里我们只需要验证一下

#一定是完成了代理检测的,而且成功了

proxy = result['proxy']

self.assertTrue(proxy.has_key('http'))

没错我们新添加这么一点东西,运行一下看一下结果 (不用说,肯定会出错的,但是我们先看一下在说):

.E

======================================================================

ERROR: test_ip_check_function (__main__.CheckProxyTest)

----------------------------------------------------------------------

Traceback (most recent call last):

File "check_proxy.py", line 92, in test_ip_check_function

self.assertTrue(proxy.has_key('http'))

AttributeError: 'NoneType' object has no attribute 'has_key'

----------------------------------------------------------------------

Ran 2 tests in 0.001s

我们看到了,断言错误了:这是当然嘛我们还没有做检测呢,那么,我们接下来要去完善 CheckProxy.test() 这里的方法了。

#----------------------------------------------------------------------

def test(self):

'''Test the self.target whether is a http proxy addr

Returns:

A dict: if the result is True:

Example:

{

'result':True,

'proxy':{

'http':'xxx.xxx.xxx.xx:port',

'https':'xxx.xxx.xxx.xx:port'

}

}

if the target isn' t the http proxy addr,

the result['proxy'] will be None

'''

result = {}

result['result'] = False

result['proxy'] = self._check_ip()

return result

#----------------------------------------------------------------------

def _check_ip(self):

"""Check IP return the proxy or None

Returns:

A dict or None: if the result is True(the addr can be a proxy)

the result is the dict like {'http':'http://xx.xx.xx.xx:xx',

'https':'https://xx.xx.xx.xx:xx'}

And if the https proxy can't be used the key named 'https',

Well, if the result is False(the addr can't be used as a proxy)

the result is None"""

check_ip_http = 'http://1212.ip138.com/ic.asp'

check_ip_https = 'https://1212.ip138.com/ic.asp'

addr_proxy = {}

addr_proxy['http'] = 'http://' + self.target

addr_proxy['https'] = 'https://' + self.target

result = {}

http_rsp = requests.get(check_ip_http, proxies=addr_proxy, timeout=5)

if self.target in http_rsp.text:

result['http'] = 'http://' + self.target

https_rsp = requests.get(check_ip_https, proxies=addr_proxy,

verified=False, timeout=5) # close https verify

if self.target in https_rsp.text:

result['https'] = 'https://' + self.target

if result.has_key('http') or result.has_key('https'):

return result

else:

return None
[page]

这样写出来的代码实际上还是非常漂亮的对吧?基本符合 Google 开源 Python 规范,尝试一下 Pylint

C:\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>pylint check_proxy.py

No config file found, using default configuration

************* Module pr0xy.lib.check_proxy

C: 15, 0: Trailing whitespace (trailing-whitespace)

C: 17, 0: Trailing whitespace (trailing-whitespace)

C: 24, 0: Trailing whitespace (trailing-whitespace)

C: 25, 0: Trailing whitespace (trailing-whitespace)

...

...

...

...

...

Messages

--------

+-----------------------+------------+

|message id |occurrences |

+=======================+============+

|trailing-whitespace |10 |

+-----------------------+------------+

|too-few-public-methods |1 |

+-----------------------+------------+

Global evaluation

-----------------

Your code has been rated at 7.56/10 (previous run: 7.56/10, +0.00)

大致看了一下 trailing-whitespace 指的是结尾无意义的空格,too-few-public-methods 指的是公共方法太少了,实际上无意义空格这个是我的 IDE 导致的,应该在 IDE 的 preferences 中可以设置改掉的,嗨呀可是这导致扣了好多分,只能打 7.56 分,笔者好懒啊~来运行一下测试用例

EE

======================================================================

ERROR: test_check_ip (__main__.CheckProxyTest)

----------------------------------------------------------------------

Traceback (most recent call last):

File "check_proxy.py", line 100, in test_check_ip

result = master.test()

...

ConnectTimeoutError(connection.HTTPConnection object at 0x000000000333BB00>, 'Connection to 78.6.5.45 timed out. (connect timeout=5)'))

======================================================================

ERROR: test_ip_check_function (__main__.CheckProxyTest)

test function

----------------------------------------------------------------------

Traceback (most recent call last):

File "check_proxy.py", line 121, in test_ip_check_function

...

object at 0x00000000033C90B8>, 'Connection to 108.59.10.129 timed out. (connect timeout=5)'))

----------------------------------------------------------------------

Ran 2 tests in 10.032s

FAILED (errors=2)

太惨了,全失败,自己看一下原因,好像都是因为 timeout, 那么,我们就去处理一下异常好了,顺便再看一下,好像需要调整一下 timeout 的时间,那么我们就顺手改一下 testCase 什么的。

.E

======================================================================

ERROR: test_ip_check_function (__main__.CheckProxyTest)

test function

----------------------------------------------------------------------

Traceback (most recent call last):

File "check_proxy.py", line 136, in test_ip_check_function

self.assertTrue(proxy.has_key('http'))

AttributeError: 'NoneType' object has no attribute 'has_key'

----------------------------------------------------------------------

Ran 2 tests in 12.039s

FAILED (errors=1)

好像又不能通过了,看下错误,别慌张,我们仔细看一下这个错误的原因,这显然就是这个代理已经失效了,所以我们换个能用的代理,总之通过就好了~最后我们测试通过,这是现在的 check_ip 代码

def _check_ip(self, timeout):

"""Check IP return the proxy or None

Returns:

A dict or None: if the result is True(the addr can be a proxy)

the result is the dict like {'http':'http://xx.xx.xx.xx:xx',

'https':'https://xx.xx.xx.xx:xx'}

And if the https proxy can't be used the key named 'https',

Well, if the result is False(the addr can't be used as a proxy)

the result is None"""

headers = {}

headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36'

check_ip_http = 'http://1212.ip138.com/ic.asp'

check_ip_https = 'https://1212.ip138.com/ic.asp'

addr_proxy = {}

addr_proxy['http'] = 'http://' + self.target

addr_proxy['https'] = 'https://' + self.target

result = {}

http_rsp = ''

try:

http_rsp = requests.get(check_ip_http, proxies=addr_proxy, timeout=timeout,

headers=headers).text

except:

pass

if self.ip in http_rsp:

result['http'] = 'http://' + self.target

https_rsp = ''

try:

https_rsp = requests.get(check_ip_https, proxies=addr_proxy,

verify=False, timeout=timeout,

headers=headers).text # close https verify

except:

pass

if self.ip in https_rsp:

result['https'] = 'https://' + self.target

if result.has_key('http') or result.has_key('https'):

return result

else:

return None

大家看 已经要比原来的代码看起来美观很多了是不是?下面是我们的测试结果:

C:\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>python check_proxy.py

..

----------------------------------------------------------------------

Ran 2 tests in 6.030s

OK

可是适当修改测试用例,添加一些说明:

API test

API test success!

.function test

function test success!

.

----------------------------------------------------------------------

Ran 2 tests in 7.545s

OK

当然 看到最后的测试成功,大家还是有很多细节需要改动的,那么经过轮番的测试,修改循环,我的这个检测模块已经感觉不错了,至少现在是满足我们所有的东西了。 具体的代码呢,可以见 github 地址。

0×04 接下来?

那么收工之后呢,既然我们要做代理扫描器,针对单个的 IP:PORT 的地址进行代理检测已经完成了,那么我们接下来要考虑的事情,就是,提供一个大的 IP 段,然后可以对这个 IP 段中的 IP 进行代理检测,然后再把结果汇总,看起来很简单么?是吧!其实并不是这样的,我们要考虑的问题还有特别多,比如:并发我们怎么处理?我们如何筛选高质量的 IP 段?(至少我们得知道国外和国内的 IP 段不同对吧?)我们扫描到的代理,如何使用?还有,直接扫描太慢了,有更快的方法么?

当然,上面的问题,我们在后面解决,包括针对渗透测试 Python 编程的各种细节,我都会在后面讲给大家,希望对大家有帮助~

总结:

当然今天讲了看起来很多的东西,其实也并没有多少,同样大多东西需要读者亲手去做了才会有体会。那么我反过来再来说一下之前有读者问到的问题:真正要开始写一个工具的时候,第一步或者说第一行代码应该是什么呢?那么现在,大家应该懂了吧,从测试用例开始一个功能模块一个功能模块开始写。当然如果想要详细了解 TDD 这种开发思想的话,你可以去看一下《Python Web开发:测试驱动方法》这本书,很详细的讲了 TDD 的开发思想,当然我只是把他用在了渗透工具的开发上了。那么当然,这一篇文章显然还没有结束,关于 Python 编程的一大堆东西,准备在下一篇文章中分享给大家。

笔者水平有限,希望能对大家有所帮助。

所有代码地址:GITHUB 地址: https://github.com/VillanCh/pr0xy

相关TAG标签
上一篇:英彩票用户网络账号遭黑客攻击
下一篇:一个CMS案例实战讲解PHP代码审计入门
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站