WebGoat之AJAX Security

DOM-Based cross-site scripting

基于DOM的跨站点脚本。DOM是文档对象模型,它是中立于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。W3C DOM标准被分为3个不同的部分:

  • 核心DOM-针对任何结构化文档的标准模型
  • XML DOM-针对XML文档的标准模型
  • HTML DOM-针对HTML文档的标准模型(HTML DOM是关于如何获取、修改、添加或HTML元素的标准)

由于它允许动态修改网页内容,这样就可以被攻击者用来进行恶意代码注入。而这种恶意代码注入一般是未经验证的用户的输入直接修改了客户端的页面。HTML DOM实体中可随时插入新的HTML语句或JavaScript语句,因此很容易被恶意代码利用,从而来改变页面显示内容或执行恶意代码。HTML标记语言中很多标签的特殊属性参数中允许插入JS代码,如:IMG/IFRAME等。
如下图所示:

1

发现当你在输入框输入什么时,页面上方就会显示出什么。

Stage 1

因此这部分课程要求我们构造恶意代码输入,使用图片OWASP IMAGE(localhost:8080/WebGoat/images/logos/owasp.jpg)来改变原来Web页面的显示方式。

在输入框中输入如下内容:

1
<IMG SRC="images/logos/owasp.jpg">

就会显示其图片:

2

这是一个反射型的XSS。
关于反射型XSS和存储型XSS,插播以下内容:

相同点:两者的本质问题都是让对方浏览器执行你插入的js代码。
不同点:存储型的XSS代码放在了服务器中,具有持久性;反射型的XSS需要用户点击交互才能执行,非持久化。前者是枪直接打就行了,后者是地雷,需要别人踩才能触发。

Stage 2

这部分是要用image标签创建一个JavaScript Alert对话框出来。
输入如下内容,然后提交

1
<img src=x onerror=;;alert('xss')/>

得到网页弹出其对话框

Stage 3

这部分要求用iframe标签创建JavaScript Alert对话框。
输入如下内容,然后提交

1
<iframe src="javascript:alert('xss');"></iframe>

得到网页的输入框

Stage 4

要求在提交数据中创建虚假的输入框,骗取用户的密码。
在输入框中输入如下代码:

1
2
3
4
Please enter your password:<BR><input type = "password" name="pass"/><button
onClick="javascript:alert('I have your password: ' +
pass.value);">Submit</button><BR><BR><BR><BR><BR><BR><BR><BR>
<BR><BR><BR><BR><BR><BR><BR><BR>

得到如下界面:
6

Stage 5

这部分需要我们修复该漏洞。可以在tomcat\webapps\WebGoat\javascript中找到相关的脚本文件。编辑DOMXSS.js文件,将如下代码:

1
2
3
4
5
function displayGreeting(name) {
if (name != ''){
document.getElementById("greeting").innerHTML="Hello, " + name + "!";
}
}

修改为:

1
2
3
4
5
function displayGreeting(name) {
if (name != ''){
document.getElementById("greeting").innerHTML="Hello, " + escapeHTML(name); + "!";
}
}

其中,escapeHTML()为:

1
2
3
4
5
6
function escapeHTML (str) {
var div = document.createElement('div');
var text = document.createTextNode(str);
div.appendChild(text);
return div.innerHTML;
}

最后成功了。

Client Side Filtering

客户端过滤。

Stage 1

这部分假设我是公司财务系统中的一员,我可以看到所有人的工资信息,但只有经理的工资信息我是没有权限看到的,需要我想办法看到经理的工资并提交。

这部分的漏洞是在于服务器收到客户端的请求后,向客户端发送了过多的数据,虽然有一部分被隐藏起来了,普通页面看不到,但是仍然可以通过Firebug找到。如下图所示:

3

以上是其页面相应的选择框,其对应的页面源代码为:
4

而显示出来的结果,表格内容,其对应的页面源代码为:
5

由此看到了相对应的权限外的信息。

Stage 2

这部分需要修补这个漏洞,让服务器只返回应该返回的信息。
这个漏洞的主要问题是服务端将所有数据返回给客户端,尽管这些敏感数据被隐藏了,但仍然可以被发现。因此该漏洞的填补是在XPath查询中增加一个过滤器。
在tomcat\webapps\WebGoat\lessons\Ajax\clientSideFiltering.jsp中,将源代码:

1
2
3
4
5
6
7
StringBuffer sb = new StringBuffer();
sb.append("/Employees/Employee/UserID | ");
sb.append("/Employees/Employee/FirstName | ");
sb.append("/Employees/Employee/LastName | ");
sb.append("/Employees/Employee/SSN | ");
sb.append("/Employees/Employee/Salary ");
String expression = sb.toString();

修改为:

