Skip to content

Latest commit

 

History

History
1033 lines (642 loc) · 59.5 KB

File metadata and controls

1033 lines (642 loc) · 59.5 KB

二、Python 脚本的基础知识

在开始编写第一个 Python 脚本之前,应该了解一些概念。现在学习这些项目将帮助您在将来更快地开发代码。这将提高您作为渗透测试人员的能力,或了解评估人员在创建实时自定义代码时正在做什么,以及您应该问什么问题。您还应该了解如何创建脚本以及您试图实现的目标。你经常会发现你的脚本会随着时间的推移而变形,目的可能会改变。这可能是因为您意识到对脚本的真正需求可能并不存在,或者存在用于特定功能的现有工具。

许多脚本编写者发现这令人沮丧,因为他们可能已经从事了很多时间的项目,您可能会发现该工具与更高级的工具有重复的功能。不要将此视为失败的项目,而应将此活动视为一次体验,在体验中,您学到了最初不知道的新概念和技术。此外,在开发将来可用于其他项目的代码片段时,请始终牢记这一点。

为此,请尝试干净地构建代码,用您正在做的事情对其进行注释,并将其模块化,以便在学习如何构建函数后,将来可以将它们剪切并粘贴到其他脚本中。本课程的第一步是从较高的层次描述计算机科学术语表,以便您能够理解未来的章节或其他教程。如果不理解这些基本概念,您可能会误解如何实现预期结果。

在运行本书中的任何脚本之前,我建议您在 git 存储库上运行安装脚本,将为您的 Kali 实例配置所有必要的库。该脚本可在找到 https://raw.githubusercontent.com/funkandwagnalls/pythonpentest/master/setup.sh

理解解释语言和编译语言之间的差异

Python 与 Ruby 和 Perl 一样,是一种解释语言,这意味着代码被转换成机器语言,并在脚本执行时运行。在运行之前需要编译的语言,例如 Cobol、C 或 C++,可以更有效和更快,因为它在执行之前编译,但也意味着代码通常不那么便携。由于编译后的代码是为特定环境生成的,因此当您必须在异构环境中移动时,它可能没有那么有用。

异构环境是一个具有多种系统类型和不同分布的环境。因此,可能有多个 Unix/Linux 发行版、Mac OS 和 Windows 系统。

只要解释器可用,解释代码通常具有可移植到不同位置的优点。因此,对于 Python 脚本,只要脚本不是为操作系统开发的,解释器是安装的,库是本地可用的,Python 脚本就应该可以工作。请始终记住,在一个环境中会有一些特性,在使用脚本之前,应该在类似的测试台上对它们进行彻底测试。

那么,为什么要学习 Python 而不是其他脚本语言呢?我不是在这里提出这个论点,原因是最好的评估人员使用他们正在评估的环境中可用的工具。您将构建对评估环境有用的脚本,Python 非常适合这样做,但当您获得对系统的访问权限时,最好使用您可用的脚本。

高度安全的环境可能会阻止您使用利用框架,或者评估规则也可能会这样做。当这种情况发生时,您必须查看系统上可用的内容,以便利用并向前推进。如今,新一代 Windows 系统已被 PowerShell 破坏。通常在当前的 Mac、Linux、Unix 和 Windows操作系统操作系统)中,您可以找到 Python 的版本,尤其是在开发环境中。在 web 服务器上,可以找到 Ruby、Python 或 Perl。在所有形式的操作系统上,都可以找到本机 shell 语言。它们提供了许多功能,但通常情况下,它们具有古老的语言结构,需要比其他脚本语言更多的代码行来完成相同的任务。这些 shell 语言的示例包括Bourne Reash shellBASH)、Korn shellKSH)、Windows 命令 shell 以及类似的语言。

在大多数攻击系统中,您会发现所有语言,因为大多数黑客笔记本电脑或黑客电脑使用多个具有多种操作系统的虚拟机虚拟机。旧的评估工具是用 Perl 编写的,因为该语言提供了当时其他解释语言无法提供的多种功能。较新的工具通常是用 Ruby 和 Python 创建的。事实上,今天创建的许多库都是为了提高这些语言的能力,特别是为了评估一个组织在被恶意参与者破坏时的潜在生存能力。

提示

请记住,您的 HackTop 有多个虚拟机,不仅可以为您提供攻击工具,还可以提供一个测试平台来安全地测试您的脚本。恢复到 HackTop 上 VM 的快照很容易,但告诉客户您为什么用未经测试的脚本损坏了他们的业务关键组件却很难。

编译语言并非没有价值;许多工具已经在 C、C++和 java 中创建。这些类型的工具包括 Burp Suite、Cain&Abel、DirBuster、Zed 攻击代理ZAP)、CSRFtester 等。您可能会注意到,这些工具中的大多数最初是在评估环境的早期生成的。随着系统的功能越来越强大,解释器的效率越来越高,我们看到更多的工具转向了被解释为与编译语言相反的语言。

那么这里的教训是什么呢?尽可能多地学习,以便在尽可能多的环境中操作。这样,当遇到障碍时,可以返回代码并编写脚本,以达到所需的访问级别。

巨蟒——好与坏

Python 是创建一段代码的最简单的语言之一,该代码可以实现具体的结果。事实上,Python 有一个本机交互式解释器,通过它,您只需在 CLI 上执行单词python即可直接测试代码。这将提供一个接口,在编写脚本之前,可以在其中测试代码的概念。此外,此接口允许测试人员不仅测试新概念,还可以将模块或其他脚本作为模块导入,并使用它们创建强大的工具。

Python 的这种测试功能不仅允许评估人员验证概念,而且还可以避免处理大量的调试器和测试用例来快速原型化攻击代码。这一点在交战中以及在确定特定利用序列是否能及时获得有用结果时尤为重要。最重要的是,Python 的使用和特定库的导入通常不会破坏整个工具套件,卸载特定库非常容易。

要维护客户环境的完整性,应避免在客户机系统上安装库。如果需要这样做,请确保您与您的联系人一起工作,因为可能会产生意外后果。也可以认为违反了组织的系统开发生命周期****SDLC及其变更控制流程。最终结果是,您可能会为客户带来比最初评估意图更大的风险。

Python 的语言结构虽然不同于许多其他形式的编码,但非常简单。阅读 Python 与阅读一本书类似,但有一些轻微的警告。在编写本书 Python2.X 和 Python3.X 时,基本上有两种不同形式的 Python 开发树。大多数评估工具都运行在 2.X 版本上,这是我们将重点关注的,但是语言版本的改进已经停止。您可以编写适用于这两个版本的代码,但这需要一些努力。

