SQL 注入攻击是一种网络安全威胁,攻击者通过在应用程序的用户输入中注入恶意的 SQL 代码,从而实现对数据库的非法访问和操纵。这种攻击利用了应用程序对用户输入的处理不当,导致攻击者能够执行未经授权的数据库操作。
Overview
SQL injection is a code injection technique that exploits the vulnerabilities in the interface between web applications and database servers. The vulnerability is present when user’s inputs are not correctly checked within the web applications before being sent to the back-end database servers.
Many web applications take inputs from users, and then use these inputs to construct SQL queries, so they can get information from the database. Web applications also use SQL queries to store information in the database. These are common practices in the development of web applications. When SQL queries are not carefully constructed, SQL injection vulnerabilities can occur. SQL injection is one of the most common
attacks on web applications.In this lab, we have created a web application that is vulnerable to the SQL injection attack. Our web application includes the common mistakes made by many web developers. Students’ goal is to find ways to exploit the SQL injection vulnerabilities, demonstrate the damage that can be achieved by the attack, and master the techniques that can help defend against such type of attacks. This lab covers the following topics:
• SQL statements: SELECT and UPDATE statements
• SQL injection
• Prepared statement
实验资料:https://seedsecuritylabs.org/Labs_20.04/Web/Web_SQL_Injection/
使用docker搭建实验环境的步骤略。
本次task的目标是熟悉sql语句,需要进入开放数据库服务的容器,在里面进行数据库操作。
实验步骤:
进入开放数据库服务的容器的shell,进入数据库服务。
选择数据库中的表,打印期中的信息。
Select * from credential where Name = ‘Alice’
本次task的目标是在不知道任何一个employee的密码的情况下从主页登录。
实验手册给出了前端的验证代码,对代码进行简单的审计即可发现前端验证系统存在sql注入漏洞。
并没有对要输入的username和password进行处理的代码,但是由于password要进行hash运算,所以password的值不好控制,可以选择构造username的值实现sql注入。
在网页上进行sql注入,在知道管理员账号为admin的情况下登录管理员账号。
实验步骤:
将构造好的username值输入,直接登录。
点击Login直接登陆成功了。
分析:
因为验证部分没有对输入进行检查处理,并且代码逻辑不够完善,我们构造username的值使原始的sql语句中途截断。
Username = admin’ – (最后有一个空格)
使得sql语句变成了 SELECT * FROM credential WHERE name = ‘admin’ – and Password = …….
由于‘ – ’ 在sql语句中表示注释,所以后面对Password的判断没有生效,所以查询出来的结果就是admin的信息,根据后面代码的逻辑,就可以成功以admin的身份登录。
使用命令行发送http请求完成sql注入。
注意:使用curl命令时,如果有特殊符号(如&),需要放在同网址一起放在单引号中,不然会被shell编译。其次,在网址中的一些符号需要被url编码,比如说单引号、空格等。
实验步骤:
登录时发送的的http请求为:
同样,我们还是构造恶意的username,实现sql注入的效果,不过需要对单引号和空格进行url编码。
所以我们在终端的输入为:
发现成功返回了所有employee的信息。
分析:
Sql注入的原理与上一个小实验仍然是相同的,只是本次实验使用curl在命令行上进行操作,需要额外对特殊字符进行url编码。
在前两次task中,都是通过sql注入从数据库拿数据,如果可以修改数据库就更好了,在SQL中,分号;用来分割两个sql语句,在本次task中需要在登陆界面尝试执行两个sql语句。
但是对抗机制会阻止两个sql语句的运行,我们需要找出对抗机制是什么。
实验步骤:
先尝试分号的效果。
的确执行了两条sql语句,证明分号确实可以用来分割sql语句。
下面在登录页面上使用sql注入构造两个sql语句。
点击登陆后,提示语法错误,无法执行第二条sql语句。
分析:
执行失败了,通过查看unsafe_home.php发现其使用query函数执行sql语句。
查询php的mysqli::query函数:
此函数只能一次执行一个sql语句,如果需要一次执行多条SQL命令,就必须使用mysqli对象中的 multi_query()方法。具体做法是把多条SQL命令写在同一个字符串里作为参数传递给multi_query()方法,多条SQL之间使用分号 (;)分隔。
因为代码中使用的query函数来执行sql语句,所以当输入的是多条sql命令的时候,query无法处理,不会成功执行。
在task2中,sql注入漏洞发生在SELECT语句处,所以我们可以利用此漏洞获取数据库内的信息。但如果sql漏洞发生在UPDATE语句处,后果将会非常严重,因为我们可以通过注入,在UPDATE的基础上对数据库中的数据做修改。
实验说明中给出了employee修改个人资料的代码逻辑:
可以看出,代码并没有对输入的数据进行进一步的检测和处理,所以在修改个人资料的功能处是存在sql注入漏洞的。在接下来的小task中,会利用此漏洞修改数据库中的数据。
对于每一个employee,只能修改Nickname、emails等数据,无法修改Salary,本次task的目标就是以employee的身份登录并且利用sql注入漏洞修改自己的salary。
实验步骤:
登录Alice的账户,打开编辑个人信息的页面,在Nickname栏输入进行构造的数据,执行sql注入。
点击save提交,再查看Alice的信息可以发现Salary已经被修改了。
分析:
Salary的值被修改了,表明Salary=’9999999’被执行了。
因为我们输入的NickName的值传到了后端会导致sql语句变成:
Salary=’9999999’被插入并且成功执行。
本次task的目标是将上司Boby的工资改为1。
实验步骤:
根据sql漏洞构造特殊的输入。
点击Save,提交表单,然后查看Boby的工资发现的确被修改为1。
分析:
原理同上,利用sql漏洞将Salary=’1’插入要执行的sql语句中。
只是在task3.2中需要用WHERE条件对Boby进行定位,所以需要用注释符 – 将后面的无关的sql语句部分注释掉,如果不注释掉就会有两个WHERE条件,出现语法错误。
在Task3.2中,我们登录Alice的账户,通过sql漏洞修改了上司Boby的工资。本次Task的目标是通过sql漏洞修改Boby的password,进而我们可以用新的password登录Boby的账户。
注意:数据库中存放的密码是原密码经过散列运算的值,所以如果要直接修改Password,就必须要先得到其散列运算的值。
查看源码,我们可以知道散列函数为SHA1。
实验步骤:
假设要Boby的password改为’pwd’,先获得SHA1(‘pwd’)。
根据sql漏洞构造特殊的输入。
我们的输入会使得后端真正执行的sql语句为:
这会将Boby的密码改为SHA1(‘pwd’)。
我们在登陆界面用这个密码(pwd)登录Boby的账号。
发现可以成功登录。
分析:
注入的原理仍然与前两次实验相同,都是通过将想要执行的sql语句插入到原来的sql语句中,然后再使用WHERE对Boby进行定位,最后使用注释符 – 将多余的sql语句部分注释。
但值得注意的是,Password在数据库中是以散列值的方式存储的,所以直修改Password进行修改时,要先获得新密码的散列值,将散列值存入数据库,而不是新密码本身。
在本次task中,介绍了如何用预处理的方式处理防止sql注入。
在实验说明中,介绍了sql服务器对于前端传递的sql语句的处理过程。
在数据库系统中,当执行一个SQL查询时,会经历几个关键的阶段,这些阶段包括解析和规范化(Parsing and Normalization)、编译(Compilation)、查询优化(Query Optimization)和缓存(Cache)。以下是每个阶段的详细解释:
Parsing and Normalization Phase
这是SQL查询处理的第一阶段,在这个阶段中,查询的文本被分析并转换成数据库系统能理解的结构化表示形式。
解析(Parsing):解析器首先检查SQL查询语法是否正确。它将SQL文本分解成个别的元素,并构建一个初始的内部数据结构,通常是一种称为解析树(Parse Tree)的东西。
规范化(Normalization):在解析树创建之后,数据库进一步处理这个树来产生一个规范化的查询。这包括消除歧义、应用优化规则(如推导出表达式的简化版)以及转换成一个标准形式。
Compilation Phase
一旦SQL查询被解析和规范化,它就会被编译成数据库可以执行的形式。
编译:编译阶段涉及将规范化的查询转换成一个或多个执行计划。执行计划是一系列数据库操作(如扫描、连接、排序等)的集合,这些操作定义了如何从数据库中检索或修改数据。
Query Optimization Phase
在编译阶段创建的执行计划可能不是最有效的。查询优化器的任务是找到最佳的执行计划。
查询优化:优化器评估不同的执行计划,并选择成本最低(例如,执行时间最短、资源使用最少)的计划。这个选择是基于数据库的统计信息,如表的大小、索引的存在以及数据分布情况。
Cache Phase
最优的执行计划可能会被缓存以便将来重用,这样在处理相同或相似的查询时可以节省优化和编译的时间。
对于预处理的理解:
预处理指的是预先将SQL语句编译并优化,然后在实际执行时再提供具体的参数值。
那么对于预处理的sql语句,只需要经历一次Parsing and Normalization Phase,SQL语句被解析为一个解析树,并进行规范化,此时占位符代替了实际的参数值。
之后也会经历Compilation Phase,转换为一个内部的执行计划,但由于还没有提供参数值,所以编译生成的执行计划会考虑到各种可能的参数。
在Query Optimization Phase,sql系统的优化器会为这个没有具体参数值的SQL语句生成最优的执行计划。由于没有具体的参数值,优化器可能会选择一个适用于各种可能参数的通用执行计划。
进入Cache Phase,编译和优化后的执行计划会被缓存。对于预处理语句,这意味着当相同的语句需要被重复执行时,它可以直接使用缓存中的执行计划,无需重新经历解析、编译和优化阶段。
预处理的好处:
预处理语句通过使用占位符来提供参数,从而避免了SQL注入攻击的风险。参数不会被解释为SQL的一部分,因此不能改变执行计划的结构。并且预处理语句通常只需要解析、编译和优化一次,会提高程序的性能。
所以本次task的目标就是利用预处理修复sql注入漏洞。
实验步骤:
登录/defense网页,此网页具有sql注入漏洞。
我们对username栏进行sql注入,发现可以成功登录admin的账号。
查看unsafe.php源码,发现其并没有采用过滤输入或者预处理的方式来预防sql注入。
这使得我们很容易就可以登录任意账户。
下面对unsafe.php进行修改,采用预处理的方式对sql注入进行防御。
保存,重新dcbuild,dcup,再次访问/defense页面,尝试进行sql注入攻击。
发现用同样的sql注入手段已经无法成功获取到admin的数据了,证明预编译成功防御了这种sql注入攻击。