转自:http://www.secpulse.com/archives/35893.html
都说Python大法好,作为一名合格的安全从业人员,不会几门脚本语言都不好意思说自己是从事安全行业的。
而Python是最容易入门且使用最顺手的脚本语言,为了不引起程序员世界大战,我们不说Python是世界上最好的语言,没有之一。
这是《从零单排之玩转Python安全编程》的第二篇。第一篇参见安全脉搏
本教程继续展示一些基本的Python脚本概念。我们把我们的代码转换成一个脚本、函数、类和系统模块。
Python的脚本结构:
下面是可用于启动Python脚本(script)。
开始 我们通过“#!/usr/bin/env python”来告诉操作系统(OS)使用哪个编译器(interpreter) 。
然后,我们声明一个主功能 "def main()" 和最后2行代码来让 main()先运行。您可以在脚本中定义的其他功能,使代码更容易理解和修改:
#!/usr/bin/python
import ,
def myFunction():
def main():
myFunction()
if __name__=="__main__":
main()
功能Functions:
利用函数的一种常用的方法是有一段代码执行一些动作和返回输出。下面是一些基本的代码演示这个概念:
# Declare function/setup logic
def MyFunction:
...do work...
return output
#Call the function from main:
def main():
output = MyFunction(input)
类(classes):
Python类(classes)一开始可能会显得混乱,因为它是一个不同的方式来设计你的代码。
如果你把握了定义的概念,那么你可以把类(class)当成的数据(data)和定义(definition)的逻辑分组(logical grouping)。
这样类将具有某些属性(attribute)和与之相关联的方法(method)。当你定义以后可以创建该类将继承的属性和与之关联的方法的对象类,这被称为面向对象编程(object-oriented programming)。
如果这个概念对于你来说很混乱,我建议你不要太在意类(classes)。你其实并不需要利用类(classes),但它可以使你的代码少冗余。
下面我们将使用“类”关键词来定义一类新的“域”(Domain) 。
有各种方法在类代码可供选择( various methods within the class code are available)当你需要一个Domain类型的对象(object of type Domain)。
>>> import os
>>> class Domain:
... def __init__(self, domain, port, protocol):
# Stores the variabled passed inside two variables
... self.domain=domain
... self.port=port
... self.protocol=protocol
# Defines a method to build a URL
... def URL(self):
... if self.protocol == 'https':
... URL = 'https://'+self.domain+':'+self.port+'/'
... if self.protocol == 'http':
... URL = 'http://'+self.domain+':'+self.port+'/'
... return URL
# Sets up a method to lookup resolve domain to IP using host command via os.system
... def lookup(self):
... os.system("host "+self.domain)
...
>>>
>>> domain=Domain('google.com', '443', 'https')
>>>
>>> dir(domain)
['URL', '__doc__', '__init__', '__module__', 'ip', 'lookup', 'port', 'protocol']
>>> domain.URL()
'https://8.8.8.8:443/'
>>> domain.ip
'8.8.8.8'
>>> domain.port
'443'
>>> domain.protocol
'https'
>>> domain.lookup()
google.com has address 74.125.228.233
google.com has address 74.125.228.227
google.com has address 74.125.228.232
如上图 你可以看到在实例化域名类的一个实例后,你能运行类中的方法。
同样,这个概念可以在第一会造成混淆,尤其是当你刚刚掌握Python和编程的时候。
你可以尝试在一个你已经写好的Python脚本里实现一个新的类,我发现这可能是开始把握概念的有效途径。
处理命令行参数“sys”:
这个介绍里的最后一个模块触及的是sys模块。这使您可以在读取 CLI 给定的参数(argument),并将其拉进变量(variable)在脚本中。
语法是非常简单的, sys.agrv[0]就是脚本名称,并在命令行(command line)中给定每个参数(argument)后,每个参数会被分配一个号码。
下面是一个简单的例子:
import sys
script = sys.argv[0]
ip = sys.argv[1]
port = sys.argv[2]
print "[+] The script name is: "+script
print "[+] The IP is: "+ip+" and the port is: "+port
当这种快速脚本调用在命令行中,加上几个参数就产生以下的输出:
~$ python sys.py 8.8.8.8 53
[+] The script name is: sys.py
[+] The IP is: 8.8.8.8 and the port is: 53
继续探索更多的Python模块和内置的功能,因为他们会允许你解决问题容易得多,你开始编写更复杂的代码。
-----------------------------------------------------------------------------------------
接下来的教程将介绍建立网络连接与Python通过构建一个基本的端口扫描器的概念。
在本教程中,我们将演示如何通过建立一个基本的端口扫描(port scanner)程序,使与Python的网络连接。
我们将做的是建立网络套接字连接一遍又一遍的使用IP/端口组合。为了做到这一点,我们将引入一个新的概念,循环(for loop):
>>>
>>> for port in range(1000,1024):
... print "[+] The port is: "+str(port)
...
[+] The port is: 1000
[+] The port is: 1001
[+] The port is: 1002
[+] The port is: 1003
[+] The port is: 1004
[+] The port is: 1005
[+] The port is: 1006
[+] The port is: 1007
[+] The port is: 1008
[+] The port is: 1009
[+] The port is: 1010
[+] The port is: 1011
[+] The port is: 1012
[+] The port is: 1013
[+] The port is: 1014
[+] The port is: 1015
[+] The port is: 1016
[+] The port is: 1017
[+] The port is: 1018
[+] The port is: 1019
[+] The port is: 1020
[+] The port is: 1021
[+] The port is: 1022
[+] The port is: 1023
请注意,for loop以上的代码片段有缩进。通常人们缩进2个空格或制表符,不过只要你在整个脚本一致这些都无所谓。
为了做一个简单的端口扫描程序(port scanner),我们将更换打印语句(print statement)为一个代码片段(code snippet)来建立套接字连接(socket connection)。
下面的代码演示了如何使用内置的socket 模块(built-in socket module)进行套接字连接(socket connection):
>>>
>>> import socket
>>>
>>> s = socket.socket()
>>> s.connect(('127.0.0.1', 22))
>>> s.send('Primal Security \n')
17
>>> banner = s.recv(1024)
>>> print banner
OpenSSH
上面我们导入了socket 模块(socket module)以及调用了connect()功能(function)来连接到给定的IP地址和端口号。
这将建立一个TCP连接( SYN / SYN - ACK / ACK ),我们实际上是使用send()功能(function)来将数据发送到指定的服务,并使用recv()来打印响应(response)。
现在,如果端口未打开socket 将抛出一个异常(exception):
>>>
>>> s.connect(('127.0.0.1', 23))
Traceback (most recent call last):
File "", line 1, in ?
File "", line 1, in connect
socket.error: (111, 'Connection refused')
这可以以多种方式来解决。现在,我们将用一种非常简单的方法,使用“尝试(try)/除外(except)”循环(loop),并通过异常(exception)。
>>>
>>> try:
... s.connect(('127.0.0.1', 23))
... except: pass
...
>>>
请注意没有“error!” 这是一个很好的方式来让你的代码看起来像是它们在工作O(∩_∩)O~。现在,让我们使用这些概念,并做出一个快速的环路(for loop)端口(port)扫描器(scanner):
>>>
>>> for port in range(20,25):
... try:
... print "[+] Attempting to connect to 127.0.0.1:"+str(port)
... s.connect(('127.0.0.1', port))
... s.send('Primal Security \n')
... banner = s.recv(1024)
... if banner:
... print "[+] Port "+str(port)+" open: "+banner
... s.close()
... except: pass
...
17
[+] Attempting to connect to 127.0.0.1:20
[+] Attempting to connect to 127.0.0.1:21
[+] Attempting to connect to 127.0.0.1:22
[+] Port 22 open: OpenSSH
[+] Attempting to connect to 127.0.0.1:23
[+] Attempting to connect to 127.0.0.1:24
[+] Attempting to connect to 127.0.0.1:25
上面我们展示的“试(try)/除外(except)”循环(loop)的基本用法来传递(pass) 端口(port)关闭时被socket (socket)抛出的异常(exception)。我们还展示了如何利用一个基本的条件语句 "if" 来只尝试打印开放的端口,如果该端口回应我们的探头(probe)。另一种方法来创建一个端口扫描器是 定义一个列表来包括你想用数组(array)来扫描的端口,然后循环(loop)通过这个数组(array):
>>>
>>> ports = [22, 445, 80, 443, 3389]
>>> for port in ports:
... print port
...
22
445
80
443
3389
>>>
如果我们想一次性处理多个主机(hosts),我们将利用一个嵌套循环(nested for loop)。这将涉及外层(outter layer)环路(for loop)来通过主机循环然后内循环(inner for loop)通过该端口(port)循环。下面是一个基本的例子来讲述如何利用嵌套for循环来建立一个稍微复杂一些的扫描器:
>>>
>>> hosts = ['127.0.0.1', '192.168.1.5', '10.0.0.1']
>>>
>>> ports = [22, 445, 80, 443, 3389]
>>>
>>> for host in hosts:
... for port in ports:
... try:
... print "[+] Connecting to "+host+":"+str(port)
... s.connect((host, port))
... s.send('Primal Security \n')
... banner = s.recv(1024)
... if banner:
... print "[+] Port "+str(port)+" open: "+banner
... s.close()
... except:pass
...
[+] Connecting to 127.0.0.1:22
[+] Port 22 open: OpenSSH
[+] Connecting to 127.0.0.1:445
[+] Connecting to 127.0.0.1:80
[+] Connecting to 127.0.0.1:443
[+] Connecting to 127.0.0.1:3389
[+] Connecting to 192.168.1.5:22
[+] Connecting to 192.168.1.5:445
[+] Connecting to 192.168.1.5:80
[+] Connecting to 192.168.1.5:443
[+] Connecting to 192.168.1.5:3389
[+] Connecting to 10.0.0.1:22
[+] Connecting to 10.0.0.1:445
[+] Connecting to 10.0.0.1:80
[+] Connecting to 10.0.0.1:443
[+] Connecting to 10.0.0.1:3389
正如你可以通过输出看到,它循环阵列的主机(hosts array),并尝试端口阵列(port array)中的每个端口然后再移动到下一个主机。对于最终的端口扫描器,你可能会想要要修改打印报表来只打印开放的那些端口。
在一天结束时,你会发现Nmap仍然是端口扫描一个更好的选择,但在以后的博客帖子内 我们将建立在这些概念来完成一些更实际的使用案例。花一些时间来探索socket 模块(socket module) "dir(socket)" 内 提供的各种功能。