Sunday, August 9, 2026

Type confusion in the Blueprint and UnrealScript interpreters

This is kind of a preliminary write-up, I'm not sure if there will be more or a proof-of-concept because compiling UE5 from source consumes a huge amount of disk space and I want my disk space back for other things.  I am not an information security expert, I can barely hack my way out of a paper bag, but I hope I can at least bring the receipts to demonstrate that this is a real problem.

Some history of this problem

If you want some background, here's a write-up on CVE-2024-34492, a DLL-drop exploit which is different from the problem I'll be discussing, but ultimately led to this one.  After UT2003 made the DLL drop no longer possible, a few modders wanted to find new routes to "enhanced functionality" that involved escaping the UnrealScript sandbox.

Unreal Engine 4 and 5 work much differently than the prior iterations that happily let players run their own servers that distributed mod packages to anyone connecting though.  Unreal-based games these days, including Fortnite, mostly use developer-controlled servers and don't distribute arbitrary packages to users unless the developers explicitly add paths for doing so.  So, what changed?

Recently, there was an incident involving using custom maps in Meccha Chameleon to distribute malware, so this has now drawn the attention of hackers as a viable line of attack.  The Meccha Chameleon attack was performed using an insecure function (Launch URL), and much of the discussion about avoiding this type of attack has centered on limiting what functions UGC Blueprints have access to.

Figuring out what functions a compiled Blueprint is even trying to call in the first place is actually very difficult, and they have access to a huge number of functions, but in the current version of the engine, the core Blueprint loader and interpreter lack sufficient validation when given malformed Blueprints and are unsafe no matter what functions they call because they are still prone to the same type confusion vulnerabilities as the UnrealScript interpreter.

A brief primer on UnrealScript, Blueprint, and type confusion

UnrealScript is a Java-like scripting system, although it was originally inspired by Visual Basic and retains some hints of that history, like a "let" opcode and case insensitivity. It compiles into bytecode that uses what is kind of an inverted stack machine.  In a stack machine, an operation like "1+2" would have the encoding instructions for the operands 1 and 2 first, followed by an instruction that consumes the operands, adds them, and pushes the result on to the stack.  UnrealScript is a bit different: It encodes the opcode for an operation, followed by the operands, and evaluates them recursively.

While UnrealScript no longer exists in the modern Unreal Engine, Blueprint is based on Kismet, which compiles down to UnrealScript bytecode, and ultimately the Blueprint interpreter (which is in UObject/ScriptCore.cpp in Unreal) works the same way.

All of these systems are strictly-typed, meaning all properties and variables have a type and are only allowed to store a value of a compatible type.  This is supposed to, in theory, prevent unsafe arbitrary memory access because you can only access object and array memory through correctly-typed values.

The problem though is that for this type of system to be useful with untrusted bytecode, the loader or runtime must validate that the type constraints are being respected.  .NET and Java both enforce type correctness at load-time by doing semantic analysis of the bytecode.  If the type constraints are being violated, then they will refuse to load the module.  Unreal has no type-correctness checks in its bytecode loader (see: UObject/ScriptSerialization.inl) or in the parsing of subexpressions at runtime (see: UObject/Stack.h).

There are two ways that this can manifest as a problem and Unreal is vulnerable to both of them.

Type confusion using malformed bytecode

This is the more difficult route because it involves manipulating the bytecode directly.  If the bytecode contains an expression of the form:

EX_Let <Target that accepts Integer64> <Expression that returns Object>

... it will put the object's address in the Integer64.  More importantly, the reverse is also true, allowing arbitrary memory addresses to be treated as object references, which internally are UObject pointers.

There are no type safety constraints on ANY operations in UnrealScript, including any checks that the location designated for storage of a value is large enough to accommodate the value being stored into it.

Type confusion using out-of-sync function calls

This is the route that the UT2004 mods were using, it may be simpler because it doesn't involve manipulating the bytecode, but rather, taking advantage of the fact that function call dispatches don't validate parameter types.  UnrealScript and Blueprint don't allow overloads, so function calls are resolved by name alone.

