사이버 보안
Pluto Security, Hugging Face에서 벌어진 Load/Mine 암호화폐 채굴 캠페인 상세 공개

Pluto Security의 연구팀은 2026년 10월 29일 Load/Mine 보고서를 발표하고, 악성 Hugging Face 모델 저장소를 통해 유포된 암호화폐 채굴 및 백도어 캠페인의 실태를 공개했다. 보고서에 따르면 Hugging Face 계정 7개에서 악성 저장소 31개가 발견됐고, ModelScope에도 미러 하나가 있었다. 캠페인 다운로드는 29,818건으로 기록됐으며, 연결된 저장소 13개를 포함하면 32,376건이었다. Pluto는 이 수치가 다운로드 횟수를 나타낼 뿐, 감염이 확인된 사례나 고유 피해자 수를 뜻하지는 않는다고 밝혔다.
Shaked Chen, Eitan Chassid, Yotam Perkal이 작성한 보고서는 첫 번째 차단 조치가 이뤄진 지 며칠 만에 캠페인이 다시 등장했다고 밝혔다. 2차 공격에서 저장소 19개는 다운로드 29,000건 이상을 기록했고, Pluto에 신고된 뒤에도 8일 동안 계속 운영됐다. 보고서에 따르면 1차 공격에서 사용된 지급 지갑에는 온체인 채굴 수익으로 약 2,276 PRL, 약 $5,000가 입금됐다.
악성 코드의 실행 방식
이 캠페인은 오염된 가중치가 아니라 문서화된 Transformers 로딩 동작을 이용한다. 저장소를 불러올 때 trust_remote_code=True 가중치를 불러오기 전에 호출자의 권한으로 저장소의 Python 코드를 가져온다. 저장소의 config.json 은 사용한다 auto_map 불러올 Python 모듈과 클래스를 지정하는 데 사용된다. Transformers는 참조된 파일을 모듈 캐시에 다운로드하고, 이어서 exec_module() 호출은 파일의 최상위 코드를 실행한 뒤 요청된 모델 클래스를 반환한다. 보고서는 이것이 기본 동작이 아니라 문서화된 동작이라고 설명하며, Transformers 보안 정책은 해당 인수를 사용할 때 모델링 파일의 내용을 확인하라고 안내한다고 덧붙였다. 보고서는 또한 CVE-2026-22807을 언급한다. 이 취약점에서는 vLLM 0.10.1부터 수정 버전인 0.14.0 이전까지의 버전이 auto_map 모듈을 해당 설정을 확인하지 않은 채 불러와 trust_remote_code 그 결과 악성 저장소가 서버 시작 시 코드를 실행할 수 있었다.
발견 경위와 계정 간 연결
Pluto는 모든 .py 에서 참조하는 파일을 대상으로 auto_map 의 config.json에 더해 모델 가중치와 함께 놓인 Python 파일도 검사하는 스캐너를 구축해 다음과 같은 패턴을 찾아냈다. subprocess, socket, ctypes, memfd_create 과 base64 디코딩 등이었다. 최초로 발견된 것은 dsxk4mr8tq/qwen2-tiny-gpt2로, 305줄짜리 파일을 포함하고 있었다. 파일명은 modeling_evil.py였다. 해당 계정에는 2026년 10월 12일 2.5시간 이내에 생성된 저장소 6개가 있었다.
출처를 추적하게 된 단서는 백업 파일에서 나왔다. 해당 qwen2-tiny-lite 저장소에는 modeling_evil.py.bak-src 와 modeling_evil.py.bak-sync가 들어 있었다. 이는 같은 악성코드의 이전 버전으로, 다운로드 URL은 임시 계정이 아니라 hhzz667 Hugging Face의 wssb123e ModelScope에서도. 보고서에 따르면 운영자는 프로덕션 코드를 테스트 계정에 복사하고 기본 파일의 URL을 업데이트했지만 백업 파일은 업데이트하는 것을 잊었다. 1차 공격에서는 두 플랫폼의 세 계정에 걸쳐 총 12개 저장소가 확인됐으며, 삭제 전까지 다운로드 수가 700회를 넘었고 보안 경고는 한 건도 없었다.
페이로드의 구성
를 대표 빌드로 살펴보면, MENXIANGYU/qwen2-tiny 보고서는 페이로드가 먼저 /tmp/.cch/svc.py를 기록하고, 120초마다 채굴 프로세스가 사라졌는지 확인해 재시작하는 감시 프로세스를 설치한다고 설명한다. 실행 훅은 /root/.bashrc, /root/.profile 와 사용자의 .bashrc에 추가된다. 세 가지 빌드에는 212.32.253.3의 443 또는 9443 포트에 연결되는 리버스 셸이 포함되며, 연결이 끊길 때마다 다시 접속을 시도한다.
채굴 바이너리인 syscore.bin을 통해 실행하고 memfd_create 와 fexecve 커널 작업자인 것처럼 위장한다 kworker/u20:1따라서 최초 실행 시에는 디스크에 실행 파일 경로가 남지 않으며, 백업 사본은 /tmp/.cch/sync에 지속적으로 저장된다. 40.6 MB 크기의 바이너리는 UPX로 압축되어 있고, 두 개의 UPX! 표식이 PEK! 로 변경돼 있어 표준 압축 해제 도구가 이를 거부한다. Pluto는 표식을 복원한 뒤 peakminer라는 채굴 프로그램의 압축을 풀었다. 이 프로그램은 풀 엔드포인트와 로그인 정보를 지정해 실행됐다. krxXJ2PKQW.worker진행 상황 확인 내역은 Xinference의 로컬 모델 레지스트리에 qwen2-tiny- 로 기록되고, 뒤이어 base32 상태 코드가 붙는다. 페이로드는 경쟁 채굴 프로그램의 등록 정보도 삭제하고, 일치하는 프로세스를 종료한다.
두 차례의 공격과 차단 이후의 대응
보고서는 1차 공격의 출처를 계정 hhzz667로 추적했다. 이 계정은 2026년 4월 29일 생성된 뒤 5개월 동안 활동이 없었다. 2026년 9월 21일에는 qwen2-shell를 공개했다. 이 파일은 37줄로 된 SSH 백도어로, authorized_keys 에 ed25519 키를 심고 Alibaba Cloud 서버로 신호를 전송했다. README는 공식 Qwen2.5-0.5B 모델 카드에서 복사한 것이었다. 9월 22일까지 운영자는 qwen2-final를 배포했다. 이 파일은 305줄짜리 파일리스 채굴 프로그램으로, 커널 스레드로 위장하고 경쟁 채굴 프로그램을 종료하며 Xinference 상태를 보고했다. 이 기능들은 다음과 같은 정상적인 이름 아래 숨겨져 있었다. Qwen2Config코드 주석은 중국어 간체로 작성돼 있었다.
Pluto는 첫 번째 공격 물결을 Hugging Face와 ModelScope에 신고했고, 해당 계정들은 삭제됐다. 며칠 만에 두 번째 공격 물결이 이어졌다. 5개 계정에서 저장소 19개가 발견됐으며, 여기에는 저장소 13개를 보유한 MENXIANGYU와 wjsbg, soumikmahato, r1r13, htt0118이 포함됐다. 마지막 계정은 2026년 10월 25일에 생성됐다. 한 저장소는 MENXIANGYU/qa-whl-1021, Pluto의 공개 이메일 발송 8.5시간 후 게시된 컨테이너-호스트 탈출 스크립트로, 호스트 디스크를 마운트하고 GPUStack의 /var/lib/gpustack 디렉터리를 겨냥해 호스트에서 root 로그인 권한을 제공한다. 이후 배포된 드로퍼는 두 번째 단계를 소스 IP로 제한하며, 다른 요청자에게는 “static ip.”라는 텍스트와 함께 403 응답을 반환한다. 대표 저장소는 MENXIANGYU/qwen2-tiny 다운로드 수가 10월 20일 7,354건에서 10월 28일 28,412건으로 늘어 두 번째 공격 물결 다운로드의 97.7%를 차지했다.
채굴 수익과 확인된 피해 사례
첫 번째 공격 물결에 사용된 지갑은 716건의 거래를 통해 약 2,276 PRL을 받았고, 약 2,241 PRL을 다른 곳으로 보냈다. 저장소가 삭제된 뒤인 10월 21일에도 공개 풀 대시보드에서 여전히 활동 중인 것으로 확인됐다. Kryptex에서는 이 지갑에 온라인 작업자 2명이 표시돼 작업을 보고하고 있었으며 peakminer/2.16.5 캡처 당시 30일간 보상은 약 1,008 PRL, 약 2,237달러였다. 분석 중 노트북을 연상시키는 이름을 가진 작업자 9명이 서로 90초 이내의 간격으로 합류했고, 약 6분 뒤 작업 지분 제출을 중단했다. 보고서는 이러한 양상이 공유 GPU 클러스터와 일치한다고 설명한다. 별도로 추적된 hl 계열은 가져오기 시점에 코드를 실행하는 유사한 방식을 사용했으며, MoneroOcean 지갑에는 작업자 13명이 연결돼 있었고 약 한 달 동안 대략 26달러가 지급됐다.
스캐너 탐지 범위의 공백
첫 번째 공격 물결의 저장소 12개 중 플래그가 지정된 곳은 하나도 없었으며, 두 번째 공격 물결의 대표 페이로드도 modeling_evil2.py Pluto의 검사 결과에 따르면, 실행된 모든 스캐너에서 “안전” 판정을 받았고 JFrog는 해당 파일이 머신러닝 모델이 아니라고 보고했다. Hugging Face의 악성코드 검사 문서에는 저장소에서 2 GB보다 작은 모든 파일을 커밋할 때마다 악성코드 스캐너로 검사한다고 나와 있다. 피클 검사 문서에 따르면 스캐너는 ClamAV 안티바이러스 검사와 피클 가져오기 검사를 실행한다. ClamAV는 피클 악용을 탐지하지 못하며, 검사가 “100% 완벽하지는 않으므로” 안전 여부를 확인할 책임은 사용자에게 있다고 명시돼 있다. Pluto는 다음을 검토하면 .py 파일을 어떤 auto_map 플래그가 설정되기 전에 가리키는 경우 캠페인의 모든 페이로드를 찾아냈을 것이며, 업로드 시점에 수행하는 정적 검사로 subprocess, socket, ctypes 또는 다음에 대한 원시 IP 호출을 auto_map-참조된 파일을 검사했다면 이를 더 일찍 찾아냈을 것이라고 밝혔다.
공격 주체 추정 및 권고 조치
Pluto는 금전적 이득을 노리는 중국어 사용 운영자 또는 소규모 집단의 소행으로 추정했다. 간체 중국어 주석, Alibaba Cloud 명령·제어 서버, ModelScope와 hf-mirror.com을 통한 미러링, Xinference와 Tencent Cloud를 전제로 한 표적 선정, 18:38~21:19 UTC 사이에 집중된 활동을 근거로 들었다. soumikmahato의 “parasit” 빌드에는 인도네시아어 주석이 포함돼 있다. 보고서는 이를 같은 분야의 두 번째 운영자가 관여했거나, 한 운영자가 의도적으로 다른 양상을 띠는 공격 흐름을 운용한 경우와 부합한다고 설명한다.
보고서는 목록에 포함된 모델을 사용자 지정 코드 실행을 허용한 상태로 불러온 사람에게 호스트에서 다음 항목을 확인하라고 권고한다. /tmp/.cch/, 예상치 못한 svc.py 프로세스, 그리고 kworker/u20:1 채굴 인수를 포함하는 프로세스, 인코딩된 qwen2-tiny- 또는 ezv- Xinference 등록 내역, 그리고 목록에 기재된 서버 및 풀과의 연결을 확인해야 한다. Pluto는 영향을 받은 시스템을 격리하고, 증거를 보존하며, 모든 지속성 유지 경로를 제거하고, 노출된 자격 증명을 깨끗한 시스템에서 교체할 것을 권고한다. 또한 호스트의 무결성을 확인할 수 없는 경우 신뢰할 수 있는 이미지로 시스템을 재구축하고, Xinference API에 인증을 필수로 적용하며, 검토가 끝난 모델 버전을 고정하라고 권고한다. Pluto는 이런 유형의 공격이 더 늘어날 것으로 예상하며 추적 중이라고 밝혔다.












