企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 环境搭建 这个渗透环境的搭建有以下几点 基于session的会话 登录界面 登录成功界面 注销界面 数据库搭建 数据库连接 # session会话 服务器端利用session_start()函数发起一次session的会话 此时我们登录成功后用户的数据被保存在服务器端的Cookie: session= ,即sessionID 如果需要再次访问 服务器端的$_SESSION['...']会获取用户session 然后与原本存在于服务器的sessionID进行比对,如果比对成功,则证明用户正确 # 环境搭建代码 ## 创建数据库脚本 在MySQL中使用source命令即可运行脚本: ``` drop database if exists lab; create database lab; use lab; create table users ( id int not null auto_increment, username char(32) not null, passcode char(32) not null, primary key(id) ); insert into users(username,passcode) values('admin','admin123'); insert into users(username,passcode) values('alice','alice456'); ``` 登录界面html: ``` <html> <head> <meta charset="UTF-8"> <title>Login</title> <style> #a { width: 500px; text-align: center; } .b { width: 200px; height: 30px; } </style> </head> <body> <div id=a> <h2>Login!</h2> <form name="form_login" method="POST" action="check_login.php"> Username:<input type="text" class="b" name="username" /><br> <br> Password:<input type="password" class="b" name="password" /><br> <input type="submit" name="Submit" value="Submit" /> <input type="reset" name="reset" value="Reset" /> </form> </div> </body> </html> 查询数据库是否为正确的账号密码php代码 <?php include('con_database.php'); $username=isset($_POST['username'])?$_POST['username']:''; $password=isset($_POST['password'])?$_POST['password']:''; if($username=='' || $password==''){ echo "<script>alert('请输入账号和密码!')</script>"; exit; } $sql="select * from users where username='$username' and passcode='$password'"; $query=mysqli_query($con,$sql) or die('SQL语句执行失败'.mysqli_error($con)); if ($row=mysqli_fetch_array($query)){ session_start(); $_SESSION['username']=$row[1]; echo "<a href='welcome.php'>欢迎访问</a>"; }else{ echo "<script>alert('登录失败!');history.go(-1)</script>"; } mysqli_close($con); ?> ``` 连接数据库php代码: ``` <?php $con=mysqli_connect('127.0.0.1','root','root') or die("数据库连接失败!"); mysqli_select_db($con,'lab')or die("数据库连接失败"); ?> ``` 注销登录代码(即关闭session会话) ``` <?php session_start(); session_unset(); session_destroy(); echo "注销成功"; ?> ``` 登录成功欢迎界面: ``` <?php session_start(); if(isset($_SESSION['username'])){ echo "欢迎用户".$_SESSION['username']."登录"; echo "<br>"; echo "<a href=logout.php>退出登录</a>"; }else{ echo "您没有权限访问"; } ?> ``` 至此,我们的渗透环境就构建好了 # 万能密码漏洞剖析 用户名输入' or 1=1 or',密码随意,发现可以登录进去 密码输入 'or '1=1 也可以登录进去 当然登录方法不止一种: 原来查询语句是这样的:$sql="select * from users where username='$username' and passcode='$password'"; 经过注入之后,变成:$sql="select * from users where username='' or 1=1 or ' and passcode='****'"; 我们观察到,where后面呃字句中的username被闭合,并且字句分成三个句子并用or连接。 在SQL语句中 and的优先级要大于or,所以1=1先判断,为真,即where后面的语句为真,即整个SQL语句为真,即表示查询正确 而形成的语句可以将整个users表查询,后面的$row=mysqli_fetch_array($query)选择的是查询的第一行值,这样满足了SQL语句并跳过了登录验证 由此可以引申出,只要where后面字句为真,即可跳过验证,有如下衍生方法: ' or 1=1 # ' or 1=1 -- (后面有空格) 'or"="or' # 万能密码攻击防护 使用正则表达式限制用户输入: 可以使用正则表达式限制用户的用户名输入,比如:/^[a-z0-9A-Z_]{5,16}$/ 这个限制了用户5位以上16位以下的字母数字下划线为用户名的输入 这个限制在check_login.php中添加 ``` <?php include('con_database.php'); $username=isset($_POST['username'])?$_POST['username']:''; $password=isset($_POST['password'])?$_POST['password']:''; if (!preg_match("/^[a-Z0-9A-Z_]{5,16}$/",$username)){ echo "<script>alert('用户名格式错误')</script>"; exit; if($username=='' || $password==''){ echo "<script>alert('请输入账号和密码!')</script>"; exit; } $sql="select * from users where username='$username' and passcode='$password'"; $query=mysqli_query($con,$sql) or die('SQL语句执行失败'.mysqli_error($con)); if ($row=mysqli_fetch_array($query)){ session_start(); $_SESSION['username']=$row[1]; echo "<a href='welcome.php'>欢迎访问</a>"; }else{ echo "<script>alert('登录失败!');history.go(-1)</script>"; } mysqli_close($con); } ?> ``` 使用PHP转义函数: addslashes()函数: 能够将单引号、双引号、反斜杠和null转义 mysql_escape_string()函数、mysql_real_escape_string()函数 这个是转义SQL语句中的符号,php7.x版本的都要变成mysqli ``` $username=isset($_POST['username'])?addslashes($_POST['username']):''; $password=isset($_POST['password'])?mysqli_real_escape_string($con,$_POST['password']):''; ``` 转义函数的弊端 因为使用的是UTF-8编码,不是宽字节编码,形成的'会被变成%5c%27 Windows下默认的是宽字节的gbk编码 如果在%5c前面加上一个字符形成一个复杂的汉字,那么单引号仍然会被输出 # MySQLi 参数化查询 在使用参数化查询的情况下,服务器不会将参数的内容是为SQL指令中的一部分 而是在数据库完成SQL指令的编译之后,再代入参数运行 此时就算参数里面有恶意数据 但是此时SQL语句以及编译完成 就不会被数据库运行 PHP提供了三种访问mysql数据库的拓展: MySQL (PHP5.5起,已经废除) MySQLi PDO(PHP Data Object PHP数据对象) PDO和MySQLi提供面向对象的api MySQLi也存在面向过程的api,所以容易从MySQL转换到MySQLi 下面是mysqli形式的check_login.php 写法,新建check_login_mysqli.php ``` <?php include('con_database.php'); $username=isset($_POST['username'])?$_POST['username']:''; $password=isset($_POST['password'])?$_POST['password']:''; if($username==''||$password==''){ echo "<script>alert('错误!');history.go(-1);</script>"; exit; } $sql="select * from users where username=? and passcode=? ;";//问号表示需要一个参数 $stmt=$con->prepare($sql);//预编译SQL语句 if(!$stmt){ echo 'prepare 执行错误'; } else{ $stmt->bind_param("ss",$username,$password); //为预编译绑定SQL参数,ss表示两个字符串 //i——int d——double s——string b——boolean $stmt->execute(); $result=$stmt->get_result(); $row=$result->fetch_row(); if($row){ session_start(); $_SESSION['username']=$row[1]; echo $row[1]."<a href='welcome.php'>欢迎访问</a>"; }else{ echo "<script>alert('登录失败!!');history.go(-1);</script>"; } $stmt->close(); } $con->close(); ?> ``` 一些内容已经标记在代码的注释里面 参数化的PHP代码真的能够很有效地防止SQL注入。