小菜鸡太菜了,只能复现
题目
先审计源码,可以看到EditorController.php
有个download
函数1
2
3
4
5
6
7
8
9
10
11
12
13private function download($url){
$maxSize = $this->config['catcherMaxSize'];
$limitExtension = array_map(function ($ext) {
return ltrim($ext, '.');
}, $this->config['catcherAllowFiles']);
$allowTypes = array_map(function ($ext) {
return "image/{$ext}";
}, $limitExtension);
$content = file_get_contents($url);
$img = getimagesizefromstring($content);
......
}
可以看到这个函数里面有个file_get_contents
函数,通过get方法传进去一个url
,而且这个参数完全可控,因此我们可以利用它进行phar反序列化操作,而且文件上传的点还很容易找到,能直接上传图片。
但是有个注意的是,php的版本是7.2,8能动态调用assert
函数,还禁用了很多系统函数system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec,mail,apache_setenv,mb_send_mail,dl,set_time_limit,ignore_user_abort,symlink,link,error_log
,因此反序列化难度+++
分析
找pop链
PendingBroadcast
先看illuminate/boradcasting/PendingBroadcast.php
这个文件1
2
3public function __destruct(){
$this->events->dispatch($this->event);
}
可以通过这个方法将一些类的__call
方法调用出来
ValidGenerator
在faker/src/Facker/ValidGenerator.php
里面有个__call
方法,这个方法里面调用了两个动态调用函数1
2
3
4
5
6
7
8
9
10
11public function __call($name, $arguments){
$i = 0;
do {
$res = call_user_func_array(array($this->generator, $name), $arguments);
$i++;
if ($i > $this->maxRetries) {
throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries));
}
} while (!call_user_func($this->validator, $res));
return $res;
}
这个类里面传进去的name
参数是不可控的,因此我们第一次调用$res = call_user_func_array(array($this->generator, $name)
并不能如我们所愿实现任意命令,但是我们可以找到一个类,使上面调用的出来的结果可控,从而在第二次调用call_user_func($this->validator, $res)
的时候实现控制。而且,name
的值就是dispatch
,由于在call_user_func_array
里面,generator
类中没有定义dispatch
函数,因此会自动调用__call
函数
Generator
fzaninotto/faker/src/Faker/Generator.php
先看__call
函数1
2
3public function __call($method, $attributes){
return $this->format($method, $attributes);
}
跟进去format
函数1
2
3public function format($formatter, $arguments = array()){
return call_user_func_array($this->getFormatter($formatter), $arguments);
}
formatter
参数8可控,继续跟1
2
3
4
5public function getFormatter($formatter){
if (isset($this->formatters[$formatter])) {
return $this->formatters[$formatter];
}
...
好了,这里我们能看到他的return值是一个数组的值,因此,我们先让第一次$this->getFormatter($formatter)
返回的值是一个数组,数组值为getFormatter
,然后由于call_user_func_array
,他会再调用一次getFormatter
方法,参数为空,而这个方法传空值时,就会返回第一个formatters
成员的值
StaticInvocation
接下来最后一步就是要找一个比较好的类了phpunit\src\Framework\MockObject\Stub\ReturnCallback.php
里面看invoke
函数1
2
3public function invoke(Invocation $invocation){
return \call_user_func_array($this->callback, $invocation->getParameters());
}
invoke
方法调用了call_user_func_array
而且里面的两个参数都是反序列化的时候可以控制的,Invocation
只是一个接口,找到那个类就能利用了
找下类的方法1
2
3
4
5
6class StaticInvocation implements Invocation, SelfDescribing{
public function getMethodName(): string{
return $this->methodName;
}
}
利用这个返回回到ValidGenreator
中利用call_user_func
就能成功完成攻击了
exp
1 |
|
上传图片然后访问http://xiaorouji.cn:8080/server/editor?action=Catchimage&source[]=phar:///var/www/html/upload/image/843ac0c4952e20f0d95d1651b86d6792/201904/10/00c7ab10a3e36d18dd9d.gif
去触发反序列化
接着访问http://xiaorouji.cn:8080/upload/a.php
,成功getshell
参考kk师傅的博客