1
2
3
4
5
6
7
StringBuffer sb = new StringBuffer();
sb.append("/Employees/Employee[Managers/Manager/text() = " + userId + "]/UserID | ");
sb.append("/Employees/Employee[Managers/Manager/text() = " + userId + "]/FirstName | ");
sb.append("/Employees/Employee[Managers/Manager/text() = " + userId + "]/LastName | ");
sb.append("/Employees/Employee[Managers/Manager/text() = " + userId + "]/SSN | ");
sb.append("/Employees/Employee[Managers/Manager/text() = " + userId + "]/Salary ");
String expression = sb.toString();

此时,客户端显示的代码如下:
7
可看到,不该看到的内容,已经被很好的隐藏起来了。

Same Origin Policy Protection

同源策略保护。同源策略是指,域名,协议,端口相同。它是客户端脚本,尤其是JavaScript的重要安全度量标准。它阻止从一个源加载的文档/脚本用来获取或设置另一个源加载的文档的属性。
AJAX技术的一项关键元素是XMLHttpRequest(XHR),该技术允许客户端向服务器端发起异步调用。然而,作为一项安全措施,这些请求最好只能从客户端原始页面向服务端发起访问。
这部分的主要目的是让我们熟悉同源策略。开始的时候是如下图所示的:
8
可以看到,下面该页面下面依次有两个超链接,第一个是链接符合同源策略的,第二个链接是不符合同源策略的。
XMLHttp只能向同源服务器发送请求,任何向非同源服务端发送数据的尝试都将失败。

DOM Injection

9
可以看到该课程是要求我们输入激活码,然后网站拿去验证,我们的目标就是激活被禁用的按钮。我们首先需要理解激活码的验证过程。
浏览器与服务器通过Ajax技术实现XML通信。首先查看HTML源代码,发现代码中使用了XMLHttpRequest:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
function validate() {
var keyField = document.getElementById('key');
var url = 'attack Screen=80&menu=1150&from=ajax&key=' +
encodeURIComponent(keyField.value);
if (typeof XMLHttpRequest !='undefined') {
req = new XMLHttpRequest();
} else if (window.ActiveXObject) {
req = new ActiveXObject('Microsoft.XMLHTTP');
}
req.open('GET', url, true);
req.onreadystatechange = callback;
req.send(null);
}
function callback() {
if (req.readyState == 4) {
if (req.status == 200) {
var message = req.responseText;
eval(message);
}
}
}
</script>

可以看到XML响应给客户端浏览器的内容包括激活按钮的JavaScript语句,我们可以拦截服务器的响应,并修改其响应。
在链接传入请求:

10

修改burpsuit的默认设置,开启响应拦截:
11

请求拦截后,依次点击:Action ——> Do intercept ——> Response to this request
设置拦截该请求的响应后,点击Forward,拦截到如下服务器的响应:
12

修改该响应,找到submit这里,

13

之后修改,去掉“disabled”字段,然后forward,可见浏览器显示的激活按钮已经可以按动了!

XML Injection

AJAX应用程序使用XML与服务端进行信息交互。但该XML内容能够被非法用户轻易拦截并篡改。该课程进入之后是如下页面:

14

这是一个积分兑换奖品的页面。
上面显示的是每件商品所对应的积分值。我的编号是:836239,当输入这个编号后,浏览器页面可以看到:
15

我的积分是100分,下面是我能够兑换到的商品列表,我可以选择性勾选后,点击提交。

那么现在就是要求我们找到漏洞,使能够尽可能的买更多的东西,甚至能够超过我的所有积分的商品。

我们可以猜测这件事情,在服务器端的处理逻辑是这样的:当客户输入其编号后,提交给服务器,服务器在后台进行查询,首先查到该客户的积分,然后把低于该积分的商品全部返回给客户端,由客户端进行选择后提交,由于之前已经进行过判断了,所以这里服务器没有再进行验证,因此我们把高于我们积分的商品勾选提交的时候,服务器有可能是通过的。

同样我们用burpsuit拦截服务器返回的消息,如下:
17

然后修改为如下:

18
此时,客户端显示为如下:

16
最后两个也是可以勾选提交的了。

JSON Injection

JSON是JavaScript对象表示法,是存储和交换信息的语法。类似于XML,但JSON比XML更小、更快、更易解析。对于AJAX应用程序来说,JSON比XML更快更易使用。
开始的时候页面是这样的:
19
这是一个关于机票的页面,输入出发地和目的地,浏览器和服务器之间实现Ajax通信,返回机票价格和路线信息,供客户选择,如下所示:
20
可以看到有两条路线,一条便宜的,但中途会停。一条贵的,是直达的。我们的目标就是用便宜的钱坐直达的飞机。