从本质上讲,Python 版本 3.X 已经发展为更面向对象OO),这意味着为其编码意味着关注 OO 方法和属性。这并不是说 2.X 不是 OO;只是它没有 3.X 版那么完善。最重要的是,有些库与这两个版本不兼容。

信不信由你,Python 脚本版本不完全兼容的最常见原因是内置的print函数。

在 Python2.X 中,print是一个语句,在 3.X 中,它是一个函数,您将在下面看到。在本书中,语句和函数的使用可以互换使用,但理解它们的区别是构建版本无关脚本的关键。

尝试使用print在屏幕上打印内容可以通过两种方式完成。一种是使用包装参数,另一种是不使用包装参数。如果是包装参数,则与 2.X 和 3.X 兼容;如果没有,那么它将只适用于 2.X。

以下示例显示了仅 2.X 版本的print函数的外观:

print "You have been hacked!"

这是一个与 2.X 和 3.X Python 解释器兼容的print函数示例:

print("You have been hacked!")

在您开始创建脚本后,您会注意到您在脚本中使用print功能的频率。因此,即使使用自动化方法,在大型脚本中进行大规模文本替换也可能非常费力且容易出错。示例包括使用sedawk和其他数据处理工具。

当您成为一名更好的评估员时,您应该努力编写脚本,以便它们可以在任何一个版本中运行。原因是,如果您破坏了一个环境,并且需要一个自定义脚本来完成某些利用漏洞后的活动,那么您不希望因为它与版本不兼容而减慢速度。最好的开始方法是确保您使用的print函数与两个版本的 Python 兼容。

OO 编程意味着该语言支持可以根据需要创建和销毁的对象,以完成任务。整个培训课程都是在解释和扩展 OO 概念的基础上开发的。对这些概念的深入解释超出了本书的范围,但仍建议进一步研究。

除了 OO 思维过程和 OO 支持代码的构建之外,还可以创建脚本“Pythonically”或“pythonicscripts”。这不是虚构的;相反,它是定义创建和编写 Python 脚本的正确方法的一种方式。有很多方法可以编写 Python 脚本,多年来,最佳实践不断发展。这被称为Python式,因此,我们应该始终努力以这种方式写作。原因是,当我们作为贡献者向社区提供脚本时,它们更易于阅读、维护和使用。

Pythonic 是一个很好的概念,因为它处理了一些影响到其他语言的采用和社区中不良做法的最大问题。

Python 交互式解释器与脚本

有两种方法可以使用 Python 语言。一种是通过交互式解释器,可以快速测试函数、代码片段和想法。另一种是通过一个可以在系统之间保存和传输的成熟脚本。如果您想试用交互式解释器,只需在命令行 shell 中键入python

在不同的操作系统中,交互式解释器将以相同的方式运行,但与系统交互的库和调用函数可能不会。如果引用了特定位置,或者命令和/或库使用操作系统特定的功能,则功能将不同。因此,在脚本中引用这些细节将极大地影响其可移植性,因此不被认为是一种主流做法。

环境变量与路径

这些变量对于执行用 Python 编写的脚本非常重要,而不是编写它们。如果未配置它们,则 Python 二进制文件的位置必须由其完全限定路径位置引用。例如,以下是在 Windows 中不声明环境变量的情况下执行 Python 脚本:

C:\Python27\python wargames_print.py

如果脚本顶部未列出对适当解释器的引用,并且文件位于当前目录中,则以下内容在 Linux 或 Unix 中是等效的:

/usr/bin/python ./wargames_print.py

在 Windows 中,如果设置了环境变量,只需键入python和脚本名称即可执行脚本。在 Linux 和 Unix 中,我们在脚本顶部添加了一行代码,以使其更具可移植性。对我们(渗透测试人员)的一个好处是,这使得脚本在许多不同类型的系统上都很有用,包括 Windows。这一行被 Windows 操作系统本机忽略,因为它被视为注释。以下引用行应包含在所有 Python 脚本的顶部:

#!/usr/bin/env python

这一行允许操作系统根据PATH环境变量中设置的内容确定要运行的正确解释器。在互联网上的许多脚本示例中,您可能会看到对解释器的直接引用,例如/usr/bin/python。这不被认为是好的实践,因为它会降低代码的可移植性,并且更容易在潜在的系统更改中出错。

提示

设置和处理PATH和环境变量对于每个操作系统都是不同的。参见https://docs.python.org/2/using/windows.html#excursus-为 Windows 设置环境变量。对于 Unix 和 Linux 平台,详细信息可在中找到 https://docs.python.org/2/using/unix.html#python-相关路径和文件。此外,如果有一天您需要为特定工具创建专业环境变量,您可以在中找到详细信息 https://docs.python.org/2/using/cmdline.html

理解动态类型语言

Python 是一种动态类型的语言,这意味着很多事情,但最关键的方面是如何处理变量或对象。动态类型语言通常是脚本语言的同义词,但事实并非总是如此。当您编写脚本时,这对您意味着变量是在运行时解释的,因此它们不必按大小或内容定义。

第一个 Python 脚本

现在,您已经对 Python 有了一个基本的概念,让我们创建一个脚本。我们将使用一个邪教电影的例子,而不是著名的Hello World!介绍。脚本将定义一个函数,该函数将打印 1983 年邪教经典战争游戏中的著名引用。如前所述,有两种方法可以做到这一点;第一种是通过交互式解释器,第二种是通过脚本。打开交互式解释器并执行以下行:

print("Shall we play a game?\n")

前面的 print 语句将显示代码执行工作正常。要退出交互式解释器,请在 Windows 中键入exit()或使用Ctrl+Z或在 Linux 中使用Ctrl+D。现在,在您首选的编辑工具(如 vi、vim、emacs 或 gedit)中创建一个脚本。然后将/root/Desktop中的文件保存为wargames_print.py

#!/usr/bin/env python
print("Shall we play a game?\n")

保存文件后,使用以下命令运行该文件:

python /root/Desktop/wargames_print.py

您将再次看到脚本以相同的结果执行。请注意本例中的几个项目。python脚本通过引用完全限定的路径来运行,以确保调用正确的脚本,无论位置如何。如果脚本位于当前位置,则可以按以下方式执行:

python ./wargames_print.py

提示

Kali 本机不需要./来执行这些脚本,但这是一个好习惯,就像大多数其他 Linux 和 Unix 操作系统一样。如果您在评估时有点睡眠不足的习惯,您可能没有意识到脚本最初没有执行的原因。这项技术可以让您在多成员团队参与时避免一点尴尬。

开发脚本并识别错误

