ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SQL注入实战:从原理到CTF夺旗,掌握MariaDB数据库安全攻防

SQL注入实战:从原理到CTF夺旗,掌握MariaDB数据库安全攻防 1. 项目概述与核心思路拆解拿到这个标题【CTF_SQL】[极客大挑战 2019]LoveSQL 1 熟悉CTFCapture The Flag夺旗赛的朋友立刻就能定位到这是一道典型的Web安全题目考察的核心是SQL注入漏洞的发现与利用。题目来自“极客大挑战 2019”这个系列赛事的题目往往设计精巧贴近实战但又不会过于晦涩非常适合用来学习和巩固Web安全基础技能。这道题名为“LoveSQL”更是直接点明了其与数据库操作的紧密关联。简单来说这道题会给你一个存在漏洞的Web应用通常是一个登录框或者查询页面你的目标就是通过构造特殊的输入欺骗后端数据库执行非预期的SQL命令从而一步步从数据库中“偷”出隐藏的Flag一串用于得分的特定字符串。这整个过程就是一次完整的SQL注入攻击实战演练。对于刚接触安全的新手这道题能帮你建立起对SQL注入最直观的认识对于有一定经验的选手它则是一次检验基本功和思维严谨性的好机会。下面我就结合常见的CTF出题思路和MariaDB数据库特性带你完整地拆解这道题并分享每一步背后的原理和踩坑经验。2. 环境准备与初步信息收集在真正动手“注入”之前盲目尝试是大忌。专业的渗透测试或CTF解题第一步永远是信息收集。我们需要尽可能多地了解目标。2.1 目标应用初步探测通常这类题目会提供一个简单的Web界面。我们首先用浏览器访问题目给出的URL。映入眼帘的很可能是一个登录页面或者一个搜索框页面上也许还会有“LoveSQL”之类的提示文字。这时我们需要做几件事查看页面源码按F12打开开发者工具仔细查看HTML、JavaScript代码。出题人有时会把提示信息比如数据库类型、表名提示藏在注释里。这是成本最低的信息获取方式。测试基础功能尝试正常的登录如输入admin/admin或搜索观察应用的反应。是提示“用户名或密码错误”还是直接显示“登录成功”不同的回显方式决定了我们后续注入时采用的技术比如是基于错误的注入、基于布尔的盲注还是基于时间的盲注。探测WAF或过滤规则简单输入一个单引号‘看看页面是否报错。如果返回了详细的数据库错误信息比如“You have an error in your SQL syntax...”那太好了这是一道“报错注入”题我们可以利用错误信息提取数据。如果页面只是返回一个通用的错误页面或者没有任何变化那我们可能需要尝试“布尔盲注”或“时间盲注”。如果输入某些特殊字符如and,or,select后页面行为异常或被拦截说明存在过滤我们需要考虑绕过技巧。注意在真实CTF环境或授权的靶场中可以大胆测试。但在未经授权的网站上任何测试行为都可能构成违法。务必在合法合规的环境下练习。2.2 数据库类型判断题目相关热词中提到了“MariaDB”这是一个重要的线索。MariaDB是MySQL的一个流行分支两者语法高度兼容。在CTF中指明数据库类型可以节省我们大量时间。如何确认呢报错信息特征如果输入单引号导致报错错误信息中如果包含“MariaDB”字样即可直接确认。注释符测试MySQL/MariaDB支持的注释符有#、--注意--后面有个空格和/* */。我们可以构造如admin #这样的输入如果注释符生效意味着我们很可能面对的是MySQL/MariaDB家族。版本信息探测通过报错注入或联合查询可以尝试获取version()函数的结果。假设我们通过输入‘得到了类似下面的错误那么就可以确定是MySQL/MariaDB环境并且开启了错误回显这为我们后续利用提供了极大便利。Warning: mysqli_fetch_array() expects parameter 1 to be mysqli_result, boolean given in /var/www/html/login.php on line 15或者更直接的数据库错误You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near at line 13. SQL注入漏洞原理与利用链构建在动手之前我们必须清楚我们在做什么。假设目标登录页面的后端PHP代码是这样的这是CTF题中最常见的场景$username $_POST[username]; $password $_POST[password]; $sql SELECT * FROM users WHERE username$username AND password$password; $result mysqli_query($conn, $sql);这段代码直接将用户输入拼接进了SQL语句造成了经典的“字符型注入”漏洞。3.1 漏洞点分析与Payload构造我们的目标是操控$sql这个字符串。正常输入admin和123456语句是SELECT * FROM users WHERE usernameadmin AND password123456如果我们用户名输入admin #密码任意比如xxx那么拼接后的语句就变成了SELECT * FROM users WHERE usernameadmin # AND passwordxxx在SQL中#是注释符它会让后面的所有内容被忽略。所以这条语句等价于SELECT * FROM users WHERE usernameadmin它只检查用户名是否为admin完全绕过了密码验证这就是最简单的“万能密码”绕过。相关热词中出现了“sql注入万能密码”其原理就在于此。常见的万能密码Payload还有admin or 11、admin--等。但CTF题目的目标不是登录而是获取Flag。Flag通常不在users表里而是在另一个隐秘的表中。因此我们需要利用注入点执行更多的查询这就是“联合查询注入Union Injection”。3.2 联合查询注入详解UNION操作符用于合并两个或多个SELECT语句的结果集。前提是每个SELECT语句必须拥有相同数量的列且列的数据类型也需要相似。我们的利用步骤是确定查询列数这是使用UNION的前提。我们使用ORDER BY或UNION SELECT来猜测。ORDER BY法admin ORDER BY 1 #admin ORDER BY 2 #... 直到页面报错如ORDER BY 5时报错说明列数为4。UNION SELECT法admin UNION SELECT 1,2,3,4 #不断增减数字直到页面正常回显。数字本身会被显示在页面上这能告诉我们哪一列的内容会被输出到页面这对我们后续提取数据至关重要。探测输出点假设我们确定列数为3且输入admin UNION SELECT 1,2,3 #后页面原本显示用户名的地方变成了数字2说明第二列是输出点。获取数据库信息利用输出点我们可以用数据库函数替换数字来获取信息。当前数据库admin UNION SELECT 1, database(), 3 #数据库版本admin UNION SELECT 1, version(), 3 #当前用户admin UNION SELECT 1, user(), 3 #爆表名MySQL/MariaDB中数据库的表信息存储在information_schema.tables中。admin UNION SELECT 1, group_concat(table_name), 3 FROM information_schema.tables WHERE table_schemadatabase() #group_concat()函数会将所有结果拼接成一个字符串避免多次查询。执行后我们可能会得到类似users,flag的结果。flag表就是我们的目标。爆列名知道了表名flag接下来获取它的列名。列信息存储在information_schema.columns中。admin UNION SELECT 1, group_concat(column_name), 3 FROM information_schema.columns WHERE table_schemadatabase() AND table_nameflag #可能返回id, flag。最终提取Flag现在表名和列名都知道了直接查询即可。admin UNION SELECT 1, flag, 3 FROM flag #这样Flag就会显示在页面的输出点第二列上。4. 针对“LoveSQL 1”的实战推演与技巧结合题目名称和常见出题套路我们可以进行更具体的推演。题目叫“LoveSQL”数据库里很可能存在一个名为love、l0ve或l0v3的表或者列名与此相关。这提示我们在爆表爆列时要特别关注这类名字。4.1 完整手工注入流程实录假设我们访问目标发现是一个登录框输入‘后产生数据库报错确认是MariaDB且存在注入。测列数输入用户名admin ORDER BY 5 #密码随意。页面报错。输入用户名admin ORDER BY 4 #页面正常显示登录失败。输入ORDER BY 3 #也正常。说明主查询有3 列。找输出点输入用户名admin UNION SELECT 1,2,3 #密码随意。提交后页面可能显示“登录失败”但仔细看页面源代码或者在某些位置如欢迎语、页脚发现了数字2和3。这说明第2、3列是输出点。查库、查表输入用户名admin UNION SELECT 1, database(), 3 #。页面显示当前数据库名假设是geek。输入用户名admin UNION SELECT 1, group_concat(table_name), 3 FROM information_schema.tables WHERE table_schemageek #。返回结果可能是users, l0ve1ysq1出题人喜欢用这种变形来增加趣味性。查列名输入用户名admin UNION SELECT 1, group_concat(column_name), 3 FROM information_schema.columns WHERE table_schemageek AND table_namel0ve1ysq1 #。返回id, username, password等等这看起来像用户表。Flag可能不在这里。重新审视表名或者尝试另一个表。也可能Flag就在users表的某个用户密码字段里但被命名为flag。我们需要检查所有可疑表的列。输入用户名admin UNION SELECT 1, group_concat(column_name), 3 FROM information_schema.columns WHERE table_schemageek AND table_nameusers #。返回id,username,password。没有flag。这时一个技巧是不指定表名直接爆当前库所有表的列名admin UNION SELECT 1, concat(table_name, :, column_name), 3 FROM information_schema.columns WHERE table_schemageek #。这样会列出geek库所有表及其列如users:id, users:username, users:password, l0ve1ysq1:id, l0ve1ysq1:username, l0ve1ysq1:password。如果有一个表叫flag或者列名包含flag就能一目了然。获取Flag假设我们发现了一个表flag其有一列fl4g。最终Payloadadmin UNION SELECT 1, fl4g, 3 FROM flag #。提交后Flag格式可能为flag{xxxx-xxxx-xxxx}就会显示在页面上。4.2 使用工具辅助Sqlmap与手工结合对于这类注入点明显、过滤简单的题目用Sqlmap可以快速验证和利用。但作为学习我强烈建议先手工完成再用工具复核。基本使用命令sqlmap -u http://target.com/login.php --datausernameadminpassword123 --level3 --risk2 --batch-u: 目标URL。--data: POST提交的数据。--level/--risk: 提高检测等级和风险级别更全面。--batch: 自动选择默认选项非交互模式。如果检测到注入可以继续用--dbs列数据库-D geek --tables列表-D geek -T flag --columns列列-D geek -T flag -C fl4g --dumpdump数据。实操心得工具虽快但容易让你变成“脚本小子”。手工注入的过程能让你深刻理解每一步的原理和SQL语句的构造。在CTF中很多题目会设置简单的过滤手工调整Payload往往比直接上工具更有效。我个人的习惯是手工找到注入点和基本Payload然后用Sqlmap的--tamper参数或自定义脚本来测试绕过或者直接用Sqlmap来验证和提取数据提高效率。5. 常见问题、过滤绕过与深度排查在实际解题或真实环境中不会总是一帆风顺。下面记录几个我遇到过的典型问题及解决思路。5.1 页面无回显怎么办盲注如果输入Payload后页面只有“登录成功”或“登录失败”两种状态没有具体数据回显这就是布尔盲注。我们需要利用SQL语句的布尔结果来影响页面状态。原理通过构造条件判断语句根据页面反应差异来逐位推断数据。 例如判断数据库名第一个字符的ASCII码是否大于100admin AND ascii(substr(database(),1,1)) 100 #如果页面返回“登录成功”说明原查询有结果即AND条件为真说明判断成立。通过二分法可以快速定位字符的ASCII码从而还原出数据。这个过程非常繁琐必须借助脚本自动化。Python配合requests库是常见选择。时间盲注则是利用sleep()函数如果条件为真则让数据库等待几秒通过页面响应时间来判断。admin AND if(ascii(substr(database(),1,1))100, sleep(3), 0) #5.2 关键字被过滤怎么办题目可能会过滤select,union,or,and,空格等关键字。大小写绕过SeLeCtUnIoN。双写绕过如果过滤是删除关键字可以用selselectect删除中间的select后剩下的部分又组成了select。等价替换and-or-||-like,rlike,regexp空格-/**/(注释符),%0a(换行符)%0d(回车符)%09(制表符)编码绕过十六进制编码。例如select的十六进制是0x73656c656374在MySQL中可以直接使用admin UNION SELECT 1,2,3可以写成admin UNION 0x73656c656374 1,2,3。表名flag也可以写成0x666c6167。内联注释绕过MySQL特有的/*!...*/其中的代码会被执行。例如/*!select*/。5.3 单引号被过滤或转义怎么办如果注入点是数字型如id$id则不需要单引号。如果是字符型且单引号被处理可以尝试整数型注入点直接使用数字和运算。如果参数被引号包裹但引号被转义可以尝试利用转义符本身来“吃掉”转义符但这需要具体分析代码逻辑。5.4 使用Sqlmap遇到问题无法检测到注入尝试提高--level和--risk 使用--technique指定注入技术如B布尔盲注T时间盲注或者手动指定注入点-p username。被WAF拦截使用--random-agent随机化User-Agent--delay设置请求延迟--proxy使用代理或者使用--tamper脚本对Payload进行混淆。Sqlmap自带很多tamper脚本如space2comment.py空格转注释。Post数据复杂可以使用-r参数加载一个包含完整HTTP请求的文件。6. 从这道题延伸出的学习路径与资源解出一道题不是终点而是起点。基于“LoveSQL”这类基础注入题你可以系统地构建自己的Web安全知识体系。夯实SQL语言基础这是根本。务必熟练掌握SELECT,INSERT,UPDATE,DELETE,UNION,JOIN, 子查询以及各种函数substr(),ascii(),concat(),group_concat(),if(),sleep()。理解information_schema数据库的结构。系统学习注入类型除了联合查询注入还要掌握报错注入利用extractvalue(),updatexml(),floor()等函数报错带出数据。布尔/时间盲注编写Python脚本自动化这个过程。堆叠查询注入能否执行多条语句;后跟其他语句这取决于数据库驱动配置。二次注入数据存入时被转义但取出使用时未被转义导致的注入。宽字节注入针对GBK等编码的特殊绕过。搭建靶场实战在本地用Docker或虚拟机搭建靶场环境如DVWA、SQLi-Labs、Web for Pentester。这些靶场提供了从易到难的各种注入场景并有源码可供学习。学习绕过技巧研究现代WAFWeb应用防火墙的过滤规则学习先进的绕过技术这需要不断阅读安全研究文章和参与实战。了解防御之道作为开发者如何避免SQL注入使用参数化查询Prepared Statements或ORM框架是唯一根治的方法。拼接SQL语句时必须对用户输入进行严格的过滤和转义。最后这道【LoveSQL】题就像一把钥匙它为你打开了SQL注入这扇大门。门后的世界很广阔也有点危险但充满挑战和乐趣。我的建议是在合法靶场上从这种基础题开始一遍遍手工复现理解每一个字符的作用直到肌肉记忆。然后逐步挑战更复杂的题目。当你不再依赖工具提示能独立构思出绕过过滤的Payload时你对SQL注入的理解才算真正入门了。安全之路道阻且长但每一步扎实的手工练习都会在未来某个关键时刻给你回报。
返回列表