来自i春秋作者:万年死宅
root@1~# Training: MySQL II的简单分析 root@2~# MySQL高级注射技巧load_file()来读文件 root@3~# MySQL高级注射技巧into otfile来getshell
OK,昨天的Training:MySQL I给大家的启发很大吧。总之,对我启发很大,因为,我就是从这道题开始才真正的算是开始了解MySQL注射的(之前就是背注射语句,然后套。。。) 好了,我们不废话,直接来看这道题: 小宅的传送门 OK,大家可以先尝试解一下,然后在回来继续看这份教程,首先,我们还是先来看高亮的代码: 小宅的传送门 我们定位到HTML的Form表单(68~83行):
我们注意表单的method为POST,要传送的参数是username和password,于是定位到接收参数的地方(10~13行):
我们看到接收了POST参数username和password就调用了auth2_onLogin()函数,我们跟踪到该函数的定义处(36~66行):
我们来看函数的流程,可以发现于昨天的No.6paper极其相似,但是,有一处不同,就是认证不同了,我们来观察一下。首先是用于我们可控的SQL语句不同了(42行): 本次的SQL语句成了这样,接着,我们来看44~47行,这是和上道题一样的判断,只是SQL语句变了,但是还是一个道理:
if (false === ($result = $db->queryFirst($query)))
可以看到,还是调用result,然后以这个整体(也就是这次查询的返回值)与false进行比较。当为false时自然就是说查询语句失败,例如,传入“xxx”,SQL语句则是:
SELECT * FROM users WHERE username='xxx';
若查询失败,则证明不存在xxx这条记录,若查询成功则证明存在xxx这条记录。这次的查询语句还有不同的是select的是*,所以,我们来看下users表的表结构(2~8行):
可以看到这个users表里有三个字段,分别是:“userid,username,password”。因为我们select的是*所以$result里就同时存在这三个与username同行的userid和password。 接着,我们来看重头戏,新的认证检测步骤(50~56行):
我们来看这个新增的步骤:
if ($result['password'] !== $password)
这个if语句的条件是查询结果里的与我们赋值的username同行的password是否与我们传入的password相同,如果不相同则执行if里的内容。 看起来简直天衣无缝,这样的验证从逻辑上来讲几乎没有问题了,所以,我们的突破口,绝不会在PHP代码上。 于是,我们就发现这次的SQL语句也是存在注射漏洞的,因为我们来看这个代码片段:
从图中可以看到,从SQL语句的生成到查询之间并未进行任何过滤,所以存在SQL注射。所以问题就出在我们可控的SQL语句上,我就提示到这里,大家先自己动动脑筋,想想怎么绕过认证。
我们得清楚,首先,我们不知道正确的username和password,而且不是以SQL的返回值来认证的,那怎么办?
这个,其实解决方案,我在No.5paper里讲过,就不知道大家有没印象了。
好了,大家想完之后呢,就来看正确的解题思路吧。 首先,既然我们不知道正确的用户名和密码,那我们就构造啊,什么意思呢?
大家记不记得UNION的神奇功效,假设我们如下SQL语句:
SELECT A FROM B WHERE C='$aaa'
假设在B表里有C等于111,但是我们不知道,那么如果我们构造如下$aaa的值会发生什么?
xxx' UNION SELECT 1#
这样一拼接就成了:
SELECT A FROM B WHERE C='xxx' UNION SELECT 1#'
所以A就成了1,对吧,嘿嘿,其实就是这么简单,只是由于我们对SQL的理解还不够深,才导致我们一时半会没想出解法。 于是我们在还去看题里的SQL语句:
SELECT * FROM users WHERE username='$username';
所以我们构造:
xxx' UNION SELECT 1,'test','123456'#
就拼接成了:
SELECT * FROM users WHERE username='xxx' UNION SELECT 1,'test','123456'#';
由于xxx这个username本就不存在,所以查询结果自然的变成了UNION的,于是userid变为1,username变为test,而password变为123456。
所以,我们username填"xxx' UNION SELECT 1,'test','123456'#",password填123456就OK了吗?我们试试就知道了,如下图:
我们点Login听天由命吧,哈哈:
密码错误!知道为什么吗?恩?嘿嘿,大家细想一个细节,在PHP程序中处理我们传入password的细节(在40行,哈哈):
知道问题出在哪里了吗?哈哈,对的,我们UNION的password一定要是输入的password的MD5值,当然,也可以是我们输入的password是UNION的password的MD5值。 我们来试试看(123456的MD5值是:e10adc3949ba59abbe56e057f20f883e)。把username填成:"xxx' UNION SELECT 1,'test','e10adc3949ba59abbe56e057f20f883e'#",把password填成123456,我们来看: 结果是: 成功!Bypass了新的认证方式!但是,这道题并没解出来,我们来看原题的题意:
要以admin的身份登录,而不是test,所以,我们得稍稍的改一下payload:
xxx' UNION SELECT 1,'admin','e10adc3949ba59abbe56e057f20f883e'#
我们再来看: 来,和我一起欢呼!yeah!(O了,别**了,我们还有事呢。)
root@2~# MySQL高级注射技巧load_file()来读文件
...
有关该文章更多内容可到 http://bbs.ichunqiu.com/thread-10359-1-1.html?from=paper 查看