JavacDoor는 컴파일 시점에 실행되는 Maven 악성 프로그램입니다.

JavacDoor: 설치 과정이 아닌 컴파일 과정에서 실행된 Maven 아티팩트

TL; DR

Maven Central에 게시된 아티팩트 io.github.davidtimur:c2-lab 총 9개의 릴리스가 배포되었으며, 그중 8개는 해당 jar 파일을 어노테이션 프로세서 경로에 배치한 하위 프로젝트의 컴파일 중에 원격 액세스 페이로드를 실행했습니다. 애플리케이션 코드는 해당 jar 파일을 가져올 필요가 없었고, 소스 코드에서 참조할 필요도 없었습니다. 설치 스크립트도 없었고, 설치 후 작업도 없었으며, 도구가 검사할 수 있는 어떤 종류의 라이프사이클 후크도 없었습니다.

실행 벡터는 JAR 파일 내부에 있는 46바이트 크기의 단일 파일입니다. 이 파일은 Java 서비스 제공자 등록 파일로, 특정 인터페이스를 구현하는 클래스의 이름을 지정합니다. javax.annotation.processing.Processor자바 컴파일러는 이러한 등록을 자동으로 찾아냅니다. 일단 발견되면, javac 컴파일 과정의 정상적인 부분으로 클래스를 인스턴스화하고 실행합니다. 즉, 페이로드의 런타임 환경은 빌드 머신이며, 현재 빌드 머신은 본래의 목적대로 작업을 수행하고 있습니다.

9번의 릴리스에 걸쳐 명령 및 제어 채널은 세 번 재구축되었습니다. 처음에는 운영자가 제공하는 콜백 URL을 사용했고, 그 다음에는 ngrok TCP 터널을 통한 역방향 셸을 사용했으며, 마지막으로 경로가 두 번 더 변경된 HTTP 폴링 채널을 사용했습니다. 마지막 릴리스에서는 아무런 기능도 하지 않는 TLS 트러스트 관리자가 JVM 기본값으로 설치되었습니다. 컴파일 기간 동안 모든 인증서를 허용합니다..

해당 아티팩트는 자체 POM 파일에 "C2 Lab Payload"라는 문구와 MIT 라이선스를 포함하고 있었습니다. 저희가 관찰한 기간 내내 Maven Central에 등록되어 있었습니다. 이후 해당 부분은 전체와 함께 제거되었습니다. io.github.davidtimur 그룹입니다.

구조: 실행 엔진으로서의 컴파일러

Java의 어노테이션 처리 프레임워크는 라이브러리가 컴파일 시간에 코드를 생성할 수 있도록 존재합니다. Lombok, Dagger, 그리고 수많은 ORM 및 직렬화 도구의 기반이 되는 메커니즘입니다. 프로세서는 JAR 파일 내부에 있는 일반 텍스트 파일을 통해 자신을 알립니다.

META-INF/services/javax.annotation.processing.Processor

영향을 받는 모든 릴리스에서 해당 파일의 내용은 다음과 같습니다(원문 그대로 완전함):

io.github.davidtimur.c2lab.C2Processor

해당 파일은 1.0.1부터 1.0.8 버전까지 바이트 단위로 동일하며, MD5 해시값도 동일합니다. 7d2a08a5c8869a47eea9fa62487dfbe4. 버전 1.0.0에는 해당 기능이 포함되어 있지 않습니다.

인셀덤 공식 판매점인 javac 실행되면 어노테이션 프로세서 경로를 스캔하여 이러한 서비스 항목을 찾고 발견된 항목을 로드합니다. 컴파일되는 프로젝트의 어떤 부분에서도 프로세서를 언급하거나, 어노테이션을 추가하거나, 설정을 구성할 필요가 없습니다. 경로에 존재하기만 하면 충분합니다. 이것이 JavacDoor를 대부분의 툴링이 기반으로 하는 공급망 패턴과 구분 짓는 특징입니다.

무늬트리거다음과 같이 보입니다
npm install hooknpm installscripts.postinstall 매니페스트에서
파이썬 가져오기 시간 페이로드모듈의 첫 번째 가져오기소스 코드의 모듈 수준 문장
자바크도어javac 모든 하위 프로젝트에서서비스 등록 파일 이름

