You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

14 KiB

12注入SQL注入起手式

你好,我是王昊天。

从这节课开始,我们就进入了排名第三的风险种类——注入。

这是在上一份OWASP TOP 10榜单中排名第一的风险种类同时也是在过去十年中最具威慑力的漏洞类型之一关于这类安全风险出现了许多重大的漏洞同时也有众多著名的安全工具致力于解决这类问题其中最著名的安全工具之一就是sqlmap作为SQL注入领域优秀的自动化检测工具我们也会在之后的课程中深入探究。

那么什么是注入呢?

在著名的西游世界,就有许多次战斗生动地诠释了注入的威力,而这位善用注入的战斗大师就是我们勇敢机智的齐天大圣——孙悟空。在《西游记》中,孙悟空一共六次通过钻入敌人肚子的方式取得战斗的胜利。

注入

孙悟空的行为堪称注入攻击的典范,那么安全领域的注入攻击又是什么呢?

从抽象定义来说,注入攻击的本质是数据段与指令段的混淆,攻击者在原本应该作为数据段的输入中插入了恶意指令,同时将该恶意指令作为代码执行。正如孙悟空的战斗一般,妖怪肚子原本是消化食物用的,但却让孙悟空潜入其中,并且大显神威。

事实上注入包含很多种类型比如SQL注入、命令注入、XSS、资源注入等其中SQL注入是最具代表性也是极为危险的一类漏洞这一讲我们就从SQL注入入手来展开探讨。

查询过程与SQL注入

图片

提到注入,就不得不提到查询过程

以最常见的SQL注入为例这类安全风险就是将不可信的用户输入与SQL查询语句拼接产生的。事实上SQL注入是Web安全领域最危险的漏洞种类之一一方面SQL注入漏洞的利用过程比较简单另一方面SQL注入漏洞可能导致数据库失窃、数据被篡改及清除等安全风险。在更严重的情况下SQL注入可以通过应用程序传递恶意命令控制托管数据库的操作系统并以此为跳点成功进入内网。

SQL注入的危害

接下来我们来看一看SQL注入会产生哪些危害。

首先,SQL注入可能会导致数据的泄露。我们以一个电商系统为例,看看这个过程是怎么发生的。

在一个电商系统中我们都会有一个个人信息页面用于编辑、展示和存储我们的相关信息通常的URL地址如下

https://example.com/user_info?username=talentsec

在这种场景下我们可以选择使用这种SQL语句来实现相应功能

SELECT * FROM users WHERE username = 'talentsec';

这里我们可以展开做一下思考如果SQL语句变为如下格式是否随着id的数值变化可以取得不同的数据呢很明显是可以的。

SELECT * FROM users WHERE username = 'talentsec' or id = '1';

我们只需要尝试去修改username参数将其设置为talentsec' or id = '1即可实现上述攻击过程。
进一步地设想一种更复杂的场景在这种场景下SQL语句不只有一个条件除了用户名还增加了账户活跃这一条件。

SELECT * FROM users WHERE username = 'talentsec' and account_status = 'alive';

因为SQL语句变得更加复杂了如果我们还用之前的方式攻击就会破坏语句完整性导致攻击无法顺利进行

SELECT * FROM users WHERE username = 'talentsec' or id = '1' and account_status = 'alive';

为了保证SQL语句的完整性我们可以通过增加--注释符让SQL语句重新恢复为合法格式保证攻击能够顺利执行。调整后username参数被设置为talentsec' or id = '1' ; -- and account_status = 'alive。 最终目的语句构造如下:

SELECT * FROM users WHERE username = 'talentsec' or id = '1' ; -- and account_status = 'alive';

可以发现由于SQL注入漏洞的存在攻击者可以通过调整参数内容获取到本不应该访问到的数据这就会造成业务系统关键数据的泄露。

除了会造成数据泄露外,SQL注入漏洞还能修改程序的执行逻辑

这个过程我们将在登录场景下复现。正常的Web应用系统都会具备登录功能通过将用户输入的用户名及密码传入后端程序可以在后端匹配账户信息来验证正确性其中一种SQL语句实现方式如下

