- A+
crown丶prince (我用双手成就你的梦想) | 2015-09-26 19:38
原作者:Chris Katsaropoulos
第一译者:@草帽小子-DJ
第二译者:crown丶prince
网络
套接字模块提供了一个可以使python建立网络连接的库。让我们快速的编写一个获取提示信息的脚本,连接到特定IP地址和端口后,我们的脚本将打印提示信息,之后,我们使用connect()函数连接到IP地址和端口。一旦连接成功,就可以通过套接字进行读写。这种recv(1024)的方法将读取之后在套接字中1024字节的数据。我们把这种方式的结果存到一个变量中,然后打印到服务器。
>>> import socket
>>> socket.setdefaulttimeout(2)
>>> s = socket.socket()
>>> s.connect(("192.168.95.148",21))
>>> ans = s.recv(1024)
>>> print ans
220 FreeFloat Ftp Server (Version 1.00).
选择
像大多数编程语言一样,python提供了条件选择的方式,通过if语句,计算一个逻辑表达式来判断选择的结果。继续写我们的脚本,我们想知道,是否指定的FTP服务器是容易受到攻击的。要做到这一点,我们要拿我们的结果和已知的易受攻击的FTP服务器版本作比较。
>>> import socket
>>> socket.setdefaulttimeout(2)
>>> s = socket.socket()
>>> s.connect(("192.168.95.148",21))
>>> ans = s.recv(1024)
>>> if ("FreeFloat Ftp Server (Version 1.00)" in ans):
... print "[+] FreeFloat FTP Server is vulnerable."
...elif ("3Com 3CDaemon FTP Server Version 2.0" in banner):
... print "[+] 3CDaemon FTP Server is vulnerable."
... elif ("Ability Server 2.34" in banner):
... print "[+] Ability FTP Server is vulnerable."
... elif ("Sami FTP Server 2.0.2" in banner):
... print "[+] Sami FTP Server is vulnerable."
... else:
... print "[-] FTP Server is not vulnerable."
...
[+] FreeFloat FTP Server is vulnerable."
异常处理
即使一个程序设计师编写的程序语法正确,该程序仍然可能在运行或执行时发生错误。考虑经典的一种运行错误——除以零。因为零不能做除数,所以python解释器显示一条消息,把错误信息告诉程序设计师:该错误使程序停止执行。
>>> print 1337/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by ze
如果我们想在我们预设的范围内处理错误,会对运行的程序产生什么影响呢?python语言提供的异常处理能力就可以这样做。让我们来更新前面的例子,我们使用try/except进行异常处理。现在程序试图除以零。当错误发生时,我们的异常处理捕获错误并把错误信息打印到屏幕上。
>>> try:
... print "[+] 1337/0 = "+str(1337/0)
... except:
... print "[-] Error. "
...
[-] Error
>>>
不幸的是,这给了我们非常少的关于错误的异常处理的信息。但在对待特殊错误时,这可能很有用,要做到这一点,我们将存储异常信息到一个变量中,来打印出异常信息。
>>> try:
... print "[+] 1337/0 = "+str(1337/0)
... except Exception, e:
... print "[-] Error = "+str(e)
...
[-] Error = integer division or modulo by zero
>>>
现在,让我们用异常处理来更新我们的脚本,我们用异常处理把网络连接代码包装起来,接下来,我们连接到一台围在TCP端口21上开放FTP服务的机器。如果我们等待连接超时,我们将看到一条信息来表明网络连接操作超时。然后,我们的程序可以继续运行。
>>> import socket
>>> socket.setdefaulttimeout(2)
>>> s = socket.socket()
>>> try:
... s.connect(("192.168.95.149",21))
... except Exception, e:
... print "[-] Error = "+str(e)
...
[-] Error = Operation timed out
在本书中,让我们为你提供一个与异常处理有关的警告,为了清楚的说明各种各样的概念,在下面的内容中,我们已经在最小的地方都添加了异常处理,但我们仍然欢迎你更新这新脚本,并把强化的异常处理代码分享到配到网站上。
函数
在python中,函数提供了组建好的,可反复使用的代码片段。通常,这允许程序设计师写代码来执行单独或关联的行为。
尽管python提供了许多内置函数,程序设计师仍然可以创建自定义的函数。关键字def()开始了一个函数,程序设计师可以把任何变量放到括号里。这些变量随后被传递,这意味着在函数内部对这些变量的任何变化,都将影响调用的函数的值。继续以我们的FTP漏洞扫描器为例,让我们创建一个函数来执行只连接到FTP服务器的操作并返回提示信息
import socket
def retBanner(ip, port):
try:
socket.setdefaulttimeout(2)
s = socket.socket()
s.connect((ip, port))
banner = s.recv(1024)
return banner
except:
return
def main():
ip1 = '192.168.95.148'
ip2 = '192.168.95.149'
port = 21
banner1 = retBanner(ip1, port)
if banner1:
print '[+] ' + ip1 + ': ' + banner1
banner2 = retBanner(ip2, port)
if banner2:
print '[+] ' + ip2 + ': ' + banner2
if __name__ == '__main__':
main()
在返回信息后,我们的脚本需要与已知存在漏洞的程序进行核对。这也反映了函数的单一性和相关性。该函数checkVulns()用获得的信息来对服务器存在的漏洞进行判断。
import socket
def retBanner(ip, port):
try:
socket.setdefaulttimeout(2)
s = socket.socket()
s.connect((ip, port))
banner = s.recv(1024)
return banner
except:
return
def checkVulns(banner):
if 'FreeFloat Ftp Server (Version 1.00)' in banner:
print '[+] FreeFloat FTP Server is vulnerable.'
elif '3Com 3CDaemon FTP Server Version 2.0' in banner:
print '[+] 3CDaemon FTP Server is vulnerable.'
elif 'Ability Server 2.34' in banner:
print '[+] Ability FTP Server is vulnerable.'
elif 'Sami FTP Server 2.0.2' in banner:
print '[+] Sami FTP Server is vulnerable.'
else:
print '[-] FTP Server is not vulnerable.'
return
def main():
ip1 = '192.168.95.148'
ip2 = '192.168.95.149'
ip3 = '192.168.95.150'
port = 21
banner1 = retBanner(ip1, port)
if banner1:
print '[+] ' + ip1 + ': ' + banner1.strip('\n’)
checkVulns(banner1)
banner2 = retBanner(ip2, port)
if banner2:
print '[+] ' + ip2 + ': ' + banner2.strip('\n')
checkVulns(banner2)
banner3 = retBanner(ip3, port)
if banner3:
print '[+] ' + ip3 + ': ' + banner3.strip('\n')
checkVulns(banner3)
if __name__ == '__main__':
main()
迭代
上一章中,你可能会发现我们几乎重复三次写了相同的代码,来检测三个不同的IP地址。
代替反复做一件事,使用for循环便利多个元素会更加容易。举个例子:如果我们想便利整个整个IP地址从192.168.98.1到192.168.95.254的子网,我们要用一个for循环从1到255进行遍历,来打印出子网内的信息。
>>> for x in range(1,255):
... print "192.168.95."+str(x)
...
192.168.95.1
192.168.95.2
192.168.95.3
192.168.95.4
192.168.95.5
192.168.95.6
... <SNIPPED> ...
192.168.95.253
192.168.95.254
同样,我们可能需要遍历已知的端口列表来检查漏洞。代替一系列的数字,我们可以通过一个元素列表遍历他们。
>>> portList = [21,22,25,80,110]
>>> for port in portList:
... print port
...
21
22
25
80
110
嵌套了两个for循环,现在我们可以打印出每个IP地址和端口了。
>>> for x in range(1,255):
... for port in portList:
... print "[+] Checking 192.168.95."\
+str(x)+": "+str(port)
...
[+] Checking 192.168.95.1:21
[+] Checking 192.168.95.1:22
[+] Checking 192.168.95.1:25
[+] Checking 192.168.95.1:80
[+] Checking 192.168.95.1:110
[+] Checking 192.168.95.2:21
[+] Checking 192.168.95.2:22
[+] Checking 192.168.95.2:25
[+] Checking 192.168.95.2:80
[+] Checking 192.168.95.2:110
<... SNIPPED ...>
随着程序有了遍历IP和端口的能力,我们也将个更新我们的漏洞检测脚本,现在,我们的脚本将测试全部254个IP地址所提供的telnet, SSH, smtp, http,imap, and https服务。
import socket
def retBanner(ip, port):
try:
socket.setdefaulttimeout(2)
s = socket.socket()
s.connect((ip, port))
banner = s.recv(1024)
return banner
except:
return
def checkVulns(banner):
if 'FreeFloat Ftp Server (Version 1.00)' in banner:
print '[+] FreeFloat FTP Server is vulnerable.'
elif '3Com 3CDaemon FTP Server Version 2.0' in banner:
print '[+] 3CDaemon FTP Server is vulnerable.'
elif 'Ability Server 2.34' in banner:
print '[+] Ability FTP Server is vulnerable.'
elif 'Sami FTP Server 2.0.2' in banner:
print '[+] Sami FTP Server is vulnerable.'
else:
print '[-] FTP Server is not vulnerable.'
return
def main():
portList = [21,22,25,80,110,443]
for x in range(1, 255):
ip = '192.168.95.' + str(x)
for port in portList:
banner = retBanner(ip, port)
if banner:
print '[+] ' + ip + ': ' + banner
checkVulns(banner)
if __name__ == '__main__':
main()
文件I/O
虽然我们的脚本已有了一些能帮助检测漏洞信息的if语句,但加进一个漏洞列表会更好,举个例子,假设我们有一个叫做vuln_banners.txt的文本文件。在每一行该文件列出了具体的服务版本和已知的之前的漏洞,我们不需要构建一个庞大的if语句,让我们读取这个文本文件,并用他来判断是否我们的提示信息存在漏洞。
programmer$ cat vuln_banners.txt
3Com 3CDaemon FTP Server Version 2.0
Ability Server 2.34
CCProxy Telnet Service Ready
ESMTP TABS Mail Server for Windows NT
FreeFloat Ftp Server (Version 1.00)
IMAP4rev1 MDaemon 9.6.4 ready
MailEnable Service, Version: 0-1.54
NetDecision-HTTP-Server 1.0
PSO Proxy 0.9
SAMBAR
Sami FTP Server 2.0.2
Spipe 1.0
TelSrv 1.5
WDaemon 6.8.5
WinGate 6.1.1
Xitami
YahooPOPs! Simple Mail Transfer Service Ready
我们将会把我们更新后的代码放到函数checkVulns()中。在这里我们将用只读模式(’r’)打开文本文件。然后使用函数readlines()遍历文件的每一行,对每一行,我们把他与我们的提示信息作比较,注意我们必须用方法.strip(‘\r’)去掉每行的回车符,如果发现一对匹配了,我们打印出有漏洞的服务信息。
def checkVulns(banner):
f = open("vuln_banners.txt",'r')
for line in f.readlines():
if line.strip('\n') in banner:
print "[+] Server is vulnerable: "+banner.strip('\n')
SYS模块
内置的sys模块提供访问和维护python解释器的能力。这包括了提示信息,版本,整数的最大值,可用模块,路径钩子,标准错误,标准输入输出的定位和解释器调用的命令行参数。你能够在python的在线模块文档上找到更多与此相关的信息(http://docs.python.org/library/sys)。在创建python脚本时与sys模块交互会十分有用。我们可以,例如,想在程序运行时解析命令行参数。
思考下我们的漏洞扫描器,如果我们想要把文本文件的名字作为命令行参数传递会怎么样呢?领标sys.argv包含了全部的命令含参数。第一个索引sys.argv[0]包含了python脚本解释器的名称。列表中剩余的元素包含了以下全部的命令行参数。因此,如果我们只想传递附加的参数,sys.argv应该包含两个元素。
import sys
if len(sys.argv)==2:
filename = sys.argv[1]
print "[+] Reading Vulnerabilities From: "+filename
运行我们的代码片段,我们看到代码成功的解析了命令行参数并把他打印到了屏幕上。你可以花时间来学习下全部的sys模块提供给程序设计师的丰富的功能。
programmer$ python vuln-scanner.py vuln-banners.txt
[+] Reading Vulnerabilities From: vuln-banners.txt
OS模块
内置的OS模块提供了丰富的与MAC,NT,Posix等操作系统进行交互的能力。这个模块允许程序独立的与操作系统环境。文件系统,用户数据库和权限进行交互。思考一下,比如,上一章中,用户把文件名作为命令行参数来传递。他可以验证文件是否存在以及当前用户是否有权限都这个文件。如果失败,他将显示一条信息,来显示一个适当的错误信息给用户。
import sys
import os
if len(sys.argv) == 2:
filename = sys.argv[1]
if not os.path.isfile(filename):
print '[-] ' + filename + ' does not exist.'
exit(0)
if not os.access(filename, os.R_OK):
print '[-] ' + filename + ' access denied.'
exit(0)
print '[+] Reading Vulnerabilities From: ' + filename
为了验证我们的代码,我们尝试读取一个不存在的文件,该文件使我们的程序打印出了错误信息,接下来,我们创建这个文件,我们的脚本成功的读取了他。最后我们限制了权限,我们的脚本正确的打印了拒绝访问的消息。
programmer$ python test.py vuln-banners.txt
[-] vuln-banners.txt does not exist.
programmer$ touch vuln-banners.txt
programmer$ python test.py vuln-banners.txt
[+] Reading Vulnerabilities From: vuln-banners.txt
programmer$ chmod 000 vuln-banners.txt
programmer$ python test.py vuln-banners.txt
[-] vuln-banners.txt access denied
现在我们可以重新组合漏洞扫描程序的各个零件。不用担心他会错误终止或是在执行时缺少使用线程的能力或是更好的分析命令行的能力,我们将会在后面的章节继续改进这个脚本
Import socket
import os
import sys
def retBanner(ip, port):
try:
socket.setdefaulttimeout(2)
s = socket.socket()
s.connect((ip, port))
banner = s.recv(1024)
return banner
except:
return
def checkVulns(banner, filename):
f = open(filename, 'r')
for line in f.readlines():
if line.strip('\n') in banner:
print '[+] Server is vulnerable: ' +\
banner.strip('\n')
def main():
if len(sys.argv) == 2:
filename = sys.argv[1]
if not os.path.isfile(filename):
print '[-] ' + filename +\
' does not exist.'
exit(0)
if not os.access(filename, os.R_OK):
print '[-] ' + filename +\
' access denied.'
exit(0)
else:
print '[-] Usage: ' + str(sys.argv[0]) +\
' <vuln filename>'
exit(0)
portList = [21,22,25,80,110,443]
for x in range(147, 150):
ip = '192.168.95.' + str(x)
for port in portList:
banner = retBanner(ip, port)
if banner:
print '[+] ' + ip + ': ' + banner
checkVulns(banner, filename)
if __name__ == '__main__':
main()
$("img").load(function(){ if($(this).attr("width")>640) $(this).attr("width",640); });