지난 10월 11일 이후, Danfoss와 Copeland 사의 유럽 전역에 인터넷에 노출된 냉장고 컨트롤러들을 대상으로 사이버 공격이 발생했습니다. 이 과정에서 공격자들은 제상(defrosting) 작업을 유발하여 수천만 유로 상당의 식료품 손상을 일으켰으며, 일부 사례에서는 화재까지 발생시켰습니다.
SentinelOne이 지적했듯이, 출처 추적은 여전히 미정이며 분석가들은 모든 사건이 동일한 위협 행위자(threat actor)와 연결되었다고 성급하게 가정하는 것을 주의해야 합니다. 하지만 이러한 사고들의 영향력(특히 심리적 영향)은 그 기술적 단순성과 극명한 대조를 이룹니다.
많은 온라인 논평에서 이러한 종류의 컨트롤러가 인터넷에 노출되어서는 안 된다는 점을 정확히 지적했지만, 유지보수 인력 제약 및 서비스 수준 협약(SLA) 또는 보험 요구 사항 때문에 어떤 형태의 원격 연결성은 종종 필수적인 요소입니다.
결과적으로 많은 자산 소유자들은 단순히 기본 자격 증명(default credentials)을 변경하고, 알려진 취약점에 대한 패치를 적용하며, 엄격한 레벨 세션 제어(level session control)를 활성화하는 것으로 만족하고 넘어갑니다. 이는 불충분하며, 자산 소유자들은 가능한 한 빨리 인터넷 노출을 제거해야 합니다 (예: VPN 사용, 사설 APN 또는 매우 제한적인 IP 화이트리스트 ACLs 사용 등을 통해). 우리는 희소한 보안 자원을 패치 노력에 할당하기보다는, 대신 노출도를 줄이는 데 집중할 것을 권장하지 않습니다.
지난 10월 11일 프랑스에서 시작된 공격 웨이브 이전에, SynAcktiv(2023년 [1,2]), Claroty(2025년 [3]), 그리고 DIVD/HackDefense(2023년 [4])는 이미 여러 Danfoss 제품 라인(AK SM-800, AK SM-800A, AK-EM100)에서 여러 취약점을 문서화한 바 있습니다.
이러한 취약점들에는 AK-SM800A의 논란이 많았던 코드 오브 더 데이 인증 우회(authentication bypass)(CVE-2025-41450), OS 명령어 주입(OS command injection)(CVE-2023-25915, CVE-2025-41451) 및 경로 탐색(path traversal)(CVE-2023-25914) 취약점뿐만 아니라, EOL AK-EM100의 SQL 주입(SQL injection)(CVE-2023-22583) 및 OS 명령어 주입(OS command injection)(CVE-2023-25911) 취약점도 포함되었습니다.
이러한 취약점들은 발견하고 악용하기가 충격적일 정도로 쉬우며, 첫 번째 공개 Proof-of-Concept (PoC) 익스플로잇은 프랑스 공격 [5, 6] 발생 후 48시간 이내에 배포되었고, 이는 11월 2일의 두 번째 공격 물결보다 앞선 것이었습니다.
Censys에 따르면 여전히 수백 개의 Danfoss 시스템이 온라인에 노출되어 있으며(물론 상당 부분은 이미 허니팟일 것입니다), 1,000개가 넘는 Copeland 및 Dixell 냉장고 컨트롤러도 존재합니다. 게다가 아직 표적이 되지 않은 유사 공급업체(Eliwell 등)의 컨트롤러들도 수백 개가 여전히 노출되어 있습니다.
이 모든 것은 이러한 컨트롤러들의 명백히 미흡한 보안 태세와 심각한 영향을 미친 여러 공격 물결에도 불구하고, 컨트롤러들이 공용 인터넷에 계속 노출되어 있음을 의미합니다. (물론 이는 이해할 만한 이유가 있습니다. 슈퍼마켓 체인이 원격 유지보수를 중단하고 VPN을 통한 접근을 제한하는 것이 SLA 또는 보험 조건 위반의 위험을 감수해야 하는 사소한 시스템 변경이 아니기 때문입니다.) 하지만, 우리는 방어자들이 Danfoss 가이드에서 제안하는 것처럼 패치를 적용함으로써 위험이 충분히 완화되었다고 판단하는 함정에 빠지지 않는 것이 지극히 중요하다고 간주합니다.
이를 입증하기 위해, 우리는 최신 Danfoss AK-SM800A 펌웨어 버전(작성 시점 기준 5.3.2)을 조사했으며, 수동 조사를 통해 (AI가 필요하지 않게도...) 단 한 시간 만에 인증 전 원격 코드 실행(pre-authentication remote code execution) 0-day 취약점 6개를 발견했습니다.
현재 우리는 Danfoss와 협력하여 취약점 공개 절차를 진행하고 있으며, 따라서 공개가 정리될 때까지 기술적 세부 사항을 보류할 것입니다. 그러나 이러한 컨트롤러들이 지속적으로 표적이 되고 있기 때문에, 자산 소유자들이 부분적인 패치 적용에 의존하기보다는 적절한 조치를 취할 수 있도록 이 장치 컨트롤러들의 전반적인 보안 태세에 대한 현실적인 시각을 갖는 것이 중요하다고 생각합니다.
완화 방안: VPN, 사설 APN 또는 매우 제한적인 IP 화이트리스트 ACL을 통해 네트워크 노출을 제한합니다.
Danfoss AK-SM800A의 펌웨어 이미지는 해당 웹사이트에서 다운로드할 수 있으며, 애플리케이션 로직을 처리하는 7-zip 압축 ARM 바이너리를 포함하고 있습니다. Danfoss AK-SM800A 컨트롤러는 i.MX6 프로세서에서 커스텀 Yocto 기반 Linux 배포판인 Danux를 실행합니다.
먼저 펌웨어를 위한 에뮬레이션 환경을 설정하는 것부터 시작하겠습니다. 여기서 가장 쉬운 경로는 전체 시스템(커널 포함)을 에뮬레이션하기보다는 사용자 영역(userland)에서 바이너리를 에뮬레이션하는 것입니다. 파일 시스템 마운트 후, qemu-armel-static 파일을 그곳에 복사하고 다음 명령어를 실행하여 셸을 얻을 수 있습니다:
$ sudo chroot . /qemu-armel-static /bin/sh
sh-4.4#
Claroty가 지적했듯이, SM800A 플랫폼은 NGINX 리버스 프록시, 여러 CGI 핸들러, 그리고 애플리케이션 로직을 처리하는 독립적인 프로세스로 구성되어 있습니다. .cgi 접미사로 끝나는 URL에 대한 요청은 sm_app 실행 파일이 생성한 태스크로 전달됩니다.
알고 보니 sm_app은 장치에서 발생하는 거의 모든 것을 담당하는 거대한 모놀리식(monolithic) 애플리케이션입니다. 이는 사용자 영역 프로세스 내에서 실행되는 미니 실시간 운영체제(Real-Time Operating System, RTOS)와 같습니다 (이는 lift-and-shift 플랫폼 마이그레이션의 결과로 발생하는 일반적인 임베디드 아키텍처 선택지입니다). 이 애플리케이션은 52개의 항목으로 구성된 태스크(스레드) 목록을 가지고 있으며, 여기에는 Task_Modbus_drv_task, Task_Ethernet, Task_IOHandler, Task_Web 등이 있습니다. 장치 및 드라이버의 상당 부분이 누락된 이 환경에서 sm_app을 실행하면 거의 확실하게 충돌(crash)할 것입니다. 충돌을 피하기 위해 바이너리에 임시 패치를 적용하는 것은 작동할 수 있지만, 잠재적으로 고통스러울 수 있습니다.
다행히도 저희는 Task_Web에만 관심이 있으므로, 모든 것을 수정하는 대신 LD_PRELOAD를 사용하여 바이너리로 진입하고, 초기화 코드가 main()에 도달할 때까지 실행되도록 한 다음, 거기서 코드 흐름을 가로채고(hijack), 여기서 자세히 다루지 않을 몇 가지 훅(hooks)을 적용한 후 웹 서버를 시작할 수 있습니다. Danux가 편리하게 gcc와 함께 제공된다는 것이 밝혀졌습니다:
$ gcc -shared -g -fPIC -o libpreload.so preload.c
$ sudo chroot . /qemu-armel-static -E LD_PRELOAD=/libpreload3.so /danfoss/sm_app
hi from hijacked code
몇 가지 추가적인 사소한 수정만으로, 이제 에뮬레이션된 웹 서버와 통신할 수 있게 되었습니다:
$ curl -v -d '' http://127.0.0.1:8080/xml.cgi
* Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080
...
가장 먼저 할 일은 소위 코드 스멜(code smells)과 안티패턴(anti-patterns)을 통해 코드베이스의 보안 품질을 파악하는 것입니다.
첫 번째로 발견된 나쁜 코드 스멜은 strcpy를 극도로 자유롭게 사용한 점입니다. *strcpy*() 함수를 사용하는 것은 크기 매개변수(size parameter)가 없기 때문에 권장되지 않습니다. 대신, 이 함수는 소스 버퍼에서 목적지까지 널 바이트(null byte)에 도달할 때까지 계속해서 바이트를 복사합니다. 이는 특히 소스 버퍼가 사용자로부터 제공되는 경우 메모리 손상 취약점의 일반적인 원인이 됩니다. 코드베이스에는 수백 개의 strcpy 호출이 존재하며, 이들은 외부에서 접근 가능하고 인증되지 않은 핸들러 함수에 속해 있습니다. 실제로 이전 펌웨어 버전에서는 각 명령어 핸들러가 중앙 집중식으로 처리하기보다는 자체적으로 인증을 강제하는 책임이 있었습니다. 이는 불필요하게 사전 인증 공격 표면(pre-authentication attack surface)을 넓혔습니다.
다음으로, 바이너리 보안 메커니즘 및 익스플로잇 완화 기법(exploit mitigations)을 확인합니다:
$ checksec sm_app
[*] '/mnt/SM800A-4.2.4/sm_app'
Arch: arm-32-little
...
스택 카나리(stack canaries)와 포지션 독립 실행 파일(Position Independent Executable, PIE)이 활성화되어 있지 않은 것으로 나타났습니다. 이 메커니즘들은 수십 년 동안 메모리 손상 방어의 핵심 요소였으며, 현대 ARM 애플리케이션 프로세서에서 실행되는 최신 Linux 배포판에 이러한 기능을 활성화하는 것은 전혀 공학적 작업이 필요하지 않습니다. 단지 올바른 컴파일러 플래그를 설정하는 문제일 뿐입니다. 이 기능들은 Ubuntu 17.10부터 기본값(default)이 되었으며, 몇 달 전에 마지막으로 업데이트된 네트워크 연결 애플리케이션에 이러한 기능들이 활성화되어 있지 않을 이유가 없습니다.
더욱 우려스러운 점은 Danfoss의 여러 사업부(Climate Solutions 및 Electronic Controllers 포함)가 Secure Product Development Lifecycle에 대한 IEC 62443-4-1 인증을 받았으며, 이는 눈에 띄게 광고되고 있다는 것입니다. 게다가 Danfoss는 IEC 62443-4-2가 사이버 보안 요구 사항 설계 및 개선 구현을 위한 지침 프레임워크임을 명시하고 있습니다. 자산 소유주, 시스템 통합업체, 그리고 구매를 담당하는 사람들은 이러한 인증이 제품의 실제 보안 태세에 대해 얼마나 적은 정보를 제공할 수 있는지 정확히 인식해야 합니다.
Danfoss의 초기 보안 태세 인상은 역사적 취약점의 특성과 결합되어 우려스러운 수준입니다. 하지만 이 문제가 실제로 어느 정도까지 심각한지 확인하기 위해, 최신 펌웨어를 수동으로 분석하는 데 한 시간도 채 걸리지 않는 범위에서 발견할 수 있는 것들을 살펴보겠습니다.
저희는 하네스(harness)를 개발하여 바이너리를 커버리지 기반 퍼징(coverage-guided fuzzing)에 노출시킬 수 있습니다. 하지만 이는 심각한 노력을 요하는 작업이며, 제가 파악한 코드 품질의 인상을 고려할 때, 대신 strcpy()가 호출되는 임의의 장소들을 살펴보는 것이 나을 것 같습니다. 검사한 첫 번째 함수 중 하나는 많은 구문 분석 로직과 strcpy 호출을 포함하는 CGI 핸들러의 인증되지 않은 엔드포인트로 밝혀졌습니다.
정확히 영향을 받은 엔드포인트를 공개하지는 않겠지만, 원라인(oneliner)으로 얼마나 쉽게 악용할 수 있는지 보여드리겠습니다:
$ curl -v -d <redacted>"$(echo 'print(
물론, 원격으로 충돌(crash)을 유발하는 것 자체도 나쁘지만, 버퍼 오버플로우(buffer overflow)를 악용하여 원격 코드 실행(remote code execution)을 얻는 것은 훨씬 더 심각합니다. 합리적으로 경험이 있는 익스플로잇 개발자라면, 익스플로잇 완화(exploit mitigations)가 없는 ARM Linux 환경에서 간단한 스택 기반 버퍼 오버플로우를 악용하는 것이 매우 쉽다는 데 동의할 것입니다. 만약 그렇지 않더라도, 그들에게 도움을 줄 의향이 있는 프론티어 또는 오픈 웨이트 모델이 어딘가에 있을 가능성이 높습니다.
극복해야 할 유일한 난관(그렇게 부를 수 있다면)은 `strcpy()`가 널 바이트(null byte)에 도달하면 복사하는 것을 중단한다는 사실입니다. 만약 원격 코드 실행을 얻고자 한다면, 우리는 필연적으로 널 바이트를 포함하는 여러 주소(addresses)를 작성해야 합니다. 따라서 우리는 `strcpy()`의 이러한 내재적 한계를 어떻게든 극복해야 하며, 현재로서는 우리의 접근 방식을 공유하지 않겠지만, 이는 결코 로켓 수술(rocket surgery) 수준은 아닙니다. 또한, 악용되는 애플리케이션은 root 권한으로 실행됩니다 (물론 그렇습니다).
현재 조정된 공개(coordinated disclosure)가 진행 중이므로 모호하게 설명해야 하지만, 최소한 우리의 100% 장인 정신으로 제작되고, 수작업으로 만들어졌으며, AI와 글루텐 프리(gluten free)인 익스플로잇의 결과를 PoC(Proof of Concept)로 보여드리겠습니다:
위에 시연된 익스플로잇은 버전 3.3.0을 대상으로 했지만, 해당 버그는 최신 펌웨어(작성 당시 기준 5.3.2)에서도 존재합니다.
하지만 이후의 펌웨어 버전에서는 Danfoss가 *세션 제어(session control)*와 *보안 레벨(security levels)* 개념을 도입했습니다 [1,2]. Danfoss에 따르면, 세션 제어는 다양한 원격 인터페이스(StoreView Browser, StoreView Web, Alsense 및 서드파티 XML)를 위한 *인증 규칙 엔진(authentication rules engine)*입니다. 이 세션 제어 메커니즘은 위에 언급된 개별 핸들러 레벨의 인증 강제 적용을 대체합니다.
세션 제어는 백워드 호환(backward compatible), 허용적(permissive), 엄격(strict)의 세 가지 보안 레벨을 가지고 있습니다. 4.0 버전부터 보안 레벨은 *엄격(strict)*으로 기본 설정되었으며, 이는 다음과 같습니다:
이 AKSM-auth 헤더 메커니즘은 과거에는 인증 키로 base64로 인코딩된 사용자 이름과 비밀번호를 포함했지만, 이제는 초기 인증에서 파생된 세션 토큰을 포함해야 합니다 [3]. *엄격한* 보안 수준에서는 웹 접근 가능한 공격 표면의 거의 모든 부분이 비인증 사용자로부터 차단되어 있습니다 (위 익스플로잇에 사용된 버그 포함). 이것이 CERT-FR의 공식 완화 조치에 이 보안 제어 활성화가 포함된 이유입니다.
불행하게도, 세션 제어 메커니즘 자체에도 적어도 하나의 사소한 사전 인증(pre-authentication) 메모리 손상 취약점이 존재하는 것으로 밝혀졌습니다.
이것은 *sm_fcgi_app*이라는 별도의 바이너리를 통해 구현됩니다. 이것은 사용자가 인증되었다고 판단하면 모든 명령을 NGINX로 전달하는 웹 애플리케이션 방화벽(WAF) 역할을 합니다. 그렇지 않은 경우, 통과가 허용되는 유일한 명령어는 인증에 사용되는 *getauth* 명령어입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20