DouPHP audit record (a failed audit)

DouPHP audit records

0x01 Overview


DouPHP Zhangzhou bean shells Network Technology Co., Ltd. developed a lightweight enterprise website management system based on PHP + Mysql architecture, runs on Linux, Windows, MacOSX, Solaris and other platforms, systems equipped with Smarty template engine, custom pseudo-static, foreground template using DIV + CSS design, concise background interface design, simple functions tend to have a good user experience, stability, scalability and security, and can be installed through the back-line module, such as module members, orders module, can provide site-building solutions for small and medium sites.
The audit solely for the purpose of learning, set up test on the local computer.

0x02 SQL injection


Because the system does not set the access route, so a few files directly from the front desk to start the audit. Here direct record worthy of our attention point:
init.php
beginning index.php contains /include/init.php made some initial setup

init.php, turn off the magic_quotes_runtime

A new 67-line firewall class, and 88 line call dou_firewall (). We continue to follow up to see what had been done operation

Follow dou_magic_quotes ()

Follow addslshes_deep ()

You can see the final call is addslshes_deep (), regardless of the $ _GET, $ _ POST, $ _ COOKIE, $ _ REQUEST is passed in a single value or array, all evil will go through addslashes () baptism. And by looking at the configuration, coding database connection settings are utf-8, so just call the dou_firewall () where, we can only look int SQL injection.

But after reading through the full text of sql statement also did not find quotes parameter is not wrapped, and placed in front of the parameters in the query have carried out regular match, are only allowed [0-9a-zA-Z_.], Can be said that the system developed very standardized. And all possible secondary injection where I found all of the parameter values ​​of the regular match or escape.

0x03 logical flaw


Since there is no injection, you can audit other types of loopholes.
Set up by local environmental observation, the front desk is to show functional information, only the background log entry of password recovery feature may appear logical loopholes.

Position: admin / login.php 136 to the last line, processing code to retrieve the password is

elseif ($rec == 'password_reset_post') {
    // Action操作项的初始化
    $action = $check->is_rec($_POST['action']) ? $_POST['action'] : 'default';
    
    // 验证管理员用户名
    if (!$check->is_username(trim($_POST['user_name']))) {
        $dou->dou_msg($_LANG['login_password_reset_fail'], ROOT_URL . ADMIN_PATH . '/login.php?rec=password_reset', 'out');
    } else {
        $user_name = trim($_POST['user_name']);
    }
    
    // 验证对应的管理员邮箱
    if (!$check->is_email(trim($_POST['email']))) {
        $dou->dou_msg($_LANG['login_password_reset_fail'], ROOT_URL . ADMIN_PATH . '/login.php?rec=password_reset', 'out');
    } else {
        $email = trim($_POST['email']);
    }
    
    // 找回密码提交、重置密码提交
    if ($action == 'default') { // 密码找回提交操作
        // 根据用户名和邮箱获取对应的用户信息
        $user = $dou->get_row('admin', '*', "user_name = '$user_name' AND email = '$email'");
        
        // 对应的用户信息不存在
        if (!$user)
            $dou->dou_msg($_LANG['login_password_reset_wrong'], ROOT_URL . ADMIN_PATH . '/login.php?rec=password_reset', 'out');
    
        // CSRF防御令牌验证
        $firewall->check_token($_POST['token'], 'password_reset');
        
        // 生成包含找回密码链接的邮件正文
        $time = time();
        $code = substr(md5($user['user_name'] . $user['email'] . $user['password'] . $time . $user['last_login'] . DOU_SHELL) , 0 , 16) . $time;
        $site_url = rtrim(ROOT_URL, '/');
        $body = $user['user_name'] . $_LANG['login_password_reset_body_0'] . ROOT_URL . ADMIN_PATH . '/login.php?rec=password_reset' . '&uid=' . $user['user_id'] . '&code=' . $code . $_LANG['login_password_reset_body_1'] . $_CFG['site_name'] . '. ' . $site_url;
        
        // 发送找回密码邮件
        if ($dou->send_mail($user['email'], $_LANG['login_password_reset'], $body)) {
            $dou->dou_msg($_LANG['login_password_mail_success'] . $user['email'], ROOT_URL . ADMIN_PATH . '/login.php', 'out', '30');
        } else {
            $dou->dou_msg($_LANG['mail_send_fail'], ROOT_URL . ADMIN_PATH . '/login.php?rec=password_reset', 'out', '30');
        }
    } elseif ($action == 'reset') { // 密码重置操作
        // 获取会员ID和安全码
        $user_id = $check->is_number($_POST['user_id']) ? $_POST['user_id'] : '';
        $code = preg_match("/^[a-zA-Z0-9]+$/", $_POST['code']) ? $_POST['code'] : '';
        
        // 验证密码
        if (!$check->is_password($_POST['password'])) {
            $dou->dou_msg($_LANG['manager_password_cue'], '', 'out');
        } elseif (($_POST['password_confirm'] !== $_POST['password'])) {
            $dou->dou_msg($_LANG['manager_password_confirm_cue'], '', 'out');
        }

        // 找回密码操作
        if ($dou->check_password_reset($user_id, $code)) {
            // 重置密码
            $sql = "UPDATE " . $dou->table('admin') . " SET password = '" . md5($_POST['password']) . "' WHERE user_id = '$user_id'";
            $dou->query($sql);
            $dou->dou_msg($_LANG['login_password_reset_success'], ROOT_URL . ADMIN_PATH . '/login.php', 'out', '15');
        } else {
            $dou->dou_msg($_LANG['login_password_reset_fail'], ROOT_URL . ADMIN_PATH . '/login.php', 'out', '15');
        }
    }
}

