ThinkPHP5.0学习-某盘审计

ThinkPHP5.0完全开发手册

某盘代码审计

学习完开发手册,先来套源码试试手。
想要快速入手代码审计,就要先看手册了解一些底层架构等等
index.php开始
和原来的相比改动不大,应用目录是application,直接进行查看
先看config.php,看一些配置信息能够帮助我们快速了解此套源码
全局过滤方法
访问模式,最最最重要的是我们的控制器
可以看到,它开启了路由但是并没有打开强制路由
这里看一下路由,并没有什么东西
想要得到未授权RCE,还是得先看Index模块下面的控制器,因为可能有未授权访问,像这种 MVC 架构admin模块下,未授权太少了Orz
除了ApiLogin控制器,其他控制器均继承了Base控制器
那他们为什么要都继承Base呢?进入看一下
Orz在构造函数下面做了权限验证,扫了一眼除非知道数据库里的$header_uid不然无法绕过
但是,我们上面还提到了一个api控制器,没有继承。进入查看一下

0x01 SSRF

curlfun()函数下面,看到了一个很明显的SSRF

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 public function curlfun($url, $params = array(), $method = 'GET')  
    {  
  
        $header = array();  
        $opts = array(CURLOPT_TIMEOUT => 10, CURLOPT_RETURNTRANSFER => 1, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => $header);  
  
        /* 根据请求类型设置特定参数 */  
        switch (strtoupper($method)) {  
            case 'GET' :  
                $opts[CURLOPT_URL] = $url . '?' . http_build_query($params);  
                $opts[CURLOPT_URL] = substr($opts[CURLOPT_URL],0,-1);  
  
                break;  
            case 'POST' :  
                //判断是否传输文件  
                $params = http_build_query($params);  
                $opts[CURLOPT_URL] = $url;  
                $opts[CURLOPT_POST] = 1;  
                $opts[CURLOPT_POSTFIELDS] = $params;  
                break;  
            default :  
  
        }  
  
        /* 初始化并执行curl请求 */  
        $ch = curl_init();  
        curl_setopt_array($ch, $opts);  
        $data = curl_exec($ch);  
        $error = curl_error($ch);  
        curl_close($ch);  
  
        if($error){  
            $data = null;  
        }  
  
        return $data;  
  
    }  

先本地起一个服务
构造一个poc试试,
/index.php/index/api/curlfun?url=http://localhost:9080/ssrf.txt

0x02 SSRF

api控制器下,还存在一个post_url方法,也存在SSRF

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public function post_curl($url,$data){  
	$ch = curl_init($url);  
	curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");  
	curl_setopt($ch, CURLOPT_POSTFIELDS,$data);  
	curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);  
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);  
	$result = curl_exec($ch);  
	if (curl_errno($ch)) {  
		print curl_error($ch);  
	}  
	curl_close($ch);  
	return $result;  
}  

0x03 后台 RCE

admin模块setup控制器editconf方法处,存在文件上传漏洞

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public function editconf()  
    {  
		  
		// echo "test";  
		  
		// if($this->otype != 3){  
		// 	echo '死你全家!';exit;  
		// }  
          
        if(input('post.')){  
  
            $data = input('post.');  
              
             
  
            foreach ($data as $k => $v) {  
                $arr = explode('_',$k);  
                $_data['id'] = $arr[1];  
                $_data['value'] = $v;  
                $file = request()->file('pic_'.$_data['id']);  
                  
                if($file){  
                      
                    $info = $file->move(ROOT_PATH . 'public' . DS . 'uploads');  
                    if($info){  
                        $_data['value'] = '/public' . DS . 'uploads/'.$info->getSaveName();  
                    }  
                }  
                if($_data['value'] == '' && isset($arr[2]) && $arr[2] == 3){  
                    continue;  
                }  
                  
                Db::name('config')->update($_data);  
  
            }  
            cache('conf',null);  
            $this->success('编辑成功');  
        }  
  
          
    }  

可以看到直接input接受参数,并且利用了request()->file来上传文件
而在thinkphp中的file函数,是没有安全设置的
所以可以直接进行上传,
构造poc
public目录下面,成功进行了上传
但是文件名字看起来是一串随机的字符串组成的,这怎么办呢?我们根进去查看一下是怎么生成的,跟进move函数
看到了buildSavename函数正是文件保存命名规则,
跟进去查看一下,命名规则是date日期
前面的date函数是 uploads 下面的目录,而后面的md5(microtime(true))正是生成的那一串看似随机的字符串,爆破一下即可出文件名
那针对microtime(true)这样的该如何爆破呢?
ctfshow 元旦水友赛中出过这样一道题
https://docs.qq.com/doc/DRlBMcWdhZW9ZUnFB
里面有爆破脚本
System控制器下,同样存在类似的功能点

0%