Malicious PyPI package that steal Cryptocurrency wallet data

암호화폐 지갑 데이터 복호화를 가장해 니모닉 시드를 탈취하는 악성 파이썬 패키지와 공격자의 ETH 주소를 분석해본다.

1. 도둑맞은 0.025 ETH

때는 2023년 9월 18일, 한창 2023 KDFS Challenge 문제를 제작하던 중, 문제 시나리오에 사용하기 위해 Metamask ETH 주소에 입금한 0.025 ETH가 12초 만에 도난당한 사건이 발생했다. 당시 도난당한 이유를 밝히기 위해 다음과 같이 여러가지 가설을 세웠다.

  1. Metamask가 해킹당했다.
  2. 얼마 전 암호화폐 지갑 분석 주제로 발표를 했었는데 당시 Metamask 복호화 과정 소개 때 니모닉 시드가 유출됐다.
  3. 나의 부주의함으로 니모닉 시드가 유출되었다.

확인 결과 1번은 알려진 소식이 없었고, 2번은 발표 당시 사용했던 지갑과 이번에 도난당한 지갑이 달랐다. 그렇다면 나의 부주의로 인해 니모닉이 유출되었다고 밖에 볼 수 없는 상황이었다.

예전에 dApp 조사를 하면서 Metamask 지갑을 몇몇 dApp에 연결했던 적이 있었기 때문에 이것이 문제였을 것이라 생각하고 기억을 더듬어 다시 찾아봤다. 하지만 딱히 피싱 사이트에 접속했던 적이 없고 기술적으로 맞지 않는 부분이 있어 원인을 찾는데 어려움이 있었다. 어쨌거나 KDFS 문제는 새 Metamask 지갑을 생성해서 제작을 했고 잘 마무리 할 수 있었다(지갑을 새로 생성할 수 밖에 없었던 자세한 이유는 뒤에서 설명하겠다). 그리고 시간이 흘러 최근에야 ETH를 도난당한 이유를 알아내는데 성공했다.

[그림 1] 도난당한 주소에서 발생한 관련 트랜잭션

Coinone 거래소를 통해 전송된 0.025 ETH의 수수료를 제외한 금액이 0x3BC2FaC06FdFF8D18D759544D935Ffdc2E978E23 주소로 전송된 것을 확인할 수 있다. Etherscan에서 해당 주소를 조회하면 피해를 받은 다른 사람들이 작성한 댓글을 볼 수 있다.


2. 암호화폐 탈취 원인

KDFS 문제를 제작하기 전에 우리는 연구 과제로 암호화폐 지갑 프로그램의 아티팩트 연구를 진행하고 있었다. 연구 대상으로 Windows 데스크톱 설치형 지갑 프로그램, 하드웨어 연동형 지갑 프로그램, 크롬 웹 브라우저 확장형 프로그램을 합쳐서 약 20 개의 프로그램을 분석했는데, 이 중 Metamask가 포함되어 있었다.

연구 내용으로 암호화폐 지갑 프로그램을 이용했을 때 생성,수정,삭제되는 아티팩트를 분석하는 것을 비롯해 지갑 데이터를 복호화 할 수 있는 알고리즘에 대해서도 연구했다. 이 때 자체적으로 연구한 복호화 알고리즘을 사용하면서도, 시중에 오픈되어 있었던 Metamask 복호화 파이썬 패키지를 사용해본 것이 화근이었다.

[그림 2] PyPI 패키지 - hashdecrypt 1.0.2

당시 사용했던 패키지는 hashdecrypt 1.0.2으로 여기에서 확인할 수 있다. 패키지 설치 명령어 하단의 설명을 보면 Metamask 뿐만 아니라, Brawe, Ronin, BinanceChain의 Vault Data 복호화를 지원한다.


3. hashdecrypt 패키지 분석

hashdecrypt 패키지는 암호화폐 지갑 프로그램의 암호화된 Vault Data를 복호화하는 도구로 보이지만, 실제로는 니모닉 시드를 탈취하는 Stealer 행위를 포함하고 있다.

3-1) 다운로드 통계

