Unity 보안 이슈 살펴보기
2025년 10월에 발생한 CVE-2025-59489 보안 이슈에 대해 살펴보도록 한다.
2025.10.06
2025년 10월에 발생한 CVE-2025-59489 보안 이슈에 대해 살펴보도록 한다.
즐거운 추석 연휴가 시작되는 2025년 10월, 연휴 시작과 동시에 발생한 CVE-2025-59489
보안 이슈에 대해 살펴보도록 하자.
여느떄와 다름없던 어느 날, Unity 계정으로 등록된 메일로 다음과 같은 메일이 한통 도착했다.

처음에는 내가 뭘 잘못해서 이런 메일을 받은줄 알았는데, 그런건 딱히 아니었고 Unity 엔진 자체에서 보안 이슈가 터진듯 했다.

해당 보안 이슈는 GMO Flatt Security
사의 연구원 RyotaK
가 올해 6월에 찾았다고 알려졌다고 하는데, 정확히 이게 무슨 일이지? 하고 살펴 보던 도중, 메일의 최하단부에 다음과 같은 문구를 발견했다.
기술과 관련된 종합적인 세부 정보는 패치 툴 및 문제 해결 가이드, 보안 권고, CVE-2025-59489를 참고하시기 바랍니다.
이 중 눈에 띄었던 것은 CVE-2025-59489
였다. 구글링을 해본 결과, 마침 이 이슈를 발견한 GMO Flatt Security
사의 리서치 포스트에서 관련 내용을 찾을 수 있었다.
본 포스트에서는 해당 리서치를 톺아보도록 한다.
- 📗 참고 자료
Unity 2017.1f 이상을 사용하고 있나요? 그럼 Unity Hub를 최신으로 받고 에디터를 업데이트 하세요!
🔷 톺아보기
본 이슈는 Unity 런타임의 인텐트 처리 프로세스에서 취약점이 발견되었다는 내용이다.
그럼 여기서 인텐트 (Intent)
란 무엇일까?
🔶 Intent가 뭐지?
Intent
란, Android에서 컴포넌트 간의 메시지를 전달하기 위한 핵심 메커니즘이다. 앱 내부의 Activity 간 이동뿐 아니라, 외부 앱(예: 브라우저, 카메라, 전화 앱 등)과의 상호작용까지 모두 이 객체를 통해 이루어진다. 이름 그대로 의도(Intent)를 표현하는 객체로, 개발자는 이를 통해 무엇을 할지(Action)
와 무엇에 대해 할지(Data)
를 명시하여 시스템에 요청할 수 있다.
예를 들어, 다음 코드처럼 Intent.ACTION_VIEW
와 URL 데이터를 지정하면 브라우저를 통해 해당 웹페이지를 열 수 있다. (Kotlin 코드이다.)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://KimYC1223.github.io"))
startActivity(intent)
Intent에는 명시적 인텐트(Explicit Intent)와 암시적 인텐트(Implicit Intent)가 있는데, 여기서는 너무 내용이 깊어지므로 설명하지 않도록 한다. 아래는 Implicit Intent가 동작하는 방식을 설명하는 그림이다.