在我们开始创建大规模脚本之前,您需要了解可能产生的错误。如果您开始创建脚本并生成一系列错误,您可能会感到气馁。请记住,Python 在引导您了解需要查看的内容方面做得非常好。但是,错误的产生者通常在引用的行或调用的函数之前。这反过来可能会产生误导,因此为了防止气馁,您应该理解 Python 在错误中可能引用的定义。

保留字、关键字、内置函数

保留的字、关键字、内置函数也被称为禁止的,表示名称不能作为变量或函数使用。如果重复使用该字或函数,将显示错误。Python 中有固定的单词和内置函数,根据您使用的版本,它们可以更改。现在你不应该太担心这个问题,但是如果你看到与变量或值的定义相关的错误,考虑一下你可能使用关键字或内置函数的事实。

有关关键字和内置函数的更多详细信息,请参见https://docs.python.org/2/library/keyword.html

下面是一些 Python 关键字示例和一些简短的定义。本章其余部分将详细介绍这些内容:

|

示例关键字

|

意图

| | --- | --- | | for | 一种类型的 Python 循环,主要用于迭代 | | def | 将在当前脚本中创建的函数的定义 | | if | 一种评估陈述并确定最终行动方案的方法 | | elif | if陈述的后续评估,允许两种以上不同的结果 | | import | 导入库的方式 | | print | 将数据输出到标准输出标准输出的语句 | | try | 条件处理程序测试 |

如果要确认名称为关键字,请启动交互式解释器并将变量设置为特定关键字名称。然后,通过关键字函数运行它。如果返回true,则您知道它是一个关键字;如果它返回false,您知道它不是。请参阅以下屏幕截图以更好地理解此概念:

Reserved words, keywords, and built-in functions

全局和局部变量

全局变量在函数外定义,局部变量在特定函数内定义。这一点很重要,因为如果在函数中重用名称,则其值通常仅保留在该函数中。如果希望更改全局变量的值,可以使用 global 关键字调用全局版本并设置新值。如果可能的话,应该避免这种做法。作为局部和全局变量用法的示例,请参见以下代码:

#!/usr/bin/env python

hacker = "me"

def local_variable_example():
    hacker = "you"
    print("The local variable is %s") % (hacker)

local_variable_example()
print("The global variable is %s") % (hacker)

此脚本的以下输出显示了在local_variable_example函数示例中打印局部变量hacker。然后,我们在函数执行后打印全局变量hacker

Global and local variables

前面的示例演示了如何通过变量将值插入字符串。在本章中,还提供了几种实现这一点的方法。

理解名称空间

Python 中变量的基本思想是名称;这些名称驻留在一个桶中。每个模块或脚本都接收自己的全局名称空间,并且名称驻留在这个称为名称空间的 bucket 中。这意味着在使用名称时,它是为特定目的保留的。如果您再次使用它,将导致以下两种情况之一:要么您将覆盖该值,要么您将看到一个错误。

模块和导入

在 Python 中,可以导入库或模块来执行特定任务或补充功能。编写了自己的脚本后,可以将脚本作为模块导入到新脚本中使用。有几种方法可以做到这一点,每种方法都有其优点和缺点:

import module

这允许您导入模块并通过引用类似于函数的模块和函数来使用它。例如,您可以将模块和模块内的函数引用为module.function()。这意味着您的命名空间保持简单,不必担心覆盖和冲突,这与以下方法不同:

from module import *

这在 Internet 上的 Python 脚本和示例中非常常见。危险在于模块内的所有功能都直接引入。这意味着,如果您在脚本中定义了一个名为hacker_toolhacker_tool的函数(导入的模块包含一个同名的模块),则可能会发生名称空间冲突并产生多个错误。在运行时,当解释脚本时,由于导入了不必要的函数,它将占用更大的内存空间。然而,这样做的好处是,您不必识别必要的功能,也不必使用module.function()的方法。您可以直接拨打function()

接下来的两种方法是以不同的名称引用模块或函数的方法。这允许您缩短需要重用的语句,并且通常可以提高可读性。存在相同的命名空间冲突,因此应该仔细定义导入和引用。第一个是将模块声明为不同的名称:

import module as a

第二个是将函数声明为不同的名称:

from module import function as a

执行这些任务还有其他方法,但这足以阅读生成的大多数脚本并创建有用的工具。

提示

您知道 Python 模块本身就是脚本吗?您可以通过查看 Windows Python 安装中的Lib目录来了解这些产品是如何工作的,对于 Python 2.7,该目录默认为C:\Python27\Lib。在 Kali Linux 中,可以在/usr/lib/python2.7找到它。

Python 格式化

对我来说,这种语言最畅销的特点是它的格式。把一个脚本放在一起只需要很少的工作,而且由于它的格式要求过于简单,所以可以减少出错的机会。对于有经验的程序员来说,讨厌的;{}符号将不再因为语法错误而影响您的开发时间。

压痕

Python 中要记住的最重要的事情是缩进。Python 使用缩进来显示逻辑块的更改位置。所以,如果你像前面提到的那样写一个简单的print脚本,你不一定会看到这个,但是如果你写一个if语句,你会看到。请参见以下示例,其中打印了前面提到的语句:

#!/usr/bin/env python
execute=True
if execute != False:
    print("Do you want to play a game?\n")

有关此脚本如何操作和执行的更多详细信息,请参阅本章的复合语句部分。如果 execute 不是False,下面的示例将语句打印到屏幕上。此缩进表示函数将其与全局代码的其余部分分离。

有两种方法可以创建缩进:通过空格或通过制表符。四个空格相当于一个选项卡;前面代码中的缩进表示逻辑代码与全局代码其余部分的分离。这样做的原因是,当从一种系统类型移动到另一种系统类型时,空间可以更好地转换,这再次使代码更具可移植性。

Python 变量

Python 脚本语言有五种类型的变量:数字、字符串、列表、字典和元组。这些变量具有不同的预期用途、使用原因和声明方法。在了解这些变量类型如何工作之前,您需要了解如何调试变量并确保脚本正常工作。

列表、元组和字典属于变量类别,称为数据结构。本章提供了足够的详细信息,可以帮助您起步并运行 Python,但是您在帮助论坛中注意到的有关 Python 的大多数问题都与正确使用和处理数据结构有关。当你在本书给出的细节之外开始冒险进行自己的项目时,请记住这一点。有关数据结构及其使用方法的更多信息,请访问https://docs.python.org/2/tutorial/datastructures.html

调试变量值