SELECT * FROM users WHERE username = 'talentsec' and password = 'TALENTSEC';

如果该查询能够返回结果则说明登录成功否则说明登录失败。一名恶意的攻击者可以构造如下SQL语句使其绕过登录限制也就是说攻击者可以在仅知道用户名的情况下成功完成登录

SELECT * FROM users WHERE username = 'talentsec';--' and password = 'test';

可以看到,在这次攻击中攻击者所使用的用户名是talentsec';--,密码是test。虽然真实密码并不是test,但攻击者却成功登录了talentsec账户。

SQL注入的危害是巨大的其攻击过程也较为复杂接下来我们就来进一步探讨如何发现以及实施SQL注入攻击。

SQL注入实战技法

漏洞发现

知道吗对于SQL注入漏洞来说最难的一点其实是发现漏洞而非利用漏洞。因为正如我们前面讨论的SQL注入漏洞的利用过程相对简单只要能够发现漏洞利用不是问题。

为了更准确地发现SQL注入漏洞我们需要了解应用程序在什么时候会与数据库通信。这里,我给你提供两点参考。

你首先需要关注的是通过表单提交认证的过程。这一过程会将用户输入的凭据发送到应用程序后端并与数据库中存储的凭据进行比对。另外一些管理后台和CMS系统通过用户传入的参数针对性查询数据的时候也需要我们特别关注。

一名优秀的攻击者需要对应用系统的接口参数有全面的了解这些参数包括POST请求中隐藏的字段也包括HTTP Header和Cookie。接下来攻击者需要对每个参数进行针对性的攻击尝试并观测系统结果。通常最初的攻击尝试由';组成这两个字符分别用于闭合字符串以及SQL查询语句。如果存在SQL注入漏洞这两个字符的输入很有可能会触发系统报错。除此以外一些注释符--/**/以及SQL关键字ANDOR同样也可以被用来检测SQL注入漏洞点。

值得一提的是虽然我们常常通过观测系统报错的方法来判断SQL注入漏洞点但是并非所有SQL注入漏洞都会触发显性的系统报错。许多时候我们也会遇见500 Server Error或者系统的自定义报错界面,这时我们就需要考虑使用盲注或是其他攻击方式。

SQL注入漏洞作为一个漏洞大类利用方式是比较多的这使得它看起来有些复杂。但是这类漏洞的利用过程见得多了就会发现它们其实有相似的套路借用《卖油翁》中的话说就是“无他唯手熟尔”。

我们仍以经典的SQL查询语句为例

SELECT * FROM users WHERE username='$username' AND password='$password';

我们对username和password构造一种特殊的输入

$username = 1' or '1' = '1
$password = 1' or '1' = '1

代入参数后SQL查询语句变成

SELECT * FROM users WHERE username='1' or '1' = '1' AND password='1' or '1' = '1';

可以发现在上述SQL语句中不管orAND操作符谁的优先级高,WHERE语句的条件都是永远为真的,因此我们可以通过这个操作,实现认证过程的绕过。

接下来我们对原始SQL语句进行一次升级这种升级后的语句也是非常常见的

SELECT count(*) FROM users WHERE ((username='$username') AND (password=MD5('$password')));

可以看到这次升级一方面在username部分新增了括号另一方面在处理password时选择了MD5散列结果进行比较最后又统计了查询结果的数量经过升级之后上一次的构造就无法生效了。

这时如果我们想要构造新的参数完成SQL注入就要解决三个问题**第一是括号的闭合第二是password部分的比对第三是要确保查询数量为1。**于是我们构造如下输入:

$username = 1' or '1' = '1')) LIMIT 1 --
$password = test

将参数代入后查询语句变成:

SELECT count(*) FROM users WHERE ((username='1' or '1' = '1')) LIMIT 1 --') AND (password=MD5('$password')));

可以发现上述SQL语句实现了括号的闭合、忽略了password判断部分并且限制结果数量为1这段语句同时满足了3个限制条件成功实现了SQL注入攻击。

