
머신러닝을 활용한 리버스 셸 탐지
Dmitrijs Trizna · Luca Demetrio · Battista Biggio · Fabio Roli
Linux living-off-the-land (LOTL) 리버스 셸은 합법적인 바이너리(bash, python, nc, …)를 악용하여 은밀한 아웃바운드 연결을 설정하므로, 시그니처 기반 탐지는 새로운 변종 및 회피 시도에 대해 신뢰할 수 없습니다. QuasarNix는 이 분야의 두 가지 공개된 격차를 해결합니다:
이 프레임워크는 34개의 리버스 셸 템플릿에서 100만 개 명령어 훈련 코퍼스를 합성하고, FPR = 10⁻⁶의 운영 지점에서 14개 모델 아키텍처를 평가합니다. 이는 실제 SIEM 경고 피로 제약 조건을 반영합니다.
보류된 테스트 세트에서 기준 휴리스틱 및 QuasarNix 아키텍처 전반의 성능. TPR은 FPR = 10⁻⁶에서 10번의 독립적인 훈련 실행에 대한 평균 ± 표준편차로 보고됩니다. 굵은 글씨는 최고 결과를 나타내며, 별표(*)는 논문에서 논의된 모델을 강조합니다.
핵심: GBDT는 FPR = 10⁻⁶에서 60% TPR을 달성합니다 — 이는 시그니처(3.37%)보다 18배 높은 수치이며, 일반 하드웨어에서 14초 만에 훈련됩니다.
아래 표는 공격자의 도구 상자에서 Linux 셸 이스케이프 기법을 정리한 것입니다. 세 번째 열은 해당 기법이 auditd 커널 텔레메트리 정규화에서 살아남는지 여부를 나타냅니다. 굵게 표시된 네 개 항목만이 별개의 EXECVE 레코드를 생성하여 실제 공격 표면을 구성합니다.
15개 기법 중 4개만 커널 수준 정규화에서 살아남으며, 이들은 적대적 공격 공간으로 사용됩니다.
세 가지 공격 계열이 평가됩니다 — 정상 콘텐츠 주입, 셸 이스케이프 변조, 그리고 이 둘의 혼합:
추론 시간 회피 외에도 훈련 시간 공격을 평가합니다:
레이블 뒤집기 오염 (훈련 레이블의 0–20% 뒤집기): 모델은 점진적으로 성능이 저하됩니다. GBDT는 앙상블 투표를 통해 본질적인 저항성을 보여주며, 높은 오염 비율에서도 기능을 유지합니다.
백도어 공격 (0.01–1% 중독 비율, 2–10 토큰 트리거): 짧은 트리거(2–4 토큰)는 정상 트래픽에서의 빈번한 출현으로 인해 신뢰할 수 있는 백도어를 설치하지 못합니다. 최적의 백도어 설치는 0.03% 이상의 중독 비율에서 6–10 토큰 트리거가 필요합니다.
핵심: 중독 공격이 성공하려면 상당하고 통계적으로 감지 가능한 데이터 주입이 필요합니다. GBDT의 앙상블 메커니즘은 적대적 훈련 비용 없이 본질적인 강건성을 제공합니다.
이 작업이 공개된 후, Google은 CAMLIS 2025(arXiv:2512.08802)에서 개념적으로 정렬된 프로덕션 시스템을 발표했습니다: 수만 개의 시스템에 배포되어 하루 최대 2500억 개 이벤트를 처리하는 2단계 YARA + ML 파이프라인입니다. 해당 시스템은 여기서 제안된 하이브리드 ML-for-SIEM-탐지 패러다임과 능동 학습 피드백 루프를 독립적으로 검증하여 산업 규모에서의 실행 가능성을 입증합니다.
| 리소스 | 링크 | 세부 사항 |
|---|---|---|
| 데이터셋 | dtrizna/QuasarNix | 1,003,122개 명령어 · 훈련 533k / 테스트 470k · Apache-2.0 |
| 사전 훈련된 모델 | dtrizna/QuasarNix | GBDT, Random Forest, MLP, 1D-CNN, ... |
from datasets import load_dataset
ds = load_dataset("dtrizna/QuasarNix")
src/
augmentation.py 규칙 기반 및 생성적 데이터 합성
evasion.py 화이트박스/블랙박스 적대적 공격
models.py 모델 정의 (CNN, LSTM, Transformer, XGBoost, ...)
preprocessors.py 토크나이저 및 특징 구성기
scoring.py 고정 FPR에서의 평가 메트릭
experiments/
ablation_*.py 절제 연구 (토크나이저, 어휘 크기, 임베딩)
adversarial_*.py 공격 및 적대적 훈련 파이프라인
train_release_models.py 종단 간 훈련 스크립트
logs_*/ TensorBoard 실행, CSV 메트릭, 모델 체크포인트
data/
signatures/ 진화적 탐색으로 생성된 Sigma 규칙
nix_shell/ 정상 명령어 코퍼스 원본
powershell/ 플랫폼 간 명령어 샘플
img/ 출판 준비된 플롯
uv venv # .venv 생성 (특정 인터프리터를 위해 --python 3.11 추가)
source .venv/bin/activate
uv sync # pyproject.toml에서 의존성 설치
@article{trizna2025quasarnix,
author = {Trizna, Dmitrijs and Demetrio, Luca and Biggio, Battista and Roli, Fabio},
title = {Robust Large-Scale Detection of Living-Off-the-Land Reverse Shells via Data Synthesis},
journal = {ACM Transactions on Privacy and Security},
year = {2025},
doi = {10.1145/3807450},
url = {https://dl.acm.org/doi/10.1145/3807450}
}
| 아키텍처 | 매개변수 | FPR=10⁻⁶에서 TPR | F1 | 정확도 | AUC | 훈련 시간 |
|---|
| 시그니처 (Sigma) | 184 | 3.37% | 6.52% | 51.68% | 51.68% | N/A |
| One-Class SVM (정상 데이터 기반) | 1K | 0.00% | 82.87% | 79.33% | 79.33% | 10초 |
| One-Class SVM (악성 데이터 기반) | 1K | 0.00% | 40.14% | 25.20% | 25.20% | 10초 |
| 1D-CNN (비증강, 불균형) | 1K | 0.00% | 80.29% | 77.91% | 87.44%* | 15분 |
| 1D-CNN (비증강, 균형) | 1K | 0.06% | 82.38% | 79.33% | 88.12%* | 29분 |
| SLP (비증강) | 1K | 0.00% | 0.00% | 50.00% | 91.58% | 1시간 12분 |
| 아키텍처 | 매개변수 | FPR=10⁻⁶에서 TPR | F1 | 정확도 | AUC | 훈련 시간 |
|---|
| Random Forest | 1K | 42.23 ± 6.27% | 96.07% | 96.21% | 99.84% | 18초 |
| GBDT (XGBoost) | 1K | 60.20 ± 8.22% | 89.92% | 90.84% | 99.89% | 14초 |
| MLP (임베딩 없음) | 264K | 54.16 ± 2.14%* | 94.00% | 94.34% | 99.80% | 18분 |
| 아키텍처 | 매개변수 | FPR=10⁻⁶에서 TPR | F1 | 정확도 | AUC | 훈련 시간 |
|---|
| MLP (임베딩) | 297K | 10.76 ± 17.32% | 67.70% | 75.60% | 89.15% | 18분 |
| LSTM | 318K | 21.52 ± 23.66% | 64.16% | 74.05% | 99.75% | 24분 |
| 1D-CNN | 301K | 46.42 ± 32.67%* | 85.97% | 88.20% | 99.99% | 29분 |
| 1D-CNN + LSTM | 316K | 20.48 ± 22.08% | 58.92% | 71.06% | 98.21% | 29분 |
| 1D-CNN + LSTM + Attention | 402K | 17.19 ± 22.59% | 62.53% | 73.08% | 98.46% | 26분 |
| Transformer (Mean Pooling) | 335K | 0.00 ± 0.00% | 83.39% | 86.07% | 98.78% | 1시간 18분 |
| Transformer (CLS Token) | 335K | 0.00 ± 0.00% | 78.55%* | 82.67% | 99.38% | 1시간 30분 |
| Transformer (Attn. Pooling) | 335K | 0.00 ± 0.00% | 87.82% | 89.41% | 98.85% | 1시간 24분 |
| 조작 | 기능 예시 | auditd에 의해 보존됨 |
|---|
' | ba's'h -i | 아니오 |
" | ba"s"h -i | 아니오 |
\ | ba\s\h -i | 아니오 |
$@ | ba$@sh -i | 아니오 |
[char] | ba[s]h -i | 아니오 |
{form} | {bash,-i} | 아니오 |
| IFS 변수 | bash${IFS}-i | 아니오 |
| 빈 변수 | bas${u}h -i | 아니오 |
| 가짜 명령어 | bas$(u)h -i | 아니오 |
| Base64 | echo c2ggLWk= | base64 -d | sh | 아니오 |
| 16진수 | echo \x73\x68 \x20\x2d\x69 | sh | 아니오 |
| 플래그 변조 | bash -x -li | 예 |
| 10진수 IP | ping 2130706433 | 예 |
| 바이너리 이름 변경 | cp bash a; a -i | 예 |
| 무의미한 코드 | mkfifo a; id; cat a | 예 |