PHP代码审计之----如何挖PHP的SQL注入漏洞
发布于 2021-05-12 22:35 ,所属分类:区块连和PHP开发学习资料
代码审计流程
反向查找流程
全局查找危险函数,某些危险函数的出现容易产生漏洞 确定用户输入是否可以传入到危险函数
之前整理的关于Z-Blog
的xxe
漏洞就是一个典型的这种例子:https://mp.weixin.qq.com/s/R6mDpyeQjSUrY_40pgDzZg
特点
上下文无关 危险函数,调用即漏洞 适用于自动化代码审计工具 适应性差,不适合存在全局过滤的站点
注:这种漏洞相对来说越来越少。
常见的自动化审计工具:
RISP
VCG
Fortify SCA
Cobra
grepbugs
Sky wolf
Taint
正向查找流程
从入口点函数出发 找到控制器,理解URL派发规则 跟踪控制器调用,以理解代码为目标进行源码阅读 发现漏洞
特点
需要了解目标源码的功能与架构 需要了解常见的框架模式如: MVC
,MTV
等涉及到多个漏洞的组合,很可能存在组合漏洞,逻辑漏洞 自动化工具不好挖掘,更加需要挖洞者理解业务逻辑
注:这种漏洞潜力无限,很可能就会存在潜在的漏洞
双向查找流程
实际代码审计过程中通常会将反向查找流程和正向查找流程进行结合。
略读代码,了解架构
看是否有全局过滤机制
完全没有处理:直接可以完成筛子 有处理:寻找遗漏的处理点 可以:寻找漏洞触发点 不可以:寻找没有过滤的变量 有过滤机制:是否可以绕过? 没有过滤机制:
特点
需要理解程序执行过程,寻找危险逻辑点 高效,双向开工 需要掌握的知识面广,同时掌握正向和方向挖掘
PHP
SQL
注入漏洞挖掘技巧
PHP
项目经典的项目架构:LAMP
或者LNMP
, 所以经常使用到的数据库就是MySQL
PHP
连接MySQL
的方式
Mysql
(已经废弃,可能特别老的项目还在使用)Mysqli
PDO
PHP
中SQL
注入常见的过滤方法
intval/addslashes/mysql_real_escape
mysqli_escape_string / mysqli_real_escape_string / mysqli::escape_string
PDO::quote
参数化查询,即预编译
intval
: 获取变量的整数值
addslashes
: 该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线。这些字符包括:单引号('
)、双引号("
)、反斜线(\
)与 NUL
(「null
」 字符)。
mysql_real_escape_string
: 自 PHP 5.5.0
起已废弃, 「mysql_real_escape_string()」 调用mysql库的函数 mysql_real_escape_string
, 在以下字符前添加反斜杠: \x00
, \n
, \r
, \
, '
, "
和 \x1a
.
当使用了 addslashes
或者 mysql_real_escape
的时候并不是一定就不存在注入了,思路有:
宽字节注入 是否存在字符串转换函数: urldecode
base54_decode
iconv
json_decode
stripshasles
simple_xml_loadstring
使用了 mysqli::escape_string / PDO::quote
也是同样的,思路:
宽字节注入 是否会主动加引号包裹
参数化查询,也是会有漏网之鱼,思路:
寻找非 SQL
值的位置, 因为在预编译的时候只有SQL
值的地方会进行预编译,那么剩下的非值的地方依然是有可能存在SQ
L注入
哪些地方可能存在SQL
注入?
主要包含两个方面:
开发者容易遗漏的点 引入单引号(转义符)的方法
开发者容易遗漏的点有:
HTTP
头:X-Forwarded-For
,User-Agent
,Referer
PHP_SELF
REQUEST_URI
文件名 php://input
引入单引号(转义符)的方法 的点有:
stripslashes
base64_decode
urldecode
substr
iconv
str_replace('0', $sql)
xml
json_encode
举个栗子
common.php
代码如下:
<?php
try{
$dbhost='127.0.0.1';
$dbname='test_sql';
$dbuser='root';
$dbpass='123456'
$option=[
PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION
];
$conn=newPDO("mysql:host=$dbhost;dbname=$dbname",$dbuser,$dbpass,$option);
}catch(PDOException$e){
echo"Error:".$e->getMessage()."<br/>";
die();
}
栗子1
1.php
代码如下:
<?php
include_once'./common.php';
try{
$name=$_GET["name"];
$query="SELECTname,age,email,countryFROMuser_detailsWHEREname='{$name}';";
$stmt=$conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('email',$email);
while($row=$stmt->fetch(PDO::FETCH_BOUND)){
echo"$email"."<br/>";
}
}catch(PDOException$e){
echo$e->getMessage();
}
很明显,这个代码中并未对传入的name字段进行过滤,所以是存在过滤的,例如我们通过如下方式即可进行注入:
http://192.168.78.135:40004/1.php?name=aaa%27%20%20union%20select%201,2,database(),4--%20#
栗子2
2.php
代码如下:
<?php
include_once'./common.php';
try{
$name=addslashes($_GET["name"]);
$query="SELECTname,age,email,countryFROMuser_detailsWHEREname='{$name}';";
$stmt=$conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('email',$email);
while($row=$stmt->fetch(PDO::FETCH_BOUND)){
echo"$email"."<br/>";
}
}catch(PDOException$e){
echo$e->getMessage();
}
抛开宽字节的问题,当前代码其实是没有注入的
栗子3
3.php
代码如下:
<?php
include_once'./common.php';
try{
$name=htmlspecialchars($_GET["name"]);
$query="SELECTname,age,email,countryFROMuser_detailsWHEREname='{$name}';";
$stmt=$conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('email',$email);
while($row=$stmt->fetch(PDO::FETCH_BOUND)){
echo"$email"."<br/>";
}
}catch(PDOException$e){
echo$e->getMessage();
}
这段代码其实还是存在注入的,注入的方式:
http://192.168.78.135:40004/1.php?name=aaa%27%20%20union%20select%201,2,database(),4--%20#
栗子4
4.php
代码如下:
<?php
include_once'./common.php';
try{
$age=addslashes($_GET["age"]);
$query="SELECTname,age,email,countryFROMuser_detailsWHEREage>{$age};";
echo$query;
$stmt=$conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('email',$email);
while($row=$stmt->fetch(PDO::FETCH_BOUND)){
echo"$email"."<br/>";
}
}catch(PDOException$e){
echo$e->getMessage();
}
这段代码虽然使用了addslashes
,但是sql
语句中的变量并没有使用引号,所以addslashes
没有起作用,还是存在注入的,注入的方式:
http://192.168.78.135:40004/4.php?age=18%20union%20select%201,2,database(),4
栗子5
5.php
代码如下:
<?php
include_once'./common.php';
try{
$name=str_replace("'","\\'",$_GET["name"]);
$query="SELECTname,age,email,countryFROMuser_detailsWHEREname='{$name}';";
echo$query;
$stmt=$conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('email',$email);
while($row=$stmt->fetch(PDO::FETCH_BOUND)){
echo"$email"."<br/>";
}
}catch(PDOException$e){
echo$e->getMessage();
}
这段代码是一些开发者可能经常会犯的一个错误,就是认为只要把单引号进行了替换为\'
就没事了,但是却忽略了如果攻击者传入的是\'
这个时候其实会被替换成 \\'
,从而使我们依然可以通过如下进行注入:
http://192.168.78.135:40004/5.php?name=aaa\%27%20%20union%20select%201,2,database(),4--%20#
栗子6
6.php
代码如下:
<?php
include_once'./common.php';
try{
$id=intval($_GET["id"]);
$query="SELECTname,age,email,countryFROMuser_detailsWHEREid>{$id};";
echo$query;
$stmt=$conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('email',$email);
while($row=$stmt->fetch(PDO::FETCH_BOUND)){
echo"$email"."<br/>";
}
}catch(PDOException$e){
echo$e->getMessage();
}
这个代码是没有注入漏洞的
栗子7
7.php
代码如下:
<?php
include_once'./common.php';
try{
if(intval($_GET["id"])){
$query="SELECTname,age,email,countryFROMuser_detailsWHEREid>$_GET["id"];";
echo$query;
$stmt=$conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('email',$email);
while($row=$stmt->fetch(PDO::FETCH_BOUND)){
echo"$email"."<br/>";
}
}
}catch(PDOException$e){
echo$e->getMessage();
}
这段代码是存在注入的,虽然intval
对参数进行了转换,但是这里并不会进行判断当前参数是否是数字
栗子8
8.php
代码如下:
<?php
include_once'./common.php';
try{
if(!is_numeric($_GET["id"])){
header("status:404notfound");
}
$query="SELECTname,age,email,countryFROMuser_detailsWHEREid>$_GET["id"];";
echo$query;
$stmt=$conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('email',$email);
while($row=$stmt->fetch(PDO::FETCH_BOUND)){
echo"$email"."<br/>";
}
}catch(PDOException$e){
echo$e->getMessage();
}
这段代码的问题在于,虽然使用了is_numeric对参数进行的判断,但是这里使用了header函数之后并没有退出,而是接着往下执行,导致依然会存在注入
栗子9
9.php
的代码如下:
<?php
include_once'./common.php';
try{
$order=addslashes($_GET['order']);
$query="SELECTname,age,email,countryFROMuser_detailsORDERBYid{$order};";
echo$query;
$stmt=$conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('email',$email);
while($row=$stmt->fetch(PDO::FETCH_BOUND)){
echo"$email"."<br/>";
}
}catch(PDOException$e){
echo$e->getMessage();
}
这个代码依然存在注入漏洞
栗子10
10.php
代码如下:
<?php
include_once'./common.php';
try{
$name=addslashes($_GET['order']);
$name=urldecode($name);
$query="SELECTname,age,email,countryFROMuser_detailsWHEREname='{$name}';";
echo$query;
$stmt=$conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('email',$email);
while($row=$stmt->fetch(PDO::FETCH_BOUND)){
echo"$email"."<br/>";
}
}catch(PDOException$e){
echo$e->getMessage();
}
虽然使用了addslashes
但是在下文使用了urldecode
那么就会依然导致注入漏洞的存在
相关资源