标题 简介 类型 公开时间
关联规则 关联知识 关联工具 关联文档 关联抓包
参考1(官网)
参考2
参考3
详情
[SAFE-ID: JIWO-2024-3158]   作者: 浩丶轩 发表于: [2022-08-12]

本文共 [225] 位读者顶过

发现漏洞

一、环境准备

1、在Linux主机上准备一套Xampp:模拟攻防

2、在VSCode利用Remote Development进行远程调试

3、在Lampp的htdos目录下创建security目录,用于编写服务器PHP代码

二、编写Login.html[出自:jiwo.org]

三、编写Login.php


<?php
 // 获取用户提交的登录请求数据
$username = $_POST['username'];
$password = $_POST['password'];
$vcode = $_POST['vcode'];


// 验证码的验证,OWASP-认证和授权失败
if($vcode !== '0000'){
    die("vcode-error");    //向前端输出一条消息同时结束代码的运行
}


// 连接数据库
$conn = mysqli_connect('127.0.0.1','root','123456','learn') or die("数据库连接不成功!");


// 设置数据库的编码格式
mysqli_query($conn,"set names utf8;");


$sql = "select * from user where username ='$username' and password = '$password'";
$result = mysqli_query($conn,$sql);     //$result称之为结果集


// 以下代码没用进行爆破的防护(OWASP-认证和授权失败)


if (mysqli_num_rows($result) == 1){
     echo "login-pass<br/>";


    echo "<script>location.href='welcome.php'</script>";
}
else{
    // echo "login-fail<br/>";
    echo "<script>location.href='login.html'</script>";
}


//关闭数据库
mysqli_close($conn)
?>


四、编写一个登录后才能访问的welcome.php


<?php
// OWASP-失效访问控制
    // 修复方法:在显示文本之前,先进行SESSION变量的验证
include "common.php";
//isset() 函数用于检测变量是否已设置并且非 NULL。
if (!isset($_SESSION['islogin']) or $_SESSION['islogin'] != 'true'){
    die ("请登录后再访问此页面</br>");
}
echo '欢迎来到安全测试平台';
?>


五、进行登录的渗透测试