调试变量值的简单解决方案是确保将预期数据传递给变量。如果需要将变量中的值从一种类型转换为另一种类型,这一点尤其重要,本章稍后将介绍这一点。因此,您需要知道变量中的值是什么,通常是什么类型。这意味着您必须在构建脚本时调试脚本;这通常通过使用print语句来完成。在整个代码中,您经常会看到初始脚本中散布着print语句。为了帮助您在以后清理这些文件,我建议您添加一条注释。我通常使用一个简单的#DEBUG注释,如下所示:

print(variable_name) #DEBUG

这将允许您快速搜索和删除#DEBUG行。在 vi 或 vim 中,这非常简单,首先按Esc,然后按*:*,然后执行以下命令,搜索并删除整行:

g/.*DEBUG/d

如果您想临时注释掉所有的#DEBUG行并在以后删除它们,您可以使用以下方法:

%s/.*DEBUG/#&

字符串变量

包含字符串的变量基本上是放置在引用中的单词、语句或句子。此项允许在整个脚本中根据需要轻松重用值。此外,可以在脚本过程中操纵这些变量以生成不同的值。要将值传递给变量,请在选择单词以指定值后使用等号。在字符串中,值用引号或双引号括起来。以下示例显示如何使用双引号指定值:

variable_name = "This is the sentence passed"

以下示例显示分配给变量的单引号:

variable_name = 'This is the sentence passed'

允许单引号和双引号的原因是允许程序员将一个或另一个作为句子的一部分插入变量。请参见以下示例以突出显示差异:

variable_name = 'This is the "sentence" passed'

除了在此方法中传递字符串或打印值外,还可以使用相同类型的引号转义特殊字符。这是通过在任何特殊字符前面加一个\符号来实现的,该符号有效地避开了特殊能力。以下示例强调了这一点:

variable_name = "This is the \"sentence\" passed"

关于声明字符串,重要的是选择一种引号类型使用 single 或 double,并通过脚本一致地使用它。此外,正如您在 Python 中看到的,变量大小不必在最初声明。这是因为它们是在运行时解释的。现在您知道了如何创建包含字符串的变量。下一步是创建包含数字的变量。

数字变量

创建包含数字的变量非常简单。定义变量名,然后通过在等号的右侧放置一个数字为其赋值,如下所示:

variable_name = 5

一旦定义了一个变量,它就会保存一个对它传递的值的引用。这些变量可以被重写,可以对它们执行数学运算,甚至可以在程序中间改变。以下示例显示将相同类型的变量添加到一起并打印。首先,我们显示添加和打印的相同变量,然后显示两个不同的变量。最后,将这两个变量相加,分配给一个新变量,然后打印。

Number variables

请注意,传递给变量的数值没有引号。如果他们这样做了,Python 解释器 Go.T0.将把它们看作字符串,结果将是明显不同的。请参阅以下屏幕截图,其中显示了对具有字符串等效项的数值变量规定的相同方法:

Number variables

正如您所看到的,这些值被合并成一个字符串,并将它们相加。Python 具有内置函数,允许我们将字符串解释为数字,将数字解释为字符串。此外,您可以使用type function来确定变量是什么。此屏幕截图显示了两个变量的声明,一个作为字符串,一个作为整数:

Number variables

如果在变量中声明了一个十进制值,那么它将被声明为浮点数或简称为float。这仍然是一个数值变量,但它需要不同的存储方法,如您所见,解释器已经为您确定了这一点。以下屏幕截图显示了一个示例:

Number variables

转换字符串和数字变量

如数字变量部分所述,Python 具有内置函数,允许您将一种变量类型转换为另一种变量类型。作为一个简单的例子,我们将把一个数字转换成一个字符串,把字符串转换成一个数字。使用交互式解释器时,如果变量值未传递给新变量,则会立即打印变量值;但是,在脚本中,它不会。如果数据通过命令行界面CLI)传递,并且您希望确保数据将被处理的方法,则此操作方法非常有用。

这是使用以下三个功能执行的:int()str()float()。这些函数完全按照您认为的那样执行;int()将其他类型的适用变量改为整数,str()将其他适用变量类型改为字符串,float()将适用变量改为浮点数。请务必记住,如果无法将变量转换为所需类型,您将收到一个ValueError异常,如此屏幕截图所示:

Converting string and number variables

举个例子,让我们取一个字符串和一个整数并尝试把它们加在一起。如果这两个值不属于相同的类型,您将收到一个TypeError异常。以下屏幕截图演示了这一点:

Converting string and number variables

在这里,您必须确定变量的类型,并选择其中一个转换为相同的类型。您选择转换哪一个取决于预期结果。如果希望变量包含两个数字的总值,则需要将字符串变量转换为数字类型变量。如果希望将这些值组合在一起,则需要将非字符串变量转换为字符串。此示例显示了两个值的定义:一个是字符串,另一个是整数。字符串将转换为整数,以允许数学运算继续进行,如下所示:

Converting string and number variables

现在你可以看到,这是多么容易,考虑如果一个 Ty2 T2 字符串变量代表一个值 T0 值,并转换成一个整数会发生什么。数字的小数部分将丢失。这不会使值向上或向下舍入;它只是去掉小数部分,然后给出一个整数。请参阅以下屏幕截图以了解此示例:

Converting string and number variables

因此,请确保将数值变量更改为适当的类型。否则,一些数据将丢失。

列表变量

列表是一种数据结构,它以一种可以组织、调整和轻松操作的方法保存值。在 Python 中识别列表的一种简单方法是通过[],它表示值将驻留在何处。这些列表的操作通常基于按位置调整值。要创建列表,请定义一个变量名,并在等号的右侧用逗号分隔的值放置括号。这个简单的脚本计算预定义列表的长度,迭代并打印列表的位置和值。重要的是要记住,列表从位置 0 开始,而不是从 1 开始。由于列表可以包含不同类型的变量以包含其他列表,因此为了安全起见,我们将把值打印为字符串:

#!/usr/bin/env python

list_example = [100,222,333,444,"string value"]
list_example_length = len(list_example)
for iteration in list_example:
 index_value = list_example.index(iteration)
 print("The length of list list_example is %s, the value at position %s is %s") % (str(list_example_length), str(index_value), str(iteration).strip('[]'))

print("Script finished")

以下屏幕截图显示了此脚本的成功执行:

List variables

如您所见,从列表中提取值并将其转换为数值或字符串值是很重要的概念。列表用于保存多个值,通常需要提取这些值以便表示它们。以下代码显示了如何对字符串执行此操作:

#!/usr/bin/env python

list_example = [100,222,333,444]
list_value = list_example[2]
string_value_from_list = str(list_value)
print("String value from list: %s") % (str(list_value))