Suppose you have a Blueprint that does this:

 

... and the Pass Through class has this as the implementation of "Fake Obj to I64": 

 

This will successfully compile and cook.  However, suppose you then retype the input parameter to Integer64 and return it:

This will successfully compile, but the other Blueprint using it won't.  The trick is finding a way to cook the first blueprint using the old parameter type, cook the new one using the new parameter type, and then stuff the mismatched assets into the same package.

The current Unreal packaging workflow in 5.8.1 makes this extremely cumbersome, because it doesn't have an easy way to skip compilation of a particular Blueprint.  Maybe there is one, but I haven't found it yet.  This also makes it very difficult to iterate on it, because you can't run it in the editor.  You can, however, disable Zen Server in the packaging options to get the raw .uasset and .uexp files in the Cooked directory, replace one, and then manually stage the package with UnrealPak, so this is possible even with an unmodified editor.

Using a modified editor to just act as if Int64-Object conversions are valid would drastically simplify this process though, and make the package mismatch technique mostly unneeded.  I'll dive into that some more when I'm further along.

Another avenue for mismatched calls is mismatched parameters types in a function override.  Function calls in Unreal are dispatched by name only, rather than name-and-signature, because it doesn't support overloads.  If a derived class's implementation of a function has mismatched types from the parent class, then it can be used to pass the parameters to a mismatched function.

Disabling type checks in the editor

Pin type compatibility in the Blueprint editor is determined by UEdGraphSchema_K2::ArePinTypesCompatible.  You can manipulate the logic there to allow any kind of illegal type usage.  For example, this will allow object and int64 inter-conversion:

How this may lead into more dangerous problems

The route from a fake object pointer to arbitrary memory access or arbitrary code execution is probably relatively short for a skilled attacker.

If you can pretend an arbitrary memory location is a UObject pointer, then fake vtables, fake arrays, and fake struct member accesses can probably be used to do just about anything.

There are also some obvious stack-smashing routes from returning oversized structs into temporaries. 

Mitigations for your game

I can't provide specific advice on how to implement any of this, but at minimum, block untrusted packages that contain a UClass or any derivative, including UBlueprintGeneratedClass.

While I have no evidence that other asset types are affected (see below), I would strongly recommend that UGC systems restrict allowed asset types to the absolute minimum subset of asset types that can be proven to be safe.

Further questions worth researching

Do other asset types have sufficient security hardening and validation?

I don't know the answer to this, but it's worth researching rather than assuming.  It would be good to investigate out how well-validated other asset types actually are, especially as Unreal is a massive codebase with a lot of complicated asset types.

Epic does not, to my knowledge, provide guidance on what types of data they consider safe for use from untrusted sources.

How should this be fixed?

It is unclear if this is even considered a real problem in UE4 and UE5, or if Blueprints are only intended for trustworthy content.  It seems like the latter is the intent, given that Fortnite Creative doesn't allow Blueprint for "security reasons."  The most helpful thing would be for Epic to communicate what their stance is on what asset types are supposed to be safe for use with untrusted content.

Beyond that, if this is considered a real problem, the best fix would be for the Blueprint loader to add a bytecode validation stage.  Since Blueprint bytecode loading happens after all of the object references have been resolved, it should be possible to statically validate expression correctness and function overload correctness.

Does this affect networking/RPC?

Mostly no.  Type confusion in the RPC system would be much more serious because it would affect a large number of Unreal multiplayer games.  UPackageMapClient::SerializeObject has validation to ensure that object references sent over RPC are of the expected class.

There is an extremely narrow exception that I will not be disclosing at this time.

Does this affect assets from Fab?

... Probably not?

The most obvious path would be if someone distributes a "Compiled Blueprint" asset. Using those requires enabling cooked content in the editor, which currently appears to be broken anyway.  When I tried it, it crashed the editor.  I haven't found a reliable way to force a Blueprint to not be recompiled, and recompiling breaks this exploit because it will either fail, or overwrite the invalid bytecode.

