# 三、Python 应用指纹
web 应用程序安全评估过程中的一个重要步骤是指纹识别。作为一名安全研究员/pentester,我们必须精通指纹识别,指纹识别提供了大量有关底层技术的信息,如软件或框架版本、web 服务器信息、操作系统等。这有助于我们发现影响应用程序和服务器的所有已知漏洞。
在本章中,我们将介绍以下主题:
* 刮网
* 电子邮件收集
* 操作系统指纹
* EXIF 数据提取
* 应用指纹
# 刮网
尽管有些网站提供 API,但大多数网站主要是为人眼设计的,只提供为人类格式化的 HTML 页面。如果我们想要一个程序从这样一个网站获取一些数据,我们必须解析标记以获得我们需要的信息。网页抓取是一种使用计算机程序分析网页并获取所需数据的方法。
使用 Python 模块从站点获取内容的方法有很多:
* 使用`urllib`/`urllib2`创建获取网页的 HTTP 请求,并使用`BeautifulSoup`解析 HTML
* 要解析整个网站,我们可以使用 Scrapy([http://scrapy.org](http://scrapy.org) ),这有助于创建网络蜘蛛
* 使用请求模块获取数据,使用 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
```
* 输出如下所示:
![Useful methods of urllib/urllib2](images/image_03_001.jpg)
* 我们也可以使用`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 不支持 cookie 和身份验证。而且,它只支持 GET 和 POST 请求。Urllib2 构建于 urllib 之上,具有更多的功能。
* 我们可以通过编码方式获取状态码:
```
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()
```
* Cookie 可按如下方式使用:
```
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 文件,并从中提取数据。要运行解析器,我们必须创建一个解析器对象并向其提供一些数据。它将扫描数据并触发各种处理程序方法。Beauty Soup 4 适用于 Python 2.6+和 Python 3。
以下是一些简单的例子:
* 要美化 HTML,请使用以下代码:
```
from bs4 import BeautifulSoup
parse = BeautifulSoup('
Title of the
pageThis is a paragraphoneExample Link 1
This is a paragraphtwoExample Link 2
')
print parse.prettify()
```
* 输出结果如下:
![Parsing HTML using BeautifulSoup](images/image_03_004.jpg)
* 使用`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
>>> Page title
head.nextSibling.name
>>> u'body'
head.nextSibling.contents[0]
>>> This is a
paragraphoneExample
Link 1
head.nextSibling.contents[0].nextSibling
>>> This is a paragraphtwoExample Link 2
```
* 在 HTML 中搜索标记和属性的一些方法如下:
```
parse.find_all('a')
>>> [Example Link 1, Example Link 2]
parse.find(id="para2")
>>> This is a paragraphtwoExample Link 2
```
### 下载页面上的所有图像
现在,我们可以编写一个脚本来下载页面上的所有图像,并将其保存到特定位置:
```
# 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 Select。XPath 用于在元素和属性之间导航,以查找结构化文档(如 HTML 或 XML)中的信息。
我们可以使用任何 page inspect 工具(如 Firebug 或 Chrome developer 工具)获取元素的 XPath:
![Parsing HTML with lxml](images/image_03_007.jpg)
如果我们想从列表中获得书名和价格,请在源代码中找到以下部分。
```
Book 1
```
由此,我们可以创建 Xpath,如下所示:
```
#Create the list of Books:
books = tree.xpath('//div[@class="book-block-title"]/text()')
```
然后,我们可以使用以下代码打印列表:
```
print books
```
### 注
在[了解有关 lxml 的更多信息 http://lxml.de](http://lxml.de) 。
## 痒
Scrapy 是一个用于 web 抓取和 web 爬行的开源框架。这可以用来解析整个网站。作为一个框架,这有助于为特定需求构建 spider。除了 Scrapy 之外,我们还可以使用 mechanize 编写脚本来填写和提交表单。
我们可以利用 Scrapy 的命令行界面为新的爬行脚本创建基本样板。Scrapy 可与`pip`一起安装。
要创建新的 spider,我们必须在安装 Scrapy 后在终端中运行以下命令:
```
$ scrapy startproject testSpider
```
这将在当前工作目录`testSpider`中生成一个项目文件夹。这也将为我们的 spider 在文件夹中创建一个基本结构和文件:
![Scrapy](images/image_03_010.jpg)
Scrapy 有 CLI 命令来创建爬行器。要创建爬行器,我们必须输入由`startproject`命令生成的文件夹:
```
$ cd testSpider
```
然后我们必须输入 generate spider 命令:
```
$ scrapy genspider pactpub pactpub.com
```
这将生成另一个名为`spiders`的文件夹,并在该文件夹内创建所需的文件。然后,文件夹结构将如下所示:
![Scrapy](images/image_03_011.jpg)
现在打开`items.py`文件,在名为`TestspiderItem`的子类中定义一个新项:
```
from scrapy.item import Item, Field
class TestspiderItem(Item):
# define the fields for your item here:
book = Field()
```
大多数爬行逻辑是由`spider`文件夹中`pactpub`类中的 Scrapy 给出的,因此我们可以扩展它来编写我们的`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',
)
```
之后,我们必须定义 parse 方法,它将创建我们在`items.py`文件中定义的`TestspiderItem()`实例,并将其分配给 items 变量。
然后我们可以添加要提取的项,这可以通过 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 开始,爬网的 URL 将被传递到`testspiderItems`并为每个项目创建一个新实例。
## 电子邮件收集
使用前面讨论的 Python 模块,我们可以从 web 上收集电子邮件和其他信息。
要从网站获取电子邮件 ID,我们可能需要编写定制的抓取脚本。
这里,我们讨论一种使用 Python 从网页中提取电子邮件的常用方法。
让我们看一个例子。这里我们使用的是`BeautifulSoup`和请求模块:
```
# 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 指纹
pentesting 中的一个常见过程是识别主机使用的操作系统。通常,这涉及诸如 hping 或 Nmap 之类的工具,在大多数情况下,这些工具在获取此类信息时非常积极,可能会在目标主机上生成警报。操作系统指纹主要分为两类:主动操作系统指纹和被动操作系统指纹。
主动指纹识别是将数据包发送到远程主机并分析相应响应的方法。在被动指纹识别中,它分析来自主机的数据包,因此不向主机发送任何流量,并充当嗅探器。在被动指纹识别中,它嗅探 TCP/IP 端口,从而避免被检测或被防火墙阻止。被动指纹识别通过分析 IP 头数据包中的初始**生存时间**(**TTL**),以及 TCP 会话的第一个数据包中的 TCP 窗口大小来确定目标操作系统。TCP 会话的第一个数据包通常是 SYN(同步)或 SYN/ACK(同步和确认)数据包。
以下是一些操作系统的正常数据包规范:
| **操作系统** | **初始 TTL** | **TCP 窗口大小** |
| Linux 内核 2.x | 64 毫秒 | 5840 千字节 |
| Android/Chrome 操作系统 | 64 毫秒 | 5720 千字节 |
| 视窗 XP | 128 毫秒 | 65535 千字节 |
| Windows 7/Server 2008 | 128 毫秒 | 8192KB |
| 思科路由器(IOS 12.4) | 255 毫秒 | 4128 千字节 |
| FreeBSD | 64 毫秒 | 65535 千字节 |
被动操作系统指纹识别不如主动方法准确,但它有助于渗透测试仪避免检测。
指纹识别系统中另一个有趣的字段是**初始序列号**(**不是**)。在 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`,我们可以使用`p0f`来猜测 Scapy 的操作系统:
```
>>> 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 下载网站。
# 总结
在本章中,我们讨论了下载和解析网站的可能方法。使用本章讨论的基本方法,我们可以构建自己的扫描仪和 web 刮板。
在下一章中,我们将讨论更多使用 Python 的攻击脚本技术。