需要注意的是,列表不能作为整数打印,因此必须将其转换为字符串或迭代并打印。为了仅显示简单的差异,以下代码演示了如何从列表中提取整数值并打印它和字符串:

#!/usr/bin/env python

list_example = [100,222,333,444]
list_value = list_example[2]
int_value_from_list = int(list_value))
print("String value from list: %s") % (str(list_value))
print("Integer value from list: %d") % (int_value_from_list)

可以使用特定于列表的函数进一步操作列表值。您只需调用列表的名称,然后将.function(x)添加到列表中,其中function是您想要完成的特定活动的名称,x是您想要操纵的位置或数据。使用的一些常见功能包括向列表末尾添加值,如数字 555,其实现方式如下:list_example.append(555)。你甚至可以合并列表;这是使用extend功能完成的,该功能将相关项目添加到列表末尾。这是通过执行以下功能来完成的:list_example.extend(list_example2)。如果要删除 555 的值,只需执行list_example.remove(555)。可以使用适当命名的insert函数将值插入特定位置,如:list_example.insert(0, 555)。这里描述的最后一个函数是pop函数,它允许您通过传递位置值来删除特定位置的值,或者通过不指定值来删除列表中的最后一个条目。

元组变量

元组类似于列表,但与列表不同,元组是使用()定义的。而且,它们是不可变的;也就是说,它们不能改变。这背后的动机是提供一种在复杂操作中控制数据的方法,这种方法不会在过程中破坏数据。可以删除一个元组,并创建一个新的元组来保存不同元组的部分数据,并显示数据是否已更改。使用元组的简单规则如下:如果希望数据保持不变,请使用元组;否则,请使用列表。

字典变量

字典是将键与值关联起来的一种方式。如果你看到花括号,这意味着你在看字典。键表示对存储在未排序数据结构中的特定值的引用。您可能会问自己,当标准变量已经做了类似的事情时,为什么要这样做。字典为您提供了将其他变量和变量类型存储为值的方法。它们还允许在必要时快速方便地引用。您将在后面的章节中看到字典的详细示例;现在,请查看以下示例:

#!/usr/bin/env python
dictionary_example = {'james':123,'jack':456}
print(dictionary_example['james'])

此示例将打印与'james'键相关的数字,如以下屏幕截图所示:

Dictionary variables

向字典添加数据非常简单;您只需为字典分配一个新的键和该键的值。例如,要将789的值添加到'john'键,可以执行以下操作:dictionary_example['john'] = 789。这将为字典分配新的值和键。关于词典的更多细节将在后面介绍,但这足以让我们了解它们。

了解默认值和构造函数

以前编写过程序或脚本的人可能习惯于使用默认值声明变量或设置构造函数。

在 Python 中,这不是入门所必需的,但是在使用变量之前在变量中设置默认值是一个好习惯。除了良好的实践之外,它还将减轻脚本出现意外错误和崩溃的一些原因。如果将值传递给意外的变量,这也将增加可跟踪性。

提示

在 Python 中,当实例化一个新对象时,构造函数方法由__init____new__处理。但是,在创建新类时,只需要使用__init__函数作为类的构造函数。直到很久以后才需要,但要记住这一点;如果要开发多线程应用,这一点很重要。

将变量传递给字符串

假设您想要生成一个具有动态值的字符串,或者在打印时在字符串中包含变量并实时解释该值。使用 Python,您可以通过多种方式来实现。您可以使用算术符号(如+)组合数据,也可以使用特殊字符组合插入值。

第一个示例将使用两个字符串和一个与语句连接的变量的组合来创建动态语句,如下所示:

#!/usr/bin/env python
name = "Hacker"
print("My profession is "+name+", what is yours?")

这将产生以下输出:

Passing a variable to a string

创建第一个脚本后,可以通过直接在字符串中插入值来改进它。这是通过使用%特殊字符并为字符串追加s或为数字追加d来产生预期结果。然后,print语句附加了%符号,并将参数包装在必需的一个或多个变量周围。这使您能够快速、轻松地控制数据,并在原型或创建脚本时清理详细信息。

传递参数中的变量以替换语句中的键控符号。以下是此类脚本的一个示例:

#!/usr/bin/env python
name = "Hacker"
print("My profession is %s, what is yours?") % (name)

下图显示了正在执行的代码:

Passing a variable to a string

另一个好处是可以在不大幅修改的情况下将多个值插入该脚本,如下例所示:

#!/usr/bin/env python

name = "Hacker"
name2 = "Penetration Tester"

print("My profession is %s, what is yours? %s") % (name, name2)

Passing a variable to a string

这种插入形式可以通过前面几行中提到的数字和将%s更改为%d来完成:

#!/usr/bin/env python

name = "Hacker"
name2 = "Penetration Tester"
years = 15

print("My profession is %s, what is yours? %s, with %d years experience!") % (name, name2, years)

可以在此屏幕截图中看到输出:

Passing a variable to a string

可以直接传递语句,而不是使用变量。通常很少有理由这样做,因为变量为您提供了更改代码并将其应用于整个脚本的方法。如果可能,应根据需要使用变量来定义语句。当您开始编写将传递给系统的语句时,这一点非常重要。使用连接变量的组合来创建将在 Python 脚本中执行的命令。如果您这样做,您可以通过简单地更改特定值来更改提供给系统的内容。关于这方面的更多示例将在后面介绍。

操作员

Python 中的运算符是表示函数执行的符号。

更多详情可参见https://docs.python.org/2/library/operator.html

需要记住的重要一点是,Python 具有广泛的功能,允许进行复杂的数学和比较操作。这里只介绍其中的几个,为您准备更详细的工作。

比较运算符

比较运算符根据评估方法检查条件是否为真。简单地说,我们试图确定一个值是否等于、不等于、大于、小于、大于或等于或小于或等于另一个值。有趣的是,Python 比较运算符非常简单。

下表将帮助定义操作员的详细信息:

|

对比试验

|

操作人员

| | --- | --- | | 这两个值相等吗? | == | | 这些值不相等吗? | != | | 左侧的值是否大于右侧的值? | > | | 左边的值是否小于右边的值? | < | | 左侧的值是否大于或等于右侧的值? | >= | | 左边的值是否小于或等于右边的值? | <= |

赋值运算符

赋值运算符在从不同语言转换时会使大多数人感到困惑。原因是和赋值运算符与大多数语言不同。习惯于使用variable++格式在其他语言中编写variable = variable + 1的 incrementors short hands 的人,他们经常会困惑地发现 Python 中没有完成确切的操作。