在基本起手式阶段,除了这种使用'来进行注入探测的方式以外,我们还可以使用AND符号来判断参数是否可以被当做运算符参与代码执行。以常见的CMS类型系统为例常见的帖子链接如下

https://example.com/article.php?id=1

在已知【id=1帖子】存在的情况下我们可以将id参数设置为1 AND 1=0并且和SQL语句完成拼接

SELECT * from articles WHERE article_id = 1 AND 1=0;

如果该页面无法显示内容,就说明1 AND 1=0中后半部分的False结果参与了运算依此可以判断SQL注入漏洞的存在。

数据库信息

通过起手式阶段的基本动作我们可以发现SQL注入漏洞是否存在。

在成功发现SQL注入漏洞之后为了给后续更高级的注入动作做好铺垫我们需要进入一个新的阶段获取数据库信息。而之所以展开讲述这部分的主要原因是虽然SQL语句是相对标准化的但是每种DBMS在特殊指令、获取数据操作等方面都会有一些差异因此在实际SQL注入过程中我们需要考虑到数据库差异使用不同的语法。

如果我们想要判断一个后端数据库的类型让这个数据库报错是一个快速的方法。这里我们列举几种常见的错误信息可以发现MySQL、MS SQL Server、PostgreSQL的报错信息都有比较明显的特征

You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version ...
Microsoft SQL Native Client error ...
Query failed: ERROR: syntax error at or near ...

事实上对于不同类型的数据库获取其数据库种类和版本号的SQL语句也会有一些差异

SELECT @@version; -- Microsoft, MySQL
SELECT * FROM v$version; -- Oracle
SELECT version(); -- PostgreSQL

图片

除了数据库类型和版本信息还有哪些信息是攻击者想知道的呢我们只要想想后面注入过程需要哪些信息答案就不辩自明了其实就是数据库中包含的表和每张表的结构信息。令人欣慰的是数据库中有一张表存储了这些信息这张表是information_schema。通过如下SQL语句可以查询出数据库中有哪些表以及每张表的结构

SELECT * FROM information_schema.tables;
SELECT * FROM information_schema.columns WHERE table_name = 'users';
SELECT * FROM all_tables; -- For Oracle
SELECT * FROM all_tab_columns WHERE table_name = 'USERS'; -- For Oracle

总结

这节课我们进入了第三大风险种类——注入。

作为上一份榜单中排名第一的安全风险其威力不可小觑。在Web业务系统中注入普遍以SQL注入形式存在而SQL注入又与查询过程密不可分。通过利用SQL注入漏洞不仅可以造成数据泄露、数据删除等后果甚至可能使数据库服务器执行任意命令进而导致整个内网的沦陷。

从技术角度来看SQL注入的影响主要是两个方面一方面是影响数据查询逻辑,使得攻击者能够越权访问数据库数据,另一方面是通过篡改数据查询逻辑进而修改SQL语句执行过程

事实上SQL注入漏洞虽然威力巨大而且利用过程相对复杂但是难度并不高。其难点主要在于如何发现SQL注入点以及如何找到正确的利用方式

关于如何发现SQL注入点我向你介绍了一种有效的方法那就是在了解应用系统的完整接口以及接收的参数清单后有针对性地逐一进行注入点排查。比较经典的注入点验证方案有';,以及ANDOR等 。

在确定正确的利用方式上SQL注入虽然招式繁多但是却有着完整的体系和套路熟练掌握之后完全可以“一招鲜吃遍天”。起手式阶段最主要的组合拳就是ANDOR'-- ,第一招打完之后,为了给高级招式做铺垫,我们要学习下一招–知己知彼,通过利用information_schema获取数据库中包含的表以及对应的表结构

截止到目前你已经掌握了初级SQL注入功力能够在简单的场景下完成SQL注入攻击同时掌握数据库的结构。下节课我们一起来学习高级招式包括UNION注入、BOOLEAN注入、时间盲注、DNS带外注入等多种攻击变种。

思考

你可以尝试使用MiTuan 靶场中的Sqlilabs完成基本的起手式和知己知彼这两招吗

期待你的动手实践,也欢迎你把这节课分享给有需要的朋友,我们下节课再见!