또한 Intent는 단순히 어떤 액션(Action) 을 실행하라는 요청뿐 아니라 그 요청과 함께 추가 정보
도 같이 보낼 수 있는데, 이걸 extra 라고 부른다.
예를 들면, 이메일 앱 열어줘 라는 액션이 있다면, 받는 사람, 제목, 본문 같은 데이터를 extra로 담아서 보내는 것이다.
설명이 길었는데, Intent 란, 안드로이드의 Intent는 컴포넌트 간 메시지를 전달하며, 실행할 동작(Action)과 대상(Data), 그리고 추가 정보(extra)를 함께 담아 시스템에 요청하는 핵심 메커니즘이다. 정도만 기억하면 될 듯 하다.
🔶 Unity와 Intent
Intent에 대한 설명은 이쯤 해두고, 슬슬 이번 사안에 관한 내용을 살펴 보도록 하자.
Unity Documents에 따르면, Android OS에서는 Unity 애플리케이션의 디버깅을 지원하기 위해 unity extra를 포함한 인텐트를 처리하는 핸들러를 UnityPlayerActivity에 자동으로 추가하게 된다고 한다. 이때, UnityPlayerActivity는 애플리케이션의 기본 진입점 역할을 하며, 다른 앱에서도 접근할 수 있도록 export된다.
예를 들어, Android에서 다음과 같이 -systemallocator
라는 extra와 함께 게임을 실행시킬 수 있다는 뜻이다.
adb shell am start -n "com.Company.MyGame/com.unity3d.player.UnityPlayerActivity" -e unity "-systemallocator"
알다시피 Unity 엔진은 일반적으로 PC용 실행 환경처럼 명령줄 인자를 받을 수 있다. ( -force-vulkan
같은 설정 ) 그런데 Android 쪽에선 이런 명령줄 인자를 직접 줄 수 없으므로, Unity에서는 이 -e unity
를 명령줄 인자 대체 수단으로 쓴 것이라고 한다.
이런 Intent와 extra를 사용한다면 동작 흐름은 대략 이렇게 된다고 보면 된다.
- 앱을 실행할 때, Intent 생성자나 am start -e unity "..." 처럼 Intent의 extra에 "unity" 키를 넣고, 거기에 명령줄 인자 문자열을 담는다.
- UnityPlayerActivity (Unity가 Android에서 기본으로 쓰는 Activity)가 이 Intent를 받는다.
- 내부 코드에서 getIntent().getStringExtra("unity") 함수를 통해 extra 값을 읽는다.
- 읽은 문자열을 Unity 런타임 초기화 시점에 명령줄 인자처럼 해석해서 엔진 쪽에 넘긴다.
실제로 UnityPlayerActivity.java
를 확인해보면 다음과 같은 부분이 있음을 알 수 있다.
// Setup activity layout
@Override protected void onCreate(Bundle savedInstanceState)
{
// ...
String cmdLine = updateUnityCommandLineArguments(getIntent().getStringExtra("unity"));
// ...
}
( Windows 기준, 기본 설치 경로에 Unity Hub 설치했다고 한다면 )
C:\Program Files \ Unity \ Hub \ Editor \ 유니티버전 \ Editor \ Data \ PlaybackEngines \ AndroidPlayer \ Source \ com \ unity3d \ player
에 있는 UnityPlayerActivity.java
를 보면 된다!
🔶 CVE-2025-59489 : Unity 런타임에서 임의 코드 실행
근데 왜 이게 왜 문제가 될까? 바로 지금까진 Unity에서 이 Intent에 대해 특별히 검사를 하지 않았기 때문이다.
While Android’s permission model manages feature access by granting permissions to applications, it does not restrict which intents can be sent to an application. This means any application can send the unity extra to a Unity application, allowing attackers to control the command line arguments passed to that application
안드로이드의 권한 모델은 애플리케이션에 권한을 부여하여 기능 액세스를 관리하지만, 애플리케이션에 전송할 수 있는 인텐트는 제한하지 않습니다. 즉, 모든 애플리케이션이 Unity Extra를 Unity 애플리케이션으로 전송할 수 있으며, 이를 통해 공격자는 해당 애플리케이션에 전달되는 명령줄 인수를 제어할 수 있습니다.
Android에는 Permission이라는 것이 있다. 말 그대로 권한
으로, 앱이 기기 내의 민감한 자원이나 기능 ( 예: 카메라, 위치, 연락처, 파일 저장소 등 ) 에 접근하기 위해 사용자에게 명시적으로 허용을 받아야 하는 시스템 메커니즘으로, 보안을 위해 OS단위에서 앱이 접근 가능한 범위를 엄격히 제한하며, 사용자 동의 없이는 개인정보나 시스템 리소스에 접근할 수 없도록 하는 것이다.
이는 주로 Manifest에서 미리 선언해두거나, 런타임에서 사용자에게 요청하는 식으로 받아올 수 있다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package=" ... " >
<!-- ... -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- ... -->
</manifest>
하지만 이렇게 Permission을 엄격하게 관리하고 있는 반면, Intent 자체는 별도의 제한을 하고 있지 않기 때문에 다른 앱이 Unity에게 악의적인 Intent를 보낼 수 있다는 뜻이다.
그럼 여기서 다시, 다른 앱이 Unity에게 악의적인 Intent를 보낼 수 있다는건 왜 문제가 될까? 이 질문에 답을 하려면, 여기서 RyotaK
씨가 발견한 취약점을 살펴 봐야 한다.
RyotaK
씨는 Unity 빌드 파일을 Ghidra로 분석을 해본 결과, 다음과 같은 코드를 찾아냈다고 한다.
initLibPath = FUN_00272540(uVar5, "xrsdk-pre-init-library");
여기서 Ghidra란 역어셈블러 프레임워크다. 조금 찾아보니 Ghidra를 사용해서 유니티 빌드를 분석한 국내 포스트도 찾을 수 있었다.
그리고 이 값은 나중에 dlopen
에 전달되어 xrsdk-pre-init-library 에 지정된 경로가 네이티브 라이브러리로 로드되도록 한다고 한다!
lVar2 = dlopen(initLibPath, 2);
이런 로직을 통해 악의적인 의도를 가진 사용자가 -xrsdk-pre-init-library
인수에 특정 공유 라이브러리 ( .so 파일 )를 로드하고 악성 코드를 실행할 수 있다는 것이다.
이때 실행되는 코드는 Unity의 컨텍스트에서 실행되므로, Unity의 Permission으로 실행된다!
Unity 개발자 문제 해결 가이드의 설명에 따르면,
-xrsdk-pre-init-library
는 Unity가 실행될 때 내부 설정 파일 ( Data/boot.config ) 에서 불러와야 하는 인자가 있는데, 이 인자는 Unity가 실행 시 네이티브 라이브러리를 로드하도록 하는 역할을 한다. 하지만 이 인자는 커맨드 라인 인자로 사용하도록 설계된 것이 아니라고 한다.
🔶 공격 시나리오
그럼 실제로 이걸 이용해서 나쁜짓을 하려면 어떻게 해야할까? 다음과 같은 시나리오가 가능할 수 있다.
💠 Local Attack
악의적인 의도를 가진 앱을 만든다면, 다음과 같이 공격을 할 수 있다.
AndroidManifest.xml
에서android:extractNativeLibs
속성을true
로 설정하여 네이티브 라이브러리를 추출한다. ( 이 네이티브 라이브러리에는 나쁜짓(?)을 할 수 있는 코드가 포함되어 있다. )- 그리고, Intent에
-xrsdk-pre-init-library
인수로 악성 라이브러리를 지정하여 Unity 앱을 실행한다. - 그러면 Unity 앱의 권한을 가지고 악성 코드를 로드하고 실행하게 된다.
물론 이 공격이 성공하려면, 악의적인 의도를 가진 앱과 Unity가 같은 기기에 설치되어 있어야 하고, Unity앱이 나쁜짓에 필요한 권한을 가지고 있어야 한다.
💠 Remote Attack
웹 브라우저를 사용해서, 링크를 눌렀을 때 비슷한 동작을 하게 만들수 도 있다.
Unity 앱이 android.intent.category.BROWSABLE
카테고리 ( 브라우저 실행 허용 )를 사용하는 경우, 악성 웹사이트에서 Intent URL을 사용하여 Activity에 전달되는 추가 기능을 지정할 수 있다.
intent:#Intent;package=com.example.unitygame;scheme=custom-scheme;S.unity=-xrsdk-pre-init-library%20/data/local/tmp/malicious.so;end;
즉, 사용자가 /data/local/tmp/malicious.so
파일을 다운로드 받게 만든 후, 해당 Intent URL을 호출하면 Unity 앱이 켜지면서 malicious.so
가 실행되어 나쁜짓을 하게 되는 것이다!
하지만 안드로이드의 엄격한 SELinux 정책은 dlopen
이 다운로드 디렉터리의 파일을 열지 못하도록 차단하여 거의 모든 원격 악용 시나리오를 막게 된다. 🫤
library "/sdcard/Download/libtest.so" ("/storage/emulated/0/Download/libtest.so") needed
or dlopened by "/data/app/~~24UwD8jnw7asNjRwx1MOBg==/com.DefaultCompany.com.unity.template.
mobile2D-E043IptGJDwcTqq56BocIA==/lib/arm64/libunity.so" is not accessible for the
namespace: [name="clns-9", ld_library_paths="",default_library_paths="/data/app/~~24UwD8jnw7asNjRwx1MOBg==/com.DefaultCompany.com.unity.template.
mobile2D-E043IptGJDwcTqq56BocIA==/lib/arm64:/data/app/~~24UwD8jnw7asNjRwx1MOBg==/com.DefaultCompany.com.unity.template.mobile2D-E043IptGJDwcTqq56BocIA==/base.apk!/lib/arm64-v8a", permitted_paths="/data:/mnt/expand:/data/data/com.DefaultCompany.com.unity.template.mobile2D"]
이때 주의할 점은, /data/
디렉터리는 permitified_paths
에 포함되어 있으므로 Unity 앱이 Internal Storage
에 파일을 쓰는 경우 이 제한을 우회하는 데 사용될 수 있게 된다는 점이다.
즉, 다음과 같은 2가지 조건을 만족하면 브라우저를 통한 공격에 취약하다는 뜻이다.
- Unity 앱이
android.intent.category.BROWSABLE
카테고리를 사용하며, UnityPlayerActivity 또는 UnityPlayerGameActivity를 진입점으로 빌드 하는 경우 - Unity 앱이
Internal Storage
에 파일을 읽고 쓰는 경우
물론 이 2가지 조건을 만족하지 않는다 하더라도, Local Attack
에는 취약하다는 점은 기억해야 한다.
💠 데모 영상
아래는 RyotaK
씨가 올린 해당 보안 이슈의 Remote Attack 데모 영상이다. 로그를 자세히 보면 HijackLib
이 호출 된 모습을 볼 수 있다.
- 📗 참고 자료
- Unity 플랫폼 보호: 개발자 문제 해결 가이드
- CVE-2025-59489: Arbitrary Code Execution in Unity Runtime
- Android Developer : 인텐트와 인텐트 필터
- Android Developer : 런타임 권한 요청
- Unity Documents : Specify Android Player command-line arguments
- RedHat : SELinux(Security-Enhanced Linux)란?
- 개발자 향기 블로그 : Il2CppDumper와 ghidra로 유니티 il2cpp빌드 분석하기
🔷 플랫폼 취약점 현황 및 대응 방법
여기까지 알아보고 다시 Unity 플랫폼 보호: 개발자 문제 해결 가이드를 다시 읽어보았다.
RyotaK
씨가 찾은 문제는 Android에서만 문제가 발생했다는 것이기에 Android에서만 문제가 발생한 것인가 보다! 라고 생각했는데, Unity가 자체적으로 더 조사해서 비슷한 문제가 있는 커맨드 라인 을 더 찾아낸 것 같다.
일단 문제가 있는 커맨드 라인은 다음과 같다.
커맨드 라인 | 영향을 받는 최종 릴리스 버전 |
---|---|
-xrsdk-pre-init-library |
Android, Windows, Mac, Linux: Unity 2019.1 이상 |
-dataFolder |
Windows: Unity 2022.2 이상 Mac: Unity 2023.2 이상 Linux Unity 2022.3 이상 |
-overrideMonoSearchPath |
Android 32-bit(mono), Windows(mono), Mac(mono), Linux(mono): Unity 2017 이상 |
-monoProfiler |
Windows(mono): Unity 2018.3 이상 |
자세한 설명은 개발자 가이드를 참고하면 되는데,
여기서는 주로 모바일 환경에 대해 알아보도록 한다.
🔶 Android
-
Android의 경우, 제작한 Unity 앱이
Unity 2019.1 이상
버전으로 빌드되었다면 어떠한 특별한 권한이나 설정에 관계없이 조치를 취해야 한다. (xrsdk-pre-init-library
) -
또한 앱이
Unity 2017 이상
버전으로 빌드되었고Mono 런타임 (32비트만 해당)
을 사용하는 경우 취약점이 존재할 수 있다. (overrideMonoSearchPath
) -
Android 앱은
datafolder
나monoProfiler
커맨드 라인 인자에 취약하지 않다.
보안 이슈 관련 Android 부분 수정 사항
또한 Unity 애플리케이션 패치 프로그램은 Mono를 사용하는 32비트 빌드에서 overrideMonoSearchPath 인수를 비활성화하며, libunity.so에서 overrideMonoSearchPath 문자열의 첫 번째 문자를 유효하지 않은 유니코드 문자 0xC0로 교체합니다.
🔶 iOS
- iOS 플랫폼에서는 취약점이 악용될 가능성이 발견되지 않았습니다. 다만 최고 수준의 보안을 위해, 유니티에서는 최신 패치가 적용된 Unity 에디터로 앱을 다시 빌드할 것을 권장합니다.
🔶 그래서 어떻게 업데이트 하는데?
그럼 어떻게 보안 패치를 받아야 하는걸까? 개발자 가이드에 따르면 Unity Editor를 업데이트 하거나, Patch Tool을 받거나라고 하는데, 에디터를 새로 받는게 제일 간단하므로 여기서는 그 방법만 공유한다.
일단 Unity Hub를 켜보면, 다음과 같이 기분 나쁜 빨간 느낌표를 마주할 수 있다. ( 만약 뜨지 않는다면 Unity Hub를 최신 버전으로 업데이트 하라! )

여기서는 내가 받은 프로젝트 버전중 하나인 6000.0.37f1
버전을 업데이트 받아보려고 한다. 빨간 느낌표를 눌러본다.

창이 뜨면, Install other Editor version
버튼을 클릭한다.

Archive 탭을 누르고, download archive 라는 링크를 눌러 Unity 다운로드 웹 아카이브 페이지로 이동한다.

자신의 버전의 맨 마지막 버전을 뗀 버전의 가장 최신 버전
을 다운로드 받는다.
예를 들어, 업데이트 하고자 하는 유니티의 버전이 6000.0.37f1
이라면 .37f1
을 뗀 버전은 6000.0
이므로, 6000.0.X
버전들 중 가장 최신 버전을 받으면 된다. ( 2025년 10월 기준, 6000.0.58f2
이다. )

Unity 버전을 강제로 올려야 한다는 점이 매우 마음에 들지 않지만… 일단 빨간 느낌표는 없어졌다
- 📗 참고 자료
🔷 마치며
갑작스럽게 보안 메일이 도착해서, 내용을 찾아보고 정리를 해 보았습니다.
평소 익숙하지 않은 부분이라 공부도 많이 됐던 것 같은데요!
잘못 찾은 내용이나 정리가 안된 부분도 있으니, 이상한 부분이 있으면 피드백 주길 바랍니다!