CVE-2018-16621 漏洞分析报告+Nexus Repository Manager3 EL注入+vulfocus题解

张开发
2026/4/9 16:25:21 15 分钟阅读

分享文章

CVE-2018-16621 漏洞分析报告+Nexus Repository Manager3 EL注入+vulfocus题解
0x00 废话笔者前期是做了些靶场题目但是初入cve还没多久对很多内容以及大佬题解的术语都不熟悉。对于我来讲直接做那些平台的靶场题目更容易一点但肯定也存在像我一样的新人看大佬题解发现跟不上思路初期不太看得懂所以打算写篇简单的cve分析报告。0x01 介绍本题来源于vulfocus不用搭建docker也能直接复现很是方便啊题目Nexus Repository Manager3 EL注入CVE-2018-16621描述: 看不懂没关系后面会讲的我第一次看到的时候也看不懂Nexus是一个强大的Maven仓库管理器它极大地简化了自己内部仓库的维护和外部仓库的访问。在 org.sonatype.nexus.security.privilege.PrivilegesExistValidator 和 org.sonatype.nexus.security.role.RolesExistValidator 类中会将没有找到的 privilege 或 role 放入错误模板中而在错误模板在渲染的时候会提取其中的EL表达式并执行密码为admin/admin1230x02 知识点1.Java 表达式语言EL注入Expression Language (EL) Injection介绍笔者一开始以为这是ssti注入所以我们来看看区别区别SSTI攻击的是模板引擎如 Jinja2, FreeMarker, Thymeleaf。EL Injection攻击的是 Java EE 或 Spring 框架自带的表达式求值功能。维度普通代码注入 (如 ${7*7})字节码注入 (你的 Payload)发送的内容源代码文本编译后的二进制数据转成了文本解析方式表达式引擎解析类加载器直接把数据塞进虚拟机JVM绕过能力弱容易被敏感词过滤拦截强代码特征被编码彻底隐藏这里如果看不懂类加载器没关系介绍一下在 Java 里代码.java编译后会变成字节码.class。要使.class文件 运行就需要类加载器ClassLoader。com.sun.org.apache.bcel.internal.util.ClassLoader他会找到 二进制数据字节码然后解析成能够被java运行的“类”结构然后去JVMJava 虚拟机找块地方存折2.POC (Proof of Concept)概念验证笔者看不懂这是啥去搜了一下它是用来证明漏洞确实存在的一段代码或请求。POC (Proof of Concept - 漏洞验证) 指的是整套动作比如你发了一个带有payload的请求包这个请求包就是poc。Payload 具有攻击能力的那串具体代码。3.BCEL 字节码绕过Byte Code Engineering Library定义The Byte Code Engineering Library (Apache Commons BCEL™) is intended to give users a convenient way to analyze, create, and manipulate (binary) Java class files (those ending with .class)字节码工程库(Apache Commons BCELTM)旨在为用户提供一种分析、创建和操控(二进制)Java类文件(以 .class 结尾的文件) 的便捷方式为什么要使用字节码绕过因为后端存在关键字黑名单过滤了 Runtime、exec 等或者JSON 语法校验Payload 里的双引号破坏了 JSON 结构我们要绕过原理先理解运行流程BCEL 允许我们直接操作.class文件的二进制内容也就是字节码。我们就可以篡改字节码内容加入恶意代码 只要这个 字节码 被实例化成对象就会执行静态代码块或者构造函数中的恶意代码而ClassLoader 加载器这个加载器重写了loadClass方法看到 以$$BCEL$$开头 的类名会认为后面的字符串是经过特定算法编码的字节码内容。然后类加载器会将字节码好好保存起来放在jmv中就等执行了执行流程实例化字节码需要实例化加载器。举例payload//payload ${.class.forName(com.sun.org.apache.bcel.internal.util.ClassLoader).loadClass($$BCEL$$...).newInstance().exec(ls /tmp)}先通过${.class.forName(...)}.newInstance()实例化BCEL加载器再调用loadClass方法 解压和解码出 字节码源码被放入jvm中然后newInstance()实例化 字节码(类)直接执行恶意代码5.举例此题具体执行流程​ ​ { action: coreui_User, method: update, data: [ { userId: admin, version: 1, firstName: Administrator, lastName: User, email: adminexample.org, status: active, roles: [ ${.class.forName(com.sun.org.apache.bcel.internal.util.ClassLoader).newInstance().loadClass($$BCEL$$...).newInstance().exec(ls /tmp)} ] } ], type: rpc, tid: 16 } ​ ​ ​ // 第一步从一个空字符串开始获取 Java 的反射入口 .class ​ // 第二步通过反射在系统库里找到 BCEL 专用的类加载器ClassLoader .forName(com.sun.org.apache.bcel.internal.util.ClassLoader) ​ // 第三步实例化加载器 .newInstance() ​ // 第四步让加载器去解析那串 $$BCEL$$ 开头的乱码并在内存里还原成一个“类” // loadClass 函数内置了逻辑它会自动把这段乱码进行 Base64 解码 和 Gzip 解压 .loadClass($$BCEL$$$l$8b$I$I$7c$m$n_$A$Deval...) ​ // 第五步实例化还原出来的“类” .newInstance() ​ // 第六步调用该对象的方法执行 Linux 系统命令 .exec(ls /tmp)4.Hibernate ValidatorHibernate Validator是一个实现了Jakarta Bean Validation (JSR 380)规范的Java 数据校验框架。它的核心任务是判定数据是否合法并生成错误报告。一个用 Java 写好的程序包。它的代码逻辑里写了如果方法返回false它就去执行一段“解析字符串”的代码。5.JSR 技术规范Java Specification Requests是Java 社区过程JCP, Java Community Process中提出的一种Java 技术标准提案机制用于定义、改进或扩展 Java 平台的功能和 API。它规定了所有的校验程序包比如 Hibernate都必须遵守一套相同的工作流程。0x03 探索先登录然后点击 设置 选项进入user随便改个user名这样我们就创造了一个不存在的user而服务器看到这些数据时他摸不清这个user的role是啥0x04 复现role的值就是注入点然后实在找不到payload我就去抄答案了太恶心了这个payload这个长长的一串就是经过 base64编码和gzip压缩的 字节码也就是类文件我们要让 加载器 去把它实例化实例化不就是会执行我们放在静态代码块或者构造函数中的恶意代码了0x05 原理CVE-2018-16621漏洞成因表达式注入EL Injection核心问题是 Java 的Bean Validation校验机制导致的。注入点Nexus 在处理用户角色的roles参数时会校验角色名是否合法。二次解析如果校验失败Nexus 会构造一个错误消息。为了让消息动态化它使用了 Java 的表达式引擎。触发当它把非法角色名放入错误消息模板并进行解析时表达式被执行了在进行黑名单和json语法绕过成功注入我们的恶意代码。1.核心部分源代码搜索missing找到漏洞发生的地方2.漏洞发生流程一RolesExistValidator.java中用号把 Missing roles: 和${7*7}拼成了一个长字符串然后调用addConstraintViolation()。这个方法把拼接好的字符串存进了内存里的一个ArrayList集合中。执行return false;。二Hibernate的ValidatorImpl.java它收到了返回的false立刻调用一个get方法把你刚才存进ArrayList的那个字符串拿出来。三Hibernate的AbstractMessageInterpolator.java拿到字符串发现里面有${符号。它通过代码逻辑判断认为这是一个需要执行的指令。四执行EL引擎Hibernate 把字符串传给 EL 引擎。EL 引擎使用Java 反射机制解析字符串中的内容找到对应的 Java 类和方法并在服务器上运行它们。2.不核心部分如果也有人想跟我一样看一下Hibernate框架的具体代码源码地址hibernatehibernate-validator接下来的内容是详细讲了一下刚刚说的这个方法把拼接好的字符串存进了内存里的一个ArrayList集合中。这句话的过程储存参数templatepathlevelbcvwt方法被调用返回是cvbt方法并传进了模板消息和路径cvbi继承老爸的ctbi方法传模板和路径老爸把传进来的模板、路径以及本身的el等级设置成默认默认就等于一直渲染了把这些参数都当成自己的成员然后调用add方法add把老爸的那些参数放到 content数组列表 里面存着然后返回老爸此时作为 外部类的实例对象那我们就叫他老子object而这个object刚好存着我们的 content数组列表。private ListConstraintViolationCreationContext constraintViolationCreationContexts; ....... private abstract class NodeBuilderBase { //老子外部类 ​ ... public ConstraintValidatorContext addConstraintViolation() { if ( constraintViolationCreationContexts null ) { constraintViolationCreationContexts CollectionHelper.newArrayList( 3 ); //注意这里值是数组列表 } if ( !( expressionVariables null || expressionVariables.isEmpty() ) expressionLanguageFeatureLevel ExpressionLanguageFeatureLevel.NONE ) { LOG.expressionVariablesDefinedWithExpressionLanguageNotEnabled( constraintDescriptor.getAnnotation() ! null ? constraintDescriptor.getAnnotation().annotationType() : Annotation.class ); } constraintViolationCreationContexts.add( new ConstraintViolationCreationContext( //内部类 messageTemplate, expressionLanguageFeatureLevel, true, propertyPath, messageParameters ! null ? Map.copyOf( messageParameters ) : Collections.emptyMap(), expressionVariables ! null ? Map.copyOf( expressionVariables ) : Collections.emptyMap(), dynamicPayload ) ); return ConstraintValidatorContextImpl.this; } }代码图片这个Impl是啥解释一下ConstraintValidatorContext这是一个接口Interface。它列出了一个校验上下文应该有哪些功能比如buildConstraintViolationWithTemplate但它不写代码实现。ConstraintValidatorContextImpl这是具体的类Class。里面写了具体的 Java 代码来操作内存、管理列表。拿到数组列表返回false之后就是isvalid返回了false由于Bean Validation (JSR 303/380)机制Hibernate Validator把一开始传入的字符串重新出来发现${}然后解析运行这么简单的一份分析报告居然花了我八个小时这就是对小白的惩罚吗

更多文章