원문 캡처 · adversary.icu
원문 캡처 · adversary.icu

Yggdrasil을 Windows 사용자 공간(userspace)에서 실행하면 오버레이 내부 연결은 Sysmon과 Windows 필터링 플랫폼(WFP)에 기록되지 않을 수 있지만, 외부 피어 연결은 프로세스의 네트워크 이벤트로 남는다. 격리된 Windows 10 가상 머신 두 대에서 진행한 실험은 yggstack의 로컬 포트 전달과 애플리케이션에 네트워크 스택을 직접 넣는 방식이 서로 다른 흔적을 남긴다는 점을 보여준다.

실행 방식에 따라 달라지는 기록

ESET은 2026년 10월 MythicRatatoskr 분석에서 UAC-0099와 Sandworm의 협력에 연결된 백도어가 Yggdrasil 오버레이 네트워크를 명령·제어에 사용했다고 보고했다. 백도어는 표준 가상 네트워크 어댑터 대신 yggstack을 이용했고, expose 명령으로 오버레이 연결을 피해 시스템의 로컬 TCP 포트에 전달했다. ESET은 별도의 SOCKS 도구가 127.0.0.1:33690에서 수신 대기한 사례도 관찰했다. ESET의 조사

실험은 yggdrasil-go v0.5.14와 yggstack을 사용해 외부 인터넷과 분리한 Windows 10 가상 머신 두 대에서 진행했다. Sysmon v15.22에서 프로세스 생성(Event 1)과 네트워크 연결(Event 3)을 기록하고 루프백 수집을 켰다. 악성코드 샘플이나 공개 피어, 외부 인프라에는 접속하지 않았다.

실행 방식Windows에서 확인된 네트워크 흔적오버레이 내부 연결 기록
표준 TUN 데몬 yggdrasil.exeWintun 가상 어댑터 ygtun, 200::/7 경로, 외부 피어 소켓Sysmon Event 3과 WFP Event 5156에 기록
사용자 공간 포워더 yggstack.exe외부 피어 소켓과 127.0.0.1로 이어지는 루프백 연결gVisor 안에서 처리돼 기록되지 않음
애플리케이션 내장 스택외부 피어 소켓gVisor 안에서 처리돼 기록되지 않음. 루프백 전달도 없음

표준 TUN 방식에서는 Windows가 Wintun 드라이버를 통해 오버레이 경로를 관리했다. 실험에서 오버레이 주소로 들어온 웹 서비스 연결은 Sysmon Event 3과 WFP Event 5156에 남았다. 반면 yggstack은 gVisor 사용자 공간 네트워크 스택에서 오버레이 패킷을 처리해 0200::/7 소켓 이벤트를 만들지 않았다. Windows에는 피어 연결과 127.0.0.1:8080으로 전달된 연결이 기록됐다.

애플리케이션이 gVisor 스택을 프로세스 안에 직접 넣고 127.0.0.1 소켓을 열지 않으면 루프백 전달 흔적도 사라진다. 이 방식에서도 Windows는 외부 피어 전송용 TCP 또는 UDP 소켓을 프로세스에 연결해 기록했지만, 내부 HTTP 연결은 추가 Event 3 이벤트를 만들지 않았다.

루프백 기록은 전달 행위를 보여주지만 Yggdrasil을 특정하지 않는다

yggstack.exe가 -remote-tcp 80:127.0.0.1:8080으로 실행된 실험에서는 외부 피어 연결을 받은 뒤 웹 서버로 세 차례 연결했다. Sysmon Event 3의 Initiated 값은 연결 방향을 구분하는 데 도움이 된다. false는 로컬에서 수락한 연결, true는 외부 연결뿐 아니라 127.0.0.1로 나가는 연결도 나타낸다. 외부 인터페이스의 발신 연결만 검색하면 수신한 피어 연결을 놓칠 수 있고, 루프백 이벤트를 수집하지 않으면 내부 서비스로 이어진 전달 연결을 잃는다.

