What a Job Take-Home Taught Me About npm Supply-Chain Attacks
채용 과제에서 발견한 npm 공급망 공격 — 정상으로 보이던 가짜 기업의 함정
개발자가 실제 채용 과제를 받는 과정에서 위장 기업이 심어둔 악성 npm 패키지를 발견한 사례입니다. 의존성 트리에 숨어든 transitive dependency가 C2 서버와 통신하며 임의 JavaScript를 실행할 수 있는 구조였고, package.json만으로는 감지할 수 없었습니다. npm install 전에 저장소 자체를 신뢰할 수 없는 소프트웨어로 취급해야 한다는 경고를 담고 있습니다.
- 주제
본문
한 개발자가 Antfarm DAO라는 회사로부터 풀스택 코딩 과제를 받았습니다. 처음에는 회사와 채용 과정 모두 진짜처럼 보였습니다. 회사는 theantfarm.tech라는 깔끔한 웹사이트와 dev.to 계정을 갖고 있었고, 정상적인 엔지니어링 채용 절차처럼 보였습니다. 과제를 보낸 담당자도 온라인상에서 확립된 프로필을 가진 인물처럼 보였고, 커뮤니케이션은 전문적이었으며 과제 자체도 평범한 1시간짜리 엔지니어링 작업처럼 보였습니다.
의존성 트리에서 드러난 진짜 목적
그런데 저장소의 의존성 트리를 살펴보니 [email protected]이 @aaron205whitmore/[email protected]를 끌어오고 있었고, 두 번째 패키지에 악성 코드가 들어 있었습니다. 이 패키지는 153.75.81.2:1224 주소와 통신하고, base64로 인코딩된 페이로드를 받아 new Function('require', dynamicCssRules)(require) 형태로 Node의 require를 넘겨 실행할 수 있었습니다. 즉 임의의 원격 JavaScript 실행이 가능한 구조였습니다. 작성자가 복구한 1.0.2 패키지의 SHA-1 해시는 183f39ef3531ad688f168c81e25b528b55630d67로 기록되어 있습니다.
npm install을 실행한다는 것은 전체 의존성 그래프를 신뢰한다는 뜻입니다. 개발자가 그 악성 패키지 이름조차 들어본 적이 없더라도 transitive dependency를 통해 도착할 수 있습니다. 작성자는 package.json만 보지 말고, 낯선 저장소에서는 npm ls로 의존성 목록을 확인하고 특히 비공개 스코프(private scoped) 의존성을 조사하라고 조언합니다. 이 저장소에는 npm 인증 토큰까지 포함되어 있었는데, 비공개 의존성과 저장소가 제공하는 npm 크리덴셜의 조합은 설치 전에 반드시 조사해야 할 대상이라고 지적합니다.
악성 코드는 postinstall 없이도 실행된다
중요한 포인트는 패키지에 수상한 postinstall 스크립트가 없더라도 라이브러리가 import되거나 사용되는 시점에 악성 코드가 실행될 수 있다는 것입니다. 즉 '악성 설치 스크립트가 없음'은 '안전한 패키지'를 의미하지 않습니다.
침해 흔적 조사 절차
작성자는 악성 패키지를 식별한 뒤 애플리케이션 실행을 중단하고 자신의 머신을 조사했습니다. sudo lsof -i -n -P | grep 153.75.81.2로 활성 연결을 확인하고, macOS에서는 log show --predicate 'eventMessage contains "153.75.81.2"' --last 24h로 로그를 검색했으며, ps aux | grep -E '[n]ode|[n]pm'으로 실행 중인 Node/npm 프로세스를 확인하고 npm 캐시와 패키지 내용물을 조사했습니다. C2 통신이나 지속성(persistence)의 증거는 발견하지 못했지만, 이런 확인만으로 연결이 한 번도 일어나지 않았다고 증명할 수는 없다고 덧붙입니다.
모든 것이 정상으로 보였다는 것이 문제
이 사건에서 흥미로운 부분은 악성 npm 패키지 자체보다, 사전에 모든 것이 얼마나 정상적으로 보였느냐입니다. 그럴듯한 회사 웹사이트, 전문적인 커뮤니케이션, GitHub 접근 권한, 현실적인 기술 과제, 확립된 것처럼 보이는 온라인 존재감이 모두 경계심을 낮춥니다. 작성자는 이제 take-home 과제의 저장소 자체를 신뢰할 수 없는 소프트웨어로 취급하며 '먼저 조사하고, 나중에 설치(inspect first → install later)' 원칙을 따른다고 합니다. 마지막으로 @aaron205whitmore/postcss-animate-utils, animatecss-tailwind-adapter, 153.75.81.2, antfarm-tech/proof_of_dev 같은 식별자를 접한 사람이 있다면 증거를 비교해보고 싶다며 IoC(Indicator of Compromise)를 공유했습니다.
원문: dev.to / 번역·요약: Trawling