# 해킹당해서 제 Hetzner 서버가 Monero 채굴을 시작했어요

> Clean Markdown view of GeekNews topic #25165. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=25165](https://news.hada.io/topic?id=25165)
- GeekNews Markdown: [https://news.hada.io/topic/25165.md](https://news.hada.io/topic/25165.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2025-12-19T00:36:00+09:00
- Updated: 2025-12-19T00:36:00+09:00
- Original source: [blog.jakesaunders.dev](https://blog.jakesaunders.dev/my-server-started-mining-monero-this-morning/)
- Points: 1
- Comments: 1

## Topic Body

- 개인이 운영하던 **Hetzner 서버에서 비정상적인 CPU 사용률**이 발견되어 조사한 결과, **암호화폐 Monero 채굴 프로그램(xmrig)** 이 실행 중이었음  
- 원인은 **Next.js의 원격 코드 실행 취약점(CVE-2025-66478)** 을 포함한 **Umami 분석 도구 컨테이너**가 공격받은 것이었음  
- 다행히 해당 컨테이너는 **비루트(non-root) 사용자**로 실행되고 **호스트 마운트나 권한 상승이 없었기 때문에**, 침입은 컨테이너 내부에 국한됨  
- 공격자는 **Next.js 서버 컴포넌트의 비안전한 역직렬화**를 악용해 악성 페이로드를 실행하고 채굴기를 설치함  
- 이번 사례는 **의존성 관리와 컨테이너 보안 설정의 중요성**을 보여주는 사례로, 작성자는 방화벽 활성화·SSH 강화·정기 업데이트 등 보안 조치를 강화함  

---

### 해킹 발생과 초기 대응
- Hetzner로부터 **서버가 외부 네트워크를 스캔했다는 경고 메일**을 수신  
  - 4시간 내 조치하지 않으면 서버가 차단될 수 있다는 통보 포함  
- SSH 접속 후 확인 결과, `/tmp/.XIN-unix/javae` 경로에서 **819% CPU를 사용하는 프로세스**와 여러 `xmrig` 프로세스가 발견됨  
- 약 **10일간 암호화폐 채굴이 진행**된 것으로 확인  

### 침입 경로 조사
- 모든 악성 프로세스가 **UID 1001 사용자**로 실행 중이었으며, 이는 **Umami 컨테이너**와 일치  
- `/app/node_modules/next/dist/server/lib/xmrig-6.24.0/` 디렉터리에서 **채굴기 실행 파일**이 발견됨  
- 실행 명령에는 `auto.c3pool.org:443` 마이닝 풀 주소와 사용자 키가 포함되어 있었음  

### Next.js 취약점과 공격 방식
- **Next.js의 React Server Components 역직렬화 취약점(CVE-2025-66478)** 이 원인  
  - 공격자가 조작된 HTTP 요청을 전송하면 서버에서 임의 코드가 실행됨  
  - 결과적으로 **암호화폐 채굴기 설치 및 실행**이 가능  
- 작성자는 “Next.js를 직접 사용하지 않는다”고 생각했으나, **Umami가 Next.js 기반**임을 뒤늦게 인지  

### 컨테이너 격리 확인
- `/tmp/.XIN-unix/javae`가 **호스트 파일시스템에 존재하지 않음**을 확인  
  - Docker의 `ps` 출력에 컨테이너 프로세스가 표시되는 현상일 뿐, 실제로는 격리 상태 유지  
- `docker inspect` 결과  
  - 사용자: `nextjs`  
  - `Privileged`: false  
  - `Mounts`: 없음  
- 따라서 악성코드는 **호스트 접근, 크론 등록, 시스템 서비스 생성, 루트킷 설치** 등을 수행하지 못함  

### 복구 및 보안 강화
- 감염된 Umami 컨테이너를 **중지 및 삭제** 후 CPU 사용률 정상화  
- **UFW 방화벽 활성화**로 SSH·HTTP·HTTPS만 허용  
- Hetzner에 조사 결과 보고 후 **티켓이 1시간 내 종료**됨  

### 교훈과 개선점
- “내가 X를 사용하지 않는다”는 말은 **의존성까지 포함하지 않음**  
  - 사용하는 도구가 어떤 기술 스택으로 구성되었는지 확인 필요  
- **컨테이너 격리의 효과** 입증  
  - 비루트 사용자, 비권한 모드, 불필요한 볼륨 미사용이 피해 확산을 막음  
- **보안 다층 방어(Defense in Depth)** 의 필요성  
  - 방화벽, fail2ban, 모니터링, 정기 업데이트가 필수  
- **직접 Dockerfile 작성**과 **컨테이너 권한 최소화**의 중요성 강조  

### 사건 이후 조치
- Umami를 최신 버전으로 재배포하고 **모든 서드파티 컨테이너를 감사**  
  - 실행 사용자, 마운트, 업데이트 시점, 필요성 점검  
- **SSH 키 기반 인증 전환**, **비밀번호 로그인 비활성화**, **fail2ban 설정**  
- **Grafana와 Node Exporter를 통한 모니터링 강화**, **보안 업데이트 즉시 적용**  

### 결론
- Umami의 Next.js 취약점으로 인해 **10일간 Monero 채굴에 악용**되었으나,  
  **컨테이너 격리와 비루트 실행 설정 덕분에 피해가 제한적**이었음  
- 이번 경험을 통해 작성자는 **의존성 파악, 보안 설정, 업데이트 관리의 중요성**을 체득  
- 사건은 약 2시간 내 수습되었으며, **컨테이너 보안의 실제 효과를 검증한 사례**로 남음

## Comments



### Comment 47966

- Author: neo
- Created: 2025-12-19T00:36:00+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=46305585) 
- 예전에야 UFW를 켰지만, 지금은 **firewalld**를 추천함  
  UFW는 시간이 지나면 관리가 어려워지지만 firewalld는 XML 기반 설정이라 훨씬 안정적임  
  `firewall-cmd` 명령으로 SSH, HTTPS, 80포트 등을 설정할 수 있고, nftables 백엔드를 쓰는 게 좋음  
  Docker의 경우 방화벽 규칙을 우회해 포트를 열어버리는 경우가 많으므로, `/etc/firewalld/firewalld.conf`에서 `StrictForwardPorts=yes`로 설정하는 게 안전함
  - 포트를 `8080:8080`처럼 열지 말고 `192.168.0.1:8080:8080`처럼 **사설 IP에 바인딩**하는 게 좋음  
    나는 10.0.10.11 VM에서 Docker를 돌리는데, WireGuard로만 접근 가능하게 해서 Caddy로 리버스 프록시를 두는 방식이 편리했음
  - Docker 대신 **Podman**을 설치하라고 권함. Docker는 방화벽 우회 문제가 흔함
  - 어떤 netfilter 프론트엔드를 쓰든, **아웃바운드 연결 제한**이 없으면 무의미함  
    악성코드는 외부 마이닝 풀이나 C2 서버로 연결하려 하기 때문에, 허용되지 않은 바이너리의 네트워크 접근을 막아야 함  
    `/tmp`, `/var/tmp`, `/dev/shm`의 실행 권한을 제거하는 것도 유용함
  - Hetzner는 무료 **외부 방화벽 서비스**를 제공하므로, 1차 방어선으로 활용할 수 있음
  - 개인적으로는 `nftables.conf`만으로도 충분히 단순하고 명확하다고 느낌  
    iptables는 이미 폐기된 상태라 firewalld 같은 추가 계층이 꼭 필요하진 않음

- 이건 “**React2Shell CVE-2025-55182**” 관련 이슈로 보임  
  1년 넘게 영향을 준 취약점인데도 주목을 거의 못 받는 게 이상함  
  지난 12개월간 Next.js로 배포한 웹앱이라면 이미 봇넷 일부일 가능성이 높음  
  업계가 “Docker 써라”, “방화벽 켜라” 수준의 조언만 하는 게 답답함  
  요즘 프론트엔드 생태계에 회의감이 들어서 **C++** 쪽으로 커리어를 옮길까 고민 중임
  - 프론트엔드의 **기술 교체 주기**는 예전보다 훨씬 완화되었음  
    지금은 Next.js, React, Tailwind, Postgres 조합이 5년째 표준처럼 굳어 있음  
    2000년대 후반~2010년대 초반의 프레임워크 난립 시절에 비하면 안정적임  
    유행과 변화가 싫다면 AI 개발 쪽이 훨씬 더 빠르게 변하고 있음
  - 최신 JS 프레임워크를 안 써도 웹앱은 충분히 만들 수 있음  
    백엔드는 .NET, Java, Go 같은 **견고한 기술 스택**을 쓰고, 프론트는 자유롭게 선택하면 됨  
    이렇게 하면 CVE도 줄고 기술 피로도도 낮아짐
  - 내 **Pangolin 인스턴스**도 이 취약점에 뚫렸음  
    [관련 GitHub 토론](https://github.com/orgs/fosrl/discussions/2014)
  - 나도 100개 정도의 Next.js 프론트를 배포했는데, **Server Components**를 안 써서 다행히 영향은 없었음

- Docker 컨테이너의 CPU 사용량을 `--cpus="0.5"`로 제한하면, **오작동 서비스나 마이너**가 시스템 전체에 영향을 주지 않음  
  - 컨테이너를 **read-only 모드**로 돌리면 공격 표면을 더 줄일 수 있음  
  - 다만 CPU 제한이 너무 낮으면 침입이 눈에 띄지 않아 **탐지 지연**이 생길 수도 있음  
  - 앱의 **성능 프로파일**을 잘 알고 있어야 함. 나중에 버스트 트래픽이 생기면 의도치 않게 쓰로틀될 수 있음  
  - 메모리의 soft/hard limit도 함께 설정하는 게 좋음

- 방화벽 없이 서버를 운영하는 건 꽤 **대담한 선택**임  
  Hetzner의 외부 방화벽을 함께 쓰면 실수했을 때도 방어층이 하나 더 생김  
  나는 SSH를 집 IP로만 허용하고, 외부에서 필요할 땐 Hetzner 웹사이트에서 임시로 열어둠
  - 대부분의 경우 방화벽은 큰 효과가 없음  
    진짜 중요한 건 **RCE 취약점이 있는 소프트웨어를 돌리지 않는 것**임  
    Docker가 구세주처럼 보이는 건 사실상 운이 좋았던 것뿐임
  - 퍼블릭 웹서비스라면 방화벽이 큰 도움이 안 됨  
    대신 **bastion host**를 두고 HTTP 프록시로 입출력을 제어하는 방법이 있지만 설정이 복잡함
  - 30년 동안 리눅스를 운영하면서 해킹당한 유일한 경우는 **잘 알려진 포트**를 열었을 때였음  
    비표준 포트를 쓰는 것만으로도 의외로 효과적이었음
  - 비밀번호 인증을 켜두는 건 위험함. 개인적으로는 **fail2ban**이 꼭 필요하진 않다고 생각함  
  - SSH 포트를 무작위로 바꿔서 **포트 스캐너 회피**를 시도함  
    효과가 확실한지는 모르겠지만, 일종의 심리적 우산 같은 느낌임

- Docker 컨테이너를 root로 돌리면, 호스트까지 공격 가능한가 궁금했음  
  - Docker는 **보안 경계가 아님**. 단순히 프로세스 수준의 격리만 제공함  
    신뢰할 수 없는 코드를 돌리려면 VM(KVM/QEMU)이나 **gVisor**(https://gvisor.dev/), **Firecracker**(https://firecracker-microvm.github.io/) 같은 기술을 써야 함  
    Docker는 샌드박스가 아니라 격리된 실행 환경 정도로 이해해야 함
  - 공격자는 컨테이너 안에서도 CPU를 써서 **Monero 채굴**을 할 수 있음  
    Docker의 기본 설정은 RAM, CPU, 디스크 사용량 제한이 없어 **DoS 공격**에도 취약함  
    게다가 많은 가이드가 `--privileged`나 `CAP_SYS_PTRACE` 같은 위험한 옵션을 권장함
  - **privileged 모드**에서는 Docker 소켓을 통해 호스트 루트 파일시스템에 접근 가능함  
    새 컨테이너를 띄워서 루트 권한으로 변경할 수도 있음
  - **user namespace**를 활성화해야 진짜 보안 경계가 생김  
    Docker는 아직 기본값이 아니라서, 설정하지 않으면 탈출 위험이 큼  
    과거 대부분의 컨테이너 탈출은 user namespace로 막을 수 있었음
  - 컨테이너 탈출이 존재하긴 하지만, 대부분은 단순한 **자동화된 마이닝 공격**임  
    민감한 데이터를 다루지 않는 서버라면 컨테이너만 정리해도 충분함

- 공격 표면을 줄이려면 **공개할 필요 없는 서비스는 외부에 노출하지 말아야 함**  
  예를 들어 분석 툴은 WireGuard나 SSH SOCKS 프록시를 통해서만 접근하도록 하면 됨

- 나도 Hetzner 서버에서 **Monero 마이너 감염**을 겪었음  
  다행히 Incus의 LXC 컨테이너 안에서만 발생했고, CPU 우선순위가 낮아 눈치채지 못했음  
  root 계정에 SSH 키가 추가되고 원격 관리 에이전트가 설치되어 있었음  
  결국 컨테이너를 폐기했지만, 몇 가지 교훈을 얻었음  
  - 시스템이 언젠가 뚫릴 걸 가정하고 **격리와 자원 제한**을 설정해야 함  
  - **ZFS 스냅샷**과 백업을 주기적으로 유지하면 복구가 쉬움  
  - 침해된 시스템은 폐기하는 게 이상적이지만, 상황에 따라 롤백 후 강화도 가능함  
  - 마이너가 제대로 설정되지 않아 눈에 띄었지만, 만약 조용히 침입했다면 몰랐을 수도 있음  
  - [Umami](https://github.com/umami-software/umami)를 설치한 컨테이너에서 발생했음

- 글에 사람이 교정하지 않은 **AI 환각 내용**이 포함되어 있었음  
  Puppeteer 관련 취약점이라고 반복 주장하지만 사실이 아님
  - 원문 첫 문단에 **Claude 세션의 전사본**이라고 명시되어 있었음

- 최근 **React 19 취약점**을 이용한 Monero 마이너가 여기저기 설치되고 있음  
  나도 같은 문제를 겪었음
  - 이런 **마이닝 악성코드**는 눈에 잘 띄고 피해도 적어서 오히려 유용함  
    일종의 자동 버그 바운티처럼 작동해서, 취약점을 알려주는 셈임  
    네트워크나 프로세스 모니터링이 잘 되어 있다면 쉽게 탐지 가능함
  - Oracle Cloud에서 Umami 서버가 감염되어 **서버를 초기화**했음  
    덕분에 백업 시스템을 업그레이드할 좋은 계기가 되었음

- 이런 사례를 보면 VPS를 직접 운영하지 않길 잘했다는 생각이 듦  
  예전에 해봤지만, 나는 **평범한 관리자 수준**이라 보안 유지가 어렵다고 느낌  
  그래서 지금은 전문가에게 맡기고 비용을 지불하는 편이 훨씬 마음이 편함