PyPI는 업로드된 패키지에 대한 다운로드 로그를 Google BigQuery에 공개 데이터 세트로 저장하고 있다. 다음 쿼리를 통해 5년간 다운로드 내역을 조회한 결과, 총 3,608건이 다운로드된 사실을 확인했다.

[그림 3] Google BigQuery 조회 결과

3-2) 코드 분석

# decrypt 함수

[그림 4] hashDecrypt.py - decrypt()
  • key_from_password 함수로 복호화 키를 도출한다.
  • 복호화 키를 사용해 decrypt_with_key함수로 데이터를 복호화 한다.
    • 복호화된 데이터에는 암호화폐 지갑의 니모닉 시드 정보가 포함되어 있다.
  • 그 다음으로 jsBIP39 함수가 호출되며 Stealer 행위가 시작된다.

# jsBIP39 함수

[그림 5] hashDecrypt.py - jsBIP39(), cli_keccak256()
[그림 6] 하드코딩된 문자열 디코딩 결과
  • encode_data변수에 하드 코딩된 BASE64 값이 담겨 있다.
    • 이는 공격자의 Github에서 backendapi Repository의 settings 파일에 대한 Raw 데이터 접근 주소를 가리킨다.
[그림 7] Github으로 관리되는 공격자의 C&C 주소
  • 해당 파일에는 공격자의 C&C 주소가 기록되어 있다.
    • 이는 C&C 주소를 언제든 변경하기 위한 것으로 보인다.

# cli_keccak256 함수

  • 복호화된 데이터(니모닉 시드 등)를 BASE64로 변환해 공격자의 C&C 주소로 POST Method를 통해 전송한다.

3-3) 데이터 탈취 후 증상

니모닉 시드를 획득한 공격자는 니모닉 시드로 획득할 수 있는 암호화폐 주소를 모니터링한다. 공격자는 모니터링 중인 주소를 목적지로 하는 트랜잭션이 메모리 풀에 올라오면 해당 트랜잭션이 블록에 등록되자마자 공격자 주소로 코인이 전송될 수 있도록 피해자의 키를 이용해서 추가 트랜잭션을 생성한다. 공격자가 생성하는 트랜잭션은 채굴이 빨리 될 수록 공격자에게 좋기 때문에 그림 1을 보면 공격자 주소로 ETH를 전송하는 트랜잭션의 Gas Fee가 다른 트랜잭션보다 유난히 높았던 것을 알 수 있다.

이와 같은 공격 행위를 프로그래밍해 자동화한 것을 Sweeper Bot이라고 한다. 이어질 공격자 주소 분석 내용을 읽기 전, 이해를 돕기 위해 먼저 아래 글을 보는 것을 추천한다.

스위퍼 봇(Sweeper Bot)
Sweeper Bot을 이용한 암호화폐 탈취 방법에 대해 살펴본다.

앞서 설명한 것과 같이 유출된 지갑의 주소로 ETH를 전송하면 수 초 내로 공격자 주소로 자금이 재전송되기 때문에 피해자는 지갑을 새로 생성해서 사용해야 한다.


4. 공격자 정보

4-1) 공격자 SNS 정보

[그림 8] 공격자의 youtube 영상

공격자의 youtube 채널을 확인한 결과 위와 같은 동영상 1개가 업로드 된 사실을 확인했다. 직접 개발한 프로그램을 사용하는 것을 볼 수 있으며, 해킹을 통해 획득한 것으로 추정되는 피해자들의 니모닉 시드, 주소, 개인키, 패스워드 등이 노출되어 있는 것을 알 수 있다.

[그림 9] 공격자 youtube 영상 소개 글

위 영상의 소개 글에서 공격자가 운영 중인 텔레그램 채널을 확인할 수 있다.

[그림 10] 공격자가 운영 중인 텔레그램 채널 대화 중 일부

텔레그램 채널에 접속하면 러시아어로 대화 중인 것을 볼 수 있으며 공격자가 작성한 파일을 공유하는 모습을 볼 수 있다.

[그림 11] 공격자 깃허브에서 삭제된 파일

공격자 깃허브에 다수의 이메일 주소가 포함되어 있던 파일이 업로드 되었다가 삭제된 사실을 확인할 수 있었다.

4-2) IP 평판 분석

