This is an automated email from the ASF dual-hosted git repository.
jleroux pushed a commit to branch release18.12
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
The following commit(s) were added to refs/heads/release18.12 by this push:
new 4da54af Fixed: Groovy Program sandbox bypass (OFBIZ-12305)
4da54af is described below
commit 4da54af4e093ad3de3affc39b0442af31c46f2e7
Author: Jacques Le Roux <[email protected]>
AuthorDate: Fri Sep 17 18:45:58 2021 +0200
Fixed: Groovy Program sandbox bypass (OFBIZ-12305)
This much increases the current security by creating
SecuredUpload::isValidText
and call it from ProgramExport.groovy
This said I agree with thiscodecc that a better solution would be to use a
Groovy sandbox, and not only in ProgramExport.groovy.
I had a quick glance at a such solution. Unfortunately as Cédric Champeau
reports
at https://melix.github.io/blog/2015/03/sandboxing.html and unlike
thiscodecc
suggest there are no "mature solutions on the market".
Nevertheless I'll have a deeper look at an OFBiz specific Groovy sandbox
solution.
Somehow related: I'll also soon extract the list of words used in
SecuredUpload::isValidText in a deniedWebShellWords property in
security.properties
Thanks: thiscodecc for report
Conflicts handled by hand
framework/webtools/groovyScripts/entity/ProgramExport.groovy
---
.../org/apache/ofbiz/security/SecuredUpload.java | 77 ++++++++++++----------
.../groovyScripts/entity/ProgramExport.groovy | 9 +--
2 files changed, 45 insertions(+), 41 deletions(-)
diff --git
a/framework/security/src/main/java/org/apache/ofbiz/security/SecuredUpload.java
b/framework/security/src/main/java/org/apache/ofbiz/security/SecuredUpload.java
index e3fb163..4dc79d7 100644
---
a/framework/security/src/main/java/org/apache/ofbiz/security/SecuredUpload.java
+++
b/framework/security/src/main/java/org/apache/ofbiz/security/SecuredUpload.java
@@ -34,6 +34,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -615,47 +616,53 @@ public class SecuredUpload {
return false;
}
String content = new String(bytesFromFile);
- return isValidText(content);
+ ArrayList<String> allowed = new ArrayList<>();
+ return isValidText(content, allowed);
}
- public static boolean isValidText(String content) throws IOException {
- return !(content.toLowerCase().contains("freemarker") // Should be OK,
should not be used in Freemarker templates, not part of the syntax.
- // Else
"template.utility.Execute" is a good replacement but not as much catching, who
- // knows...
- || content.toLowerCase().contains("import=\"java")
- || content.toLowerCase().contains("runtime.getruntime().exec(")
- || content.toLowerCase().contains("<%@ page")
- || content.toLowerCase().contains("<script")
- || content.toLowerCase().contains("<body>")
- || content.toLowerCase().contains("<form")
- || content.toLowerCase().contains("php")
- || content.toLowerCase().contains("javascript")
- || content.toLowerCase().contains("%eval")
- || content.toLowerCase().contains("@eval")
- || content.toLowerCase().contains("import os") // Python
- || content.toLowerCase().contains("passthru")
- || content.toLowerCase().contains("exec")
- || content.toLowerCase().contains("shell_exec")
- || content.toLowerCase().contains("assert")
- || content.toLowerCase().contains("str_rot13")
- || content.toLowerCase().contains("system")
- || content.toLowerCase().contains("phpinfo")
- || content.toLowerCase().contains("base64_decode")
- || content.toLowerCase().contains("chmod")
- || content.toLowerCase().contains("mkdir")
- || content.toLowerCase().contains("fopen")
- || content.toLowerCase().contains("fclose")
- || content.toLowerCase().contains("new file")
- || content.toLowerCase().contains("import")
- || content.toLowerCase().contains("upload")
- || content.toLowerCase().contains("getfilename")
- || content.toLowerCase().contains("download")
- || content.toLowerCase().contains("getoutputstring")
- || content.toLowerCase().contains("readfile"));
+ public static boolean isValidText(String content, List<String> allowed)
throws IOException {
+ // "freemarker" should be OK, should not be used in Freemarker
templates, not part of the syntax.
+ // Else "template.utility.Execute" is a good replacement but not as
much catching, who knows...
+ return isValid(content, "freemarker", allowed)
+ && isValid(content, "freemarker", allowed)
+ && isValid(content, "import=\"java", allowed)
+ && isValid(content, "runtime.getruntime().exec(", allowed)
+ && isValid(content, "<%@ page", allowed)
+ && isValid(content, "<script", allowed)
+ && isValid(content, "<body>", allowed)
+ && isValid(content, "<form", allowed)
+ && isValid(content, "php", allowed)
+ && isValid(content, "javascript", allowed)
+ && isValid(content, "%eval", allowed)
+ && isValid(content, "@eval", allowed)
+ && isValid(content, "import os", allowed) // Python
+ && isValid(content, "passthru", allowed)
+ && isValid(content, "exec", allowed)
+ && isValid(content, "shell_exec", allowed)
+ && isValid(content, "assert", allowed)
+ && isValid(content, "str_rot13", allowed)
+ && isValid(content, "system", allowed)
+ && isValid(content, "phpinfo", allowed)
+ && isValid(content, "base64_decode", allowed)
+ && isValid(content, "chmod", allowed)
+ && isValid(content, "mkdir", allowed)
+ && isValid(content, "fopen", allowed)
+ && isValid(content, "fclose", allowed)
+ && isValid(content, "new file", allowed)
+ && isValid(content, "import", allowed)
+ && isValid(content, "upload", allowed)
+ && isValid(content, "getfilename", allowed)
+ && isValid(content, "download", allowed)
+ && isValid(content, "getoutputstring", allowed)
+ && isValid(content, "readfile", allowed);
// TODO.... to be continued with known webshell contents... a complete
allow list is impossible anyway...
// eg:
https://www.acunetix.com/blog/articles/detection-prevention-introduction-web-shells-part-5/
}
+ private static boolean isValid(String content, String string, List<String>
allowed) {
+ return !content.toLowerCase().contains(string) ||
allowed.contains(string);
+ }
+
private static void deleteBadFile(String fileToCheck) {
Debug.logError("File :" + fileToCheck + ", can't be uploaded for
security reason", MODULE);
File badFile = new File(fileToCheck);
diff --git a/framework/webtools/groovyScripts/entity/ProgramExport.groovy
b/framework/webtools/groovyScripts/entity/ProgramExport.groovy
index 41d29e6..12b8293 100644
--- a/framework/webtools/groovyScripts/entity/ProgramExport.groovy
+++ b/framework/webtools/groovyScripts/entity/ProgramExport.groovy
@@ -20,6 +20,7 @@ import org.apache.ofbiz.entity.Delegator
import org.apache.ofbiz.entity.GenericValue
import org.apache.ofbiz.entity.model.ModelEntity
import org.apache.ofbiz.base.util.*
+
import org.w3c.dom.Document
import org.codehaus.groovy.control.customizers.ImportCustomizer
@@ -31,8 +32,7 @@ String groovyProgram = null
recordValues = []
errMsgList = []
-if (UtilValidate.isEmpty(parameters.groovyProgram)) {
-
+if (!parameters.groovyProgram) {
groovyProgram = '''
// Use the List variable recordValues to fill it with GenericValue maps.
// full groovy syntaxt is available
@@ -73,10 +73,7 @@ def shell = new GroovyShell(loader, binding, configuration)
if (UtilValidate.isNotEmpty(groovyProgram)) {
try {
- // TODO more can be added...
- if (groovyProgram.contains("new File")
- || groovyProgram.contains(".jsp")
- || groovyProgram.contains("<%=")) {
+ if
(!org.apache.ofbiz.security.SecuredUpload.isValidText(groovyProgram,["import"]))
{
request.setAttribute("_ERROR_MESSAGE_", "Not executed for security
reason")
return
}