1만 개의 GitHub 저장소가 악성코드를 배포하고 있었다: 플랫폼 신뢰의 역설
1만 개의 GitHub 저장소가 악성코드를 배포하고 있었다: 플랫폼 신뢰의 역설
GitHub은 개발자에게 안전한 공간인가, 아니면 신뢰라는 이름으로 포장된 공격 벡터인가?
도입
오픈소스 생태계에서 GitHub의 위상은 단순한 코드 호스팅 서비스를 넘어선다. 개발자들은 낯선 저장소의 코드를 내려받을 때도 “GitHub에 있으니 어느 정도는 괜찮겠지”라는 암묵적 신뢰를 갖는다. 이 신뢰는 플랫폼이 수십 년에 걸쳐 축적한 평판에 기반하며, 동시에 가장 위험한 취약점이 된다.
2026년 6월, 보안 연구자 한 명이 자신의 저장소가 대규모로 복제되는 것을 발견했다. 처음에는 일반적인 포크처럼 보였지만 자세히 살펴보자 패턴이 달랐다. 검색 결과에 등장한 클론 저장소들은 공식 릴리스처럼 보이는 zip 아카이브를 제공하고 있었고, 그 안에는 LuaJIT 기반 트로이목마가 숨겨져 있었다. 연구자가 범위를 넓혀 분석한 결과는 더 충격적이었다. GitHub 전체에서 동일한 패턴으로 운영되는 저장소가 무려 1만 개에 달했다. 자주 업데이트되는 저장소 4만 개 중 25%가 악성코드 배포에 활용되고 있었던 것이다.
이 사건은 단순한 악성코드 유포 사례가 아니다. 개발자라는 특정 집단을 정밀하게 겨냥하고, GitHub이라는 신뢰 인프라를 무기화했으며, 플랫폼의 대응 메커니즘이 얼마나 취약한지를 적나라하게 드러냈다. GitHub은 이 사실을 인지한 뒤에도 자동화된 탐지 시스템을 가동하지 않았고, 신고된 저장소만 2주 후에 제거했다. 그 사이 새로운 저장소들은 계속 생겨났다.
공격의 해부: 커밋 조작과 탐지 우회의 정교한 설계
이 캠페인의 기술적 정교함은 여러 층위에서 확인된다. 공격자들은 GitHub의 탐지 알고리즘과 보안 도구의 맹점을 정확히 이해하고 그 사이를 파고들었다.
커밋 기록의 지속적 덮어쓰기
가장 눈에 띄는 특징은 커밋 패턴이다. 각 저장소는 하루에 1~24회에 걸쳐 README.md를 업데이트하는 커밋을 생성한다. 그런데 각각의 커밋은 이전 커밋을 삭제한다. 즉, 저장소의 git 히스토리에는 항상 최신 커밋 하나만 존재한다. 이 기법은 두 가지 목적을 동시에 달성한다. 첫째, git 히스토리를 분석해서 이상 활동을 탐지하는 도구를 무력화한다. 과거 커밋이 없으니 비교 대상 자체가 사라진다. 둘째, GitHub의 최근 활동 기반 노출 알고리즘을 역이용한다. 잦은 커밋은 저장소를 “활발하게 유지관리되는 프로젝트”처럼 보이게 만들고, 검색 결과에서 더 높은 가시성을 확보하게 해준다.
모든 커밋 메시지는 “Update README.md”로 통일되어 있다. 이는 의도적인 선택으로 보인다. GitHub 내부의 이상 탐지 시스템이 커밋 메시지를 분석한다면, “Update README.md”는 지극히 정상적인 메시지이므로 필터를 통과할 가능성이 높다.
가짜 기여자와 합법성 위장
저장소에는 다수의 가짜 기여자 계정이 추가되어 있다. GitHub에서 저장소를 볼 때 기여자 목록은 신뢰성을 나타내는 주요 신호 중 하나다. “여러 사람이 함께 만든 프로젝트”라는 인상은 단독 계정이 운영하는 저장소보다 더 정당해 보인다. 공격자들은 이 심리를 이용했다.
또한 이 저장소들은 GitHub의 포크 판별 로직을 피하면서도 클론된 콘텐츠를 포함하고 있다. 포크로 분류되면 원본 저장소와의 관계가 명시되어 의심을 살 수 있다. 독립적인 저장소처럼 보이면서 합법적인 오픈소스 프로젝트의 외관을 가장하는 전략이다.
VirusTotal 우회의 치밀함
악성 파일의 배포 방식도 교묘하다. 연구자가 zip 아카이브를 VirusTotal에 업로드했을 때 탐지 결과는 0이었다. 그러나 아카이브 내부의 파일을 직접 스캔하자 악성코드임이 확인되었다. 이 차이는 단순한 우연이 아니다.
zip 아카이브로 패키징하면 대부분의 자동화된 보안 스캔 도구가 내부를 깊이 들여다보지 않거나, 들여다보더라도 해시 비교에 실패한다. 아카이브 자체는 알려진 악성 해시와 일치하지 않으므로 클린으로 판정된다. GitHub에서 zip 파일을 직접 배포하는 방식은 이 우회 기법을 완벽하게 활용한다. 사용자가 “릴리스 파일”을 내려받는 행위가 자연스러운 워크플로의 일부이기 때문에 의심을 덜 살 뿐 아니라, 보안 도구의 검사 범위 밖에 놓이기도 한다.
악성 아카이브의 구성
내려받은 zip 아카이브 내부에는 네 가지 파일이 포함되어 있다. Application.cmd라는 명령 스크립트가 실행 진입점 역할을 하며, 실행 파일, 데이터 파일, DLL 라이브러리가 함께 패키징되어 있다. 이 구성은 정상적인 소프트웨어 설치 패키지와 외관상 구분이 어렵다. 사용자가 오픈소스 도구를 설치한다고 믿고 Application.cmd를 실행하는 순간, LuaJIT 트로이목마가 시스템에 침투한다.
표적의 정밀함: 인기 없는 검색어 공략
공격자들은 GitHub에서 인기 있는 저장소를 클론하지 않았다. 대신 새로 생성된 저장소들을 복제했다. 이 선택은 전략적으로 정확하다. 인기 저장소를 클론하면 원본과의 비교가 쉽고, 원본 커뮤니티에 의해 빠르게 신고될 가능성이 높다. 반면 새로 생성되거나 잘 알려지지 않은 저장소를 클론하면 비교 대상이 없어 탐지가 어렵고, 동시에 “특정 기능을 가진 도구”를 검색하는 사용자에게 상위 결과로 노출될 수 있다. 구체적인 기술 용어나 조합어로 검색하는 개발자일수록 이 덫에 빠질 가능성이 높다.
일부 저장소는 12개월 이상 활성 상태를 유지하면서도 탐지되지 않았다. 1년이 넘도록 악성코드를 배포한 저장소가 GitHub에 존재할 수 있었다는 사실은, 이 공격이 단발성 실험이 아닌 체계적으로 운영된 장기 캠페인이었음을 보여준다.
GitHub의 대응 실패: 신고 의존 모델이 드러낸 플랫폼 책임의 공백
연구자가 발견을 GitHub에 보고했을 때, 플랫폼의 대응은 기대에 크게 못 미쳤다. GitHub은 신고된 저장소를 제거하는 데 약 2주가 걸렸고, 그것도 명시적으로 신고된 저장소만 삭제했다. 연구자가 확인한 1만 개의 저장소 전체를 자동으로 탐지하거나 제거하는 조치는 취해지지 않았다. 제거된 자리에는 즉시 새로운 저장소들이 생겨났다.
500억 개 저장소와 자동화 탐지의 부재
GitHub에는 현재 5억 개가 넘는 저장소가 존재한다. 이 규모에서 수동 검토는 현실적으로 불가능하다. 그럼에도 GitHub은 이 캠페인에서 보여준 패턴 — 하루 수십 회의 커밋, 단일 커밋 히스토리, 일괄적인 “Update README.md” 메시지, 가짜 기여자, zip 아카이브를 통한 바이너리 배포 — 을 자동으로 탐지하는 시스템을 운영하지 않고 있다.
이는 기술적 불가능의 문제가 아니다. 이 패턴들은 충분히 구조화되어 있어 규칙 기반 탐지 시스템으로도 상당 부분 걸러낼 수 있다. 하루에 20회 이상 README만 수정하는 저장소, git 히스토리를 지속적으로 덮어쓰는 저장소, 특정 기간 내에 대량의 zip 아카이브를 게시하는 신규 계정. 이런 신호들을 조합하면 의심 저장소를 추려내는 것이 불가능하지 않다.
신고 의존 모델의 구조적 한계
GitHub의 현행 대응 체계는 커뮤니티 신고에 의존한다. 이 모델에는 근본적인 비대칭이 존재한다. 공격자는 자동화된 스크립트로 저장소를 대량 생성하고 관리할 수 있지만, 피해자인 개발자와 연구자는 개별적으로 저장소를 발견하고 수동으로 신고해야 한다. 공격의 속도와 신고의 속도가 구조적으로 불균형하다.
더 심각한 문제는 신고의 낮은 성공률이다. 실제로 악성코드를 내려받은 개발자 중 GitHub에 신고하는 비율은 극히 낮다. 대부분은 감염 사실 자체를 인지하지 못하거나, 인지하더라도 어디에 신고해야 하는지 모르거나, 신고해도 효과가 없을 것이라고 판단한다. 이 캠페인이 12개월 이상 지속될 수 있었던 이유 중 하나도 이 신고 공백이다.
플랫폼 책임론의 부상
GitHub의 대응은 플랫폼 책임에 관한 근본적인 질문을 제기한다. 이 캠페인의 패턴은 충분히 탐지 가능했다. 연구자 한 명이 파악한 것을 플랫폼이 파악하지 못한 것은 능력의 문제가 아니라 의지와 자원 배분의 문제다.
물론 모든 콘텐츠를 사전 검열하는 것은 오픈 플랫폼의 성격과 충돌한다. 그러나 명확한 악성 패턴에 대한 자동화된 사후 탐지와 신속한 제거는 다른 문제다. 이메일 서비스 제공업체들이 스팸과 피싱 메일을 자동으로 걸러내듯, GitHub도 명백한 악성 저장소 패턴을 탐지하고 자동으로 격리하는 시스템을 구축할 수 있고 구축해야 한다.
Microsoft가 GitHub을 인수한 이후 보안 투자가 증가한 것은 사실이다. 그러나 이 사건은 그 투자가 아직 플랫폼 수준의 악성코드 캠페인 탐지에는 도달하지 못했음을 보여준다.
개발자 보안의 새로운 현실: 공급망 공격과 오픈소스 신뢰의 재정립
이 사건이 보안 커뮤니티에서 주목받는 이유는 피해 규모만이 아니다. 공격의 표적이 일반 사용자가 아닌 개발자였다는 점, 그리고 개발자의 신뢰 모델을 정밀하게 겨냥했다는 점이 핵심이다.
개발자를 노리는 이유
개발자는 일반 사용자보다 훨씬 가치 있는 표적이다. 개발자의 시스템에는 소스 코드, 클라우드 서비스 인증정보, API 키, 데이터베이스 접근 권한이 존재한다. 개발자의 시스템을 장악하면 단순한 개인 피해를 넘어 그가 관리하는 서비스와 고객 데이터 전체로 공격 범위가 확장될 수 있다. CI/CD 파이프라인에 침투하면 배포되는 소프트웨어 자체가 오염된다.
이것이 소프트웨어 공급망 공격이다. 개발자를 감염시키는 것은 그 개발자가 만드는 소프트웨어의 모든 사용자를 잠재적 피해자로 만드는 것과 같다.
오픈소스 신뢰 모델의 취약성
오픈소스 문화에는 코드를 공개하고 공유한다는 근본적인 신뢰가 내재되어 있다. “소스를 볼 수 있으니 안전하다”는 인식은 부분적으로는 맞지만, 이 캠페인은 그 신뢰의 허점을 찌른다. 개발자들은 코드의 소스를 확인하는 대신 플랫폼의 신뢰성에 의존하는 경향이 있다. GitHub에 있다는 사실만으로 신뢰 기준선이 형성된다.
실용적 대응 지침으로는 다음을 들 수 있다. 첫째, GitHub 검색을 통해 찾은 저장소의 zip 아카이브를 실행하기 전에 반드시 소스 코드를 직접 검토한다. 둘째, 저장소의 git 히스토리가 비정상적으로 짧거나 단일 커밋만 존재한다면 의심한다. 셋째, 공식 배포 채널(패키지 관리자 등)을 통해 검증된 소프트웨어만 사용한다. 넷째, 낯선 zip 아카이브를 실행할 때는 격리된 환경을 사용한다.
향후 전망: 패턴의 진화
이 캠페인이 노출된 이후 동일한 패턴이 그대로 유지될 가능성은 낮다. 공격자들은 이미 탐지를 피하기 위해 전술을 변경하고 있을 것이다. 커밋 패턴을 불규칙하게 만들거나, 더 다양한 커밋 메시지를 사용하거나, 가짜 이슈와 풀 리퀘스트를 추가해 합법성을 강화하는 방향으로 진화할 수 있다. GitHub npm 패키지 레지스트리나 GitHub Actions 마켓플레이스를 통한 유사 공격도 이미 보고된 바 있으므로, 공격 표면은 단순히 저장소 검색에 그치지 않는다.
결론
1만 개의 악성 저장소 캠페인은 몇 가지 불편한 진실을 재확인한다. 플랫폼의 신뢰는 보안의 증거가 아니다. GitHub에 있다는 사실이 소프트웨어의 안전성을 보장하지 않는다. 오픈소스 생태계의 개방성은 그 자체로 강점이지만, 그 개방성을 악용하는 정교한 캠페인이 존재한다는 현실도 함께 받아들여야 한다.
플랫폼 측면에서 GitHub의 대응은 미흡했다. 신고 의존 모델은 규모화된 자동화 공격 앞에서 충분하지 않다. 5억 개의 저장소를 보유한 플랫폼이라면, 명확한 악성 패턴을 감지하는 자동화 시스템을 구축하는 것은 선택이 아닌 의무다.
개발자 개인의 측면에서는 “어디에서 왔는가”보다 “무엇인가”를 중심으로 평가하는 습관이 필요하다. 코드의 출처보다 코드 자체를 검토하고, 바이너리 실행 전 격리 환경을 사용하며, 공식 패키지 채널을 우선하는 원칙은 공급망 공격의 파고를 낮추는 가장 실질적인 방어다.
신뢰는 구축하는 데 수십 년이 걸리고 무너지는 데 한 번의 감염으로 충분하다.
출처
- Orchid Files, “I found 10k GitHub repositories distributing Trojan malware”, 2026-06-18, https://orchidfiles.com/github-repositories-distributing-malware/
- Hacker News discussion, score 984, 248 comments, 2026-06-18, https://news.ycombinator.com/item?id=44300000