以下代码解释为何payload要那么构造。要先gzip压缩然后。url编码。本人对致远oa不熟悉。另外上传写的payload大部分oa版本不可用。所以影响没有想象中的大未授权通杀。如果有不对的地方。留言跟帖。漏洞原理利用未授权AjaxController。利用类名方法名可控。可控formulaManager然后利用反射invokeService给service:formulaManager类名属性赋值。然后利用致远自己的类动态编译的手法编译。触发漏洞
public ModelAndView ajaxAction(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0L);
response.setContentType("application/json; charset=UTF-8");
String responseCompress = request.getParameter("responseCompress");
String url = request.getRequestURI();
String outStr = "";
outStr = invokeService(request, response);
if (Strings.isNotBlank(request.getParameter("ClientRequestPath"))) {
outStr = ZipUtil.compressResponse(outStr, responseCompress, "UTF-8", LOGGER);
outStr = Strings.toHTML(outStr);
}
Writer out = response.getWriter();
out.write(outStr);
return null;
}
private String invokeService(HttpServletRequest request, HttpServletResponse response) throws BusinessException {
AppContext.initSystemEnvironmentContext(request, response, false);
String serviceName = request.getParameter("managerName");
String methodName = request.getParameter("managerMethod");
String strArgs = request.getParameter("arguments");
String compressType = request.getParameter("requestCompress");
strArgs = ZipUtil.uncompressRequest(strArgs, compressType, "UTF-8", LOGGER);
String ctpJSONPCallback = request.getParameter("ctpJSONPCallback");
String retValue = null;
JSONValue jsonValue = null;
try {
Object service = getService(Strings.escapeJavascript(serviceName));
Object<Object, Object> result = (Object<Object, Object>)invokeMethod(service, methodName, strArgs, serviceName);
if (result instanceof FlipInfo) {
FlipInfo fpi = (FlipInfo)result;
result = (Object<Object, Object>)new HashMap<>();
Map<String, Integer> rmap = (Map)result;
rmap.put("total", Integer.valueOf(fpi.getTotal()));
rmap.put("data", fpi.getData());
rmap.put("page", Integer.valueOf(fpi.getPage()));
rmap.put("pages", fpi.getPages());
rmap.put("total", Integer.valueOf(fpi.getTotal()));
rmap.put("size", Integer.valueOf(fpi.getSize()));
}
if (ctpJSONPCallback == null) {
retValue = JSONUtil.toJSONString4Ajax(result);
} else {
retValue = (String)result;
if (result == null)
result = (Object<Object, Object>)"null";
}
} catch (Throwable e) {
String eid;
Throwable e1 = e;
if (e instanceof InvocationTargetException)
e1 = ((InvocationTargetException)e).getTargetException();
boolean isAlert = false;
String message = null;
String stacktrace = null;
Throwable tracet = null;
if (e1 instanceof BusinessException) {
BusinessException ae = (BusinessException)e1;
Throwable rawCause = ae.getRawCause();
if (rawCause == null) {
ae = ae.getRawBusinessException();
message = ae.getMessage();
isAlert = true;
} else {
tracet = rawCause;
}
eid = ae.getCode();
} else {
tracet = e1;
String curm = String.valueOf(System.currentTimeMillis());
eid = curm.substring(curm.length() - 10);
}
if (tracet != null) {
message = tracet.getMessage();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
tracet.printStackTrace(pw);
stacktrace = sw.toString();
}
JsonErrorObject jsonErrorObject = new JsonErrorObject();
jsonErrorObject.setMessage(message);
jsonErrorObject.setCode(eid);
try {
jsonValue = JSONMapper.toJSON(jsonErrorObject);
if (ctpJSONPCallback == null) {
retValue = jsonValue.render(false);
} else {
retValue = String.format("%1$s(%2$s)", new Object[] { ctpJSONPCallback, jsonValue.render(false) });
}
} catch (Exception exception) {}
if (ctpJSONPCallback == null) {
response.setStatus(500);
} else {
response.setStatus(200);
}
if (!isAlert)
LOGGER.error("+ eid, e1);
}
return retValue;
}
public static String uncompressRequest(String source, String compressType, String responseEncoding, Log logger) {
String result = null;
if (StringUtils.isNotBlank(compressType) && StringUtils.isNotBlank(source) && ("GZIP"
.equalsIgnoreCase(compressType) || "ZIP".equalsIgnoreCase(compressType))) {
ByteArrayInputStream sourceIn = null;
InputStream uncompressIn = null;
try {
sourceIn = new ByteArrayInputStream(source.getBytes("iso-8859-1"));
if ("GZIP".equalsIgnoreCase(compressType)) {
uncompressIn = new GZIPInputStream(sourceIn);
} else if ("ZIP".equalsIgnoreCase(compressType)) {
uncompressIn = new ZipInputStream(sourceIn);
}
result = IOUtils.toString(uncompressIn, responseEncoding);
} catch (Exception e) {
logger.error("compress data error.", e);
} finally {
try {
if (uncompressIn != null)
uncompressIn.close();
if (sourceIn != null)
sourceIn.close();
} catch (Exception ex) {
logger.warn("", ex);
}
}
} else {
result = source;
}
return result;
}
package com.seeyon.ctp.common.po.formula;
import com.seeyon.ctp.common.formula.enums.DataType;
import com.seeyon.ctp.common.formula.enums.FormulaType;
import com.seeyon.ctp.common.po.BasePO;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import net.sf.json.JSONObject;
import www.seeyon.com.utils.json.JSONUtils;
public class Formula extends BasePO {
private static final long serialVersionUID = 3050472922776688005L;
private Integer formulaType;
private String formulaAlias;
private String formulaName;
private String description;
private String paramsJson;
private List<FormulaParam> params;
private Integer category;
private String templates;
private String sample;
private String formulaExpression;
private String expectValue;
private Integer dataType;
private Integer returnType;
private Long creator;
private Date createTime;
private Date updateTime;
public Integer getFormulaType() {
return this.formulaType;
}
public void setFormulaType(Integer formulaType) {
this.formulaType = formulaType;
}
public String getFormulaAlias() {
return this.formulaAlias;
}
public void setFormulaAlias(String formulaAlias) {
this.formulaAlias = formulaAlias;
}
public String getFormulaName() {
return this.formulaName;
}
public void setFormulaName(String formulaName) {
this.formulaName = formulaName;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public String getParamsJson() {
return this.paramsJson;
}
public void setParamsJson(String paramsJson) {
this.paramsJson = paramsJson;
if (null != getParamsJson() && !"".equals(getParamsJson())) {
List<FormulaParam> params = new ArrayList<FormulaParam>();
Object obj = JSONObject.fromObject(getParamsJson()).get("paramsJson");
List<Object> list = JSONUtils.parseJsonToListBean(obj.toString());
for (int i = 0; i < list.size(); i++) {
FormulaParam parm = new FormulaParam();
JSONObject jsonObject = JSONObject.fromObject(list.get(i));
parm.setName(jsonObject.getString("name"));
parm.setType(DataType.getEnum(jsonObject.getInt("type")));
parm.setDescription(jsonObject.getString("description"));
params.add(parm);
}
setParams(params);
}
}
public Integer getCategory() {
return this.category;
}
public void setCategory(Integer category) {
this.category = category;
}
public String getTemplates() {
return this.templates;
}
public void setTemplates(String templates) {
this.templates = templates;
}
public String getSample() {
return this.sample;
}
public void setSample(String sample) {
this.sample = sample;
}
public String getFormulaExpression() {
return this.formulaExpression;
}
public void setFormulaExpression(String formulaExpression) {
this.formulaExpression = formulaExpression;
}
public Long getCreator() {
return this.creator;
}
public void setCreator(Long creator) {
this.creator = creator;
}
public Date getCreateTime() {
return this.createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return this.updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getExpectValue() {
return this.expectValue;
}
public void setExpectValue(String expectValue) {
this.expectValue = expectValue;
}
public Integer getDataType() {
return this.dataType;
}
public void setDataType(Integer dataType) {
this.dataType = dataType;
if (null != getDataType())
setReturnType(getDataType());
}
public Integer getReturnType() {
return this.returnType;
}
public void setReturnType(Integer returnType) {
this.returnType = returnType;
}
public void setReturnType(Class clazz) {
if (null != clazz)
this.returnType = Integer.valueOf(DataType.valueOf(clazz).getKey());
}
public List<FormulaParam> getParams() {
return this.params;
}
public void setParams(List<FormulaParam> params) {
this.params = params;
}
public String toString() {
if (this.formulaType.intValue() == FormulaType.GroovyFunction.getKey()) {
StringBuilder script = new StringBuilder();
script.append("def static ").append(getFormulaName());
script.append("(");
if (this.params != null && this.params.size() != 0) {
int len = this.params.size();
for (int i = 0; i < len; i++) {
script.append(" ").append(((FormulaParam)this.params.get(i)).getName());
if (i < len - 1)
script.append(",");
}
}
script.append(")");
script.append("{\n");
script.append(this.formulaExpression);
script.append("\n}");
return script.toString();
}
return "";
}
}
public static void compileFormulas(Collection<Formula> formulas) throws BusinessException {
StringBuilder script = new StringBuilder("package com.seeyon.ctp.common.formula.runtime;");
script.append("import com.seeyon.ctp.common.AppContext;");
script.append("public class CtpFormula{\n");
for (Formula formula : formulas) {
Integer formulaType = formula.getFormulaType();
if (formulaType.intValue() != FormulaType.GroovyFunction.ordinal())
continue;
script.append(formula.toString()).append("\n");
}
script.append(" def static getVar(name){com.seeyon.ctp.common.formula.FormulaUtil.getVar(name)}");
script.append("}");
try {
String s = script.toString();
LOG.debug("+ s);
ScriptEvaluator.getInstance().compile(s);
} catch (Exception e) {
throw new BusinessException(e);
}
package com.seeyon.ctp.common.formula;
import com.seeyon.ctp.common.AppContext;
import com.seeyon.ctp.common.exceptions.BusinessException;
import com.seeyon.ctp.common.formula.enums.CATEGORY;
import com.seeyon.ctp.common.formula.enums.DataType;
import com.seeyon.ctp.common.formula.enums.FormulaType;
import com.seeyon.ctp.common.formula.manager.FormulaManager;
import com.seeyon.ctp.common.function.CtpFunction;
import com.seeyon.ctp.common.po.formula.Formula;
import com.seeyon.ctp.common.po.formula.FormulaParam;
import com.seeyon.ctp.common.script.ScriptEvaluator;
import com.seeyon.ctp.common.web.util.WebUtil;
import com.seeyon.ctp.util.DateUtil;
import com.seeyon.ctp.util.Strings;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.script.ScriptException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class FormulaUtil {
private static final Log LOG = LogFactory.getLog(FormulaUtil.class);
private static final Log SCRIPTLOG = LogFactory.getLog("scriptcompile");
private static FormulaManager formulaManager;
public static List<Formula> formulas = new ArrayList<Formula>();
private static Set<String> SCRIPT_BLACKLIST = new HashSet<String>();
private static Map<String, Object> realValues = new ConcurrentHashMap<String, Object>();
static {
SCRIPT_BLACKLIST.add("System.exit");
SCRIPT_BLACKLIST.add("Runtime");
SCRIPT_BLACKLIST.add("ClassLoader");
SCRIPT_BLACKLIST.add("delete");
}
public static FormulaManager getFormulaManager() {
if (formulaManager == null)
formulaManager = (FormulaManager)AppContext.getBean("formulaManager");
return formulaManager;
}
public static Map getFormulaContext() {
Map<Object, Object> context = new HashMap<Object, Object>();
if (getFormulaManager() != null) {
List<Formula> allVariable = getFormulaManager().getAllVariable();
for (Formula formula : allVariable) {
if (FormulaType.Constant.ordinal() == formula.getFormulaType().intValue())
context.put(formula.getFormulaName(), formula.getFormulaExpression());
}
}
return context;
}
public static void compileFormulas() {}
public static void compileFormulas(Collection<Formula> formulas) throws BusinessException {
StringBuilder script = new StringBuilder("package com.seeyon.ctp.common.formula.runtime;");
script.append("import com.seeyon.ctp.common.AppContext;");
script.append("public class CtpFormula{\n");
for (Formula formula : formulas) {
Integer formulaType = formula.getFormulaType();
if (formulaType.intValue() != FormulaType.GroovyFunction.ordinal())
continue;
script.append(formula.toString()).append("\n");
}
script.append(" def static getVar(name){com.seeyon.ctp.common.formula.FormulaUtil.getVar(name)}");
script.append("}");
try {
String s = script.toString();
LOG.debug("+ s);
ScriptEvaluator.getInstance().compile(s);
} catch (Exception e) {
throw new BusinessException(e);
}
}
public static boolean isValidate(String script) {
return false;
}
public static Object getVar(String variableName) throws ScriptException, BusinessException {
List<Formula> allVariable = getFormulaManager().getAllVariable();
for (Formula formulaPo : allVariable) {
if (formulaPo.getFormulaName().equals(variableName)) {
if (formulaPo.getFormulaType().intValue() == FormulaType.Constant.ordinal()) {
Object value = realValues.get(formulaPo.getFormulaName());
if (value != null)
return value;
value = eval(formulaPo.getFormulaExpression(), new HashMap<Object, Object>());
if (value != null)
realValues.put(formulaPo.getFormulaName(), value);
return value;
}
if (formulaPo.getFormulaType().intValue() == FormulaType.Variable.ordinal())
return eval(formulaPo.getFormulaExpression(), new HashMap<Object, Object>());
}
}
return null;
}
public static void updateRealValue(Formula formulaPo) throws ScriptException, BusinessException {
if (formulaPo.getFormulaType().intValue() == FormulaType.Constant.ordinal()) {
Object value = eval(formulaPo.getFormulaExpression(), new HashMap<Object, Object>());
if (value != null)
realValues.put(formulaPo.getFormulaName(), value);
}
}
public static Formula getVarFormulaByName(String variableName) {
List<Formula> allVariable = getFormulaManager().getAllVariable();
for (Formula formulaPo : allVariable) {
if (formulaPo.getFormulaName().equals(variableName))
return formulaPo;
}
return null;
}
public static boolean isGroovyFunction(Formula formula) throws BusinessException {
return (FormulaType.GroovyFunction.getKey() == formula.getFormulaType().intValue());
}
public static boolean isJavaFunction(Formula formula) throws BusinessException {
return (FormulaType.JavaFunction.getKey() == formula.getFormulaType().intValue());
}
public static boolean isVaraible(Formula formula) throws BusinessException {
if (isJavaFunction(formula) || isGroovyFunction(formula))
return false;
return !isConstant(formula);
}
public static boolean isConstant(Formula formula) throws BusinessException {
if (isJavaFunction(formula) || isGroovyFunction(formula))
return false;
try {
String expr = formula.getFormulaExpression();
Object value = eval(expr, new HashMap<Object, Object>());
return (expr.equals(String.valueOf(value)) || expr.equals("\"" + value + "\""));
} catch (ScriptException e) {
LOG.error(", e);
throw new BusinessException("+ e.getLocalizedMessage());
}
}
public static Formula transform(CtpFunction func) {
Formula formula = new Formula();
formula.setDescription(formula.getDescription());
formula.setFormulaAlias(func.getTitle());
formula.setFormulaName(func.getAliases());
formula.setCategory(Integer.valueOf(CATEGORY.Global.getKey()));
formula.setFormulaType(Integer.valueOf(FormulaType.JavaFunction.getKey()));
formula.setReturnType(func.getMethod().getReturnType());
List<FormulaParam> params = new ArrayList<FormulaParam>();
formula.setIdIfNew();
formula.setParams(params);
return formula;
}
public static void initFunctions(Collection<CtpFunction> functions) {
for (CtpFunction func : functions)
formulas.add(transform(func));
}
public static boolean validate(Formula formula, String expression, Map context, boolean isSave) throws BusinessException {
Object obj = null;
if (formula.getFormulaType().intValue() != FormulaType.GroovyFunction.getKey())
return false;
try {
String script = formula.toString() + ";" + expression;
obj = eval(script, context);
} catch (ScriptException e) {
LOG.error(", e);
throw new BusinessException("+ e.getLocalizedMessage());
} catch (StackOverflowError e) {
LOG.error(", e);
throw new BusinessException(");
}
if (!isSave &&
null != formula.getExpectValue())
if (formula.getReturnType().intValue() == DataType.DateTime.getKey()) {
try {
if (!DateUtil.parse(formula.getExpectValue()).equals(obj))
throw new BusinessException(");
} catch (ParseException e) {
LOG.error(e.getLocalizedMessage(), e);
throw new BusinessException(");
}
} else if (!obj.toString().equals(formula.getExpectValue())) {
throw new BusinessException(");
}
return true;
}
private static void log(String script) throws BusinessException {
for (String keyword : SCRIPT_BLACKLIST) {
if (script.indexOf(keyword) > -1)
throw new BusinessException("+ keyword);
}
SCRIPTLOG.info(Strings.getRemoteAddr(WebUtil.getRequest()) + "," + AppContext.currentUserLoginName() + "," + script);
}
public static boolean validate(Formula formula) throws BusinessException {
if (formula.getFormulaType().intValue() == FormulaType.GroovyFunction.getKey())
return validate(formula, formula.getSample(), new HashMap<Object, Object>(), false);
if (formula.getFormulaType().intValue() == FormulaType.Variable.getKey()) {
Object value;
String expr = formula.getFormulaExpression();
try {
log(expr);
value = ScriptEvaluator.getInstance().eval(expr, new HashMap<Object, Object>());
} catch (ScriptException e) {
LOG.error(", e);
throw new BusinessException("+ e.getLocalizedMessage());
} catch (StackOverflowError e) {
LOG.error(", e);
throw new BusinessException(");
}
return (expr.equals(String.valueOf(value)) || expr.equals("\"" + value + "\""));
}
return false;
}
public static Object eval(String scriptText, Map<?, ?> context) throws ScriptException, BusinessException {
log(scriptText);
Map<Object, Object> ctx = new HashMap<Object, Object>(getFormulaContext());
ctx.putAll(context);
return ScriptEvaluator.getInstance().eval("import com.seeyon.ctp.common.AppContext;import static com.seeyon.ctp.common.formula.runtime.CtpFormula.*;" + scriptText, ctx);
}
}