SecurityHeroblog

파일은 하나도 안 남았는데, 열쇠만 사흘째 늘었다

63초짜리 세션 하나가 검체는 한 건도 남기지 않고 SSH 공개키만 심고 나갔습니다. 같은 해시의 키가 8/16·8/17·8/18 사흘 연속 나왔고, 심은 출처는 10곳 → 12곳 → 16곳으로 늘었습니다. 백신이 잡을 파일이 없는 침해는 어떻게 알아채야 하는지 정리했습니다.

아파트 관리사무소에는 방문차량 상시등록 명부라는 게 있습니다. 한 번 올려두면 차단기가 알아서 올라갑니다. 그런데 이 명부는 종이 한 장입니다. 현관 비밀번호를 바꿔도, CCTV를 새로 달아도, 명부에서 이름이 지워지지는 않습니다. 그리고 아무도 명부를 들여다보지 않습니다 — 없어진 물건이 없으니까요.

어제 우리 허니팟에서 벌어진 일이 딱 이 모양이었습니다.

63초, 명령 17개, 저장된 검체 0건

사실 ATK-C17F4E(169.58.x.x)가 텔넷으로 로그인에 성공했습니다. 머문 시간은 약 63초, 실행한 명령은 17개. 그 안에서 분류된 의도는 5종입니다 — 정찰(시스템), 파일 다운로드, 실행 권한 부여, 다운로드 후 실행, 그리고 지속성(SSH 키 심기).

사실 그런데 이 세션의 download_count0입니다. 저장된 검체가 한 건도 없습니다.

사실 어제 완결성 상위 5개 세션 중 정찰에서 지속성까지 단계가 이어진 것은 이 세션 하나뿐이었습니다.

여기서 잠깐 용어를 풀면, 지속성은 “다음에 또 들어올 방법을 미리 만들어 두는 것”입니다. 그리고 SSH에서 그 방법은 대개 authorized_keys라는 텍스트 파일입니다. 이 파일에 공개키 한 줄이 들어가 있으면, 그 키의 짝을 가진 사람은 비밀번호 없이 로그인할 수 있습니다. 방문차량 명부에 차량번호 한 줄이 올라간 것과 같습니다.

389바이트, 사흘 연속 같은 해시

사실 어제 관측 구간 전체에서 389바이트짜리 OpenSSH RSA 공개키(sha256 a8460f44…)가 서로 다른 출처 16곳에서 16회 관측됐습니다. 이 파일에는 URL이 기록돼 있지 않습니다 — 외부에서 내려받은 게 아니라 세션 안에서 만들어졌다는 뜻입니다.

사실 아카이브를 대조해 봤습니다. 같은 해시의 공개키가 8/16에는 출처 10곳, 8/17에는 12곳으로 기록돼 있습니다. 어제는 16곳입니다. 사흘 연속 동일한 키이고, 심은 곳의 수는 늘고 있습니다.

추정 바이러스 백신이든 EDR이든, 결국 “나쁜 파일”을 찾는 도구입니다. 그런데 여기서 남은 것은 389바이트짜리 평범한 텍스트 한 줄입니다. 형식상 아무 문제가 없는 파일이고, 실제로 누군가는 매일 정상적으로 같은 파일을 씁니다. 잡을 근거가 없습니다.

사실 그리고 어제 ’지속성 — SSH 키 심기’로 분류된 명령은 18회, ’흔적 삭제’는 19회였습니다. 흔적을 지우는 행위가 키를 심는 행위보다 많았습니다.

  1. 사실 ATK-57608F 텔넷 로그인 성공. 약 16분 30초 동안 명령 586개 — 어제 전체 명령 2,966개의 약 20%를 한 세션이 차지(평균 1.7초당 1개). 다운로드 1건. TTY 녹화됨(내용 미포함)
  2. 사실 ATK-A4A3FE 텔넷 로그인 성공. 약 68초 · 명령 31개 · 다운로드 1건. 의도는 파일 다운로드 → 실행 권한 부여 → 다운로드 후 실행
  3. 사실 ATK-C17F4E 텔넷 로그인 성공. 약 63초 · 명령 17개. 의도 5종(정찰-시스템 · 파일 다운로드 · 실행 권한 부여 · 다운로드 후 실행 · 지속성 SSH 키 심기). download_count 0. 어제 유일하게 정찰부터 지속성까지 이어진 세션
  4. 사실 ATK-2A2021 텔넷 로그인 성공. 약 88초 · 명령 31개 · 다운로드 1건. ATK-A4A3FE 와 명령 수 · 의도 조합이 동일하다
  5. 사실 ATK-ABE8B7 텔넷 로그인 성공. 약 75초 · 명령 22개 · 다운로드 11건. 의도는 '방어 무력화' 단독. 11건 전부 1바이트 · 매직 넘버 없는 빈 파일(01ba4719…)이며 어제 이 파일의 전달 출처는 이 한 곳뿐
  6. 사실 389바이트 OpenSSH RSA 공개키(a8460f44…)가 서로 다른 출처 16곳에서 16회 관측. URL 미기록 — 외부 다운로드가 아니라 세션 안에서 생성된 파일
  7. 사실 같은 해시의 공개키가 8/16 출처 10곳, 8/17 출처 12곳, 어제 16곳. 사흘 연속 동일 해시이며 출처 수는 증가 추세
  8. 사실 의도 분류 — 지속성(SSH 키 심기) 18회, 흔적 삭제 19회, 방어 무력화 1회, 정찰(네트워크) 2회
  9. 사실 세션 2,027건 / 고유 출발지 249곳 / 로그인 시도 1,581회 / 명령 2,966개 / TTY 녹화 1,424건. 포트 2222(SSH) 1,657건, 포트 2223(텔넷) 370건 — 텔넷은 약 18%인데 완결성 상위 5개 세션은 전부 텔넷(사흘 연속)