Python 中变量 incrementor 的函数等价物是variable=+1,与variable = variable + 1相同。然而,你可能会注意到这里有些东西;可以在此表达式中定义添加到变量的内容。因此,与双加法符号不同,双加法符号表示“向该变量添加 1”,AND 表达式允许您向其中添加任何内容。

这在编写漏洞利用时非常重要,因为可以使用此运算符将多个十六进制值附加到同一个字符串,如前面的字符串连接示例所示,其中两个字符串被添加到一起。第 8 章利用 Python、Metasploit 和Immunity开发漏洞,当您开发远程代码执行n(RCE漏洞时,将涵盖更多内容。在此之前,请考虑此表以查看不同的赋值运算符及其使用情况:

|

转让行为

|

操作人员

| | --- | --- | | 给某物设定一个值 | = | | 向左侧的变量添加一个值,并将新值设置为左侧的同一个变量 | += | | 从左侧的变量中减去一个值,然后将新值设置为左侧的同一个变量 | -= | | 将一个值乘以左侧的变量,并将新值设置为左侧的同一变量 | *= | | 将值除以左侧的变量,并将新值设置为左侧的同一变量 | /= |

算术运算符

算术运算符总体上非常简单,是您所期望的。加法执行使用+符号,减法执行使用-,乘法执行使用*,除法执行使用/。也可以使用其他项目,但这四个项目涵盖了您将要看到的大多数情况。

逻辑和成员运算符

逻辑 and 成员运算符使用文字而不是符号。一般来说,Python 最容易混淆的操作符是成员操作符,因为新的脚本编写者认为它们是逻辑操作符。让我们来看看逻辑运算符是什么。

逻辑运算符帮助语句或复合语句确定是否满足多个条件,从而证明truefalse条件。那么,用外行的话来说,这意味着什么呢?查看以下脚本,它有助于确定两个变量是否包含继续执行所需的值:

#!/usr/bin/env python

a = 10
b = 5
if a == 10 and b == 5:
 print("The condition has been met")
else:
 print("the condition has not been met")

逻辑运算符包括andornot,可以与更复杂的语句组合。此处的not操作符可能与not in混淆,后者是成员操作符的一部分。not测试与组合条件测试相反。下面的例子特别强调了这一点;如果两个值或False或不相等,则满足条件;否则,测试将失败。这样做的原因是测试会检查两者是否同时存在。类似于此的示例并不常见,但如果您对逻辑流还不满意,则可以避免此类代码:

#!/usr/bin/env python

a = False
b = False
if not(a and b):
 print("The condition has been met")
else:
 print("The condition has not been met")

相反,成员操作符测试作为变量一部分的值。这类操作员有两种,innot in。以下是其用法的示例:

#!/usr/bin/env python

variable = "X-Team"

if "Team" in variable:
 print("The value of Team is in the variable")
else:
 print("The value of Team is not in the variable")

此代码的逻辑将导致语句返回为True,第一条条件消息将打印到屏幕上。

复合语句

复合语句包含其他语句。这意味着在truefalse执行自身语句时进行测试或执行。诀窍在于编写语句,使其高效。这方面的例子包括if then语句、循环和异常处理。

if 语句

if语句测试特定条件,如果满足(或未满足)该条件,则执行该语句。if语句可以包括一个简单的检查,查看变量是true还是false,然后打印详细信息,如下例所示:

x = 1
if x == 1:
 print("The variable x has a value of 1")

if语句甚至可以用于同时检查多个条件。请记住,它将执行符合条件的复合语句的第一部分,并跳过其余部分。下面是一个基于上一个示例的示例,使用了elseelif语句。如果不满足ifelif陈述,则else陈述是一个包罗万象的陈述。elif测试是后续if测试。可在if之后和else之前测试其状态。请参考以下示例以更好地理解这一点:

#!/usr/bin/env python
x=1
if x == 3:
 print("The variable x has a value of 3")
elif x == 2:
 print("The variable x has a value of 2")
elif x == 1:
 print("The variable x has a value of 1")
else:
 print("The variable x does not have a value of 1, 2, or 3")

从这些语句中可以看到,第二条elif语句将处理结果。将x的值更改为其他值,然后查看脚本流是如何工作的。

记住一件事:测试条件需要仔细考虑测试结果。以下是一个if测试的示例,该测试可能无法根据变量值提供预期结果:

#!/usr/bin/env python

execute=True
if execute != False:
 print("Do you want to play a game?\n")

此脚本将execute变量设置为True。然后,if是带有print语句的脚本。如果变量未设置为True且也未设置为False,则该语句仍将被打印。原因是我们只是测试execute变量不等于False。只有将execute设置为False时,才会打印任何内容。

Python 循环

循环是一个反复执行的语句,直到满足或不满足条件为止。如果一个循环是在另一个循环中创建的,则称为嵌入式循环。在渗透测试中,通常不认为在彼此之间具有多个回路是最佳实践。这是因为如果控制不当,可能会造成内存耗尽的情况。循环有两种主要形式:while循环和for循环。

while 循环

当情况为真或假,并且只要条件有效,您希望执行测试时,while循环就非常有用。例如,此while循环检查x的值是否大于0,如果大于,则循环继续处理数据:

x=5
while x > 0:
print("Your current count is: %d") % (x)
 x -= 1

for 循环

for循环是执行的,其思想是已经建立了一个已定义的情况,并将对其进行测试。作为一个简单的示例,您可以创建一个脚本,对 1 到 15 之间的一系列数字进行计数,每次一个数字,然后打印结果。下面的for循环语句示例实现了这一点:

for iteration in range(1,15,1):
 print("Your current count is: %d") % (iteration)

断裂条件

break条件用于退出循环并继续处理下一条语句中的脚本。当循环中出现特定情况而不是循环的下一次迭代时,中断用于控制循环。即使中断可以用来控制循环,你也应该考虑编写代码,这样你就不需要休息了。如果变量值等于5,则具有break条件的以下循环将停止执行:

#!/usr/bin/
numeric = 15
while numeric > 0:
    print("Your current count is: %d") %(numeric)
    numeric -= 1
    if numeric == 5:
        break
print("Your count is finished!")

此脚本的输出如下所示:

The break condition

虽然这样做是可行的,但如果脚本设计得更好,也可以达到同样的效果,如下代码所示:

#!/usr/bin/env python

numeric = 15
for iteration in range(numeric,5,-1):
 print("Your current count is: %d") % (iteration)

print("Your count is finished!")

正如您在这里看到的,使用更干净、更易于管理的代码也会产生相同的结果:

The break condition

条件处理程序

Python 和许多其他语言一样,能够处理异常或相对意外的情况。在这种情况下,将发生捕获并捕获错误和后续活动。这由处理条件的tryexcept子句完成。作为一个例子,我经常使用条件处理程序来确定是否安装了必要的库,如果没有安装,它会告诉您如何以及从何处获取它。这是一个简单但有效的例子:

try:
 import docx
 from docx.shared import Inches
except:
 sys.exit("[!] Install the docx writer library as root or through sudo: pip install python-docx")

功能

Python 函数允许脚本编写者创建可重复的任务,并在整个脚本中频繁调用。当函数是类或模块的一部分时,这意味着一旦导入以执行任务,就可以从另一个脚本(也称为模块)调用脚本的特定部分。使用 Python 函数的另一个好处是减少了脚本大小。通常意想不到的好处是能够将函数从一个脚本复制到另一个脚本,从而加快开发速度。

动态类型语言对函数的影响

请记住,变量包含对对象的引用,因此在编写脚本时,您正在使用引用该值的变量执行测试。关于这一点的一个事实是,变量可以更改,并且仍然可以指向原始值。当通过参数将变量传递给函数时,它将作为原始对象的别名进行传递。因此,在编写函数时,函数中的变量名通常是不同的,应该是不同的。这使得故障排除更容易,脚本更清晰,错误控制更准确。

花括号

如果你曾经用另一种语言写作,那么有一件事会让你感到惊讶,那就是没有像这样的花括号:{}。这通常用于描述逻辑测试或复合语句的代码在何处停止和开始,例如循环、if语句、函数甚至整个类。相反,Python 使用前面提到的缩进方法,缩进越深,语句嵌套得越多。

嵌套语句或函数意味着在逻辑测试或复合语句中,另一个逻辑测试正在执行。一个例子是另一个if语句中的if语句。本章后面将看到更多此类示例。

为了查看 Python 和其他语言中的逻辑测试之间的差异,将显示一个称为子例程的 Perl 函数示例。还将演示一个等效的 Python 函数,以展示这些差异。这将突出显示 Python 如何控制脚本中的逻辑流。请随意尝试这两种脚本,看看它们是如何工作的。

由于包含了一个return语句,下面的 Python 脚本略长于 Perl 脚本。这对于这个脚本不是必需的,但这是许多脚本编写者养成的习惯。此外,正如您所看到的,print语句已经修改,以支持 Python 的 2.X 版和 3.X 版。

以下是Perl功能的一个示例:

#!/usr/bin/env perl

# Function in Perl
sub wargames{
 print "Do you want to play a game?\n";
print "In Perl\n";
}

# Function call
wargames();

以下函数在 Python 中是等效的:

#!/usr/bin/env python

# Function in Python
def wargames():
 print("Do you want to play a game?")
print("In Python")
return

# Function call
wargames()

这两个脚本的输出都可以在这个屏幕截图中看到:

Curly brackets

相反,在 Python 中,花括号用于字典,如本章的Python 变量部分所述。

如何注释您的代码

在脚本语言中,注释用于阻止代码和/或描述它试图实现的内容。Python 中有两种类型的注释:单行注释和多行注释。单行注释使从#符号到行尾的所有内容都成为注释;它不会被解释。如果您将代码放在行上,然后在行尾添加注释,代码仍将被处理。下面是一个有效使用单行注释的示例:

#!/usr/bin/env python
#Author: Chris Duffy
#Date: 2015
x = 5 #This defines the value of the x followed by a comment

这是可行的,但是使用多行注释做同样的事情可能更容易,因为前面代码中有两行是注释。多行注释是通过在注释块开始和结束的每行中放置三个引号来创建的。以下代码显示了这方面的示例:

"""
Author: Chris Duffy
Date: 2015
"""

Python 风格指南

在编写脚本时,需要遵守一些脚本和编程常见的命名约定。这些惯例更多的是指导方针和最佳实践,而不是硬性规定,这意味着你将听取双方的意见。由于脚本是一种艺术形式,您将看到反驳这些建议的示例,但遵循这些示例将提高可读性。

这里的大多数建议都是从 Python 的样式指南中借来的,可以在中找到 http://legacy.python.org/dev/peps/pep-0008/ 和后续样式指南。

如果您在这里看到与本指南不直接匹配的细节,请记住,所有评估员都会养成不同的习惯和风格。诀窍是在不影响开发速度和质量的情况下,尽可能多地采用最佳实践。

课程

类通常以大写字母开头,第一个单词的其余部分为小写。之后的每个单词都以大写字母开头。因此,如果您看到正在使用一个已定义的引用,并且它以大写字母开头,那么它很可能是一个类或模块名。用于定义类的单词之间不应使用空格或下划线,尽管人们通常会忘记或违反此规则。

功能

在开发函数时,请记住单词应该是小写的,并用下划线分隔。

变量和实例名称

变量和实例应为小写,并用下划线分隔单词,如果它们是私有的,则必须以两个下划线开头。PublicPrivate变量在主要编程语言中很常见,但在 Python 中,它们并不是真正必要的。如果您想在 Python 中模拟private变量的功能,可以使用__引导该变量将其定义为 private。Python 中私有成员的主要好处是防止名称空间冲突。

参数和选项

有多种方式可以将参数传递给脚本;我们将在以后的章节中对此进行更多介绍,因为它们适用于特定的脚本。获取参数的最简单方法是不带选项地传递参数。参数是传递给脚本的值,为脚本提供一些动态功能。

选项是表示对脚本的特定调用的标志,说明将要提供的参数。换句话说,如果您想获得脚本的帮助或使用说明,通常需要传递-h选项。如果编写的脚本同时接受 IP 地址和 MAC 地址,则可以将其配置为使用不同的选项来表示将要呈现给它的数据。

编写脚本以获取选项要详细得多,但并不像人们想象的那么难。现在,让我们看看基本的参数传递。参数可以通过sys库和argv函数本机生成。传递参数时,将在sys.argv中创建一个包含参数的列表,该列表从位置 0 开始。

argv提供的第一个参数是脚本运行的名称,之后提供的每个参数代表其他参数值:

#!/usr/bin/env python

import sys

arguments = sys.argv
print("The number of arguments passed was: %s") % (str(len(arguments)))
i=0
for x in arguments:
 print("The %d argument is %s") % (i,x)
 i+=1

此脚本的输出产生以下结果:

Arguments and options

您的第一个评估员脚本

现在您已经了解了用 Python 创建脚本的基本知识,让我们创建一个对您实际有用的脚本。在后面的章节中,您需要知道每个接口的本地和公共 IP 地址、主机名、媒体访问控制MAC地址)和完全限定域名FQDN)。下面的脚本演示了如何执行所有这些。这里的一些概念似乎仍然是陌生的,特别是如何从接口中提取 IP 和 MAC 地址。别担心;这不是你要写的脚本。如果您愿意,您可以使用这个脚本,但这里向您展示的是,您可以修复脚本的组件,甚至是看似复杂的组件,以开发您自己的简单脚本。

