credential-stealing

1 개의 포스트

gitlab

샤이훌루드 모방 캠페인, PyPI 타이포스쿼팅으로 파이썬 개발자 노려 (새 탭에서 열림)

2026년 6월, GitLab은 PyPI를 대상으로 한 Shai-Hulud 악성코드 모방 공급망 공격을 발견했다. 공격자는 Flask·Requests·NumPy의 오타 패키지와 정상 프로젝트를 변조한 패키지에 설치 시 자동 실행되는 자격 증명 탈취 웜을 삽입했다. 이 웜은 CI/CD와 클라우드 자격 증명을 훔칠 뿐 아니라 GitHub 저장소와 패키지 레지스트리에 악성 코드를 퍼뜨려 추가 감염을 시도한다. ## 공격 대상 패키지와 위장 방식 - 공격 패키지 5개는 모두 PyPI 계정 `elitexp`에서 배포됐다. - 오타 패키지: - `rlask`, `tlask`: Flask 사칭 - `rsquests`: Requests 사칭 - `nhmpy`: NumPy 사칭 - `mflux-streamlit`은 원래 정상적으로 사용되던 프로젝트였지만, 공격자가 버전 `0.0.3`, `0.0.4`를 악성 버전으로 변조했다. - 공격자는 먼저 실제 최신 버전과 동일한 “탐색용” 정상 패키지를 등록한 뒤, 악성 페이로드가 포함된 후속 버전을 배포했다. - 따라서 단순 오타 입력뿐 아니라, 기존 프로젝트의 정상적인 의존성 업데이트도 감염 경로가 될 수 있다. ## `.pth` 파일을 이용한 설치 시 자동 실행 - npm 기반 Shai-Hulud 변종이 `preinstall` 스크립트를 사용한 것과 달리, 이번 공격은 Python의 `.pth` 메커니즘을 악용했다. - Python은 시작 시 패키지에 포함된 `.pth` 파일을 자동 처리하므로, 사용자가 악성 모듈을 직접 import하거나 함수를 호출하지 않아도 코드가 실행될 수 있다. - 예시 파일은 `rlask-setup.pth`이며, 임시 디렉터리의 `.bun_ran` 마커 파일을 확인한 뒤 다음 작업을 수행한다. - GitHub에서 Bun JavaScript 런타임 다운로드 - 패키지에 포함된 약 5MB 크기의 난독화 JavaScript 실행 - 마커 파일을 이용해 반복 실행 방지 - 초기 `rlask` 버전에는 Python 시작 시 자동 import되는 `sitecustomize.py`도 포함되어 `_index.js`를 실행하는 보조 경로가 있었다. - 이후 버전에서는 `.pth` 실행 방식만 남겨 공격 구조를 단순화했다. ## 다층 난독화와 페이로드 구성 - JavaScript 페이로드는 세 단계로 난독화됐다. - 정수 배열에 ROT-N 문자 치환 적용 - AES-128-GCM으로 두 개의 데이터 블록 암호화 - `_0x` 접두사의 변수명 난독화 - 패키지마다 ROT 값이 달랐다. - `rlask`: ROT-13 - `rsquests`: ROT-17 - `tlask`: ROT-25 - 분석 결과: - 첫 번째 블록 약 907바이트: Bun 런타임 다운로드 코드 - 두 번째 블록 약 772KB: Shai-Hulud 자격 증명 탈취 웜 전체 - 두 번째 페이로드에는 2,538개의 하드코딩된 문자열이 포함돼 있었다. - GitLab 연구팀은 실제 실행 없이 정적 분석만으로 페이로드를 복호화했다. ## 광범위한 자격 증명 탈취 웜은 주요 클라우드, CI/CD, 패키지 저장소와 개발 환경을 폭넓게 탐색한다. - GitHub: - `GITHUB_TOKEN`, 개인·Fine-grained 토큰 - OIDC 토큰, 조직·저장소 시크릿 - Actions 아티팩트와 러너 프로세스 메모리 - AWS: - IAM 액세스 키와 시크릿 키 - 세션 토큰, STS 토큰 - IMDS 주소 `169.254.169.254`의 인스턴스 자격 증명 - Secrets Manager와 SSM 파라미터 - Azure 및 GCP: - 클라이언트 시크릿, 관리형 ID 토큰, Key Vault 시크릿 - 서비스 계정 키와 Application Default Credentials - HashiCorp Vault: - `/var/run/secrets/vault-token`, `/etc/vault/token`, `/root/.vault-token` 등 알려진 경로 - API 및 Kubernetes 인증 정보 - 패키지 저장소: - npm, JFrog/Artifactory, PyPI, RubyGems 토큰 - OIDC 토큰 교환 정보 - 기타: - SSH 개인 키 - Kubernetes 서비스 계정 토큰과 kubeconfig - Sigstore OIDC 토큰 및 Fulcio 서명 인증서 - MongoDB, MySQL, PostgreSQL, Redis 접속 문자열과 비밀번호 ## 자격 증명 탈취를 넘어선 자기 전파 - 이 악성코드는 단순한 정보 탈취기가 아니라 훔친 권한을 이용해 다른 환경으로 확산하는 웜이다. - 접근 가능한 GitHub 저장소에 다음 파일을 커밋한다. - `.github/setup.js` - GitHub Actions 워크플로 파일 - 이를 통해 다른 CI 파이프라인에서 악성 코드가 다시 실행되도록 만든다. - `.github/copilot-instructions.md`를 삽입해 AI 코딩 도구의 동작을 오염시키려 한다. - 훔친 레지스트리 토큰으로 PyPI, npm, RubyGems에 추가 악성 패키지를 배포한다. - 자체 호스팅 CI 러너에서는 `sudoers` 규칙을 삽입해 권한 상승을 시도한다. - StepSecurity의 `harden-runner`가 존재하는지 확인하고 탐지되면 동작을 조정한다. ## 공격자와 정상 프로젝트 변조 - PyPI 계정 `elitexp`는 2024년 11월 생성됐으며, 원래 정상 패키지 `mflux-streamlit`을 보유하고 있었다. - 연결된 GitHub 계정은 13년 이상 된 계정으로, 대학 과제와 Laravel 프로젝트 등 다수의 공개 저장소가 있어 신뢰성을 높이는 데 활용됐을 가능성이 있다. - 모든 패키지의 업로드에는 `Bun/1.3.14` 사용자 에이전트가 사용됐다. - 공격자는 악성코드 실행 과정에서도 동일한 Bun 런타임을 다운로드한다. - `mflux-streamlit`의 `0.0.1`, `0.0.2`는 정상 버전이지만, 이후 `0.0.3`, `0.0.4`에는 동일한 `.pth` 드로퍼와 난독화 페이로드가 포함됐다. - 정상 프로젝트의 기존 사용자까지 일반적인 업데이트 과정에서 감염될 수 있다는 점이 전형적인 typosquatting보다 위험하다. ## 실용적인 대응 권고 - `rlask`, `tlask`, `rsquests`, `nhmpy`, `mflux-streamlit`의 악성 버전 설치 여부를 확인하고 즉시 제거한다. - 해당 패키지가 설치된 환경에서는 CI/CD, 클라우드, 패키지 저장소, SSH 관련 자격 증명을 모두 폐기하고 재발급한다. - GitHub 저장소의 워크플로와 `.github/setup.js`, `.github/copilot-instructions.md` 변경 이력을 점검한다. - Python 패키지 설치 시 패키지명뿐 아니라 유지보수자, 배포 이력, 버전 변화, 해시를 검증한다. - CI 환경에서는 최소 권한 토큰, 짧은 수명의 자격 증명, 네트워크 제한, 패키지 허용 목록을 적용하는 것이 안전하다.