지문이 잡히는 쪽과 실제로 뚫리는 쪽이 다릅니다

이건 어제 데이터에서 가장 마음에 걸린 부분입니다.

사실 어제 SSH 클라이언트 지문 클러스터는 36개였고, 가장 큰 것은 f555226d…(클라이언트 문자열 SSH-2.0-libssh_0.9.6, 어제 IP 16곳 · 누적 124곳)입니다. 그런데 어제 관측된 7개 지문 클러스터는 전부 delivered_samples=false 입니다. 지문이 남는 SSH 쪽에서는 검체를 실제로 내려보낸 곳이 하나도 없었습니다.

사실 실제 침투·다운로드·키 심기는 전부 텔넷에서 일어났습니다. 그리고 텔넷 세션에는 애초에 SSH 클라이언트 지문이 존재하지 않습니다 — 어제 주제 세션의 지문·클라이언트 값도 비어 있습니다.

추정 묶어서 보기 좋은 트래픽과 실제로 피해가 나는 트래픽이 서로 다른 층에 있다는 뜻입니다. 분류가 잘 되는 쪽만 들여다보면, 정작 뚫리는 쪽은 화면에 뜨지 않습니다.

사실 덧붙이면, 지문이 같다는 것은 같은 도구·같은 빌드를 썼다는 뜻이지 같은 조직이라는 뜻이 아닙니다. 같은 배포판의 기본 클라이언트를 쓰는 무관한 사람도 같은 값을 갖습니다.

어떻게 들어왔나 — 여전히 비밀번호입니다

사실 어제 로그인 시도는 1,581회, 출발지는 249곳이었습니다. 출발지 한 곳당 평균 6.4회입니다. 서로 다른 IP 3곳 이상에서 관측된 조합만 추리면 이렇습니다.

아이디 비밀번호 관측된 IP 수
admin admin 26곳
345gs5662d34 345gs5662d34 16곳
root admin 9곳
root 3245gs5662d34 8곳
root root 7곳
root password 6곳
root (빈 비밀번호) 5곳
root 1234 5곳
root 12345 5곳
support support 4곳

사실 그리고 1,388개의 조합은 싣지 않았습니다. 서로 다른 IP 3곳 미만에서만 관측된 것들입니다(8/16 934개, 8/17 830개에서 늘었습니다). 한 곳에서만 나온 조합은 어딘가에서 실제로 유출된 계정일 수 있어서, 값은 쓰지 않고 개수만 셉니다.

사실 어제 관측분에서 CVE나 익스플로잇을 참조한 경로는 없었습니다. 전부 “맞는 비밀번호를 찾아내는” 경로였습니다.

남긴 것들

sha256
a8460f446be540410004b1a8db4083773fa46f7fe76fa84219c93daa1669f8f2389바이트 OpenSSH RSA 공개키 · 16회 관측 · URL 미기록(세션 안에서 생성) · 8/16 · 8/17 과 동일 해시 · 전달 출처 16곳
sha256
a6296a79f44e21b76604d2d2bbf795d2cf380f70e39d45fbf166707ff3b4a6a4306바이트 POSIX 셸 스크립트 · 13회 관측 · 전달 출처 13곳 · 8/17 에도 동일 해시 · 동일 URL
url
hxxp://185[.]93[.]89[.]72/wget위 셸 스크립트를 내려받은 주소
sha256
6aa5054a95d23277df417a5f69cf292e19bc2ef0406bc0c1884935a44e3ce7971,608바이트 bash 스크립트 · 전달 출처 ATK-10149C
url
hxxp://5[.]182[.]210[.]174/ok위 스크립트를 내려받은 주소
sha256
4af5a5c98ad132095c6fbe7b02c242153a190a01cc321e50a916a0ca46fbaa629바이트 ASCII 텍스트 · 12회 관측 · URL 미기록 · 전달 출처 ATK-BECAD3
sha256
01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b1바이트 · 매직 넘버 없는 빈 파일 · 11회 관측 · 전달 출처 ATK-ABE8B7 · 8/17 에는 121회 · 출처 2곳

