
파일리스 x64 어셈블리 C2 프레임워크로, 이중 채널 ICMP/DNS 프로토콜 피보팅, 직접 시스템 콜 실행, ptrace 기반 프로세스 인젝션을 통해 은밀한 명령 실행 및 데이터 유출을 지원합니다.
________ ___ ___ ________ ________ _________ ________ ________
|\ ____\|\ \|\ \|\ __ \|\ ____\|\___ ___\ |\ ____\|\_____ \
\ \ \___| \ \ \\\ \ \ \|\ \ \ \___|\|___ \ \_| \ \ \___|\|____|\ \
\ \ \ __ \ \ __ \ \ \\\ \ \_____ \ \ \ \ \ \ \ ____\_\ \
\ \ \|\ \ \ \ \ \ \ \\\ \|____|\ \ \ \ \ \ \ \___|\____ \ \
\ \_______\ \__\ \__\ \_______\____\_\ \ \ \__\ \ \______\\_________\
\|_______|\|__|\|__|\|_______|\_________\ \|__| \|______\|_________|
\|_________|
파일리스, 순수 x64 어셈블리 C2 임플란트로, 듀얼 채널(ICMP / DNS) 아키텍처 사용. libc 제로. 디스크 제로. 표준 EDR 후크에 보이지 않음.
⭐️ 이 연구가 도움이 되셨다면 별표(Star)를 눌러 개발을 지원해 주시기 바랍니다.
🚨 전체 기술 연구 및 소스 코드: 이 저장소는 요약본입니다. 완전한 아키텍처 분석, W^X 메모리 회피 세부 사항, VTable 구현에 대해서는 공식 프로젝트 페이지를 방문하세요: ICMP-Ghost Technical Breakdown
Ghost-C2는 libc 의존성 없이 순수 x64 Linux 어셈블리로 작성된 명령 및 제어 프레임워크입니다. 모든 작업은 직접 시스템 콜을 통해 이루어집니다. 임포트 테이블, 동적 링커 아티팩트, 디스크 쓰기가 없습니다.
원래는 원시 ICMP 은닉 채널로 구축되었지만, 버전 3.6.2에서는 듀얼 채널 프로토콜 피보팅 아키텍처를 도입했습니다. 운영자는 조용한 ICMP Raw Socket과 회피적인 DNS UDP Tunneling 사이에서 임플란트의 통신 채널을 즉석에서 원활하게 전환할 수 있습니다. 임플란트는 RAM에만 존재하며, 커스텀 ptrace 기반 로더를 통해 실행 중인 시스템 프로세스에 주입됩니다.
이 프로젝트는 사용자 공간의 은닉성과 네트워크 상태 동기화가 커널에 닿지 않고 어디까지 가능한지 탐구하기 위해 구축되었습니다.
┌─────────────────────────────────────────────────────────────┐
│ 운영자 머신 │
│ │
│ ┌──────────────┐ │
│ │ client.asm │ ← 터미널 UI: IP/도메인 + 명령 입력 │
│ │ (운영자 콘솔)│ Rolling XOR로 페이로드 암호화 │
│ │ │ 상태 동기화: ICMP 모드 / DNS 모드 │
│ └──────┬───────┘ │
│ │ │
└──────────┼──────────────────────────────────────────────────┘
│ 채널 1: Raw ICMP (상태 비저장, 포트 없음)
│ 채널 2: DNS UDP 포트 53 (비대칭)
┌──────────┼──────────────────────────────────────────────────┐
│ │ 타겟 머신 │
│ ▼ │
│ ┌──────────────┐ ┌─────────────────────────────────┐ │
│ │ loader.asm │────▶│ sniff.asm (PIC) │ │
│ │ (팬텀 로더) │ │ RAM에만 존재 │ │
│ │ │ │ 호스트 프로세스 내부 │ │
│ └──────────────┘ └────────────────┬────────────────┘ │
│ │ │
│ 1. /proc 스캔으로 대상 PID 찾기 │ ICMP/DNS 수신 │
│ 2. ptrace ATTACH │ 인증 확인 │
│ 3. 강제 원격 mmap (RW) │ 명령 복호화 │
│ 4. PIC 셸코드 주입 │ fork+execve │
│ 5. mprotect → RX │ memfd_create │
│ 6. RIP → 셸코드로 리다이렉트 │ 압축(DPCM-RLE) │
│ 7. ptrace DETACH → 종료 │ 암호화 및 조각화 │
│ │ 응답 전송 │
└─────────────────────────────────────────┼───────────────────┘
│ 암호화된 트래픽
▼
[ client.asm ]
수신 및 검증
페이로드 복호화
압축 해제 (하이브리드)
재조립 및 출력
client.asm — 운영자 콘솔공격자 측 터미널. UI, 동적 메모리 관리, 대상 상태 동기화를 처리합니다. ICMP Echo Request 또는 DNS TXT 쿼리로 패킷을 전송할 수 있습니다. 조각화된 응답을 수신하고, 버퍼 오버플로우를 방지하며, 출력을 재구성합니다. "Active Target Reconnection" 모듈을 통해 분리된 세션을 복구할 수 있습니다.
sniff.asm — PIC 임플란트 에이전트대상에서 실행되는 임플란트입니다. 원시 바이너리(위치 독립, ELF 헤더 없음)로 컴파일되어 임의의 메모리 주소에 주입될 수 있습니다. 운영자의 피벗 명령에 따라 ICMP 스니핑과 UDP DNS 바인딩 사이를 전환하기 위해 내부 VTable을 동적으로 업데이트합니다.
Phantom_Loader/loader.asm — 주입 엔진전달 메커니즘입니다. /proc을 스캔하고 comm 이름으로 대상 프로세스를 찾은 후, 다단계 ptrace 상태 머신을 사용하여 PIC 셸코드를 해당 프로세스에 주입합니다. 주입 후 깔끔하게 종료되며 흔적을 남기지 않습니다.
Ghost-C2 v3.6.2는 운영자가 에이전트를 잃지 않고 네트워크 프로토콜을 핫스왑할 수 있게 합니다. 특정 피벗 명령을 보내면 마스터와 에이전트 모두의 VTable이 동적으로 덮어쓰여집니다:
!D (DNS로 피벗): 두 노드 모두 ICMP 소켓을 닫고 UDP 포트 53 통신을 초기화합니다. 엄격한 Layer 3 필터링을 우회하고 기업 DNS 트래픽에 혼합하는 데 이상적입니다.!I (ICMP로 피벗): 에이전트는 UDP 소켓을 닫고 포트 바인딩을 해제한 후 조용한 Raw Socket 스니핑으로 되돌아갑니다. "팬텀" 은닉 모드에 완벽합니다.Ghost-C2의 데이터 전송 엔진은 x86-64 어셈블리에서 고도로 최적화된 하이브리드 압축 및 인코딩 계층을 사용합니다.
모든 발신 ICMP 패킷은 표준 Linux ping과 구별할 수 없도록 구성됩니다:
struct timeval을 모방합니다.iputils 패딩(0x10에서 0x1F)이 기본 휴리스틱 방화벽을 우회합니다.양방향 모두 점진적으로 이동하는 QWORD 키로 암호화됩니다. 이는 섀넌 엔트로피를 낮게 유지합니다(AES는 ~8.0을 기록하여 DPI 이상을 유발함). Rolling XOR은 자연스러운 잡음 데이터처럼 보이는 엔트로피를 생성합니다. 암호학적 상수, S-박스, YARA가 매칭할 것이 없습니다.
임플란트는 ID + SEQ ≠ 45,000인 모든 ICMP 패킷을 무시합니다. 임플란트는 ID + SEQ = 55,000인 패킷으로 응답합니다. 이는 OS 에코 혼동을 방지하고 인터넷 스캐너나 허니팟을 필터링합니다.
memfd_create를 통한 파일리스 실행명령 출력은 절대 디스크에 기록되지 않습니다. 셸 출력은 익명 RAM 파일(memfd_create)을 통해 캡처되며, /proc/PID/fd에서 합법적인 공유 메모리 매핑에 혼합되도록 [shm]으로 명명됩니다.
RWX 메모리를 금지하는 최신 커널 완화를 무력화합니다. 로더는 2단계 접근 방식(원격 mmap (RW) → 주입 → 원격 mprotect (RX))을 사용합니다. 어떤 페이지도 동시에 W와 X가 되지 않습니다.
모든 시스템 콜 번호는 두 개의 명령어로 분할되어 정적 분석 및 간단한 grep 기반 스캐너를 무력화합니다.
엄격한 OPSEC를 유지하기 위해 Ghost-C2 에이전트(sniff.asm)는 외부 구성을 사용하지 않습니다. 컴파일 및 주입 전에 마스터 C2 IP, 포트, 더미 DNS 도메인을 어셈블리 코드 내에서 직접 정의해야 합니다.
sniff.asm을 열고 .text 세그먼트의 맨 아래로 스크롤합니다. (참고: 에이전트는 엄격한 PIC(위치 독립 코드)이므로 .data 세그먼트가 없습니다. 모든 구성 변수가 인라인으로 저장됩니다.)
다음 값을 마스터 서버에 맞게 수정하세요:
db 127, 0, 0, 1을 마스터의 IP로 변경합니다.dw 0xB414(포트 5300)를 원하는 포트로 네트워크 바이트 순서로 변경합니다(예: 포트 53의 경우 0x3500).nasm -f bin sniff.asm -o shellcode.bin
python3 -c "data = open('shellcode.bin', 'rb').read(); lines = ['\tdb ' + ', '.join(f'0x{b:02x}' for b in data[i:i+12]) for i in range(0, len(data), 12)]; open('c2_payload.txt', 'w').write('\n'.join(lines))"
c2_payload.txt의 내용을 복사합니다.xor.py를 열고 raw_asm 변수의 내용을 복사한 셸코드로 교체합니다.python3 xor.py를 실행하고 암호화된 출력을 복사합니다.loader.asm을 엽니다.c2_payload: 레이블을 찾습니다.target db "cron", 10을 수정하여 대상 주입 프로세스를 변경합니다).nasm -f elf64 loader.asm -o loader.o
ld loader.o -o loader
대상 머신에서 루트 권한으로 실행합니다(sudo ./loader). 이제 에이전트가 완전히 파일리스로 실행됩니다.
마스터 콘솔을 컴파일하기 전에 리스너와 대상 프로파일이 에이전트 구성과 일치하는지 확인해야 합니다. client.asm을 열고 .data 영역 섹션으로 이동합니다.
master_bind_addr을 찾습니다. 여기가 마스터가 들어오는 DNS 비콘을 수신하는 곳입니다.
UDP 포트: dw 0xB414를 에이전트가 전송하는 포트와 일치하도록 변경합니다.
참고: 네트워크 바이트 순서여야 합니다. 포트 53의 경우
0x3500을 사용하세요.
target_addr을 찾습니다. 이 포트는 DNS 피벗(!D) 또는 재연결 모듈을 사용할 때 사용됩니다.
포트 정렬: dw 0xB414를 에이전트가 수신 중인 UDP 포트와 일치하도록 변경합니다. 이 포트가 일치하지 않으면 마스터가 잘못된 곳으로 데이터를 보내므로 (데드락)이 발생합니다.
구성이 완료되면 마스터를 어셈블하고 링크합니다:
nasm -f elf64 client.asm -o client.o
ld client.o -o client
프로팁: 운영을 위한 "프로필 시트"를 항상 유지하세요. sniff.asm에서 포트를 0x3500(포트 53)으로 변경한 경우, 운영 시작 전에 client.asm의 master_bind_addr과 target_addr을 모두 업데이트해야 합니다.
참고: 운영자 콘솔은 raw 소켓과 UDP 포트 53을 바인딩하기 위해 루트 권한이 필요합니다.
지속적인 접근을 보장하고 세션 손실을 방지하려면 마스터 콘솔 세션을 종료하기 전에 항상 에이전트를 ICMP 모드(!I)로 피벗하세요.
논리: ICMP는 Ghost-C2의 "골든 채널"입니다. 상태 비저장, 수동적이며 대상 IP를 통해 항상 연결 가능합니다.
위험: DNS 모드는 동적 UDP 포트 동기화에 의존합니다. DNS 모드에서 마스터 콘솔이 닫히면 에이전트는 UDP 수신 상태에 "갇힌" 상태로 남습니다. 연결을 재설정하려면 에이전트의 특정 임시 포트를 알아야 하는데, 마스터 재시작 시 손실됩니다.
!I (ICMP로 전환)활성 트래픽 검사에 대해 통제된 실험실 환경에서 테스트:
| 테스트 | 결과 |
|---|---|
| Suricata v8.0.3 (Emerging Threats 규칙셋) | ✅ 우회 |
| Suricata v8.0.3 (커스텀 ICMP 페이로드 규칙) | ✅ 우회 |
| DigitalOcean FRA1 게이트웨이 | ✅ 100% 탈취 성공 |
| ~25KB 탈취 중 생성된 경고 | 0 |
Ghost-C2는 Linux 커널과 직접 상호작용합니다:
mprotect 의존성을 없애기 위한 동적 ASLR 무력화PTY가 없는 것은 한계가 아니라 아키텍처 결정입니다:
/dev/ptmx 및 ioctl 호출이 필요합니다.Ghost-C2는 초은닉 명령 실행 및 데이터 탈취 임플란트입니다. 상호작용성은 편의성을 위해 은폐성을 희생합니다 — 이 프로젝트는 은폐성을 선택했습니다.
설계상 Pull Request와 Fork는 엄격히 무시됩니다. 이 프로젝트의 아키텍처는 작성자가 직접 유지 관리합니다. 버그, 로직 결함 또는 기능 제안이 있으면 Issue를 열어주세요. 객관적이고 기술적으로 유지해 주세요.
Ghost-C2는 열정과 땀, 순수 x64 어셈블리로 만들어졌습니다. 이 프로젝트가 저수준 회피, 프로토콜 모방을 이해하는 데 도움이 되었거나 레드 팀 운영을 더 원활하게 만들었다면 개발을 지원해 주세요!
Ghost-C2는 교육 목적, 리버스 엔지니어링 및 승인된 사이버 보안 연구를 위해 엄격히 개발되었습니다.
버전 3.6.1부터 이 프로젝트는 **GNU Affero General Public License v3.0 (AGPLv3)**에 따라 라이선스가 부여됩니다. 네트워크를 통해 이 소프트웨어와 상호작용하거나 수정하는 모든 개체는 라이선스에 따라 전체 소스 코드를 공개해야 합니다. 상업적 이용 또는 독점/폐쇄 소스 플랫폼으로의 통합은 엄격히 금지됩니다.
Copyright (c) 2026 JM00NJ (commSync). All Rights Reserved.
작성자는 이 도구의 불법 사용 또는 이로 인한 손해에 대해 책임을 지지 않습니다. 사용은 본인의 책임 하에 이루어집니다.
| 시스템 콜 | 번호 | 용도 |
|---|
sys_socket | 41 | Raw ICMP / UDP 소켓 생성 |
sys_recvfrom | 45 | 수동적 ICMP/UDP 패킷 캡처 |
sys_sendto | 44 | ICMP/UDP 응답 전송 |
sys_bind | 49 | UDP DNS 포트 바인딩 |
sys_memfd_create | 319 | 출력용 익명 RAM 파일 |
sys_dup2 | 33 | stdout/stderr 리다이렉션 |
sys_execve | 59 | 셸 명령 실행 |
sys_fork | 57 | 프로세스 격리 |
sys_ptrace | 101 | 프로세스 주입 + 안티 디버그 |
sys_getdents64 | 217 | /proc 디렉토리 파싱 |
sys_mmap | 9 | 원격 메모리 할당 |
sys_mprotect | 10 | W^X 권한 전환 |