※ IP 평판은 나루씨큐리티에서 전달받은 정보로 작성되었습니다.

탈취한 니모닉 시드가 전송되는 공격자 IP(65.109.70.235)는 독일의 ISP 주소로 물리적 위치는 핀란드로 확인된다. 리버스 DNS 레코드는 러시아 도메인에 연결되어 있다. 공격자 IP는 인도 IP와 PPTP VPN 연결을 수행하는데, 해당 인도 IP는 북한으로 식별된 IP와 아웃바운드 연결이 지속적으로 발생하고 있다.

아래 그림은 중계지로 활용되고 있는 인도 IP에서 발생한 통신 사실로 최근 통신량이 급증하고 있는 것을 알 수 있고, 11월에는 북한으로 식별된 IP와 통신 횟수가 13,618건이나 된다.

[그림 12] 공격자 관련 IP 주소 통신량

5. 악성 PyPI 패키지 배포자의 ETH 주소 분석

※ 본 게시글의 암호화폐 주소 분석은 BIG의 암호화폐 추적 솔루션인 "QLUE"를 사용했습니다.

[그림 13] 공격자 주소 요약 정보

공격자 주소에서는 현재까지 약 800건의 트랜잭션이 발생했으며, ETH 전송이 약 20건, 토큰 전송은 약 30건 발생했다.

5-1) 공격자 주소의 토큰 스왑

[그림 14] Uniswap 이용 트랜잭션

공격자는 Uniswap을 이용해 토큰을 ETH로 스왑했다. 스왑한 토큰의 목록은 다음과 같다.

  • SG
  • VetMe
  • OMI
  • eMax
[그림 15] 1inch swap 이용 트랜잭션

또한, 공격자는 SushiSwap을 이용해 토큰을 ETH로 스왑했다. 스왑한 토큰의 목록은 다음과 같다.

  • CREAM
  • WSM
[그림 16] Metamask swap 이용 트랜잭션

공격자는 Metamask Swap을 이용해 USDT를 USDC로 스왑했으며, ETH를 USDC로 스왑하기도 했다.

[그림 17] Swap 그래프를 함축한 모습

스왑한 것으로 확인된 암호화폐 정보는 다음 표와 같다.

Swap Src Amount Swap Src Symbol Swap Dst Amount Swap Dst Symbol Swap Date (UTC+0)
8,877.261929 SG 0.23489019063592337 ETH 2023-09-28 09:23:47
114,000 VetMe 0.24865941043835574 ETH 2023-09-28 09:24:59
324,384 OMI 0.16692157208833505 ETH 2023-09-28 09:25:59
50,686,498,861.59 eMax 0.03925272015510193 ETH 2023-09-28 09:26:59
23.37760257276568 CREAM 0.2607320824625328 ETH 2023-09-29 11:03:23
31.502494492081443 CREAM 0.35134958625969753 ETH 2023-09-29 11:03:47
31.502494492081443 CREAM 0.3500569742168247 ETH 2023-09-29 11:04:11
97.494978 USDT 96.695086 USDC 2023-09-29 18:48:59
0.1 ETH 165.069962 USDC 2023-09-29 18:50:59
46,041 WSM 1.4007987524344125 ETH 2023-10-11 12:06:35
[그림 18] 공격자 주소의 잔여 암호화폐 가치

현재 공격자 주소는 약 6.423 ETH를 소유하고 있으며, 이는 약 14,400$ 가치에 해당한다. 보유 중인 토큰은 USDC, SG, OMI, STATE, USDT, C3, VetMe 등으로 약 1,400$ 가치에 해당한다.

[그림 19] 공격자 주소에서 거래소로 전송된 토큰 트랜잭션

공격자 주소에서 전송된 USDC는 Binance와 OK X 거래소로 전송된 것을 확인했다. 약 2,700$ 가치를 현금화하기 위해 보낸 것으로 추정되며 Binance와 OK X 거래소의 협조를 통해 공격자의 신원을 확인할 수 있을 것으로 기대된다.

5-2) 또 다른 피해자? or 또 다른 공격자?

[그림 20] 피해자로 추정되는 주소의 트랜잭션

