- 2015년에 완공된 영국 아파트의 정체불명 터치스크린은 NETTHINGS의 에너지 모니터링 시스템 일부로, 전력 사용량의 현재값과 과거 데이터를 보여주는 장치였음
- 구조는 계량기 쪽 에너지 매니저와 방 안 Android 태블릿형 클라이언트로 나뉘며, 가까운 거리와 벽 몇 개 사이에서도 케이블 대신 WiFi로 통신함
- 화면이 동작하지 않았던 직접 원인은 에너지 매니저 회로의 3A 퓨즈가 빠진 것이었고, 퓨즈를 교체하자 WiFi 네트워크와 웹 기반 사용량 화면이 되살아남
- 태블릿 UI는 웹뷰였고 서버는 Node.js, Express, Socket.IO를 사용했으며, 계량기 장치는
172.16.0.254에서 DNS, HTTP, SSH, TCF 서비스를 열고 있었음 - 열린 tcf-agent가 root 권한으로 파일시스템과 프로세스 접근을 제공해 SSH 비밀번호 없이도 장치를 수정할 수 있었고, 내부는 Linux 3.10 기반 ARM9 장치와 CSV 기반 전력 데이터 저장 구조였음
정체불명 터치스크린의 용도
- 새 아파트 방 안에는 버튼이나 라벨 없이 작은 노란 전원 표시등만 있는 터치스크린이 설치돼 있었음
- 집주인도 무엇을 제어하는 장치인지 알지 못했고, 이사 뒤 한동안 잊힌 상태로 남아 있었음
- 가전 설명서 바인더에서 같은 장치가 실린 브로셔가 나오면서 정체가 드러남
- 장치는 에너지 모니터링 시스템의 일부
- 현재 전력 사용량과 과거 사용량 데이터를 표시함
- 브로셔에는 전기 계량기에 직접 연결되는 두 번째 구성요소인 에너지 매니저도 소개돼 있었음
- 공동 계량기함에는 NETTHINGS 브랜드 장치가 다른 세대 장치들과 함께 설치돼 있었음
WiFi로 연결된 에너지 매니저와 Android 태블릿
- 시스템은 데이터를 수집하는 “서버” 역할의 에너지 매니저와, 이를 읽어 화면에 보여주는 “클라이언트” 역할의 터치스크린으로 구성됨
- 두 장치 사이 거리는 몇 미터에 불과하고 벽도 2~3개 정도였지만, 브로셔에는 SSID와 Pwd가 적혀 있었음
- 실제 통신도 케이블이 아니라 WiFi로 이뤄짐
- 터치스크린 옆면의 작은 구멍 안 버튼을 누르자 Android 부팅 로고가 표시됨
- 오래된 Android 태블릿이었음
- Google Talk, Flash 등 오래된 앱이 설치돼 있었음
- Android 5로 보였지만 정확한 버전은 확실하지 않았음
- “NetThings” 앱을 실행하면 WiFi 네트워크 선택 화면이 나오지만, 브로셔의 네트워크는 처음에는 목록에 나타나지 않았음
빠져 있던 퓨즈와 되살아난 모니터
- 계량기함에서 다른 세대의 에너지 매니저는 켜져 있었지만, 해당 세대 장치만 전원이 꺼져 있었음
- 원인은 퓨즈 박스 안의 퓨즈 누락이었음
- 퓨즈가 없어 전기 연결이 끊김
- 에너지 매니저에 전원이 공급되지 않음
- WiFi 핫스팟도 나타나지 않음
- 같은 계량기함의 다른 에너지 매니저 퓨즈를 확인해 필요한 규격이 3A 퓨즈임을 확인함
- Amazon에서 3A 퓨즈를 주문해 다음 날 설치했고, 이후 에너지 매니저의 녹색 LED가 깜박이며 WiFi 네트워크가 나타남
- 작업은 주전원 근처에서 이뤄져 위험했고, 이후 하루 동안 퓨즈 온도를 여러 번 확인했으며 이런 실험을 다른 사람에게 권하지 않음
실망스러운 웹 UI와 고정된 요금 값
- Android 태블릿에서 WiFi를 선택하자 자원 종류를 고르는 메뉴가 표시됨
- 실제로 동작하는 항목은 에너지 매니저가 연결된 Mains Electricity뿐이었음
- 전력 사용량 화면은 오른쪽 색상 표시기와 왼쪽 숫자 5개를 보여줬지만, UI의 의미가 명확하지 않았음
- 녹색이 낮은 사용량인지 정상 사용량인지 알 수 없음
- 색상 표시기의 세로 위치가 무엇과 비교되는지 알 수 없음
- 최대 위치가 과거 최대 사용량과 관련 있는지도 알 수 없음
- 왼쪽에 표시되는 숫자 5개 중 실제로 맞는 값은 kW 소비량 하나뿐이었음
- 전기요금과 kW당 CO2 추정치는 설정할 수 없음
- 브로셔에는 최초 설치 때 설정 가능하다고 되어 있음
- 시스템을 다시 설정 가능한 상태로 되돌리는 방법은 안내돼 있지 않음
- 브로셔에는 데이터 시간 보정을 위해 PC에서 접속하라는 내용도 있었고, Android 태블릿의 시계는 2015년 설치 이후 약 15분 틀어져 있었음
웹뷰, Socket.IO, Node.js 서버
- 에너지 매니저의 데이터를 직접 읽을 수 있다면 kW 사용량에 올바른 요율을 곱해 Grafana 등에 표시할 수 있음
- 브로셔에는 PC에서 에너지 사용량을 확인하는 사용 사례가 있었고, 여기에 IP와 포트가 적혀 있었음
- 브라우저로 접속하자 Android 태블릿과 같은 화면이 표시돼, 태블릿 UI가 웹뷰임을 확인함
- 웹 인스펙터에서 API 호출을 살펴보니 Socket.IO가 사용되고 있었음
- 클라이언트는 서버에서 숫자 5개를 받는 정도였지만, 코드에는 RequireJS 모듈, Handlebars, Backbone.js, Underscore.js 등이 포함돼 있었음
열린 포트와 tcf-agent
- 장치 IP는
172.16.0.254였고,ssh root@172.16.0.254는 처음에 “Connection refused”로 실패함 - 전체 포트 스캔 결과 다음 서비스가 열려 있었음
53/tcp: dnsmasq 2.63rc680/tcp: Node.js 기반 HTTP1534/tcp:micromuse-lm?3000/tcp: Node.js 기반 HTTP41142/tcp: OpenSSH 6.2
dnsmasq는 장치가 WiFi 액세스 포인트이기 때문에 DHCP 서버 역할과 맞아떨어짐- SSH는 41142 포트에서 열려 있었지만 root 계정은 비밀번호로 보호돼 있었고,
admin/admin,root/root같은 단순 조합은 통하지 않았음 - 1534 포트의 정체를 찾다가 Xilinx 포럼 글을 통해 tcf-agent 키워드를 확인함
TCF로 얻은 root 파일시스템 접근
- TCF는 Target Communications Framework의 약자로, 대상 시스템에서 파일시스템 읽기, 새 프로세스 시작, 프로세스에 시그널 보내기 등을 지원하는 텍스트 프로토콜임
tcf-agent는 이 프로토콜을 구현하는 서버이며, 이 장치에서는 root 사용자로 실행되고 있었음- TCF는 Eclipse 생태계와 밀접하게 연결돼 있었고, Getting Started 문서는 Eclipse 플러그인을 주요 사용 방법으로 안내함
- 새 Eclipse 버전에서 플러그인을 설치하려 했지만 의존성 충돌로 제대로 설치하기 어려웠음
- 대신 TCF 프로젝트의 Python SDK를 찾아 사용함
- 안내 페이지: TCF/Python_Scripting
- 이전 저장소 안내: git.eclipse.org 경로
- 현재 저장소: Eclipse GitLab TCF
- 프로토콜 문서: TCF Specification, TCF Services
- TCF의
FileSystem,Processes서비스로ls,cat,ps같은 명령 대체 도구를 만들 수 있었고, 결과물은 tcf-tools에 정리됨
SSH 접근과 장치 내부 사양
- 처음에는
/etc/passwd와/etc/shadow를 TCF로 가져와 John the Ripper로 root 비밀번호를 깨려 했음 - 약 7시간 실행해도 매칭을 찾지 못했고, John은 무차별 대입 완료 예상 시점을 2035년으로 표시함
- 이후
/etc/shadow를 수정해 root 비밀번호를 비우고 전원을 다시 넣었지만 SSH 로그인은 여전히 거부됨 - 원인은
sshd_config의PermitRootLogin no설정이었음- 이 줄을
PermitRootLogin yes로 바꾸자 root SSH 로그인이 가능해짐
- 이 줄을
- 장치는
Linux 3.10.28 armv5tejl을 실행 중이었음 - CPU는 ARM926EJ-S rev 5로, ARM9 계열에 해당함
/proc/cpuinfo의 기능 목록에 있는java는 Java 바이트코드 실행을 위한 ARM 확장인 Jazelle을 가리킴- 메모리는
MemTotal: 118172 kB로 표시됐고, 장치에서는 Node.js 앱이 실행되고 있었음
애플리케이션 구조와 데이터 저장
- 서버 애플리케이션은
/srv/server아래에 있었고,Gruntfile.js,app.js,bower.json,package.json,node_modules,routes,views,public같은 구조를 가짐 - 앱은 크게 두 부분으로 나뉨
- 전기 계량기에서 사용량 데이터를 읽는 Pulse app
- CSV 데이터를 읽어 웹 UI로 표시하는 Node.js 앱
- Pulse app 관련 파일은
bin폴더에 있었음pulse-apppulse.koct-read-daemon- 월별·일별·시간별·주별·연도별 디렉터리
pulse.ko의.ko확장자는 보통 Kernel Object를 뜻하며, 커널 모듈일 가능성이 있음- Pulse app은 GPIO 핀에서 데이터를 읽고 결과를 CSV 파일로 저장함
- CSV 파일은 월, 일, 시간 단위 디렉터리로 나뉘어 있었고, 웹 UI의 과거 데이터 표시도 월별·일별·시간별만 지원함
- Node.js 앱은 Node.js 0.10.26, Express.js 4.13.3, Socket.io 1.3.6을 사용함
- 의존성에는
mqtt패키지가 있었고, 소스에는 브로셔에서 약속한 클라우드 통합으로 보이는 미완성 코드와 하드코딩된 브로커 IP가 있었음- 해당 IP들은 더 이상 동작하지 않았음
- 장치 자체에는 인터넷 접근도 없었음
후속 발견
- 장치를 만든 NETTHINGS 회사는 이미 해산된 상태였음
- Hacker News 사용자 M6WIQ는 NetThings의 엔지니어링 결정 결과를 다룬 다른 글을 알려줌
- 그 글의 작성자는 Mastodon에서 장치가 아직 자신의 NTP 서버 IP를 쓰는지 물었고, 실제 상황은 더 나쁜 쪽으로 확인됨
- Marc Bevand가 GPU 자원을 제공해 에너지 매니저의 원래 Linux 사용자 비밀번호를 무차별 대입함
- 해시는 최소 30년 된 알고리듬)을 사용했음
root와gecko_user의 비밀번호는Newt@rd$prod_test계정의 비밀번호는NetTh@ng
- 한 호스팅 회사가 이 글을 러시아어로 번역함