dedecms v5.8 未授权RCE 漏洞

Posted 白昼小丑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dedecms v5.8 未授权RCE 漏洞相关的知识,希望对你有一定的参考价值。

在这篇博文中,我将分享对 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