공격자 주소에서 수신한 토큰의 출처를 밝히기 위해 수신 트랜잭션을 분석하던 중 Sweeper Bot에 의해 감시받는 주소가 다수 있는 것으로 확인됐다. 그 중, 위 그림을 보면 2023-09-25 13시 35분 경 0x07b0f87c70494bc8d1e724105af7c87ff0f0b2ce 주소에 0.01086224 ETH 가 전송되었으나 1분 사이에 공격자 주소로 해당 금액이 전송된 트랜잭션을 확인했다. 추가로 14시 43분 경 해당 주소는 다시 0.00919 ETH를 전송했으나 역시 공격자 주소로 전송되었다. 따라서 해당 주소를 잠정적으로 피해자로 추정하고 분석을 계속했다.

그 외 특이한 점으로 위 그림에서 공격자에게 전송된 USDT 토큰이 2023-09-25 12시 29분 경에 피해자 추정 주소로 전송된 사실을 알 수 있는데, 피해자 주소로 토큰이 전송되고 약 3시간 뒤에 공격자 주소로 전송된 것을 알 수 있다. 이는 Sweeper Bot이 ETH 수신 트랜잭션은 모니터링하지만, 토큰 수신은 모니터링하고 있지 않는 것을 알 수 있다.

[그림 21] 피해자 소유로 추정되는 또 다른 주소 (0x63725a4e2169931edc3a05bfb1c51c6ab0237e12)

추가로 그림 19에서 피해자 추정 주소로 0.003692011646197758 ETH를 전송한 주소를 분석했다. 해당 주소는 2023-08-31일 까지 christyana.eth라는 ENS를 이용한 이력이 있다. 발생했던 트랜잭션의 일시를 보면 앞서 살펴봤던 트랜잭션의 일시보다 이르다는 것을 알 수 있는데, 피해자는 이미 공격자에게 피해를 받고 있었으며 새로운 주소에 자금을 이동하려고 시도했으나 해당 주소마저 Sweeper Bot에 의해 감시되고 있던 것으로 볼 수 있다.

피해자의 또 다른 주소에서 2023-10-17 05시 17분 경 발생한 송신 트랜잭션은 Sweeper Bot에게 피해를 받기 전 소유하고 있었던 ETH를 공격자가 수동으로 보낸 것으로 추정된다.

[그림 22] Tornado Cash를 이용한 피해자?

피해자로 추정했던 주소의 트랜잭션을 추가로 분석한 결과 흥미로운 사실을 확인할 수 있었다. 위 그림처럼 이더리움의 대표적인 믹싱 서비스인 Tornado Cash의 수신 주소로 피해자의 주소가 다수 이용됐었던 것이다. 일반적인 사용자라면 Tornado Cash를 사용할 일이 없기 때문에 여기서 두 가지 가설을 세웠다.

  1. 피해자라고 생각했던 주소는 사실 본 사건의 공격자가 관리하는 주소 중 하나다.
  2. 본 사건의 공격자와는 무관한 또 다른 악성 행위자가 피해자가 되었다.

첫 번째 가설의 이유
공격자들은 보통 자금 세탁을 위해 Tornado Cash와 같은 믹싱 서비스를 이용해 수 많은 주소를 생성하게 된다. 이렇게 생성된 많은 주소는 일일이 관리하기가 어렵기 때문에 주소를 Sweeper Bot이 감시하도록 작성한다면 흩어진 자금을 다시 모으는 과정에서 부담을 덜 수 있을 것이다.

두 번째 가설의 이유
Tornado Cash
같은 서비스를 평소에 이용하던 사용자라면, 복호화 스크립트 등에도 관심이 있었을 가능성이 높다. 공격자는 보유한 기술을 내세우면서 뒤에서 이득을 취하는 모습을 보이기 때문에 또 다른 공격자가 기술을 이용하려다 속았을 가능성이 있다.

한 가지 특이한 부분은 2023년 9월 25일 이전의 트랜잭션을 봤을 때, 수신된 자금이 짧은 시간 내에 빠져나가는 트랜잭션이 보이지 않는다는 점이다. 공격자가 관리하는 주소라면 처음부터 Sweeper Bot에 등록될 가능성이 높을 것이다. 이전에 정상적으로 사용되고 있던 주소가 어느 시점부터 Sweeper Bot에 등록된 것으로 봐서 본 사건의 공격자와는 다른 피해자일 확률이 높다고 생각했다.

