보안 · 6 min read · Oct 24, 2025
삼성 녹스, 미국 정부의 기밀 사용 승인 받았지만 정말 안전한가?

Table Of Contents
- 삼성 녹스, 미국 정부의 기밀 사용 승인
- 삼성 녹스가 중요한 개인 이미지/비디오/문서를 저장하는 가장 안전한 방법이라는 의미인가?
- 삼성 녹스의 취약점
- 결론적으로 Ares는 삼성 녹스가 주장하는 만큼 안전하지 않다고 지적한다.
- Ares의 추천
삼성 녹스, 미국 정부의 기밀 사용 승인
삼성 전자는 자사의 솔루션이 미국 정부에 의해 기밀 정보를 처리할 수 있는 첫 번째 NIAP 검증 소비자 모바일 장치로 승인되었다고 발표했다. 10개의 양해각서(MOA)를 완료한 후, 정부는 Galaxy S4, Galaxy S5, Galaxy Note 3, Galaxy Note 4, Galaxy Note 10.1 (2014 Edition), Galaxy Note Edge, Galaxy Alpha, Galaxy Tab S 8.4, Galaxy Tab S 10.5 및 Galaxy IPSEC 가상 사설망(VPN) 클라이언트를 기밀 상용 솔루션(CSfC) 프로그램 구성 목록에 추가했다.
이 성과는 미국 정부의 공통 기준 모바일 장치 기본 보호 프로필(MDFPP) 및 VPN 보호 프로필(VPNPP) 프로그램에 따른 삼성의 성공적인 테스트 및 인증의 직접적인 결과이다. 나열된 삼성 장치는 기밀 정부 네트워크 및 데이터와 함께 사용할 수 있다. 모든 장치와 기능은 삼성 KNOX에 의해 제공되는 보안 기능을 통합하고 있다.
삼성의 녹스 서비스에 대한 설명도 동일한 사실을 지적하고 있다.
“KNOX 작업 공간 컨테이너는 사용자 경험을 개선하고, 기업 애플리케이션을 위한 직원 장치 내에 안전한 구역을 생성하여 기업 데이터를 안전하게 보호하며, 정지 상태와 이동 중 모두에서 기업 데이터를 암호화한다. KNOX 작업 공간 컨테이너는 모바일 장치 내에서 고립되고 안전한 환경을 제공하며, 자체 홈 화면, 실행기, 애플리케이션 및 위젯을 갖추어 보다 직관적이고 안전한 작업을 가능하게 한다. 컨테이너 내부의 애플리케이션과 데이터는 분리되어 있다.”
DISA 승인 제품 목록은 다음에서 확인할 수 있다: https://www.disa.mil/Services/Network-Services/UCCO.
삼성 녹스가 중요한 개인 이미지/비디오/문서를 저장하는 가장 안전한 방법이라는 의미인가?
매일 발생하는 침해 및 해킹 사건으로 인해 사용자는 개인 이미지/문서 또는 비디오를 클라우드에 온라인으로 저장하는 데 심각한 위험을 감수하게 된다. 아이클라우드 해킹 사건은 심지어 유명인들도 해킹으로부터 안전하지 않다는 것을 증명했다. 스냅챗 유출 사건은 13세에서 17세 사이의 아이들의 이미지가 이러한 유출에 취약하다는 것을 입증했다. 드롭박스 유출 사건은 드롭박스에 저장하는 것도 상대적으로 높은 위험을 수반한다는 것을 보여주었다.
그렇다면 사용자는 이러한 개인적인 순간에 찍은 이미지와 비디오를 어떻게 해야 할까? 미국 정부의 기밀 사용 승인을 받은 삼성 녹스는 해커로부터 안전하다고 보장된 첫 번째 모바일 장치가 되었다. 빅 삼이 직접 보장한 것이다.
많은 삼성 제품에 탑재된 무제한 저장 용량(최대 64GB) 덕분에 공간 옵션에서는 안전하지만, 삼성 녹스가 정말로 개인 문서/이미지 및 비디오를 안전하게 저장하는 포트 녹스인지 의문이 든다. 미국 정부는 그렇게 생각하지만, 보안 연구원인 Ares는 그렇게 생각하지 않는다.
사실 Ares는 자신의 블로그에서 삼성의 녹스가 왜 취약한지를 쉽게 보여주었다.
삼성 녹스의 취약점
Ares는 자신의 블로그에서 취약점에 대한 전체 PoC를 제공했으며, 여기서 재현된다:
삼성 전화는 두 개의 앱인 녹스와 녹스 EMM이 사전 설치되어 있다. 녹스 EMM은 모바일 장치에 대한 기업 클라우드 기반 관리 솔루션으로, 이 분석의 일부가 아니다. 분석은 녹스 앱에 초점을 맞추었으며, 이는 녹스 컨테이너와 자체 앱이 있는 별도의(안전한) 홈 화면을 제공한다. 녹스 설정은 매우 간단하며, 녹스 앱에 대한 비밀번호와 PIN을 설정하기만 하면 된다. 시스템 내부를 살펴보면, 녹스는 전화기에 많은 것을 설치한다. 녹스 관련 앱은 /data/data/에 위치한다:
com.samsung.klmsagent
com.samsung.knoxemm.mdm
com.sec.knox.app.container
com.sec.knox.containeragent
com.sec.knox.eventsmanager
com.sec.knox.seandroid
com.sec.knox.store 추가적으로 새로운 녹스 홈 화면에 설치된 모든 앱은 표준 앱 설치 폴더인 /data/data/에 위치하며 접두사: com.sec.를 가진다. 모든 것을 나열하는 것은 너무 많다. 일반적인 녹스 설치 후에는 접두사 com.sec.가 있는 139개의 앱과 서비스가 설치된다. 시스템을 좀 더 깊이 살펴보면, 녹스는 전체 시스템에 분산되어 있으며, 여러 위치에 데이터를 저장한다. 예를 들어, 암호화된 컨테이너 자체는 /data/.container_ 및 /data/container/.sdcontainer_1에 저장된다. 다양한 설정에 대한 파일과 데이터베이스는 /data/system/secure_storage 및 /data/system/container에 저장된다. 모바일 앱에 대한 각 분석은 앱이 설정 후 저장한 파일에 대한 정적 분석으로 시작된다. Android 앱의 좋은 시작점은 /data/data/ 아래의 앱 폴더이다. 이러한 폴더는 일반적으로 다음과 같은 구조를 가진다: com.aPackageName |- cache |- databases |- extracted |- lib |- shared_prefs 특히 데이터베이스 및 shared_prefs 폴더는 첫 번째 주목을 받을 만하다. 녹스의 ContainerAgent 앱(com.sec.knox.containeragent)에는 몇 가지 흥미로운 파일이 저장되어 있다: Activation.xml ContainerActivator.xml ContainerType.xml CreateSettings.xml DB_BRIDGE_INIT_SYNC.xml DB_BRIDGE_ISCHNDUOS.xml DB_BRIDGE_ONCHANGE_CONTACTS.xml DB_BRIDGE_ONCHANGE_EVENT.xml KLMSLicenseStatus.xml Pref.xml PrivacyPolicy.xml bno.xml com.sec.knox.containeragent_preferences.xml pin.xml 네, pin.xml 파일에 무엇이 적혀 있는지 추측해 보세요? 녹스를 설정하는 동안 설정해야 했던 PIN이 일반 텍스트로 저장되어 있습니다! 이제 삼성 녹스는 사용자에게 최대 20번의 사용자 이름 및 비밀번호 시도를 허용한다.*
다른 파일은 흥미로운 내용을 드러내지 않았다. 그러나 pin.xml 파일로 돌아가 보자. 녹스에서 PIN의 목적은 무엇인가? 녹스를 시작하려면 데이터 및 녹스 홈 화면에 접근하기 위해 비밀번호를 제공해야 한다. 그러나 텍스트 필드 아래에는 “비밀번호를 잊으셨습니까?”라는 작은 버튼이 있다. 이를 누르면 PIN을 제공해야 한다. PIN이 올바르면 녹스 앱은 비밀번호 힌트(비밀번호의 첫 번째 및 마지막 문자!! + 비밀번호의 원래 길이)를 보여준다. 이제 삼성 녹스가 장치 어딘가에 비밀번호를 저장할 것이라는 것이 명백해졌다! 앞서 언급했듯이, 녹스는 /data/data/에 설정을 저장할 뿐만 아니라, /data/system/container 폴더에는 containerpassword_1.key라는 파일이 저장되어 있다. 내용은 암호화된 문자열: 72C9EE6D56CB15916A4CAB01814F978FA1E2689D (명백한 이유로 문자열을 수정했다). 따라서 이것은 AES 암호화된 문자열처럼 보인다. 이제 우리는 앱을 디컴파일하여 비밀번호 암호화가 어떻게 작동하는지, 암호화 키가 어디에서 오는지 더 깊이 살펴봐야 한다. 삼성은 dex-preoptimization을 사용하여 녹스 apk에서 모든 classes.dex 파일(자바 코드는 classes.dex라는 파일에 저장되며 이 파일은 Dalvik JVM에 의해 파싱됨)을 제거하여 리버스 엔지니어링을 조금 더 어렵게 만들었다. 이진 파일을 얻으려면 /system/app/를 살펴보고 .odex 파일을 찾아야 한다(odex는 기본적으로 애플리케이션의 classes.dex의 사전 처리된 버전으로 Dalvik에서 실행 준비가 된 것이다). odex 파일은 smali 코드로 다시 변환될 수 있으며, 그 후 dex 파일로 다시 변환될 수 있다. 마지막으로 dex 파일은 jar 파일로 변환될 수 있으며, 이는 어떤 자바 디컴파일러로도 디컴파일할 수 있다. 삼성은 코드 난독화를 사용하지 않았지만 실제 비밀번호 저장 코드를 수백 개의 자바 클래스, 상속 및 프록시 내에 숨기려고 했다. 마지막으로 녹스 스토어 앱에서 프록시 IDataService가 구현되어 모든 다른 앱이 비밀번호 관련 작업을 처리할 때 지속적으로 호출되었다. 따라서 비밀번호를 저장할 때 앱은 다음과 같은 작업을 수행한다: public boolean setData(String paramAnonymousString) { this.keyGenInput = DataService.this.mPasswordUtil.getInputForKeyGenerate(); String str = SecureKeyLoader.getKeyForPassword(this.keyGenInput, this.bit_size); return DataService.this.mEncryptDecrypt.encryptAndSave(paramAnonymousString, str); } 각 줄과 해당 메서드를 살펴보자: public long getInputForKeyGenerate() { return Long.parseLong(SecureKeyLoader.getPartialString(getAndroidID()), 16); } private String getAndroidID() { return Settings.Secure.getString(this.mContext.getContentResolver(), “android_id”); } 따라서 키의 입력은 분명히 모든 장치가 가지고 있는 고유한 Android ID이다. 16바이트 ID가 Long 값으로 파싱된 후 getKeyForPassword 함수의 인수로 사용된다: public class SecureKeyLoader { static { System.loadLibrary(“mealy”); } public static native String getKeyForPassword(long paramLong, int paramInt); public static native String getPartialString(String paramString); } 좋아, 삼성은 실제 키 생성을 숨기기 위해 점점 더 난독화하고 있다. getKeyForPassword 메서드는 “mealy”라는 C로 작성된 공유 라이브러리에 위치해 있다. 장치에서 라이브러리를 가져와 IDA와 같은 디스어셈블러에 넣어보자: 이진 파일에는 “out_char”라는 하드코딩된 문자열이 있으며 다음과 같은 값이 있다: eu>q5b0KPlLwyb@#j9?!ehjl(LHukkA(di^S4UXAChr3B
_xf+@h*#S&wpfv . 코드를 살펴보면 getKeyForPassword 메서드는 getPartialString(getAndroidID())의 long 값을 인수로 받아 out_char 문자열을 변수로 사용하고 두 값을 ‘mealymachine’ 서브루틴에 전달한다. getKeyForPassword 함수와 mealymachine 서브루틴을 의사 코드로 살펴보자:* *function Java_com_sec_knox_store_SecurityManager_SecureKeyLoader_getKeyForPassword {* *r4 = r0; //partialString androidID as long value* *r0 = r2; //integer = 16* *r0 = mealymachine(r0, var_8, next, “eu>q5b0KPlLwyb@*#j9?!*ehjl(LHukkA(di^S4UXAChr3B_xf+@h#S&wpfv”, STK29);
r3 = r4; r2 = (r3 + 0x29c);
r0 = (r2)(r4, r0, r2, r3);
return r0;
}
function mealymachine {
r6 = r0; //partialString androidID as long value
r5 = r1; //var_8
r7 = r2; //next
r8 = r3; //eu>q5b0KPlLwyb@#j9?!ehjl(LHukkA(di^S4UXAChr3B`_xf+@h#S&wpfv if (r1 <= 0x64) { r4 = 0x0; memset@PLT(0x2144, 0x0, 0x64); r1 = r4; do { if (r4 >= r5) { break; } r0 = r6 & 0x1; r6 = r6 >> 0x1;
**(int8_t )(r4 + 0x2144) = (int8_t )(r0 + r8 + r1); //AndroidID + eu>q5b0KPlLwyb@#j9?!ehjl(LHukkA(di^S4UXAChr3B`_xf+@h#S&wpfv + var_8
r4 = r4 + 0x1;
r1 = (r0 0x4 + r7 + r1);
} while (true);
r0 = 0x2144;
*(int8_t )(r0 + (r5 & !r5)) = 0x0; return r0; } else { r0 = 0x0; return r0;
}
return r0;
} 서브루틴을 살펴보면, 단순히 Android ID의 long 값과 하드코딩된 문자열을 더하는 작업을 수행한다. getPartialString 메서드는 Android ID에서 일부를 빼낸다. 명확히 하기 위해: 모든 앱은 다음을 호출하여 시스템에 Android ID를 요청할 수 있다: Secure.getString(getContext().getContentResolver(),Secure.ANDROID_ID);
결론적으로 Ares는 삼성 녹스가 주장하는 만큼 안전하지 않다고 지적한다.
삼성은 키 생성 기능을 숨기려고 정말 노력했으며, 보안은 불투명성 규칙을 따랐다. 결국, 단순히 Android ID와 하드코딩된 문자열을 함께 사용하여 암호화 키를 생성한다. 나는 녹스라는 이름의 제품에서 다른 접근 방식을 기대했을 것이다:
키는 비밀번호 기반 키 파생 함수 2(PBKDF2)에서 파생되어 훨씬 더 강력한 키를 생성해야 한다.
비밀번호 힌트 기능을 위해 키를 지속적으로 저장하는 것은 이 제품의 보안을 완전히 타협하는 것이다. 이러한 제품의 경우 비밀번호는 절대 장치에 저장되어서는 안 된다. 필요할 경우에만 비밀번호를 잊어버린 경우에만 저장해야 한다. 그러나 그 경우 데이터는 손실되어야 하며, 그렇지 않으면 복구 옵션이 있는 경우 안전하지 않다.
Ares의 추천
삼성 녹스 대신, 내장된 Android 암호화 기능을 사용하여 전체 장치를 암호화하라. Android는 선택한 암호화 비밀번호에서 PBKDF2 기능을 사용하며, 절대 장치에 저장하지 않는다. 비밀번호를 잊어버리면 데이터에 접근할 수 없지만, 그것이 좋은 암호화의 요점이다.
*출처: 모바일 보안 블로그
새 게시물을 받은 편지함에서 받기
스팸은 없습니다. 언제든지 구독 해지 가능합니다.