Does this affect Fortnite Creative/UEFN?

NO.  Fortnite Creative/UEFN do not allow Blueprint.

Does this affect the Unreal Tournament series?

I reported this problem to the OldUnreal team, which maintains current builds of Unreal Tournament and Unreal Tournament 2004, in 2022.  I have absolutely no idea if they fixed it.

Unreal Tournament 3 is affected.

In all cases, you should avoid connecting to untrustworthy servers.

Monday, August 3, 2026

That weird time that one of the most popular multiplayer shooters in the world had the most obvious remote code execution exploit ever, and nobody really cared

I'm going to be publishing a multi-part series on why you shouldn't use Blueprints in user-created content because of serious vulnerabilities in the Blueprint interpreter.

This is a prelude to that, and follows the vulnerability's origins as a poorly-kept secret in the UT2004 modding, which had long been using exploits to install community-made anti-cheat mods.

I basically sat on this all for a very long time, before a very "better extremely late than never" decision to finally report it once most of these games had active maintainers again.  The new maintainers have been notified, but some of those maintainers authored these exploits in the first place.

Some history of this problem

A few years after Unreal Tournament's launch, connecting to servers would frequently greet you with this:

 

A "native mod" is a fancy term for a DLL downloaded on to your machine by the server, which is not something that the game is supposed to allow.  This was a combination of a path traversal vulnerability with an overly permissive API and is assigned CVE-2024-34492, and worked by stuffing a DLL binary into a music resource, then loading the Editor package and exporting it into the System directory.  (Ultimately, this makes it a path traversal vulnerability.)

The fact that a self-advertising DLL dropper openly existed on around half of the active servers of a popular game and never set off alarm bells or caused an incident is a testament to how different the security landscape was in the early 2000's compared to today, and how much games have lucked out by being overlooked as attack vectors.

This was only a few years after the big headline-making threat was the Melissa virus, a pointless macro virus that did nothing but spam itself to every one of your Outlook contacts.  Clearly not the same type of environment as a world full of ransomware, wipers, RATs, and crypto wallet stealers.

However, this particular method stopped working in Unreal Tournament 2003.  Despite that, some similar community-made anti-cheat systems and "enhanced functionality" were popping up for Unreal Tournament 2004 anyway, and some of the internal comments were odd things like "don't recompile this package."  It turns out that was because they were using intentionally-mismatched packages to take advantage of a type confusion vulnerability in order to escape the scripting system's sandboxing.

So, why does this matter today? 

This would only matter so much if it was about vulnerabilities in 20-year-old games with a niche audience.  Shortly after I reported this issue to Epic, Unreal Tournament, Unreal Tournament 2003, and Unreal Tournament 2004 were removed from sale, but there is no evidence that this is related to the vulnerability report.  Unreal Tournament 3, which is still being sold, is almost certainly still affected by the same exploits used in UT2004, which I will be going into more detail on in the next part.

Unreal Engine 4 and 5 work much differently than the prior iterations that happily let players run their own servers that distributed mod packages to anyone connecting though.  Unreal-based games these days mostly use developer-controlled servers and don't distribute arbitrary packages to users unless the developers explicitly add paths for doing so.

Unfortunately, about 2 weeks ago, such a thing happened when popular indie hit Meccha Chameleon allowed Blueprints in custom maps downloaded via Workshop.

That was caused by an insecure function, "Launch URL," which could be used to launch... things that were not URLs.  Most of the responses centered on limiting what functions Blueprint can access.  Cool idea, but ultimately not the correct approach.  Blueprint is not safe to load from untrusted sources, period, because the Blueprint interpreter itself is not safe when given untrusted bytecode.

It's based on the UnrealScript interpreter and none of the fundamental vulnerabilities used by the UT2004-era sandbox escapes have been fixed.

I'll be going into those in the next part.