?>

elseif in the $ rec is acquired through $ _REQUEST [ 'rec'], so long as we get or post incoming rec = password_reset_post to get into this layer of code
after see the figure at the beginning to obtain the $ action, for the post incoming the username and email were regular match, you want to do the action by these two parameters is almost impossible.

The following is a $ check-> is_username and $ check-> is_email handling code

function is_username($username) {
        if (preg_match("/^[a-zA-Z]{1}([0-9a-zA-Z]|[._]){3,19}$/", $username)) {
            return true;
        }
    }
    
function is_email($email) {
    if (preg_match("/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/", $email)) {
        return true;
    }
}

Then look at $ action == 'default' operation

    // 找回密码提交、重置密码提交
    if ($action == 'default') { // 密码找回提交操作
        // 根据用户名和邮箱获取对应的用户信息
        $user = $dou->get_row('admin', '*', "user_name = '$user_name' AND email = '$email'");
        
        // 对应的用户信息不存在
        if (!$user)
            $dou->dou_msg($_LANG['login_password_reset_wrong'], ROOT_URL . ADMIN_PATH . '/login.php?rec=password_reset', 'out');
    
        // CSRF防御令牌验证
        $firewall->check_token($_POST['token'], 'password_reset');
        
        // 生成包含找回密码链接的邮件正文
        $time = time();
        $code = substr(md5($user['user_name'] . $user['email'] . $user['password'] . $time . $user['last_login'] . DOU_SHELL) , 0 , 16) . $time;
        $site_url = rtrim(ROOT_URL, '/');
        $body = $user['user_name'] . $_LANG['login_password_reset_body_0'] . ROOT_URL . ADMIN_PATH . '/login.php?rec=password_reset' . '&uid=' . $user['user_id'] . '&code=' . $code . $_LANG['login_password_reset_body_1'] . $_CFG['site_name'] . '. ' . $site_url;
        
        // 发送找回密码邮件
        if ($dou->send_mail($user['email'], $_LANG['login_password_reset'], $body)) {
            $dou->dou_msg($_LANG['login_password_mail_success'] . $user['email'], ROOT_URL . ADMIN_PATH . '/login.php', 'out', '30');
        } else {
            $dou->dou_msg($_LANG['mail_send_fail'], ROOT_URL . ADMIN_PATH . '/login.php?rec=password_reset', 'out', '30');
        }

First post incoming user name and mailbox into a database query and returns no results ended process. So there is no way to modify the mailbox reaches the mailbox to accept any link to reset your password purpose.

After construction began password recovery link, and sent to the mailbox user corresponding link.
A password reset link included $ code (168 lines), md5 is calculated after stitching a plurality of user information, so we can not construct a password reset link directly. FIG longer rows 170 generates code $ body, not completely theme, in fact, behind the splicing $ code $ body. This can be on top of my release password recovery function to see the full code.

Reset password link to print out like this

By analyzing the links to know $ rec = password_reset, so we look at login.php directly in the code $ rec == 'password_reset' of

Here 116 $ user_id and $ code about to put $ dou-> check_passwrod_reset (), we look at what had been done to follow up the operation
can see 96 lines out front $ code 16 bit line 98 to regenerate a $ code, 101 line take $ code and $ get_code comparison. We have been here for $ code verification, so here we can not bypass the restrictions.

But this is not the place to change the password of a final deal, the final place to change the password as follows:

But unfortunately, there are 191 lines were also $ code verification, so password recovery function to this audit, I can not find loopholes.

0x04 file upload


douphp front desk did not file upload function, so this audit is the audit admin / in the file
by reading, find all file upload functions are handled using the File class, for example where one of them to audit
admin / system.php
when $ when there rec == 'update' file upload, line 103 calls the $ file-> update (), we take a look at how come $ file.

See 20 lines, $ file is an instance of a class of objects File

Follow the File class, to see __construct do the following:

Then go back and re-look $ file-> upload ()
here after the split into an array by point, to obtain the file extension and look for matches in the $ this-> file_type in. You can see from the above __construct $ file_type only a few pictures type suffix, so here is a whitelist filtering, it can not be bypassed.

The next direct splicing file name, and upload files. So the file upload function has not dug hole.

0x05 other vulnerabilities


csrf
later also looked at whether there is a background csrf, the result is a background function before performing the operation will first verify csrftoken. Here we look at how to generate csrf, whether self-constructed.
Reading of the following code can be seen for random number generation.

Unauthorized access
of unauthorized access audit is very rude, the beginning of the back page file contains admin / include / init.php do check in on the session, so look directly at admin directory directory file if there is a file that is not included init.php can. But in the end it turned manually a few and did not find.

0x06 summary

The audit of a total of dug several storage xss background, perhaps a larger harvest of code audit deeper understanding, more familiar with the audit process. This code but also learn some safety skills development, ideas. While recognizing his own shortcomings, the code is slower reading speed.
We hope to improve their combat late pass it ha ha.

Guess you like

Origin www.cnblogs.com/Gcker/p/12501933.html