외부 연결과 10분 안에 같은 HostName과 ProcessGuid에서 발생한 루프백 발신 연결을 묶는 상관 분석(correlation) 규칙은 실험의 yggstack을 찾아냈다. 하지만 이 규칙은 Yggdrasil 전용 탐지가 아니다. Yggdrasil 코드를 포함하지 않은 Go HTTP 리버스 프록시도 외부 연결을 받은 뒤 127.0.0.1로 연결해 같은 조건에 걸렸다. 반대로 스택을 프로세스에 내장한 클라이언트와 서버는 세 차례 HTTP 요청을 처리했어도 루프백 소켓을 열지 않아 탐지되지 않았다. 외부 소켓과 루프백 소켓이 서로 다른 프로세스에 나뉜 전달 방식도 이 상관 규칙으로 잡히지 않을 수 있다.

수사에서 확인할 점과 한계

실험에서 TCP, TLS, QUIC, WebSocket 피어 연결은 모두 외부 전송 소켓으로 기록됐고, 내부 HTTP 소켓은 기록되지 않았다. Sysmon Event 3은 TLS와 WebSocket을 일반 TCP로, QUIC를 UDP로 표시했다. QUIC 서버 이벤트에서는 실험 중 포트가 SourcePort에 기록돼 DestinationPort만 검색하면 누락될 수 있었다. UDP 피어를 찾을 때는 출발지와 목적지 주소·포트 필드를 모두 확인해야 한다.

Sysmon은 소켓 상태 전환을 기록하지 전송량을 측정하지 않는다. TLS 피어 연결 하나를 유지한 채 1MiB 업로드 세 건을 보낸 실험에서 애플리케이션 로그는 총 3,145,728바이트를 확인했지만, Sysmon에는 업로드 중 추가 Event 3 기록이 없었다. 하이퍼바이저 패킷 캡처에는 단일 양방향 TCP 흐름과 클라이언트에서 서버로 4,009,675바이트, 반대 방향으로 191,618바이트가 기록됐다. 다만 TLS와 Yggdrasil 암호화 때문에 내부 요청, 평문 헤더, 파일 경계는 확인할 수 없었다. 전송량과 연결 지속 시간은 Zeek conn.log 같은 네트워크 흐름 기록으로 보완할 수 있지만, 그 트래픽이 허가된 작업인지 판단하려면 목적지 서비스 로그나 복호화한 페이로드가 필요하다.

수사에서는 Event 3 XML의 ProcessGuid를 같은 호스트의 Event 1과 연결해야 한다. PowerShell Get-WinEvent의 CSV 내보내기에서 최상위 ProcessId는 이벤트를 기록한 서비스의 PID일 수 있으며, 소켓 소유 프로세스의 PID와 ProcessGuid는 XML 페이로드에 있다. 가상 머신 스냅샷 복원으로 시각이 어긋날 수 있으므로 이벤트 순서와 프로세스 식별자를 함께 살피되, EventRecordID는 같은 이벤트 채널 안에서만 순서를 보장한다.

프로세스 이름이나 파일 경로만으로 검색하는 것도 위험하다. ESET 조사에서는 백도어와 독립 SOCKS 도구가 같은 실행 파일 이름을 사용했다. 따라서 Event 1의 파일 해시와 부모·자식 프로세스 계보(process lineage)를 확인하고, 시작 지연을 고려해 네트워크 이벤트 이전까지 검색 범위를 넓히는 편이 낫다. ESET은 일부 샘플에서 피어 연결 전에 10분의 실행 지연도 관찰했다.

표준 TUN 데몬은 Wintun 어댑터, yggdrasil.exe를 실행하는 Windows 서비스, Wintun 인터페이스 GUID를 참조하는 레지스트리 키를 남길 수 있다. 기본 관리 소켓 tcp://localhost:9001에서는 yggdrasilctl getself로 노드 주소와 공개 키를 확인할 수 있다. 그러나 yggstack은 Wintun 설치를 피하고 AdminListen: "none"으로 관리 소켓을 비활성화하므로 이런 단서가 없을 수 있다. 오버레이 주소도 설정에서 키를 새로 만들면 바뀔 수 있어 지속적인 장치 식별자로 삼기 어렵다.

