1-编写一个端口扫描器

任何一个靠谱的网络攻击都是起步于侦察的。
攻击者必须在挑选并确定利用目标中的漏洞之前找到目标在哪里有漏洞。
编写一个扫描目标主机开放的TCP端口的侦察小脚本。
为了与TCP端口进行交互,我们要先建立TCP套接字。

Python提供了BSD套接字的接口。
BSD套接字提供了一个应用编程接口,使程序员能编写在主机之间进行网络通信的应用程序。
通过一系列套接字API函数,我们可以创建、绑定监听、连接,或在TCP/IP套接字上发送数据。

大多数能访问互联网的应用使用的都是TCP协议。
例如,在目标组织中,Web服务器可能位于TCP80端口,电子邮件服务器在TCP25端口,FTP服务器在TCP21端口。
要连接目标组织中的任一服务器,攻击者必须知道与服务器相关联的IP地址和TCP端口。

所有成功的网络攻击一般都是从端口扫描来开序幕的。
有一种类型的端口扫描会向一系列常用的端口发送TCP SYN数据包,并等待TCP ACK响应——这能让我们确定这个端口是开放的。
与此相反,TCP连接扫描是使用完整的三次握手来确定服务器或端口是否可用的。

TCP全连接扫描

开始编写自己用的TCP全连接扫描来识别主机的TCP端口扫描器。
要导入Python的BSD套接字API实现。
套接字API会为我们提供一些在实现TCP端口扫描程序时有用的函数。
要深入的了解请查看文档.

为了更好的了解TCP端口扫描器的工作原理,我们将脚本分成五个独立的步骤,分别为它们编写Python代码。
首先输入一个主机名和用逗号分割的端口列表,并予以扫描。
接下来将主机转换成IPv4互联网地址。对列表中的每个端口,我们都会链接目标地址和该端口。
最后,为了确定在该端口上运行什么服务,我们将发送垃圾数据并读取由具体应用发回的Banner。

在第一步中,从用户那里获得主机名和端口。
为了做到这一点,我们在程序中使用optparse库解析命令行参数。
调用optparse.OptionParser([usage message])会生成一个参数解析器(option parser)类的实例。
接着,在parser.add_option中指定这个脚本具体要解析哪个命令行参数。

e.g. 一个快速解析要扫描的目标主机名和端口的方法

import optparse

parser = optparse.OptionParser('usage %prog -H <target host> -p <target port>')
parser.add_option('-H', dest='tgtHost', type='string', help='specify target host')
parser.add_option('-p', dest='tgtPort', type='string', help='specify target port')

(options, args) = parser.parse_args()
tgtHost = options.tgtHost
tgtPort = options.tgtPort
if tgtHost == None or tgtPort == None:
    print(parser.usage)
    exit(0)
else:
    print(tgtHost)
    print(tgtPort)

接下来我么要生成俩个个函数: connScan和portScan.
portScan函数以参数的形式接受主机名和目标端口列表。
它首先会尝试用gethostbyname()函数确定主机名对应的IP地址。
接下来,它会使用connScan函数输出主机名字(或IP地址),并使用connScan()函数尝试逐个连接我们要连接的每个端口。
connScan函数接受两个参数:tgtHost和tgtPort,它会去尝试建立与目标主机端口的连接。
如果成功,connScan将打印出一个端口开放的消息。如果不成功,它会打印出端口关闭的消息。

from socket import *


def connScan(tgtHost, tgtPort):
    try:
        connSkt = socket(AF_INET, SOCK_STREAM)
        connSkt.connect((tgtHost, tgtPort))
        print('[+] %d/tcp open' % tgtPort)
        connSkt.close()
    except Exception:
        print('[-] %d/tcp closed' % tgtPort)


def portScan(tgtHost, tgtPorts):
    try:
        tgtIP = gethostbyname(tgtHost)
    except:
        print("[-] Cannot resolve '%s': Unknown host" % tgtHost)
        return

    try:
        tgtName = gethostbyaddr(tgtHost)
        print("\n[+] Scan Results for: " + tgtName[0])
    except:
        print("\n[-] Scan Results for: " + tgtIP)

    setdefaulttimeout(1)
    for tgtPost in tgtPorts:
        print("Scannint port " + tgtPost)
        connScan(tgtHost, tgtPost)

抓取应用的Banner

为了抓取目标主机上应用的Banner,必须现在connScan函数中插入一些新增的代码。
找到开放端口后,我们向它发送一个数据传并等待响应。
根据收集到的响应,就能推断出目标主机和端口上运行的应用。

