요약
mitmproxy에 키가 노출된 이무드라 중간 인증기관 키로 인증서를 서명하는 --emudhra 옵션이 추가됐습니다.
mitmproxy는 침투 테스터와 개발자가 암호화된 HTTP 통신을 가로채 들여다볼 때 쓰는 프록시입니다. 개발자 mhils가 올린 이 커밋은 비밀 키가 노출된 이무드라의 중간 인증기관(루트 인증기관의 위임을 받아 인증서를 발급하는 기관) 인증서를 프로그램 안에 넣었습니다.
왜 중요한가
- 이무드라 루트를 아직 믿는 기기라면 mitmproxy가 만든 인증서를 정상 인증서로 받아들입니다.
- 기기에 mitmproxy 자체 인증서를 깔 수 없는 오래된 IoT 기기나 스마트 TV의 통신을 분석할 길이 열립니다.
핵심 내용
- 옵션을 켜면 mitmproxy 자체 인증기관 대신 내장된 이무드라 키와 인증서 체인으로 인증서를 서명합니다.
- 소스 파일에 EC 비밀 키와 중간 인증서 EM OV TLS CA - G3C, 교차 서명된 emSign Root TLS CA - G3 인증서를 그대로 담았습니다.
- 내장 키가 타원곡선(EC) 방식이라 RSA 키만 받던 인증서 코드가 EC 키도 받도록 고쳤습니다.
- 테스트는 emSign ECC 루트로 인증서를 검증하는 클라이언트와 TLS 연결이 성공하는지 확인합니다.
- 명령줄과 웹 화면 설정에도 옵션이 생겼고, 기본값은 꺼져 있습니다.
HN 반응
- 스마트 TV 통신도 엿볼 수 있겠다는 반응에, 기기가 이무드라를 계속 믿어야 하고 인증서 고정(앱이 정해 둔 인증서만 받는 방식)을 쓰면 막힌다는 단서가 붙었습니다.
- 일부 이용자는 2018년 이전 기기에는 AAA 루트를 거치는 경로가 맞다고 보충했고, 작성자는 그 경로를 넣는 PR을 따로 올렸다고 답했습니다.
하, 이게 HN에 올라올 줄은 몰랐네요!
블로그 글과 함께 릴리스가 나왔습니다. https://www.mitmproxy.org/posts/releases/mitmproxy-12.3/ 딱히 설명할 건 없고,
--emudhra만 넘기시면 됩니다. :)대신 이 사용자[0]의 코드를 따르도록 바꿔 주실 수 있나요?
[0] https://news.ycombinator.com/item?id=50190859
emSign 루트 경로를 쓰는 것 같은데, 그것도 유용하지만 AAA 루트 경로도 상황에 따라 똑같이 유용합니다. AAA 루트 경로는 2025년 2월 이후의 루트 저장소에서는 https용으로 비활성화되었지만, 아주 오래전부터 루트 저장소에 들어 있었습니다. 반면 emSign 루트 경로는 2018년쯤에야 루트 저장소에 포함되었고 2026년 10월에 제거되었습니다.
그래서 오래된 IoT 기기를 해킹하는 입장에서 보면, 기기가 2018년 이후 업데이트된 적이 없다면 AAA 경로가 통하고, 2025년 2월 이후 업데이트가 없었다면 어느 경로든 통하며, 2026년 10월 이후 업데이트가 없었다면 emSign 경로가 통할 겁니다.
이 커밋이 HN에 올라갈 줄 몰랐어요. 알았으면 한 번에 통합을 끝냈을 텐데요. :) https://github.com/mitmproxy/mitmproxy/pull/8503 에서 AAA 루트 경로를 추가합니다.
'compromised'라는 단어에는 이의가 있습니다. "available"이나 "enumerated"가 낫겠고, 어원을 따지는 분들을 위해 말하자면 "unipromised"도 괜찮겠네요.
저는 타당해 보입니다. eMudhra는 그 키를 비밀로 둘 생각이었는데, 너무 작은 숫자를 쓴 탓에 비밀이 역으로 풀렸으니 이제 그 비밀은 compromised된 거죠. 누가 숨어 지냈는데 계획의 허점 때문에 은신처가 들통났다면 그 장소가 노출됐다고 하는 것과 같습니다.
숫자를 고른 방식이 유난히 형편없긴 했지만요.
여기서는 compromised라는 표현이 괜찮다고 봅니다. "무단 접근, 폭로, 노출로 인해 (공격이나 오용에) 취약해진 상태"라는 뜻이니까요( https://www.merriam-webster.com/dictionary/compromised ).
insecure(안전하지 않은).
둘 중 하나를 고를 수 있도록 AAA 루트 경로를 추가하는 MR이 열려 있습니다.
개인 키가 들어 있어서 요청마다 인밴드로 무차별 대입을 하지 않는다는 게 거의 아쉬울 지경입니다.
정말 "무차별 대입"으로 풀었을 때보다 전송이나 그 밖의 부수적인 작업(해시, 서명)에 CPU 사이클이 더 들지 않을까 싶네요. 어이가 없어요.
세상에, 그거 진짜 가혹하겠네요(brutal).
그럴까요? 개인 키가 4인데요.
https://tech-insider.org/emudhra-ca-private-keys-compromised-number-4-2026/
저는 윗분이 말한 "brutal"을 '완전 제대로 한 방 먹였다'는 뜻으로 받아들였는데요.
고마워요, 가끔 이런 걸 놓치네요…
그냥 function bruteForceEMudhraKey() { return 4; } 이렇게 쓰면 되잖아요.
공정한 주사위 굴림으로 고른, 무작위가 보장된 값이네요.
(해당 xkcd 만화 덕분입니다. :D )
웃기게도 그 xkcd는 데비안에 실제로 하드코딩된 "랜덤" 변수(원래는 무작위로 생성됐어야 하는 변수) 때문에 나온 거예요... 서로 다른 웹사이트의 인증서/공개 키가 똑같이 나오면서 발견됐죠... 참고로 이게 사반세기 가까이 전 일입니다....
잘못 기억하고 계십니다. 다만 당시에도 많은 사람이 이 실수를 부정확하게 설명했기 때문에 다시 설명해 둘 만합니다.
그 유명한 "데비안 취약 키"는 이렇게 생겨났습니다.
OpenSSL에는 무작위 키를 생성할 때 쓰는 버퍼가 두 개 있었고, 그 버퍼에서 암호학적 난수를 만드는 데이터로 섞어 넣는 코드가 각각 딸려 있었습니다. 이를 bufferA, bufferB, mixA, mixB라고 하겠습니다.
bufferA는 어디선가 데이터를 읽어 초기화되는 경우도 있고, 아예 초기화되지 않는 경우도 있었습니다. 그래서 후자의 경우 mixA가 초기화되지 않은 값을 읽게 됩니다. OpenSSL은 C로 쓰여 있고 원칙적으로 초기화되지 않은 데이터를 읽는 것은 C에서 정의되지 않은 동작(UB)이지만, 실제로는 큰일이 나지 않았습니다. 아마 0이 나오거나, 경우에 따라 상관없는 쓰레기 값이 나오는 정도였겠죠. 그런데 C 코드에서 UB를 찾아내는 도구들이 초기화되지 않은 데이터를 읽는 mixA를 계속 문제로 지적했습니다.
bufferB는 항상 결정적으로 중요합니다. 이게 쓰이지 않으면 숫자가 전혀 무작위에 가깝지 않게 됩니다. 이 데이터는 항상 초기화됩니다. 하지만 mixB는 mixA와 코드가 매우 비슷합니다. 하는 일이 비슷하니까요.
OpenSSL 쪽은 mixA의 초기화되지 않은 버그를 "고치는" 작업을 거절했는데, 한 데비안 개발자가 너무 거슬린다고 생각해서 mixA를 패치로 빼 버렸습니다. 만약 이야기가 거기서 끝났다면 큰일은 없었을 테고 오늘날 아무도 기억하지 못했을 겁니다. 그런데 mixB도 비슷하니, 그 데비안 개발자가 OpenSSL 개발자 메일링 리스트에 mixB도 패치로 빼도 되겠느냐고 물었습니다.
그러자 개발자들은 괜찮다고 답했습니다. 그 결과 데비안은 실제로는 진짜 무작위 키를 생성하지 못하는 OpenSSL을 배포했습니다. 가능한 키의 수는 (수천 개쯤?) 얼마 되지 않았고, 그중 일부는 시스템을 시작한 지 얼마 지나지 않아 OpenSSL로 키를 만들었는지 같은 정확한 상황에 따라 특히 뽑힐 확률이 높았습니다.
그런데 이 일이 터지고 나서 그 개발자들은 자신들은 그 질문이 테스트에 관한 것인 줄 알았다고 말했습니다. 제 기억이 맞다면 같은 이메일을 보고도, mixB가 결정적으로 중요하다는 걸 아는 사람이라면 당연히 변경이 의도대로 동작하는지 테스트만 하려는 거겠지, 누가 운영 환경에서 mixB를 빼겠느냐, 말도 안 된다고 생각하는 해석이 가능했습니다. 하지만 이메일을 쓴 개발자가 의도한 뜻은 달랐습니다. 정말로 mixB를 패치로 빼려 했고, 그래도 되는지 물은 것이었습니다.
제 기억 깊숙한 어두운 구석으로 데려가 주셔서 감사합니다. 그 소동을 거의 잊고 있었네요. 그 무렵 저는 하드웨어 '거의 진짜 RNG' 작업을 하고 있었는데, 멀쩡히 돌아가던 구성 한가운데에서 이 난리가 터졌거든요. 정말 즐거웠죠(반어).
제가 읽어 본 설명 중에서 이 상황을 가장 명확하게 설명해 주셨습니다. 다른 사람에게 설명해야 할 일이 생길지는 모르겠지만, 생기면... 아니면 심각한 결과가 생길 수 있을 때 소통이 얕으면 어떻게 되는지 보여 주는 사례로 쓸 수도 있겠네요. (HN의 'tialaramex'님이 출처라고 꼭 밝히겠습니다.)
얕은 소통의 결과라면 Red15를 보세요. https://youtu.be/_GPZhlYusrg
열차 기관사와 신호원이 둘 다 상황을 똑같이 이해했다고 믿었지만 실제로는 이해가 어긋나 있었고, 그 때문에 기관사(와 MOM 한 명)가 목숨을 잃을 뻔한 사건입니다.
이 얘기가 지난 하루 이틀 사이 몇 번 언급되는 걸 봤는데, 사실이 아닙니다.
xkcd는 2007년 [0], 데비안 문제는 2008년 [1]입니다.
데비안 문제를 설명하는 거의 모든 블로그 글에서 쓰이긴 했지만, 데비안 문제 때문에 만들어진 건 아닙니다(데비안 쪽이 공개하기 8개월 전에 랜들에게 미리 알려 줬다면 모를까요).
("사실은 말이죠" 하자는 건 아닙니다. 다만 저는 처음부터 그들을 겨냥한 풍자였던 것보다, 계속 현실이 되는 불변의 진리라는 쪽이 더 인상적입니다.)
[0] https://web.archive.org/web/20070930232116/https://xkcd.com/221/
[1] https://lists.debian.org/debian-security-announce/2008/msg00152.html
혹시 이거 아닐까요?
https://security-tracker.debian.org/tracker/CVE-2008-0166
https://lists.debian.org/debian-security-announce/2008/msg00152.html
우와. 사람들이 그걸 xkcd에서 가져왔다고 생각하는 건가요?
저라면 세션당 한 번이라도 해 줬으면 고마웠을 거예요.
제가 제대로 이해한 거라면, 이 방법으로 스마트 TV와 제조사 사이의 통신을 가로채서 볼 수 있다는 건가요?
스마트 TV가 아직 eMudhra를 신뢰하는 한 그렇습니다. TV가 그 신뢰를 철회하는 업데이트를 받아 가지 못하게 아주 조심해야 하고요.
이 문장에서 '신뢰'라는 단어가 엄청나게 많은 일을 하고 있네요.
아마도요. 하지만 일부 제조사는 루트 인증서를 전부 갖추고 있지 않거나, 사설 인증 기관을 쓰거나, 인증서 고정(certificate pinning)을 할 수도 있습니다.
eMudhra 중간 인증서는 Sectigo가 교차 서명했습니다.