一、什么是OGNL
现在有三个类分别是:Company
、Boss
、Employee
;三个类含有的属性分别如下:
public class Company {
public String name;
public Boss boss;
}
public class Boss {
public String name;
}
public class Employee {
public String name;
public Company company;
}
现在分别创建三个类的实例:company
、boss
、employee
;把boss
的实例指定为company
的boss
属性,把company
的实例指定为employee
的company
属性,这样老板、公司、员工三者就存在依赖关系,通过这种依赖关系我们就能操作对象的属性和方法;
OGNL
就是实现这种对象关系的导航语言,通过OGNL
我们可以操作Java
对象的任意对象属性和方法;
二、OGNL的使用
以下测试OGNL
版本均为:2.7.3
package com.l0cal.llll;
import ognl.Ognl;
import ognl.OgnlContext;
public class App {
public static void main(String[] args) throws Exception {
Company company = new Company();
company.setName("360");
company.setBoss(new Boss("laozhou"));
Employee employee1 = new Employee();
employee1.setName("lihua");
employee1.setCompany(company);
Employee employee2 = new Employee();
employee2.setName("liming");
employee2.setCompany(company);
OgnlContext ognlContext = new OgnlContext();
ognlContext.setRoot(employee1);
ognlContext.put("e2",employee2);
//获取Ognl根元素的相关值
Object name = Ognl.getValue("name", ognlContext, ognlContext.getRoot());
Object cname = Ognl.getValue("company.name", ognlContext, ognlContext.getRoot());
System.out.println(name + " " + cname);
//获取Ognl非根元素的相关值
Object e2Name = Ognl.getValue("#e2.name", ognlContext, ognlContext.getRoot());
Object ecn = Ognl.getValue("#e2.company.name", ognlContext, ognlContext.getRoot());
Object bName = Ognl.getValue("#e2.company.boss.name", ognlContext, ognlContext.getRoot());
System.out.println(e2Name + " " + ecn + " " + bName);
}
}
可以实现实例属性的访问;
中getValue()
函数中三个参数分别是:表达式、上下文和root;
- 表达式:表示想干什么;(核心)
- 上下文:对象运行的上下文、即:
OGNL
的操作在哪里 Root
:对谁进行操作;
主要语法
OGNL
支持静态方法调用和值访问;格式:@[类全名]@[方法名|值名]
(系统过滤关键函数,只能截图)

- 支持方法的调用

- 支持运算法的操作;
表达式内容为Runtime@getRuntime()
执行命令,通过debug
发现表达式最终到OgnlRuntime.callAppropriateMethod()#method.invoke();

三、CVE-2022-26134 confluence OGNL漏洞分析
漏洞payload如下:
通过servlet-mapping
请求处理class
为:ConfluenceServletDispatcher.java
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>com.atlassian.confluence.servlet.ConfluenceServletDispatcher</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
...
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
有请求到达容器时,容器会调用servlet
对象的service()
方法;

该类继承ServletDispatcher.java
,servlet
对象的service()
方法在该类中,service()
函数内部调用serviceAction()
,而在该函数中并调用getNameSpace()
,然后调用getNamespaceFromServletPath.java
,获取两/之间的内容;

在serviceAction()
中通过创建代理并传入namespace
,

在创建代理对象并进行初始化过程中传入namespace
(我们的payload
)

随后代理对象执行execute()
方法,在该方法中调用了this.invocation.invoke()
;

跟进invoke()
,该方法主要对拦截器的迭代,该类的有参构造方法中设置this.executed = false
,所以会执行流程会迭代拦截器,并执行this.proxy.getExecuteResult()
方法,该方法在创建代理初始化过程中默认的值为true
,因此会执行executeResult()
函数;


execute()
函数中会通过 TextParseUtil.translateVariables()
函数,并将 namespace
传入,并在此触发OGNL
表达式,且表达式可控,造成RCE
;
com.opensymphony.xwork.util#findValue()