在登录界面输入一个单引号[']作为用户名,Burp响应如下:


<b>
Warning</b>: mysqli_num_rows() expects parameter 1 to be mysqli_result, boolean given in <b>/opt/lampp/htdocs/security/login.php</b>


以上响应出现MySQL报错信息,上述报错信息存在两个漏洞:

1、单引号可以成功引起SQL语句报错,说明后台没有专门对单引号进行处理


select * from user where username ='$username' and password = '$password'
  正常情况:select * from user where username ='root' and password = '$password'
  攻击情况:select * from user where username =''' and password = '$password'
    攻击Payload:
             username:x' or userid=1#'
    Post正文:
             username=x' or+userid=1#'&password=111111&vcode=0000
    select * from user where username ='x' or userid=1#'' and password = '$password'


2、在报错信息里泄露了敏感信息


/opt/lampp/htdocs/security/login.php(当前代码的绝对路径)


六、总结上述代码一共发现了6个漏洞
1、welcome.php页面谁都可以访问,没有进行登录判断(中)2、在登录界面输入’作为用户名,报错信息存在login.php的绝对路径,暴露了系统后台的敏感信息(低)3、保存用户信息的数据表中,密码字段是明文保存的,不够安全(中)4、登录界面可以进行SQL注入,进而轻易实现登录(高)5、login.php页面使用了万能验证码(中)6、登录功能可以被爆破,没有进行爆破防护(中)

SQL注入-登录漏洞-基础修复
一、使用Python进行注册测试

import requests
# 利用Python对PHP的登录界面进行Fuzz测试


def login_fuzz():
    # 先使用单引号进行测试
    url = 'http://192.168.72.148/security/login.php'
    data = {'username': "'", 'password': '666666', 'vcode': '0000'}
    resp = requests.post(url=url, data=data)


    if 'Warning' in resp.text:
        print("本登录功能可能存在SQL注入漏洞,可以尝试")
        # 如果单引号存在利用嫌疑,则继续利用
        payload_list = ["x' or id=1#", "x' or userid=1#", "x' or userid=2#"]
        for username in payload_list:
            data = {'username': username, 'password': '666666', 'vcode': '0000'}
            resp = requests.post(url=url, data=data)
            if "login-fail" not in resp.text:
                print(f'登录成功,payload为:{data}')
    else:
        print('通过试探,发现后台界面对单引号不感兴趣;')


if __name__ == '__main__':
    login_fuzz()


二、任意访问授权界面welcome.php页面谁都可以访问,没有进行登录判断,该页面是登录后才能访问,所以在该页面需要进行登录判断,代码修改为:
1、在common.php中添加session_start(),让其他页面引入,便于直接使用Session


<?php
session_start();
function create_connection(){
    // 连接数据库
    $conn = mysqli_connect('127.0.0.1','root','123456','learn') or die("数据库连接不成功!");
    // 设置数据库的编码格式
    mysqli_query($conn,"set names utf8;");
    mysqli_set_charset($conn,'utf8');
    return $conn;
}
?>


2、在welcome.php页面中,源代码修改为


<?php
// OWASP-失效访问控制
    // 修复方法:在显示文本之前,先进行SESSION变量的验证
include "common.php";
//isset() 函数用于检测变量是否已设置并且非 NULL。
if (!isset($_SESSION['islogin']) or $_SESSION['islogin'] != 'true'){
    die ("请登录后再访问此页面</br>");
}
echo '欢迎来到安全测试平台';
?>


3、在login.php中,登录成功后添加以下代码


if (mysqli_num_rows($result) == 1){
    echo "login-pass<br/>";
    // 登录成功后,记录SESSION变量
    $_SESSION['username'] = $username;
    $_SESSION['islogin'] = 'true';
    echo "<script>location.href='welcome.php'</script>";
}


三、修复login.php暴露文件路径当在用户名输入单引号时,会引起后台报错,一方面说明后台没有对单引号进行转义处理,导致单引号可以被注入到SQL语句中,进而导致SQL语句中存在单独的一个单引号,SQL语句无法有效闭合,发生错误。同时,还将该代码的绝对路径暴露出来,属于敏感信息,应该将其屏蔽,修复代码如下:


 $result = mysqli_query($conn,$sql) or die("SQL语句执行错误!") ;
四、修改用户表密码为明文1、使用md5函数



$source = 'YikJiang';
echo md5($source);


//提示一:user表中password字段必须是32+位
//提示二:在用户注册时,必须使用md5函数将密码加密保存


SQL注入-登录漏洞-SQL注入防护

从代码和SQL语句的逻辑层面进行考虑,不能轻易让密码对比失效
基于将用户输入的引号(单引号和双引号)进行转义处理的前提,可以使用PHP内置函数addslashes进行强制转义

一、登录SQL语句的逻辑问题
1、该SQL语句在实现登录操作时,存在严重的逻辑问题,用户名和密码的对比不应该放在同一条SQL语句中。
2、应先通过用户名查询user表,如果确实找到一条记录(用户名唯一的情况下),找到记录后再进行密码的单独对比。
修复后的代码如下:


$sql = "select * from user where username ='$username'";
$result = mysqli_query($conn,$sql) or die("SQL语句执行错误!") ;     //$result称之为结果集
// 以下代码没用进行爆破的防护(OWASP-认证和授权失败)
//如果用户名真实存在,刚好找到一条,则再单独进行密码比较,即使用户名出现SQL注入漏洞,但是只要密码不正确,也无法登录
if (mysqli_num_rows($result) == 1){
    // $row = mysqli_fetch_all($result,MYSQLI_ASSOC);       //索引数组+下标数组
    // $row = mysqli_fetch_row($result);       //索引数组
    $row = mysqli_fetch_assoc($result);     //下标数组
    // var_dump($row);
    if ($password == $row['password']){
        echo "login-pass<br/>";
        // 登录成功后,记录SESSION变量
        $_SESSION['username'] = $username;
        $_SESSION['islogin'] = 'true';
        echo "<script>location.href='welcome.php'</script>";
    }
    else{
        echo "login-fail";
    }

}
else{
    echo "login-fail<br/>";
    echo "<script>location.href='login.html'</script>";
}


二、使用addslashes函数addslashes函数可以将字符串中的单引号、双引号、反斜杠、NULL值自动添加转义符,从而防止SQL注入中对单引号和双引号的预防。原始SQL语句如下:


select
* from user where username ='$username' and password = '$password'


如果用户输入x' or userid=1#',则SQL语句变成:


select
* from user where username ='x' or userid=1#'' and password = '$password'


如果使用addslashes强制为用户输入添加转义符,则变成:

select * from user where username ='x\' or userid=1#\'' and password = '$password'

上述SQL语句的用户名为:x\’ or userid=1#\’
三、使用MySQL面向对象方式1、面向过程方式使用PHP自带的函数


// 面向过程的方式
function create_connection(){
    // 连接数据库
    $conn = mysqli_connect('127.0.0.1','root','123456','learn') or die("数据库连接不成功!");
    // 设置数据库的编码格式
    mysqli_query($conn,"set names utf8;");
    mysqli_set_charset($conn,'utf8');
    return $conn;
}
// 将数据库查询的结果集中的数据取出,保存到一个数组当中
$row = mysqli_fetch_assoc($result);
//mysqli_fetch_all默认使用索引数组,也可以设定参数强制使用关联数组
// $row = mysqli_fetch_all($result,MYSQLI_ASSOC);      
// $row = mysqli_fetch_row($result);       //索引数组


2、面向对象方式


function create_connection_oop(){
    
    $conn = new mysqli('127.0.0.1','root','123456','learn') or die("数据库连接不成功!");
    // 两种方法设置字符集
    // 1、$conn->query("set names utf8;")
    // 2、
    $conn->set_charset('utf8');
    return $conn;
}


// 执行SQL语句
function test_mysqli_opp(){
    $conn = create_connection_oop();
    $sql = "select * from user where userid < 6";
    $result = $conn->query($sql);
    //获取结果集行数
    echo $result->num_rows."</br>";
    // 获取结果集数组
    // 使用关联数组
    $rows = $result->fetch_all(MYSQLI_ASSOC);
    // var_dump($rows);
    // 遍历数组
    foreach ($rows as $row){
        echo "username:". $row['username'] . ",passwrod" . $row['password'] . "</br>";
    }
}


四、使用MySQL预处理功能1、预处理功能的用法预处理的过程,就是先交给SQL数据库进行SQL语句的准备,准备好后再将SQL语句中的参数进行值的替换,引号会进行转义处理,将所有参数变成普通字符串,再进行第二次正式的SQL语句执行。MySQL的预处理既支持面向过程,也支持面向对象方式,但是我们后续直接使用面向对象的方式。

$conn = create_connection_oop();
$sql = "select userid,username,password,role from user where username= ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s",$username);       // 绑定查询参数
$stmt->bind_result($userid,$username2,$password2,$role);    // 绑定结果参数
$stmt->execute();       // 执行
$stmt->store_result();  //调用结果


2、使用预处理来防止SQL注入


3、配置MySQL临时日志查看SQL语句在MySQL数据库中运行以下语句,开启临时日志,将日志信息保存到表格mysql数据库的general_log表中。



#开启
use mysql;
set global log_output = 'TABLE';
set global general_log = 'ON';
#确认
show variables like "general_log";


MySQLi的预处理功能同样支持面向过程和面向对象 除了MySQLi用于处理数据库外,在PHP中还有最传统的MySQL和PDO两种方式
SQL注入-登录漏洞-验证码处理


一、验证码生成原理

核心目的是确保是人在使用系统,图片验证码、拖动验证码、拼图验证码、问答验证码、计算验证码等。
二、验证码代码实现

添加源代码vcode.php,基于PHP绘制基础图片,生成验证码,然后将该验证码保存到Session变量


<?php
// 利用Session保存图片验证码
session_start();
getCode();
// 生成验证码图片
function getCode($vlen=4 , $width = 80 , $height = 25){
    // 定义响应类型为PNG图片
    header("content-type:image/png");
    // 生成随机验证码字符串,并将其保存于Session中
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
    $vcode =substr(str_shuffle($chars),0,$vlen);
    $_SESSION['vcode'] = $vcode;
    // 定义图片并设置背景色RGB为100,200,100
    $image = imagecreate($width,$height);       // 创建一个画布
    $imageColor = imagecolorallocate($image,100,200,100);
    // 以RGB=0,0,0,的颜色回值黑色的文字
    $color = imagecolorallocate($image,0,0,0);
    imagestring($image,5,20,5,$vcode,$color);       // 5:字体大小  20:距离x轴的距离   5:距离y轴的距离
    // 生成随机位置的干扰点
    for($i=0;$i<50;$i++){
        imagesetpixel($image,rand(0,$width),rand(0,$height),$color);
    }
    // 输出图片验证码,并将其在内存的数据销毁
    imagepng($image);
    imagedestroy($image);
}
?>


三、修改HTML页面调用验证码

修改login.html


input[name='vcode']{
  width: 200px;
}
<body style="background-image:url(./image/1.png); background-size:cover;">
        <div class="login top-100 font-30">YikJiang</div>
        <form action="login-3.php" method="post">
            <div class="login">
                <input type="text" name="username" />
            </div>
            <div class="login">
                <input type="password" name="password" />
            </div>
            <div class="login">
                <input type="text" name="vcode" /> &nbsp;&nbsp;&nbsp;
                <img src="vocde.php"/>
            </div>


            <div class="login">
                <!--*type="submit" 代表按钮类型为提交表单-->
                <button type="submit">登录</button>
            </div>
        </form>
    <div class="footer top-100">版权所有©YikJiang 沪ICP备18011293号</div>
</body>
</html>


四、登录的验证码校验

修改login.php


if(strtoupper($_SESSION['vcode'])  != strtoupper($vcode) ){
    die("vcode-error");    //向前端输出一条消息同时结束代码的运行
}


验证码一旦生成后,不一定必须保存在Session中,任何可以存储数据的方式均可以。比如数据库、文件、内存中,或者保存在Redis缓存服务器中。比如短信验证码,通常会有一个时间限制(5分钟内有效),最好的解决方案就是使用Redis缓存,并设置Key的过期时间
声明:

仅供安全研究与学习之用,若将工具做其他用途,由使用者承担全部法律及连带责任,作者不承担任何法律及连带责任。

评论

暂无
发表评论
 返回顶部 
热度(225)
 关注微信