from socket import *


def connScan(tgtHost, tgtPort):
    try:
        connSkt = socket(AF_INET, SOCK_STREAM)
        connSkt.connect((tgtHost, tgtPort))
        connSkt.send('Hello Python\r\n')
        results = connSkt.recv(1024)
        print('[+] {}/tcp open'.format(tgtPort))
        print('[+] ' + str(results))
        connSkt.close()
    except:
        print('[-] {}/tcp closed'.format(tgtPort))


def portScan(tgtHost, tgtPorts):
    try:
        tgtIP = gethostbyname(tgtHost)
    except:
        print("[-] Cannot resolve '{}': Unknown host".format(tgtHost))
        return

    try:
        tgtName = gethostbyaddr(tgtIP)
        print("\n[+] Scan Results for: " + tgtName[0])
    except:
        print('\n[+] Scan Results for: ' + tgtIP)

    setdefaulttimeout(1)
    for tgtPort in tgtPorts:
        print('Scanning port ' + tgtPort)
        connScan(tgtHost, tgtPort)


def main():
    import optparse
    parser = optparse.OptionParser('usage %prog -H <target host> -p <target port>')
    parser.add_option('-H', dest='tgtHost', type='string', help='specify target host')
    parser.add_option('-p', dest='tgtPort', type='string', help='specify target port')

    (options, args) = parser.parse_args()
    tgtHost = options.tgtHost
    tgtPorts = (options.tgtPort).split(' ')
    if tgtPorts[0] == None or tgtHost == None:
        print('[-] You must specify a target host and port[s].')
        exit(0)
    portScan(tgtHost, tgtPorts)


if __name__ == '__main__':
    main()
<<<<<<< HEAD
=======

>>>>>>> origin/master

线程扫描

根据套接字中timeout变量的值,每扫描一个套接字都会花费几秒钟。
这看上去微不足道,但如果我们要扫描多个主机端口,时间总量就会成倍增加。
理想情况下,我们希望能同时扫描多个套接字,而不是一个一个地进行扫描。
这时,我们必须引入Python线程,线程是一种能提供这类同时执行多项任务的方法。
具体到我们这个扫描器,我们要修改的是portScann()函数中迭代循环里的代码。

from threading import *
for tgtPort in tgtPorts:
    t = Thread(target=connScan, args=(tgtHost, int(tgtPort)))
    t.start()

这让我们的速度有了显著的提升,但这又有一个缺点。connScan()函数会在屏幕上打印一个输出。
如果多个线程同时打印输出,就可能会出现乱码和失序。
为了让一个函数获得完整的屏幕控制前,我们需要使用一个信号量(semaphore)。
一个简单的信号量就能阻止其他线程的运行。
注意,在打印输出前,我们用screenLock。acquire()执行一个加锁操作。
如果信号量还没被锁上,线程就有权继续运行,并输出到屏幕上。
如果信号量已经被锁定,我们只能等待,直到持有信号量的线程释放信号量。
通过利用信号量,我们现在能够确保在任何给定时间上只有一个线程可以打印屏幕。
在异常处理代码中,位于finally关键字的是在终止阻塞(其他线程)之前需要执行的代码。

from threading import *
from socket import *

screenLock = Semaphore(value=1)
def connScan(tgtHost, tgtPOrt):
    try:
        connSkt = socket(AF_INET, SOCK_STREAM)
        connSkt.connect((tgtHost, tgtPOrt))
        connSkt.send('something')
        results = connSkt.recv(1024)
        screenLock.acquire()
        print('[+] {}/tcp open'.format(tgtPOrt))
        print('[-] ' + str(results))
    except:
        screenLock.acquire()
        print('[-] {}/tcp closed'.format(tgtPOrt))
    finally:
        screenLock.release()
<<<<<<< HEAD
        connSkt.close()        
=======
        connSkt.close()
        
>>>>>>> origin/master

把其他所有的函数放入同一个脚本中,并添加一些参数解析代码,就有了最终的端口扫描脚本。

#!/usr/bin/python3
# -*- coding: utf-8 -*-
from socket import *
from threading import *

screenLock = Semaphore(value=1)