설치 후크는 매니페스트의 선언이며, 매니페스트는 누구나 가장 먼저 읽는 부분입니다. 가져오기 시 전달되는 페이로드는 적어도 읽기 쉬운 소스 코드에 존재합니다. 하지만 서비스 등록은 그렇지 않습니다. 서비스 등록은 파일 이름과 클래스 이름을 지정하는 한 줄의 코드로 이루어져 있으며, 실제 동작은 컴파일된 바이트코드에 담겨 다른 디렉터리에 있습니다.

페이로드 클래스 자체는 셸을 실행하여 호스트 정찰을 수행합니다. 컴파일된 클래스의 상수 풀에는 다음이 포함됩니다. / 빈 / SH, WHOAMI, uname -a, pwd 유닉스 경로에서 그리고 태스크 목록 Windows 경로에서, 함께 redirectErrorStream 자식 프로세스의 오류 출력을 캡처된 스트림에 병합합니다. 두 개의 JSON 템플릿이 호스트에서 결과를 전달합니다. 하나는 등록 비콘입니다.

{"host":"%s","os":"%s","user":"%s","dir":"%s"}

다음으로부터 인구가 늘어남 호스트 이름 명령 및 os.name, user.name예산 및 사용자.dir 시스템 속성 및 결과 콜백:

{"version":"%s","host":"%s","time":"%s","output":"%s"}

컴파일러 자체 출력에 남겨진 진행 표시기는 이례적으로 솔직합니다. [C2] 컴파일 시간 실행 완료, [C2] 콜백 전송됨 → HTTP , [C2] 셸이 연결되었습니다 .

9개의 릴리스, 3세대의 C2

이 릴리스들은 하나의 페이로드를 아홉 번 복사한 것이 아닙니다. 이는 반복 작업 로그이며, 순서대로 읽어보면 전달 경로는 고정된 채로 채널이 재구축되는 과정을 보여줍니다.

해제컴파일 시 자동 실행채널
1.0.0아니요 (서비스 파일이 없습니다)운영자가 제공한 콜백 URL만 해당됩니다.
1.0.1예 (벡터가 도입되었습니다)운영자가 제공한 콜백 URL
1.0.2예리버스 셸, ngrok TCP 터널
1.0.3예HTTP 채널, /등록 /cmd /out
1.0.4예HTTP 채널, /등록 /cmd /out
1.0.5예HTTP 채널, /등록 /폴 /아웃
1.0.6예HTTP 채널, /등록 /폴 /아웃
1.0.7예HTTP 채널, /등록 /cmd
1.0.8예HTTP 채널 + TLS 유효성 검사 비활성화됨

그 표에서 주목할 만한 세 가지 세부 사항이 있는데, 각각의 세부 사항이 유물 세트를 해석하는 방식을 바꾸기 때문입니다.

벡터는 1.0.2가 아니라 1.0.1에 도달합니다. 릴리스 1.0.0은 동일한 정찰 및 콜백 로직을 포함하지만 서비스 파일과 어노테이션 처리 관련 임포트가 없습니다. 따라서 호출이 있을 때만 실행됩니다. 1.0.1 버전부터는 서비스 파일이 포함되고 컴파일된 클래스가 임포트됩니다. javax.annotation.processing.SupportedSourceVersion두 가지 유료 버전(1.0.0과 1.0.2)을 비교하는 모든 평가는 무언가 변경되었다는 결론을 내리지만, 정확히 어디에서 변경되었는지를 잘못 파악합니다.

리버스 셸은 단 하나의 릴리스에만 존재합니다. 버전 1.0.2에는 다음 내용이 포함되어 있습니다. 0.tcp.ngrok[.]io로그 라인 [C2] 셸이 0.tcp.ngrok[.]io:19823에 연결되었습니다.인터랙티브 배너 c2-쉘그리고 프레임 종료 문자 __끝__1.0.3 버전 이후부터는 이러한 기능이 전혀 없으며 대신 HTTPS 호스트에 연결됩니다. 티켓이 발부된 릴리스만 명시한 삭제 요청은 더 이상 작동하지 않는 TCP 엔드포인트를 지적하는 반면, 이후 6개 릴리스에 존재하는 활성 HTTP 채널은 언급하지 않게 됩니다.