此脚本使用一种技术,通过基于在几个 Python 模块和示例中使用的接口查询详细信息来提取 Linux/Unix 系统的 IP 地址。这项技术的具体配方可以在许多地方找到,但有关这项技术的最佳参考文献可以在中找到 http://code.activestate.com/recipes/439094-get-the-ip-address-associated-with-a-network-inter/

让我们将脚本分解为它的组件。该脚本使用了一些函数,使执行更清晰、可重复。第一个函数称为get_ip。它获取接口名称,然后尝试标识该接口的 IP 地址:

def get_ip(inter):
 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 ip_addr = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', inter[:15]))[20:24])
 return ip_addr

第二个函数称为get_mac_address,用于识别特定接口的 MAC 地址:

def get_mac_address(inter):
 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 info = fcntl.ioctl(s.fileno(), 0x8927,  struct.pack('256s', inter[:15]))
 mac_address = ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1]
 return mac_address

如您所见,这些函数依赖于套接字库的低级网络接口语言。你的注意力不应该放在理解这个函数的每一个细节上,而应该放在信息流、使用的变量类型以及库是如何集成上。原因是您将在以后生成一个脚本,该脚本需要更少的组件,并在以后复制获取公共 IP 地址的活动。

第三个函数获取主机的详细信息,并将其返回到脚本的主要部分。它确定主机是否为 Windows,以便调用正确的函数。该函数接受两个列表,一个用于以太网接口,另一个用于 Linux/Unix 中典型的无线接口。这些接口通过在此较大函数中调用的先前函数进行处理。这允许get_localhost_details函数处理决策,然后返回主机的值,该值将由脚本末尾的print语句表示:

