作者名:Demo不是emo
主页面链接:主页传送门
创作初心:舞台再大,你不上台,永远是观众,没人会关心你努不努力,摔的痛不痛,他们只会看你最后站在什么位置,然后羡慕或鄙夷
座右铭:不要让时代的悲哀成为你的悲哀
专研方向:网络安全,数据结构每日emo:总想把自己变得更好再来找你,却不知你已越走越远
如果你以前没有 Python 编程经验,这篇博客将带你浏览一下 Python 的背景,语 法,函数,迭代器等语法问题,如果你已经有 Python 的编程经验,可以跳过这 一章。以后的章节将不会介绍更多的语言细节,你可以根据兴趣自行学习。
目录
一、建立 Python 开发环境
1、下载环境
2、安装第三方库
3、解释型 python与交互型 python
[1]、解释型
[2]、交互型
二、Python 语言简介
三、变量,字符串,列表,字典介绍
1、变量
2、字符串
3、列表
4、字典
四、使用用网络,迭代器,异常处理,模块等
1、网络
2、条件选择
3、异常处理
4、函数
5、迭代
6、文件I/O
7、SYS 模块
8、OS模块
一、建立 Python 开发环境
1、下载环境
Python 的下载网站(http://www.python.org/download/)提供了 Python 在 Windows,Mac OS X 和 Linux 上的安装包。
如果您运行的是 Mac OS X 或 Linux,Python 的解释器已经预先安装在了系统上。
安装包为程序开发者提供 了 Python 解释器,标准库和几个内置模块。 Python 标准库和内置模块提供的 功能范围广泛,包括内建的数据类型,异常处理,数字和数学模块,文件处理 功能,如
加密服务,与操作系统互操作性,网络数据处理,并与 IP 协议交互,还包括许 多其他有用模块。同时,程序开发者可以很容易地安装任何第三方软件包。 第三方软件包的完整列表可在(http://pypi.python.org/pypi/) 上看到
2、安装第三方库
在后面,我们将利用 python 的 python-nmap 包来处理 NMAP扫描工具 的结果。 下面的例子描述了如何下载和安装 python-nmap 包,这种方法也适用与安装其他包
因为这里我用的是kali系统,就用kali系统来演示了,也推荐大家使用kali系统,毕竟是渗透测试专用系统,后续我们编写的脚本很多都需要配合里面的渗透测试工具。
kali系统会预安装pip,所以我们直接执行下面这个代码就可以了
pip install python-nmap
结果如下就是安装成功了
后续在编写脚本时有需要安装的包我都会提前说,大家现在只要知道怎么安装就可以了。
最近几年 python 源代码已经延伸成了 2.x 和 3.x 两个分支。一般情况我用的都是python3.10,如果有特别的情况我会提前说,(版本不用跟我相同,都是3.x即可)。
3、解释型 python与交互型 python
解释型与交互型的区别就是非交互式与交互式的区别,简单举个例子
[1]、解释型
其实就相当于文件生成后再调用解释器来解释,即执行里面的代码
例如我们新建一个hello.py文件,内容如下
print("hello world!")
此时文件已经生成,再在该目录终端下执行下面这个命令,就可以调动解释器执行其中的代码
python hello.py
此时终端就会呈现出输出结果,输出“hello world!”,
[2]、交互型
此外,python 具有交互能力,程序设计师可以调用 python 解释器,并直接与解释 器“交流”。就相当于一直调用解释器,每输入一次命令后交互解释器就会立即执行
在终端输入python即可进入交互型python,此时输入的命令就会被直接执行,效果如下
二、Python 语言简介
在以后的内容中,我们会讲解变量,数据类型,字符串,复杂的数据结构,网络, 选择,循环,文件处理,异常处理,与操作系统进行交互。为了显示这一点,我们将构建一个简单的 TCP 类型的漏洞扫描器,读取来自服务的提示消息,并把他们与已知的存在漏洞的服务版本做比较,作为一个有经验的程序设计师,你可能会发现一些最 初的示例代码的设计非常难看,事实上,我们希望你能在代码基础上进行发展, 使他变得优雅。
三、变量,字符串,列表,字典介绍
那么,让我们从任何编程语言的基础——变量开始吧!
1、变量
在 python 中,变量对应的数据存储在内存中,这种在内存中的位置可以存储不同 的值,如整型,实数,布尔值,字符串,或更复杂的数据结构,例如列表或字典。在 下面的代码中,我们定义一个存储整形的变量和一个存储字符串的提示消息。
port = 21
banner = "FreeFloat FTP Server"
print("[+] Checking for "+banner+" on port "+str(port))
为了把 这两个变量连接到一个字符串中,我们必须用 str()函数将port转换为字符型 ,结果如下
[+] Checking for FreeFloat FTP Server on port 21
怎么样,这个语句相信渗透的小伙伴都不会陌生吧,就是在这样来的,我们后面呢还会用到。
此外python定义变量与其他语言不同,它不必声明变量的类型,相反,python 解释器决定了变量类型何在内存中为他保留的空间的 大小。
思考下面的例子,我们正确的声明了一个字符串,一个整数,一个列表和一个 布尔值,解释器都自动的正确的识别了每个变量的类型(type函数用于返回变量的类型)。
banner = "FreeFloat FTP Server" # 字符串类型
type(banner)
# <type 'str'>
port = 21 # 整型
type(port)
# <type 'int'>
portList=[21,22,80,110] # 列表类型
type(portList)
# <type 'list'>
portOpen = True # 布尔类型
type(portOpen)
# <type 'bool'>
2、字符串
在 python 中字符串模块提供了一系列非常强大的字符串操作方法。阅读 http://docs.python.org/library/string.html 可以查看完整的用法。
让我们来看几个常用的函数。思考下面这些函数的用法,
upper() 方法将字符串中 的小写字母转为大写字母,
lower()方法转换字符串中所有大写字母为小写,
replace(old,new)方法把字符串中的 old(旧字符串) 替换成 new(新字符串),
find()方法 检测字符串中是否包含指定的子字符串。
比如下面的代码
banner = "FreeFloat FTP Server"
print(banner.upper())
# FREEFLOAT FTP SERVER
print(banner.lower())
# freefloat ftp server
print(banner.replace('FreeFloat','Ability'))
# Ability FTP Server
print(banner.find('FTP'))
# 10
3、列表
Python 的数据结构——列表,提供了一种存储一组数据的方式。这组数据里的元素可以是任意数据类型。另外,有一些内置的操作列表的方法,例如元素的添加,删除,插入,弹出,获取索引,排序,计数,排序和反转
请看下面的例子,一个程序通过使用 append()添加元素来建立一个列表,打印项目,然后在 再次输出前给他们排序。程序设计师可以找到特殊元素的索引(例如样例中的 80),此外,指定的元素也可以被移动。
portList = []
portList.append(21)
portList.append(80)
portList.append(443)
portList.append(25)
print(portList)
# [21, 80, 443, 25]
portList.sort()
print(portList)
# [21, 25, 80, 443]
pos = portList.index(80)
print("[+] There are "+str(pos)+" ports to scan before 80.")
# [+] There are 2 ports to scan before 80.
portList.remove(443)
print(portList)
# [21, 25, 80]
cnt = len(portList)
print("[+] Scanning "+str(cnt)+" Total Ports.")
# [+] Scanning 3 Total Ports.
4、字典
Python 的数据结构——字典,提供了一个可以存储任何数量 python 对象的哈希表。 字典的元素由键和值组成,让我们继续用我们的漏洞扫描器的例子来讲解 python 的字 典。当扫描指定的 TCP 端口时,用字典包含每个端口对应的常见的服务名会很有用。 建立一个字典,我们能查找像 ftp 这样的键并返回端口关联的值 21。
当我们建立一个字典时,每一个键和他的值被冒号隔开,同时,我们用逗号分隔元素。 注意,.keys()这个函数将返回字典的所有键的列表,.items()这个方法将返回字典的元素的列表。接下来,我们验证字典是否包含了指定的键(ftp),伴随着键,值 21 返回了。
services = {'ftp':21,'ssh':22,'smtp':25,'http':80}
services.keys()
# ['ftp', 'smtp', 'ssh', 'http']
services.items()
# [('ftp', 21), ('smtp', 25), ('ssh', 22), ('http', 80)]
services.has_key('ftp')
# True
services['ftp']
# 21
print("[+] Found vuln with FTP on port "+str(services['ftp']))
#Found vuln with FTP on port 21
四、使用用网络,迭代器,异常处理,模块等
1、网络
套接字模块(socket)提供了一个可以使 python 建立网络连接的库,用于在客户端程序和服务器程序之间创建连接.
让我们快速的编写一个获取目标端口信息的脚本,连接到特定 IP 地址和端口后,我们的脚本将打印提示信息,之后, 我们使用 connect()函数连接到 IP 地址和端口。一旦连接成功,就可以通过套接字进行读写。我们把获取的结果存到一个变量中,然后打印到服务器。
import socket # 引入套接字模块
socket.setdefaulttimeout(2) # 经过两秒还未连接到,就进入下一操作
s = socket.socket() # 创建套接字
s.connect(("192.168.95.148",21)) # 初始化tcp连接
ans = str(s.recv(1024)) # 接收tcp数据,字节型数据返回,每次最多接收1024字节
print(ans)
# 220 FreeFloat Ftp Server (Version 1.00).
上面这段代码就可以实现一个简单的21端口服务探测功能,将ip换成你想探测的目标,就可以探测到目标的21端口的具体服务信息。
2、条件选择
像大多数编程语言一样,python 提供了条件选择的方式,通过 if 语句,建立一个逻辑表达式来判断选择的结果。继续写我们的脚本,我们想知道,是否指定的 FTP 服务 器是容易受到攻击的。要做到这一点,我们要拿我们的结果和已知的易受攻击的 FTP 服务器版本作比较。
这里给出了四个易受攻击的ftp服务版本,修改后的代码如下
import socket
socket.setdefaulttimeout(2)
s = socket.socket()
s.connect(("192.168.95.148",21))
ans = str(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 ans:
print("[+] 3CDaemon FTP Server is vulnerable.")
elif "Ability Server 2.34" in ans:
print("[+] Ability FTP Server is vulnerable.")
elif "Sami FTP Server 2.0.2" in ans:
print("[+] Sami FTP Server is vulnerable.")
else:
print("[-] FTP Server is not vulnerable.\n")
3、异常处理
即使一个程序设计师编写的程序语法正确,该程序仍然可能在运行或执行时发生错误。 考虑经典的一种运行错误——除以零。因为零不能做除数,所以 python 解释器显示一 条消息,把错误信息告诉程序设计师:该错误使程序停止执行。如下
可以看到程序直接结束了,并出现了报错内容,但是我们不想让程序结束,想让他报错后继续执行就应该引入我们的捕捉异常操作
捕捉异常可以使用try/except语句。
以下为简单的try....except...else的语法:
try:
<语句> #运行别的代码
except <名字>:
<语句> #如果在try部份引发了'name'异常
except <名字>,<数据>:
<语句> #如果引发了'name'异常,获得附加的数据
else:
<语句> #如果没有异常发生
例如下面这段代码,就能成功捕捉到异常,这样就可以起到程序错误但是
try:
print("1237/0=" + str(1237 / 0))
except:
print("error")
else:
print("right")
这样的话当1237/0仍然会被识别出错误,但是不会直接结束程序输出报错信息,而是会输出我们定义的error。
现在我们继续更新我们的脚本
import socket
socket.setdefaulttimeout(2)
s = socket.socket()
try:
s.connect(("192.168.95.149", 21))
except Exception as e:
print("[-] Error = "+str(e))
4、函数
在 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()用获得的信息来对服务器存在的漏洞进行判断
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
5、迭代
上一章中,你可能会发现我们几乎重复三次写了相同的代码,来检测三个不同的 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))
同样,我们可能需要遍历已知的端口列表来检查漏洞。代替一系列的数字,我们可以通过一个元素列表遍历他们。
portList = [21,22,25,80,110]
for port in portList:
print(port)
继续更新我们的脚本,更新后代码如下,随着程序有了遍历 IP 和端口的能力,我们也将个更新我们的漏洞检测脚本,现在, 我们的脚本将测试全部 254 个 IP 地址所提供的 telnet, SSH, smtp, http,
import socket
def retBanner(ip, port):
try:
socket.setdefaulttimeout(2)
s = socket.socket()
s.connect((ip, port))
banners = s.recv(1024)
return str(banners)
except Exception as e:
print("扫描错误: ", e)
return
def checkVulns(banners):
if 'FreeFloat Ftp Server (Version 1.00)' in banners:
print('[+] FreeFloat FTP Server is vulnerable.')
elif '3Com 3CDaemon FTP Server Version 2.0' in banners:
print('[+] 3CDaemon FTP Server is vulnerable.')
elif 'Ability Server 2.34' in banners:
print('[+] Ability FTP Server is vulnerable.')
elif 'Sami FTP Server 2.0.2' in banners:
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(106, 107):
ip = '192.168.1.' + str(x)
for port in portList:
banner = retBanner(ip, port)
if banner:
print('[+] ' + ip + ':' + str(port) + banner)
checkVulns(banner)
else:
print('[+] ' + ip + ":" + str(port) + " :do not have vuln")
if __name__ == '__main__':
main()
6、文件I/O
虽然我们的脚本已有了一些能帮助检测漏洞信息的 if 语句,但加进一个漏洞列表会更好,举个例子,假设我们有一个叫做 vuln_banners.txt 的文本文件。
在每一行该文件列出了具体的服务版本和已知的之前的漏洞,我们不需要构建一个庞大的 if 语句, 让我们读取这个文本文件,并用他来判断是否我们的提示信息存在漏洞。
就是下面这样包含很多历史漏洞的txt文件
我们将会把我们更新后的代码放到函数 checkVulns()中。在这里我们将用只读模式 (’r’)打开文本文件。然后使用函数 readlines()遍历文件的每一行,对每一行,我们把他 与我们的提示信息作比较,注意我们必须用方法.strip(‘\r’)去掉每行的回车符,如果发现一对匹配了,我们打印出有漏洞的服务信息。 更新后的checkVulns函数代码如下(此时历史漏洞文件在同一目录下)
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'))
7、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.argv[1]就是demo2.py
sys.argv[2]就是第一个参数a.txt
依次类推
8、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) # 正常读取文件
现在我们可以重新组合漏洞扫描程序的各个零件。不用担心他会错误终止或是在执 行时缺少使用线程的能力或是更好的分析命令行的能力,我们将会在后面的博客继续改进这个脚本,博主后续的脚本成品都会在github上更新