최종 릴리스에서는 전송 검증 기능이 제거되었습니다. 버전 1.0.8에서는 구현 클래스가 추가되었습니다. javax.net.ssl.X509TrustManager 인증서 확인 방식이 아무런 효과가 없는 반면, 항상 참인 호스트 이름 검증기는 등록을 통해 작동합니다. setDefaultHostnameVerifier및 트러스트올 이 루틴은 두 인증서를 모두 JVM의 기본값으로 설치합니다. 그 결과, 해당 컴파일 과정의 나머지 동안 JVM은 페이로드 자체 트래픽뿐만 아니라 빌드 과정에서 TLS를 통해 수행하는 모든 작업에 대해 모든 호스트의 인증서를 허용하게 됩니다.

HTTP 채널은 이를 사용하는 6개 릴리스 모두에서 단일 호스트를 통해 전달됩니다. 테이블풀-열정-미친.ngrok-free[.]dev모든 요청에는 헤더가 포함됩니다. ngrok-skip-browser-warning이는 무료 ngrok 터널이 브라우저에 제공하는 중간 페이지를 억제합니다. 경로 설정은 릴리스마다 변경됩니다. /밖으로 1.0.7 버전에서 사라집니다. /cmd 번갈아 가며 /투표 하지만 주최자는 절대 바뀌지 않습니다.

자기 제외는 다음과 같습니다.

버전 1.0.1 이후의 모든 POM 파일은 아티팩트 자체 빌드에 대한 컴파일러 인수를 설정합니다.

-proc:없음

해당 플래그는 어노테이션 처리를 비활성화합니다. 여기서는 그 효과가 미리 나타납니다.cis예: 프로세서가 포함된 프로젝트 자체가 컴파일되면 프로세서가 실행되지 않습니다.

이 플래그는 아주 일반적인 용도로 사용됩니다. 어노테이션 프로세서를 포함하는 프로젝트는 부트스트랩 과정에서 해당 프로세서를 자체적으로 적용하지 않도록 해야 하는 경우가 많으며, 빌드 도구 문서에서도 바로 이를 권장합니다. 이 플래그 자체만으로는 아무것도 증명하지 못합니다.

하지만 프로세서의 동작과 함께 고려해 보면, 이는 특정한 비대칭성을 설명합니다. 즉, 코드는 아티팩트를 컴파일하는 모든 사용자의 컴퓨터에서 실행되지만, 아티팩트를 빌드하는 컴퓨터에서는 실행되지 않습니다. 이 플래그는 서비스 파일이 도입된 1.0.1 릴리스부터 이후 모든 릴리스에 나타납니다. "컴파일 시간 실행이 시작되는 릴리스"와 "로컬에서 컴파일 시간 실행이 비활성화되는 릴리스" 사이의 상관관계는 아티팩트 세트에서 가장 유용한 분석 신호이며, 아무것도 디컴파일하지 않고도 일반 텍스트 POM에서 확인할 수 있습니다.

우리는 그 효과를 기록하고 거기서 멈춥니다. 유물 어디에도 플래그가 설정된 이유에 대한 단서는 없습니다.

추가로 언급할 만한 메타데이터가 하나 더 있는데, 이는 주로 불필요한 부분을 제거하기 위한 것입니다. POM 파일은 프로젝트 이름을 "C2 Lab Payload"로 지정하고, "C2 연구실 페이로드 아티팩트"라고 설명하며, MIT 라이선스를 부여합니다. 이와 같은 자체 라벨링은 때때로 해당 패키지가 연구용임을 입증하는 증거로 제시되기도 합니다.cis실제 위협이라기보다는 오히려 위협으로 간주하는 경우가 있으며, 때로는 그러한 해석이 옳을 수도 있습니다. 예를 들어, 접근 가능한 인프라가 없는 선언된 카나리 샘플은 이 경우와는 다른 대상입니다. 하지만 여기서는 해당되지 않습니다. 공개 저장소에 게시되어 모든 사용자가 접근할 수 있고, 9번의 릴리스에 걸쳐 3번이나 재구축된 아웃바운드 인프라를 가진 기능성 임플란트는 메타데이터의 명칭과 관계없이 실제 기능을 수행하는 것입니다. POM 파일의 이름은 해당 파일을 컴파일하는 시스템에서 발생하는 일에 아무런 영향을 미치지 않습니다.