def get_localhost_details(interfaces_eth, interfaces_wlan):
    hostdata = "None"
    hostname = "None"
    windows_ip = "None"
    eth_ip = "None"
    wlan_ip = "None"
    host_fqdn = "None"
    eth_mac = "None"
    wlan_mac = "None"
    windows_mac = "None"
    hostname = socket.gethostbyname(socket.gethostname())
    if hostname.startswith("127.") and os.name != "nt":
        hostdata = socket.gethostbyaddr(socket.gethostname())
        hostname = str(hostdata[1]).strip('[]')
        host_fqdn = socket.getfqdn()
        for interface in interfaces_eth:
            try:
                eth_ip = get_ip(interface)
                if not "None" in eth_ip:
                    eth_mac = get_mac_address(interface)
                break
            except IOError:
                pass
        for interface in interfaces_wlan:
            try:
                wlan_ip = get_ip(interface)
                if not "None" in wlan_ip:
                    wlan_mac = get_mac_address(interface)
                break
            except IOError:
                pass
    else:
        windows_ip = socket.gethostbyname(socket.gethostname())
        windows_mac = hex(getnode()).lstrip('0x')
        windows_mac = ':'.join(pos1+pos2 for pos1,pos2 in zip(windows_mac[::2],windows_mac[1::2]))
        hostdata = socket.gethostbyaddr(socket.gethostname())
        hostname = str(hostdata[1]).strip("[]\'")
        host_fqdn = socket.getfqdn()
    return hostdata, hostname, windows_ip, eth_ip, wlan_ip, host_fqdn, eth_mac, wlan_mac, windows_mac

这个脚本中的最后一个函数叫做get_public_ip,它查询一个已知的网站,寻找与之连接的 IP 地址。此 IP 地址以简单的原始格式返回到网页。有许多站点可以进行此操作,但请确保您了解可接受的使用和授权的服务条款。该函数接受一个输入,即您正在对其执行查询的网站:

def get_public_ip(request_target):
    grabber = urllib2.build_opener()
    grabber.addheaders = [('User-agent','Mozilla/5.0')]
    try:
        public_ip_address = grabber.open(target_url).read()
    except urllib2.HTTPError, error:
        print("There was an error trying to get your Public IP: %s") % (error)
    except urllib2.URLError, error:
        print("There was an error trying to get your Public IP: %s") % (error)
    return public_ip_address

对于 Windows 系统,此脚本使用简单的socket.gethostbyname(socket.gethostname())函数请求。这确实适用于 Linux,但它依赖于/etc/hosts文件为所有接口提供正确的信息。正如前面的参考文献所指出的,netifaces库可以替换此脚本的大部分内容。这将大大简化脚本,其使用示例将在下一章中显示。默认情况下不安装netifaces库,因此您必须在要运行此脚本的每台主机上安装它。由于您通常不希望对主机的完整性造成任何影响,因此此特定脚本旨在避免这种冲突。

提示

此脚本的最终版本可在找到 https://raw.githubusercontent.com/funkandwagnalls/pythonpentest/master/hostdetails.py

下面的屏幕截图显示了运行此脚本的输出。此脚本的组件将在后面的章节中使用,它们允许自动开发利用配置和网络侦察。

Your first assessor script

因此,您有用的脚本将获取此脚本的组件,并仅查找您所在系统的公共 IP 地址。我建议您在查看以下代码(显示实际脚本的样子)之前尝试这样做。如果要跳过此步骤,可在此处查看解决方案:

import urllib2

def get_public_ip(request_target):
    grabber = urllib2.build_opener()
    grabber.addheaders = [('User-agent','Mozilla/5.0')]
    try:
        public_ip_address = grabber.open(target_url).read()
    except urllib2.HTTPError, error:
        print("There was an error trying to get your Public IP: %s") % (error)
    except urllib2.URLError, error:
        print("There was an error trying to get your Public IP: %s") % (error)
    return public_ip_address
public_ip = "None"
target_url = "http://ip.42.pl/raw"
public_ip = get_public_ip(target_url)
if not "None" in public_ip:
    print("Your Public IP address is: %s") % (str(public_ip))
else:
    print("Your Public IP address was not found")

脚本的输出应类似于以下内容:

Your first assessor script

提示

此脚本可在找到 https://raw.githubusercontent.com/funkandwagnalls/pythonpentest/master/publicip.py

总结

本章重点介绍 Python 脚本语言的基本工作原理,并通过示例开发自己的代码。它还指出了与创建评估脚本相关的常见陷阱。本章的最后一节重点介绍如何创建有用的脚本,甚至只需将已生成示例的组件拼接在一起。

在下一章中,我们将使用nmapscapy和 Python 自动化对环境进行适当的侦察,从而更深入地探讨这个主题。