[그림 23] 피해자와 공격자를 모두 알고 있는 주소(0x1b22b763eb69fca8af688fb404f8a2658e8e0a0d)

그러던 중, 위와 같은 주소를 확인할 수 있었다. 0x63725a4e2169931edc3a05bfb1c51c6ab0237e12 주소가 피해자라고 생각했지만, 해당 주소에 자금을 전송한 이력이 있는 0x1b22b763eb69fca8af688fb404f8a2658e8e0a0d 주소에서 공격자 주소로 자금을 전송한 이력이 확인된 것이다. Sweeper Bot에 의해 전송된 것으로 보여지지는 않으며, 해당 주소가 공격자와 피해자 추정 주소를 모두 알고 있는 상태여야 발생 가능한 것이기 때문에 피해자라고 보는 것보다 공격자의 또 다른 주소로 보는 것이 더 타당하다는 생각이 들었다.

[그림 24] 공격자 소유로 추정되는 또 다른 주소(0xab15c205d2e4287af8287b59486c8d4b0368389e)

피해자 추정 주소에서 전송된 0.003692011646197758 ETH는 공격자 주소가 아닌 다른 주소로 전송되었다. 해당 주소의 트랜잭션을 분석한 결과 공격자의 주소와 트랜잭션이 다수 발생한 것을 알 수 있는데, 주고받은 트랜잭션이 발생한 시각과 전송된 암호화폐 양을 보면 해당 주소 또한 Sweeper Bot에 의해 감시받고 있는 주소인 것으로 추측할 수 있다. 또한, 공격자가 Swap 했던 SG 토큰을 전송한 것을 알 수 있다.

여기서 한 가지 짚어야 할 점은, 만약에 해당 주소가 피해자의 또 다른 주소라면 공격자는 왜 피해자의 주소로 ETH를 전송했냐는 것이다. 아쉽게도 이런 형태를 설명할 수 있는 논리는 떠오르지 않았다. 이와 같이 피해자로 가정했을 때 설명하기 어려운 요소가 있기 때문에 피해자 보다는 공격자가 관리하는 다른 주소일 것으로 예상된다. 그리고 일반적인 사람이라면 주소를 몇 번이나 옮겨가면서 계속 속지는 않을 것으로 생각된다.

5-3) 공격자 소유의 ETH 추적

[그림 25] 거래소로 전송된 공격자의 ETH

앞서 0xab15c205d2e4287af8287b59486c8d4b0368389e 주소로 전송된 ETH를 제외한 나머지를 추적했다.

공격자 주소에서 전송된 0.6572775563615505 ETH 중 일부를 추적한 결과 다른 자금과 합쳐졌고, Coinbase 거래소 소유의 스마트 컨트랙트를 이용한 사실과, FixedFloat 거래소를 이용한 사실을 확인했다.

[그림 26] jaibo.eth로 전송된 공격자의 ETH

또한 0.005237578552 ETH가 jaibo.eth ENS를 사용하는 거래소로 추정되는 주소에 전송된 것을 확인했다.

  • 0xadc24d7b630759a3af7f52716d91299c999a2213
[그림 27] jaibo.eth의 트위터 정보 확인

BIG 분석 팀이 확인한 자료에 따르면 jaibo.eth 주소의 소유자는 YouTube 사용자 "Patrick Collins"의 "Solidity, 블록체인 개발 및 스마트 계약 배우기"의 일환으로 강좌 챌린지에 참여했다고 하며, https://www.foundrymints.xyz/ 사이트에서 주소와 트위터 ID를 확인할 수 있다. 트위터 프로필은 https://twitter.com/ iafhurtado이다.

[그림 28] jaibo.eth 프로필

추가로 jaibo.eth ENS 프로필을 확인한 결과 owner가 jaibo.argent.xyz 임을 알 수 있었다.

[그림 29] jaibo.argent.xyz 프로필

jaibo.argent.xyz ENS 프로필을 확인한 결과 parent가 argent.xyz인 것을 알 수 있었다.

[그림 30] argent.xyz