与上一个课程一样,同样是拦截服务器端返回的信息并修改。
原始请求返回信息为:

21

拦截修改后为:
22
价格由600改为100,嘻嘻。。

页面显示为:
23

Slient Transactions Attacks

静默交易攻击。

进去之后看到:
24
这是一个银行转账的页面,上面显示了我的金额,目标账户和转账额度,当点击“Confirm”后,我的金额就转出去了,会减少相应的数值。
通过查看页面源代码,可以看到如下重要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<script>
function processData(){
var accountNo = document.getElementById('newAccount').value;
var amount = document.getElementById('amount').value;
if ( accountNo == ''){
alert('Please enter a valid account number to transfer to.')
return;
}
else if ( amount == ''){
alert('Please enter a valid amount to transfer.')
return;
}
var balanceValue = document.getElementById('balanceID').innerHTML;
balanceValue = balanceValue.replace( new RegExp('$') , '');
if ( parseFloat(amount) > parseFloat(balanceValue) ) {
alert('You can not transfer more funds than what is available in your balance.')
return;
}
document.getElementById('confirm').value = 'Transferring'
submitData(accountNo, amount);//转账
document.getElementById('confirm').value = 'Confirm'
balanceValue = parseFloat(balanceValue) - parseFloat(amount);
balanceValue = balanceValue.toFixed(2);
document.getElementById('balanceID').innerHTML = balanceValue + '$';
}
function submitData(accountNo, balance) {
var url = 'attack?Screen=40&menu=400&from=ajax&newAccount='+ accountNo+ '&amount=' + balance +'&confirm=' + document.getElementById('confirm').value;
if (typeof XMLHttpRequest != 'undefined') {
req = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
req = new ActiveXObject('Microsoft.XMLHTTP');
}
req.open('GET', url, true);
req.onreadystatechange = callback;
req.send(null);
}
function callback() {
if (req.readyState == 4) {
if (req.status == 200) {
var result = req.responseText ;
var resultsDiv = document.getElementById('resultsDiv');
resultsDiv.innerHTML = '';
resultsDiv.innerHTML = result;
}
}
}
</script>

有两个关键函数,一个是processData(),一个是submitData()。
当用户输入账户名和额度后,点击“Confirm”,在转账交易前,会首先调用processData()来判断用户的输入以及转账额度是否合理等,然后调用函数submitData(accountNo, amount)进行转账,而该函数实际上是向后台提交转账所需的重要信息:目标账号和转账金额。
如果通过浏览器直接执行submitData(accountNo,balance)函数,那么就可以绕过客户端验证,不需要任何额外的确认和用户数字签名。这里指的是用浏览器直接执行,而不是系统给定的“Confirm”按钮。在浏览器地址栏输入如下内容:

javascript:submitData(1,1)

转账成功:

25

Dangerous Use of Eval

危险指令使用。eval()函数可以编译并执行任何JS代码。这隐藏了一个潜在的安全问题。进入之后看到如下页面:

26
这是一个购物的网页,要求通过alert()函数显示出document.cookie,3位数的授权码会进入服务器进行验证,因此可以针对该授权码进行特别构造。
输入如下内容:

123’);alert(document.cookie);(‘

则服务器收到并返回的JavaScript结果是:

eval(‘123’);
alert(document.cookie);
(‘’);

Insecure Client Storage

不安全的客户端存储。在服务器端验证所有的用户输入信息是很好的,但是在客户端输入的任何验证信息都存在被逆向分析的脆弱性。记住,客户端的任何数据都不能被视为机密!

Stage 1

进去之后,显示页面是这样的:

27

要求我们发现意外的购物优惠码。这就要求我们跟踪购物优惠码的执行过程,按F12打开firebug进行JS的调试。点击进入“脚本”,然后设置断点,在对应输入框里面随便输入某一个值,脚本自动运行到断点处停下来。如下图所示:
28
此时查看变量decrypted的值为:PLATINUM

这就是我们发现的优惠码。

Stage 2

要求我们绕过客户端的验证,提交订单却不花钱。这个需要在客户端修改价格,利用firebug,在HTML中找到readonly=””,删除该属性,则可在页面上随意修改商品价格,然后提交免费的订单!

Summarize

这一节主要是Ajax安全,有很多内容,在客户端修改,拦截服务器的响应修改等,非常实用,并且拓宽了自己的知识面。


【版权声明】
本文首发于戚名钰的博客http://qimingyu.github.io/ ),欢迎转载,但是必须保留本文的署名戚名钰(包含链接)。如您有任何商业合作或者授权方面的协商,请给我留言:mingyuqi.java@qq.com
欢迎关注我的微信公众号:科技锐新

本文永久链接:http://qimingyu.github.io/2016/04/26/Webgoat之AJAX_Security/

坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章