네트워크 측면에서는 암호화되지 않은 TCP 연결의 Yggdrasil 핸드셰이크가 123바이트 meta 프레임으로 시작하며, WebSocket 연결에서는 HTTP 업그레이드 요청 뒤에 해당 프레임이 온다. 로컬 브로드캐스트 네트워크에서는 설정으로 끄지 않은 경우 노드가 IPv6 멀티캐스트 [ff02::114]:9001로 공개 키를 담은 104바이트 탐색 패킷을 몇 초마다 보낸다. 이런 특징은 조사 단서가 될 수 있지만, 공개 피어 IP 일치나 개별 전송 특성만으로 Yggdrasil 사용 또는 공격자 활동을 확정할 수는 없다.

QUIC 연결에서 관찰한 세부값은 Yggdrasil을 단독으로 식별하거나 연결이 허가됐는지 판정하지 못한다. Go 1.25.0과 quic-go v0.60.0으로 컴파일한 실험 환경의 Yggdrasil 바이너리는 QUIC ClientHello에서 양자 내성 키 합의 방식인 X25519MLKEM768(코드 포인트 0x11ec)을 광고했고, 키 셰어는 1,216바이트였다. ALPN과 SNI 확장은 없었으며, 표준 QUIC 전송 매개변수 확장(ID 57)과 최대 ACK 지연 26ms를 광고했다. 이 값은 해당 도구 체인과 라이브러리 버전에 따른 조사 참고 정보다.

config.go를 검토한 결과 TLS 연결에서 Yggdrasil 노드는 일련번호가 1이고 만료일이 9999-12-31이며, 노드의 64자 16진수 공개 키를 Common Name으로 사용하는 임시 자체 서명 인증서를 생성한다. 표준 TLS 1.3에서는 핸드셰이크 중 인증서 필드가 암호화되므로 수동 패킷 캡처로 관찰할 수 없다. 기업의 TLS 종료 검사 프록시가 이 필드를 검사할 가능성은 있지만 이번 실험에서는 수행하지 않았으며, 수동 네트워크 모니터링으로 TLS 암호화 연결에서 오버레이 키를 추출할 수는 없다.

의심스러운 피어 연결이나 포워더 바이너리를 조사할 때는 먼저 Event 3 XML에서 ProcessGuid를 추출해 같은 호스트의 Event 1과 연결하고, 전체 프로세스 계보와 바이너리 해시, 명령줄 인수를 검토해야 한다. 시작 지연을 고려해 검색 범위를 네트워크 이벤트보다 앞쪽으로 넓힌다. 같은 프로세스의 127.0.0.1 또는 ::1 발신 연결이 있으면 목적지 포트를 확인하고, 해당 로컬 서비스의 인증 로그에서 로컬호스트에서 온 요청을 살핀다. 루프백 연결이 없다면 프로세스 내장 스택일 수 있으므로 Zeek conn.log 같은 흐름 기록에서 외부 피어 IP와 포트의 연결 시간 및 전송량을 확인한다. UDP와 QUIC는 포트가 출발지 또는 목적지 필드에 기록될 수 있으므로 두 필드를 모두 검색하고, 패킷 캡처에서 평문 meta 프레임이나 WebSocket 헤더도 살핀다.

이번 결과는 사용자 공간 실행이 모든 Windows 네트워크 가시성을 없애지는 않는다는 점을 보여준다. Sysmon은 프로세스 생성과 외부 피어 소켓, 포워더의 루프백 전달을 기록하지만, 내부 트래픽이 프로세스 안에서 처리되면 소켓 이벤트만으로 요청 횟수나 데이터 전송량을 알 수 없다. 따라서 프로세스 계보와 해시, 네트워크 흐름 기록, 목적지 서비스 로그를 함께 확인해야 한다.

참고 자료: ESET의 MythicRatatoskr 분석, IOC 저장소, yggdrasil-go v0.5.14, yggstack c39db65, public-peers, Yggdrasil FAQ, Sysmon 문서, WFP Event 5156, Zeek conn.log 문서, Go crypto/tls 문서, RFC 9001 §5.2.