argent.xyz는 DeFi와 NFT를 위한 이더리움 지갑 서비스로 확인되었는데, argent.xyz와 jaibo.eth 주소의 관계 및 공격자와 jaibo.eth의 관계는 정확하게 해석되지 않았다.

5-4) 공격자 주소 최신 상태

[그림 31] 공격자 주소에서 발생한 마지막 트랜잭션

공격자 주소에는 2023년 9월 16일 최초 트랜잭션이 발생했고, 트랜잭션이 지속적으로 발생하다가 2023년 10월 20일을 마지막으로 더 이상 발생하고 있지 않다.

[그림 32] 공격자 주소에서 발생하는 많은 pending 트랜잭션

그리고 수 많은 pending 트랜잭션이 지속적으로 발생하고 있음을 확인했다. 계속해서 발생하는 pending 트랜잭션이 블록으로 등록되지 않는 것은 Gas Fee가 낮아서 그런 것으로 보인다. 첫 트랜잭션 발생일부터 꾸준히 트랜잭션이 발생하고 있던 주소에서 특정 시점을 마지막으로 중단된 것은 피해자가 더 이상 발생하지 않는 것일 수도 있지만, Sweeper Bot의 동작이 멈췄거나 공격자에게 어떠한 사정이 있는 것일지도 모른다.


6. 요약 및 결론

6-1) 요약

  • 공격자는 PyPI를 통해 암호화폐 지갑의 중요 정보를 훔치는 Stealer 기능이 포함된 파이썬 패키지를 암호화폐 지갑의 Vault Data 복호화 패키지로 가장해 배포했다.
  • 공격자가 배포한 패키지로 지갑 데이터를 복호화하면, 복호화된 니모닉 시드 등이 공격자 서버로 전송된다.
  • 공격자는 전송 받은 니모닉 시드에서 파생될 수 있는 암호화폐 주소를 Sweeper Bot에 등록한다.
  • Sweeper Bot에 감시되는 주소로 ETH가 전송되는 트랜잭션이 메모리 풀에 올라오면, Sweeper Bot은 피해자의 키를 이용해 공격자 주소로 자금을 전송하는 트랜잭션을 추가로 생성한다.
  • 공격자는 획득한 자금 중 USDC 토큰을 Binance, OK X 거래소에서 현금화한 것으로 추정되므로 수사기관 공조를 통해 신상을 확보할 수 있는 가능성이 존재한다. 하지만 공격자가 러시아어를 사용하는 것으로 보아 검거로 이어지는 것은 어려울 수 있다.
  • 공격자가 배포한 악성 파이썬 패키지를 이용한 것 외에도 다른 수단으로 인한 피해자가 다수 있는 것으로 보인다.
  • 공격자 주소에서는 2023년 10월 20일을 마지막으로 더 이상 트랜잭션이 발생되고 있지 않으며, 원인은 확인되지 않았다.

6-2) 결론

  • PyPI에 패키지를 업로드 하는 것은 별도의 인증 없이 간단한 회원가입만으로도 가능하기 때문에 악성 패키지에 의한 피해를 줄이기 위해서는 코드 검증 절차가 필요할 것 같다.
  • hashdecrypt 패키지를 사용 중이라면 패키지를 삭제하거나, 데이터를 전송하는 함수를 삭제하자.
  • 지갑 복호화와 같이 중요 데이터를 다루는 패키지를 설치할 때는 수상한 기능을 하는 코드가 없는지 점검할 필요가 있다.


7. PyPI 회원가입 시 2FA 추가 예정

[그림 33] PyPI 신규 사용자 등록 일시 중단

악의적인 사용자 및 프로젝트의 양이 PyPI 관리자가 감당할 수 없을 정도로 증가해 신규 사용자 가입이 중단되었습니다. (2023-12-27 20:11 UTC)

[그림 34] PyPI 2FA 인증 추가 예정

2024년 1월 1일부터 PyPI 회원가입 시 2단계 인증을 설정하도록 바뀔 예정입니다.

You've successfully subscribed to PLAINBIT
Great! Next, complete checkout to get full access to all premium content.
Error! Could not sign up. invalid link.
Welcome back! You've successfully signed in.
Error! Could not sign in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.