빌드 머신용 표시기

빌드 호스트가 이 아티팩트를 대상으로 컴파일한 경우, 증거는 디스크에 영구적으로 저장되는 것이 아니라 빌드 로그와 네트워크 원격 측정 데이터에 남습니다. 페이로드는 컴파일러 프로세스 내부에서 실행되고 프로세스와 함께 종료됩니다.

JAR 파일 또는 로컬 저장소 캐시에

  • META-INF/services/javax.annotation.processing.Processor 명명 io.github.davidtimur.c2lab.C2Processor
  • 서비스 파일 md5 7d2a08a5c8869a47eea9fa62487dfbe4
  • 수업은 다음과 같습니다. io/github/davidtimur/c2lab/: C2Processor, C2Task, Task그리고 1.0.8 버전에서는 내부 클래스가 있습니다. Task$1

빌드 출력에서

  • [C2] compile-time execution complete
  • [C2] callback sent → HTTP
  • [C2] callback failed:
  • [C2] shell connected to
  • 인터랙티브 배너 c2-shell프레임 종료 문자 __END__

프로세스 원격 측정

  • javac 부모로서 /bin/sh -c (유닉스) 또는 윈도우 명령 인터프리터
  • 자식 명령 whoami, uname -a, pwd (유닉스) 또는 tasklist (Windows) 컴파일 단계에 의해 상위 항목으로 지정됨

네트워크 원격 측정에서

  • 아웃바운드 TCP 0.tcp.ngrok[.]io:19823 (버전 1.0.2)
  • HTTPS로 tableful-fervor-crazed.ngrok-free[.]dev경로 /register, /cmd, /poll, /out (버전 1.0.3~1.0.8)
  • 요청 헤더 ngrok-skip-browser-warning: true
  • 일치하는 요청 본문 {"host":...,"os":...,"user":...,"dir":...} or {"version":...,"host":...,"time":...,"output":...}

구성에서

  • 환경 변수 CALLBACK, CALLBACK_URL시스템 속성 callback.url

게시자 메타데이터

  • 그룹 io.github.davidtimur출판사 주소 davudboi999@gmail[.]com서명 키 E520C345EF94423D

1.0.8 릴리스를 실행한 빌드는 추가적인 검사가 필요합니다. 해당 릴리스에서는 JVM 전체에 기본적으로 허용적인 트러스트 관리자가 설치되므로, 동일한 JVM에서 이후에 이루어지는 모든 TLS 연결(종속성 해결, 아티팩트 업로드, 배포 단계)은 인증서 유효성 검사 없이 진행됩니다. 따라서 해당 기간 동안의 트래픽은 인증되었다고 가정해서는 안 됩니다.

컴파일된 아티팩트에 서로 다른 스캔 방식이 필요한 이유는 무엇일까요?

JavacDoor는 두 가지 일반적인 가정을 동시에 반박하기 때문에 유용한 테스트 케이스이며, 이러한 실패는 특정 공급업체의 도구에만 국한된 것이 아닙니다.

첫 번째 가정은 위험한 코드는 매니페스트에 스스로를 드러낸다는 것입니다. 공급망 관리 도구의 상당 부분은 제품 수명 주기를 중심으로 구성되어 있습니다. hooksnpm과 PyPI의 경우, 일반적으로 그곳에서 작업이 이루어지기 때문입니다. JavacDoor에는 훅이 없습니다. JavacDoor의 트리거는 이름이 Java 인터페이스이고 내용이 클래스 이름인 서비스 등록 파일입니다. 이를 정적으로 처리하려면 다음과 같이 해야 합니다. META-INF/services/javax.annotation.processing.Processor 그 자체로 실행 진입점으로서, 다음과 동등한 지위를 갖습니다. 설치 후 스크립트를 살펴본 다음 명명된 클래스를 따라 바이트코드로 이동합니다. 생태계는 이러한 형태의 자체 자동 검색 메커니즘을 가지고 있으며, 도구가 이를 진입점으로 열거하는지 여부와 관계없이 각각이 진입점이 됩니다.

