0x02 代码审计
漏洞所在文件:\framework\admin\update_control.php(在后台程序升级)
漏洞文件代码:(只贴上相关代码)

首先我们看第369行,$file = $this->get('file','int');
,这里我们看到他这里是接收GET
变量中的file
值,那么int
就是把接收的值转换成int
类型。
第370行,if(!$file)
判断$file
变量是否有赋值,如果没有复制那么就提示一个JSON
数据。
第373行,$urlext = 'file='.rawurlencode($file);
,rawurlencode
函数代表空格转换成%20
。
第374行,$rs = $this->service(5,$urlext);
,这里可以看到调用本身文件中的service
方法,那我们进入这个方法看看,在文章的第465行。

第465行,if(!file_exists($this->dir_root.'data/update.php'))
,file_exists
函数代表检查文件或目录是否存在。
第470行,$uconfig = array();
,申明一个空数组。
第471行,include($this->dir_root.'data/update.php');
,include
函数代表引入一个文件,如果没有找到这个文件只会提示个警告不会终止错误。
第478行,if(file_exists($this->dir_root.'data/update.xml'))
,file_exists
函数代表检查文件或目录是否存在。
第486行,if(substr($url,-1) != '/')
,substr
函数代表字符串切割,并且判断不等于/
那么就进入487行区间。
第489行,$url .= 'index.php?version='.rawurlencode(trim($info['version'])).'&time='.$this->time.'&type='.$type;
,URL地址拼接,rawurlencode
函数代表空格转换成%20
,trim
函数代表移除字符串两侧的空白字符。
第493行,if($type == 1 || $type == 4)
,判断外部传入的$type
是否等于1或者等于4。
第494行,$onlyid = $uconfig['onlyid'] ? $uconfig['onlyid'] : $this->_onlyid();
,这里使用了3元运算符。
第495行,$domain = $this->lib('server')->domain($this->config['get_domain_method']);
,这里代表是获取当前访问的网址。
第496行,$client_ip = $this->lib('common')->ip();
,获取客户端Ip
第497行,$url .= "&domain=".rawurlencode($domain)."&ip=".rawurlencode($client_ip);
,URL地址拼接,rawurlencode
函数代表空格转换成%20
。
第498行,$url .= "&onlyid=".$onlyid."&phpversion=".PHP_VERSION;
,也是URL地址拼接。
第499行,if(function_exists('php_uname'))
,function_exists
函数代表判断是否有某函数。
第502行,$soft = $_SERVER['SERVER_SOFTWARE'];
,获取服务器PHP版本。
第506行,$mysqlversion = $this->db->version('server');
,获取服务端mysql版本号。
第511行,$this->lib('html')->setting('timeout',900);
,这里是设置CURL请求的超时时间。
第513行,$this->lib('html')->ip($uconfig['ip']);
,设置请求IP。
第515行,$info = $this->lib('html')->get_content($url);
,请求URL地址,返回XML
内容。
下面就是返回XML
数据,那么我们回到第一张图片。

第375行,$rs = $this->lib('json')->decode($rs);
,这里代表是把接收到的XML
内容转换成JSON
数据。
第376行,if($rs['status'] != 'ok')
,判断$rs['status']
不等于ok
。
第379行,if(!$rs['content'])
,判断是否为空。
第382行,$info = base64_decode($rs['content']);
,把接收到的$rs['content']
值,从base64
转换成实体。
第383行,file_put_contents($this->dir_root.'data/tmp.zip',$info);
,写入当前文件,第一个参数代表路径,第二个参数代表内容。
第384行,$this->lib('phpzip')->unzip($this->dir_root.'data/tmp.zip','data/update/');
,我们看到这里的意思就是解压文件到某个目录。
第386行,$this->lib('file')->rm($this->dir_root.'data/tmp.zip');
,删除写入的文件。
第386行,$verinfo = substr($file,0,1).".".substr($file,1,1).".".substr($file,2);
,这里是字符串切割。
第387行,$info = $this->update_load($verinfo);
,这里调用自定义方法,也是在本文章第152行。

第154行,$list = array();
,定义一个空数组。
第155行,$this->lib('file')->deep_ls($this->dir_root.'data/update/',$list);
,这里大概意思是遍历当前文件所有文件名,这里我就不去找代码,就把代码直接复制出来。
那么代码路径在framework\libs\file.php
中第297-313行
第156行,if(!$list || count($list) < 1)
,判断$list
是否为空,并且判断他的数据是不是小于1。
第159行,$strlen = strlen($this->dir_root."data/update/");
,strlen
代表统计字符串长度。
第162行,foreach($list as $key=>$value)
,foreach
循环遍历数组。
第163行,$value = trim($value);
,trim
代表移除字符串两侧的字符。
第165行,continue;
,这里代表跳出循环。
第167行,$tmp = substr($value,$strlen);
,substr
代表字符串切割。
第168行,if($tmp == 'version.txt')
,这里判断$tmp
是否等于version.txt
。
第169行,$verinfo = trim(file_get_contents($value));
,trim
代表移除字符串两侧的字符,file_get_contents
代表写入文件。
第183行,if(substr($tmp,0,10) == 'framework/')
,substr
代表字符串切割,从0到10切割判断等于framework/
。
第185行,if(is_file($value))
,is_file
代表判断文件是否存在。
第187行,$this->lib('file')->mv($value,$this->dir_phpok.$tmp1);
,剪切文件到某个目录。
第189行,if(is_dir($value) && !is_dir($this->dir_phpok.$tmp1))
,is_dir
代表判断目录是否存在,并且判断临时文件是否不存在。
第190行,$this->lib('file')->make($this->dir_phpok.$tmp1,'folder');
,创建目录。
第194行,if(is_file($value) && $tmp != 'table.sql')
,is_file
代表判断文件是否存在,并且判断$tmp
不等于table.sql
文件的进入区间。
第194行-第210行都是一样,剪切和创建目录。
第205行,$dlist = file($delfile);
,file
代表整个文件读入一个数组中。
第209行,foreach($dlist AS $key=>$value)
,foreach
代表数组循环。
第213行,$value = trim($value);
,trim
代表移除字符串两侧的字符。
第214行,if($value && is_file($this->dir_root.$value))
,判断$value
是否有值,并且判断文件是否存在。
第215行,$this->lib('file')->rm($this->dir_root.$value);
,代表删除文件。
第218行,if($value && is_dir($this->dir_root.$value))
,判断$value
是否有值,并且判断目录是否存在。
第225行,$this->update_table();
这里又看到调用自己的方法。
为何还有那么多代码要分析。

我们接下来继续往下分析,这里的话我就把代码Copy出来,代码太多截图不好看。
我这里挑重要的函数讲解下,这里都是sql执行语句,没有什么可以分析的。
函数:
file_exists
,代表检查文件或目录是否存在。
file_get_contents
,代表读取文件,如果携带2个参数那么就是写入。
str_replace
,代表字符串替换。
strlen
,代表查看字符串长度。
substr
,代表字符串切割。
unset
,代表变量删除。
explode
,代表把字符串切割成数组。
trim
,代表移除字符串两侧的字符。
private function update_table()
{
if(!file_exists($this->dir_root.'data/update/table.sql')){
return false;
}