EL表达式的执行限制及绕过
2024-11-10 21:25:36 Author: www.freebuf.com(查看原文) 阅读量:3 收藏

freeBuf

主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

前文

对于的el表达式的注入来说,通常是通过利用其能够动态执行的特性,之后通过反射的方式进行执行

或者更常见的是通过调用JS引擎,之后通过执行js代码进行命令执行或者其他操作

这里主要是对yzddMr6师傅在KCON的议题的学习,看看师傅的优质思路

EL表达式相关

根据我们对el表达式的了解,主要是将el表达式的字符串解析成了一个一个的Node结点

image-20231229202700166.png

而在jsp中具体的是在proprietaryEvaluate方法中对其进行表达式的解析成ValueExpression的实现类

image-20231229203104897.png

image-20231229203144819.png

可以看出children变量就是抽象出的一个一个的Node结点

之后是通过调用AstValue#getValue进行表达式的执行

image-20231229204343978.png

大概的逻辑如下

  1. 取出第一个Node作为base结点,并获取结点数、ElResolver等

  2. 循环的遍历每一个子结点,因为解析表达式是根据一个一个的.进行分割的,所以下一个结点一定是AstDotSuffix类对象

  3. 这时,如果下下个结点是AstMethodParameters类对象,则表示正在调用一个方法,之后的判断语句只是对Optional#orElseGet方法的支持罢了

  4. 而在识别出在调用方法时,通过getParameters获取参数,AstMethodParameters#getParameters也就是将参数数组化并输出

  5. 而核心的执行是在ElResolver#invoke方法中,在el中支持的所有解析器如下
    image-20231229210717450.png
    核心是通过第7个BeanELResolver解析器
    image-20231229210926772.png
    流程如下

    1. 首先将method转化成String类型的方法名

    2. 之后在基类base中查看是否有该方法,如果有则获取该方法

    3. 最后通过反射调用该方法

    4. 最后的最后将其设置为成功解析,并返回执行结果

  6. 上面得到的执行结果,最后也重新赋值给了新的base基类,并进行下一次的相同循环

上面就是关于在jsp中el表达式动态执行的细节部分,总结一下

  1. 将表达式解析成Node结点

  2. 访问内部的每个结点,顺序且连续的反射调用每一个方法

Node结点

AstMapData

能够返回一个HashMap结构的数据

AstConcatenation

能够拼接两个字符串,并将拼接后的字符串返回

image-20231229213946389.png

AstLambdaExpression

可以进行lambda表达式的执行

AstListData

能够进行list数据的处理

image-20231229214244695.png

AstBracketSuffix

处理[]的方式中的数据

image-20231229214504267.png

非常规思路

其他方式执行

常规的调用方法是通过.的方式连接进行调用,而这个.xx最终也是解析成了AstDotSuffix,在Node结点中可以作为后缀的除了.格式,还存在着[]中括号的方式

类似前面 EL表达式相关提及到的关于方法的调用的过程,在确定了base结点的情况下,计算下一个结点值时这里就是AstBracketSuffix#getValue方法返回的值,直接是返回[]内的第一项的内容

image-20231229215143709.png

若其第一项是字符串,将会解析成AstString类,该类的getValue方法就会直接返回对应的值,就能够达到和使用.一样的效果进行方法的反射调用

<%--${param.getClass().forName(param.c).newInstance().eval(param.cmd)}--%>
<%--${''.getClass().forName("javax.script.ScriptEngineManager").newInstance()--%>
<%--.getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec(\"calc\")")}--%>

${''['getClass']()['forName']("javax.script.ScriptEngineManager")['newInstance']()
['getEngineByName']("js")['eval']("java.lang.Runtime.getRuntime().exec(\"calc\")")}

同样能够命令执行

image-20231229215832147.png

类似的,对于webshell来讲,可以通过param.x的方式通过x传入参数进行执行

EL中的getter/setter

之前一直只认为通过.的方式如果不是调用一个方法,就是访问一个类对象的属性值,通过学习师傅的议题,原来在el表达式中是通过调用对应属性的getter方法而获取的值

看看为什么吧!

image-20231230105803102.png

对于通过.连接的结点,将会解析成AstDotSuffix,而使用=号连接的两边解析成的是AstAssign,等号的两边分别为该结点的两个子结点

类似于前面分析过的那样,会通过调用结点的getValue方法进行表达式的evaluate,这里的即是调用AstAssign#get

已在FreeBuf发表 0 篇文章

本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022


文章来源: https://www.freebuf.com/articles/web/414894.html
如有侵权请联系:admin#unsafe.sh