두 번째 가정은 문자열이 소스 파일에 존재한다는 것입니다. JAR 파일의 경우 엔드포인트, 셸 명령, JSON 템플릿 및 로그 마커는 모두 상수 풀에 있습니다. .수업 파일들. 텍스트를 검색하는 도구는 아무것도 찾지 못합니다. 문자열이 난독화되어 있어서가 아니라, 텍스트 스캔으로는 파싱할 수 없는 구조화된 바이너리 컨테이너에 담겨 있기 때문입니다. 이 글에 나오는 모든 네트워크 지표는 상수 풀 파싱을 통해 얻어졌습니다. 그중 하나는 완전한 형태의 파일입니다. https:// 클래스 파일 안에 URL이 명확하게 드러나 있지만, JAR 파일의 읽기 가능한 내용을 텍스트 스캔해도 해당 URL은 나타나지 않습니다. 여기에는 암호화를 해제할 필요가 없고, 읽어야 할 컨테이너 형식만 있을 뿐입니다.

두 문제점 모두 형태는 동일합니다. 아티팩트 형식을 정의된 의미 구조를 가진 구조체가 아니라 단순히 파일들의 묶음으로 취급했던 것입니다. 해결책은 그다지 화려하지 않습니다. 컨테이너를 파싱하고, 생태계 자체의 자동 검색 진입점을 열거한 다음, 컴파일된 코드까지 추적하면 됩니다. 특히 빌드 타임 벡터의 경우, 세 번째 검사는 비용이 저렴하면서도 놀랍도록 유용한 진단 도구입니다. 아티팩트가 소비자에게 하는 작업과 스스로 제외하는 작업을 비교하는 것입니다. 컴파일 타임 프로세서를 등록하면서 동시에 자체 빌드에 대한 컴파일 타임 처리를 비활성화하는 아티팩트는 일반 텍스트 POM의 두 줄만으로도 그 자체에 대한 정보를 알려줍니다.

현재 Maven 아티팩트를 사용하는 팀을 위한 세 가지 실질적인 조치는 다음과 같습니다.

  • 어노테이션 프로세서 경로를 실행 경계로 간주하십시오. 해당 위치에 있는 종속성은 빌드에서 코드를 실행합니다. 빌드에서 어노테이션 처리가 필요하지 않은 경우, -proc:없음 이는 여기에서처럼 방어적으로 유용할 수 있습니다. 필요한 경우 컴파일 클래스패스에서 상속받는 대신 프로세서 세트를 명시적으로 고정하십시오.
  • 로그 컴파일러 하위 프로세스. 컴파일 단계에서 셸이 실행되는 것은 대부분의 프로젝트에서 드문 경우이며, 쉽게 경고를 보낼 수 있습니다.
  • 메타데이터를 증거로 취급하지 마십시오. 패키지 이름이나 설명에 "Lab", "test", "payload", "PoC"가 포함되어 있다고 해서 범위가 제한되는 것은 아닙니다. 도달 가능성과 동작 방식이 범위 제한 요소입니다.

해당 유물과 그 전체 무리는 제거되었습니다. 메이븐 저희 보고서에 따라 Central 측에서 아티팩트 경로와 그룹 경로 모두 404 오류를 반환하고, Central 인덱스에서 일치하는 좌표를 찾을 수 없다고 보고했습니다. 이로써 해당 아티팩트는 닫혔습니다. 하지만 벡터는 닫히지 않는데, 이는 Java 컴파일러의 공식 기능으로, jar 파일을 배포하는 모든 사용자가 이용할 수 있습니다.

참고자료

이 게시물은 외부 자료를 인용하지 않았습니다. 모든 결과는 삭제되기 전 Maven Central에서 가져온 9개의 공개된 JAR 파일에 대한 직접적인 정적 분석 결과입니다. 해당 아티팩트의 코드는 어떤 시점에서도 실행되지 않았습니다.

sca-tools-software-composition-analysis-tools
소프트웨어 위험을 우선순위화하고, 해결하고, 보호하십시오.
무료 계정을 만드세요.
신용 카드가 필요하지 않습니다.

소프트웨어 개발 및 제공을 안전하게 보호하세요

Xygeni 제품군과 함께