def connScan(tgtHost, tgtPort):
    try:
        connSkt = socket(AF_INET, SOCK_STREAM)
        connSkt.connect((tgtHost, tgtPort))
        connSkt.send('some information\r\n')
        results = connSkt.recv(1024)
        screenLock.acquire()
        print('[+] {}/tcp open'.format(tgtPort))
        print('[+] ' + str(results))
    except:
        screenLock.acquire()
        print('[-] {}/tcp closed'.format(tgtPort))
    finally:
        screenLock.release()
        connSkt.close()

def portScan(tgtHost, tgtPorts):
    try:
        tgtIP = gethostbyname(tgtHost)
    except:
        print("[-] Cannot resolve '{}': Unknown host".format(tgtHost))
        return

    try:
        tgtName = gethostbyaddr(tgtIP)
        print("\n[+] Scan Results for: " + tgtName[0])
    except:
        print('\n[+] Scan Results for: ' + tgtIP)

    setdefaulttimeout(1)
    for tgtPort in tgtPorts:
        t = Thread(target=connScan, args=(tgtHost, tgtPort))
        t.start()

def main():
    import optparse
    parser = optparse.OptionParser('usage %prog -H <target host> -p <target port>')
    parser.add_option('-H', dest='tgtHost', type='string', help='specify target host')
    parser.add_option('-p', dest='tgtPort', type='string', help='specify target port[s] separated by comma')

    (options, args) = parser.parse_args()

    tgtHost = options.tgtHost
    tgtPorts = str(options.tgtPort).split(', ')

    if tgtPorts[0] == None or tgtHost == None:
        print(parser.usage)
        exit(0)

    portScan(tgtHost, tgtPorts)


if __name__ == '__main__':
    main()

使用NMAP端口扫描代码

前面的例子是快速编写能进行TCP链接扫描的一个脚本。
这可能还不能够用,因为我们可能还要进行其他类型的扫描,例如,由Nmap工具包提供的ACK、RST、FIN或SYN-ACK扫描等。
实际的工业标准——Namp端口扫描工具包提供了大量功能。
这也引出一个问题,为什么不直接使用Nmap?这就是Python真正美妙的地方。
虽然Fyodor Vaskovich编写的Nmap中也能使用C和Lua编写的脚本,但是Nmap还能被非常好的很合到Python中。
Nmap可以生成基于XML的输出。这让我们能在Python脚本中使用Nmap的全部功能。在编写代码之前,你必须安装Python-Nmap。

安装好Python-Nmap之后,我们就可以将Nmap导入到现有的脚本中。
创建一个PortScanner()类对象,这使我们能用这个对象完成扫描操作。
PortScanner类有一个scan()函数,它可将目标和端口的列表作为参数输入,并对它们进行基本的Nmap扫描。
另外还可以把目标主机的地址/端口放入数组中备查,并打印出端口状态。

def nmapScan(tgtHost, tgtPort):
    import nmap
    nmScan = nmap.PortScanner()
    nmScan.scan(tgtHost, tgtPort)
    state = nmScan[tgtHost]['tcp'][int(tgtPort)]['state']
    print('[+] ' + tgtHost + ' tcp/' + tgtPort + " " + state)

def main():
    import optparse
    parser = optparse.OptionParser('usage %prog -H <target host> -p <target port>')
    parser.add_option('-H', dest='tgtHost', type='string', help='specify target host')
    parser.add_option('-p', dest='tgtPort', type='string', help='specify target port[s] separated by comma')

    (options, args) = parser.parse_args()

    tgtHost = options.tgtHost
    tgtPorts = str(options.tgtPort).split(', ')

    if tgtPorts[0] == None or tgtHost == None:
        print(parser.usage)
        exit(0)

    for tgtPort in tgtPorts:
        nmapScan(tgtHost, tgtPort)

if __name__ == '__main__':
    main()

more

  • TCP SYN SCAN —— 也称为半开放扫描,这种类型的扫描发送一个SYN包,启动一个TCP会话,并等待响应的数据包。
    如果收到的是一个reset包,表明端口是关闭的,而如果收到的是一个SYN/ACK包,则表示响应的端口是开放的。
  • TCP NULL SCAN —— NULL扫描把TCP头中的所有标志位都设为NULL。如果受到的是一个RST包,则表示响应的端口是关闭的。
  • TCP FIN SCAN —— TCP FIN 扫描发送一个表示拆除一个活动的TCP连接的FIN包,让对方关闭连接。
    如果收到了一个RST包,则表示相应的端口是关闭的。
  • TCP XMAS SCAN —— TCP XMAS扫描发送PSH、FIN、URG和TCP标志为被设为1的数据包。
    如果受到了一个RST包,则表示响应的端口是关闭的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容