주소는 일부러 망가뜨려 적었습니다(hxxp://, [.]). 그대로 붙여 넣어도 접속되지 않습니다.

사실 마지막 줄에 눈이 갑니다. 1바이트 빈 파일은 8/17의 주제였습니다. 그때 121회·출처 2곳이던 것이 어제는 11회·출처 1곳으로 줄었습니다. 왜 줄었는지는 데이터로 알 수 없어서, 사실로만 적어 둡니다.

공격 단계로 옮기면

관측된 행동을 MITRE ATT&CK 기법으로 옮기면 이렇습니다. 전부 [추정]입니다 — 우리가 본 것은 명령이지 공격자의 의도가 아닙니다.

  • T1110.001Brute Force: Password Guessing추정
  • T1078Valid Accounts추정
  • T1059.004Command and Scripting Interpreter: Unix Shell추정
  • T1082System Information Discovery추정
  • T1016System Network Configuration Discovery추정
  • T1105Ingress Tool Transfer추정
  • T1222.002File and Directory Permissions Modification: Linux and Mac추정
  • T1098.004Account Manipulation: SSH Authorized Keys추정
  • T1070Indicator Removal추정
  • T1562Impair Defenses추정

어디서 본 것 같다면

추정 2016년부터 이어진 Mirai 봇넷 계열과 진입 구조가 같습니다. 텔넷 기본 자격증명으로 들어간 뒤 웹 서버의 /bins/ 경로에서 CPU 아키텍처별 바이너리를 순서대로 내려받아 맞는 것 하나만 실행시키는 방식입니다. 어제 관측된 파일명 규칙(parm, parm5, parm64, pmips, psh4, x86_64 …)이 이 관습과 일치하고, 완결 세션이 전부 텔넷이라는 점도 부합합니다.

미국 법무부 기소·유죄인정 기록이 남아 있고 여러 벤더의 공개 분석 보고서가 있는 사건입니다. 다만 어제의 주제인 SSH 공개키 지속성은 이 사례가 설명해 주지 못하는 부분입니다. 비슷하다는 말로 넘어가면 안 되는 자리라서 따로 적어 둡니다.

그래서 뭘 해야 하나

추정 어제 데이터가 가리키는 순서는 이렇습니다.

  1. ~/.ssh/authorized_keys를 직접 열어 본다. 모르는 키가 한 줄이라도 있으면 그게 전부입니다. 키 심기 의도가 18회, 심은 출처가 16곳이었습니다.
  2. 그 파일의 변경을 알림으로 받는다. 비밀번호를 전부 바꾸고 무차별 대입을 차단해도 이미 들어간 키는 그대로 남습니다.
  3. 로그를 장비 밖에 저장한다. 흔적 삭제 19회, 방어 무력화 1회가 실제로 관측됐습니다. 어제 우리가 상황을 재구성할 수 있었던 것은 TTY 녹화 1,424건이 남아 있었기 때문입니다(내용은 이 글에 포함하지 않습니다).
  4. 관리 포트를 인터넷에 직접 열어두지 않는다. 텔넷은 전체 세션의 약 18%인데 완결 세션은 전부 텔넷입니다. 세션 수만 보고 우선순위를 정하면 실제로 뚫리는 18%를 놓칩니다.
  5. 아웃바운드를 막는다. 다만 이것만으로는 부족합니다 — ATK-C17F4E는 다운로드가 0건이었는데도 키 심기까지 도달했습니다.

어제 하루 전체

세션 2,027건
고유 IP 249곳
로그인 시도 1,581회
실행된 명령 2,966개
SSH (2222) 1,657건
텔넷 (2223) 370건

명령 2,966개는 전부 로그인에 성공한 뒤에 실행된 것입니다.

어제 관측된 유포지·C2는 C-TAS로 신고했습니다. 전송 계층 성공만 확인되며, KISA 측 적재 여부는 별도 확인이 필요합니다.

60초로 보기

같은 관측분을 짧은 영상으로도 만들었습니다.

우리가 모르는 것

이 블로그는 아는 것과 모르는 것을 나눠서 씁니다. 어제 관측분에서 확인할 수 없었던 것들입니다.

  • ATK-C17F4E가 다운로드에 실패한 것인지, 받았는데 저장이 안 된 것인지. 의도에는 ‘파일 다운로드’·’다운로드 후 실행’이 있는데 download_count는 0입니다. 데이터가 둘을 구분해 주지 않습니다. 확실한 것은 “의도는 분류됐고 저장된 검체는 없다”뿐입니다.
  • 공개키를 심은 출처 16곳과 최대 지문 클러스터의 IP 16곳이 같은 집합인지. 숫자가 같을 뿐이고, 두 집합을 연결할 근거가 없습니다. 텔넷 세션에는 지문 자체가 없습니다.
  • 8/16·8/17·8/18의 출처들이 서로 겹치는지. 별칭(ATK-XXXXXX)은 소금을 섞어 만들기 때문에 날짜를 넘겨 추적할 수 없습니다. 그래서 10+12+16을 더해 “38곳”이라고 말할 수 없습니다.
  • 지문이 같다는 것이 같은 조직이라는 뜻인지. 아닙니다. 같은 도구·같은 빌드를 썼다는 뜻이고, 같은 배포판 기본 클라이언트를 쓰는 무관한 사람도 같은 값을 갖습니다.
  • ATK-ABE8B7이 왜 어제 완결성 최고값(42)인지. 의도는 ‘방어 무력화’ 하나뿐인데 점수는 가장 높습니다. 산식이 데이터에 없어서 설명하지 못합니다.
  • 1바이트 빈 파일이 왜 줄었는지. 121회·2곳 → 11회·1곳. 감소 원인은 데이터로 알 수 없습니다.
  • 9바이트 ASCII 텍스트(4af5a5c9…)가 무엇인지. 12회 관측됐지만 내용이 브리프에 없어 판단할 수 없습니다.
  • Discord 웹훅 URL 1건. 어느 세션·어느 검체와 연결되는지 명시돼 있지 않아 어제 사실 관계에 넣지 않았습니다.
  • 집계 수치가 하루 전체를 덮는지. 주요 세션 5건이 UTC 00:45~04:49에 몰려 있는데 생성 시각은 15:05Z입니다. 관측 구간이 명시돼 있지 않습니다.
  • 16곳이 같은 키를 심은 이유. 같은 도구를 쓴 결과인지 키를 공유한 결과인지 알 수 없습니다. 8/16·8/17 브리프에도 같은 항목이 미해결로 남아 있습니다.
  • SSH authorized_keys 지속성 계열의 공개 사건. 출처를 확인하지 못해 사례로 넣지 않았습니다.
  • 배경 취약점 통계(CVE 4,252건 / CISA KEV 341건 / 랜섬웨어 연계 95건). KISA 보안공지 기반 통계이며 어제 관측분과 직접 연결되지 않습니다.

그리고 애초에 확인할 수단이 없는 것들입니다. 이건 모르는 것과 다릅니다 — 추정하지 않는 쪽이 정답입니다.

  • ASN·ISP — GeoIP·ASN 데이터베이스를 갖고 있지 않습니다. 추정하지 않습니다.
  • 지리 위치 — 동일합니다. IP 지리정보는 애초에 귀속 근거가 되지도 않습니다.
  • VPN·Tor·클라우드 여부 — 판별 수단이 없습니다.
  • VirusTotal 탐지 결과 — 미연동입니다. 검체 분류는 MalwareBazaar 태그를 씁니다.
  • User-Agent — SSH·텔넷에는 User-Agent 자체가 존재하지 않습니다. HTTP 공격은 별도 소스입니다.
  • CVE 연결 — SSH·텔넷 공격은 취약점을 참조하지 않는 경우가 대부분이고, 어제 관측분도 자격증명을 노린 것이었습니다.

명부에 이름이 하나 올라갔습니다. 사흘째, 올라간 곳은 열 곳에서 열여섯 곳이 됐습니다. 없어진 물건이 없어서 아무도 명부를 펴 보지 않는다면, 그건 계속 늘어날 겁니다.


이 글은 2026-08-18 허니팟 관측분을 바탕으로 작성됐습니다. 출발지 IP는 브리프의 마스킹 형태 그대로 표기했고, 유포 주소는 접속되지 않도록 변형(defang)했습니다. 인용한 자격증명은 서로 다른 IP 3곳 이상에서 관측된 조합만 포함했으며, 그 미만에서만 관측된 1,388개 조합은 개수만 밝히고 값은 싣지 않았습니다.