ARTEX 공개 소스를 직접 분석해서 만든 방어용 탐지·헌팅 키트를 정리했어요. 공격 기능은 없고, 로그와 호스트에서 흔적을 찾는 도구예요.
GitHub: kh20134/artex-defense-kit (MIT 라이선스)
프로젝트 개요
- 목표: 공개 소스로 검증되는 지표만 골라 탐지에 쓰고, 지표가 못 잡는 요청은 행위 휴리스틱으로 보완
- 기간: 2026년 10월
- 역할: 기획, 소스 분석, 구현
- 기술 스택: Python 3.11+, pytest, ruff, GitHub Actions(3.11/3.12), Sigma, Suricata, ModSecurity
배경
앞선 글에서 ARTEX와 금융권 침해 보도를 정리했어요. ARTEX는 AGPL-3.0으로 공개된 LLM 다중 에이전트 침투 테스트 시스템이고, 사건과의 연관은 아직 공식 확인 전이에요.
PLURA는 공개 소스의 User-Agent 3개를 후보로 냈지만, 금융권 공격 로그와 대조한 값은 아니라고 밝혔어요. 그래서 "무엇을 믿고 탐지할지"를 소스에서 직접 확인해 보기로 했어요.
분석 방법
Hinln/ARTEX 커밋 f3e3f54(2026-10-08)를 직접 읽고, v0.3.14 커밋에서도 같은 문자열을 다시 확인했어요. 모든 지표에 파일 경로와 줄 번호를 붙였어요.
채택한 지표
- 인바운드 UA 3개(높음): artex-enrich/1.0(enrich.go:233), Mozilla/5.0 (spa-api-recon)(harvest_static.py:23), 같은 문자열의 spider 변형(spider_mpa.py:22)
- 아웃바운드 UA(중간): artex-selfupdate. 내부 프록시 로그용
- 호스트 단서: 콘솔 제목, artex_token 키, 이미지 autumn27/artex, 실행 파일 이름, 스크립트 경로, 잠금 파일 spa-api-recon-runtime, 환경 변수 이름 ARTEX_PG_DSN, ARTEX_UPDATE_GITHUB_TOKEN
버린 지표와 이유
- ARTEX-worker/1.0: UI 목업 데이터일 뿐 실제 워커 값이 아님
- /api/ 같은 경로 접두사: 분류용 정규식 예시라 상시 요청 URL이 아님
- 8787·8788 포트 단독: 다른 프로그램도 쓰는 기본값
- 컨테이너 이름만 artex: 이미지가 다르면 무관
- 공격 IP: 검증 가능한 공개 목록이 없음
- v0.3.14 ZIP 해시: 다시 받아 검증할 수 없었음
- UA 없음: 정상 클라이언트도 헤더를 뺌
여기서 "높음"은 소스에 그 용도로 박혀 있다는 뜻이에요. 그 요청이 사건의 공격이라는 뜻은 아니에요.
구현
CLI
artex-hunt scan: nginx/apache combined, IIS W3C, JSON Lines 로그 검사artex-hunt host: 디렉터리, 프로세스 목록, 컨테이너 목록 검사. 토큰 값은 가림- 결과는 한국어 텍스트, JSON, HTML 보고서
행위 휴리스틱
JS 자산 폭주, API형 경로 다양성, 조회 파라미터 반복, HTML 단기 순회 4가지예요. 단독으로는 낮은 신뢰이고, 같은 출발지에 검증된 UA가 있으면 중간으로 올려요. 임계값은 옵션으로 조정해요.
탐지 룰
- Sigma: 인바운드 UA, 아웃바운드 UA, 프로세스 이름
- Suricata: UA 예시 시그니처(alert만)
- ModSecurity: 탐지 전용(pass,log)
결과 화면
(합성(가상) 데이터 실행 결과, 출처: 본인 깃허브)
(합성(가상) 데이터 실행 결과, 출처: 본인 깃허브)
IP는 문서용 대역이고 로그와 설치 흔적은 모두 합성이에요. 보고서 맨 위에 "일치가 침해 확정이 아니다"라는 경고를 항상 띄워요.
방어 가이드 요약
- UA 룰만 믿지 말고 계정·세션 단위로 속도를 제한해요.
- 조회 API는 서버에서 주체와 대상의 관계를 확인해요.
- 룰은 탐지 전용으로 하루 이상 본 뒤 차단을 검토해요.
- 콘솔형 관리 도구는 인터넷에 두지 않아요.
- 사내 LLM 에이전트는 도구 허용 목록과 사람 승인을 둬요.
- 침해가 의심되면 KISA 118과 업권 보고 체계를 따라요.
한계와 배운 점
- UA 3개는 고정 스크립트에만 있어요. LLM이 만든 클라이언트나 Chromium 런타임 요청에는 없어요.
- 스캔 결과가 깨끗해도 호스트가 안전하다는 뜻은 아니에요.
- 휴리스틱은 큰 SPA, 검색창, 모니터링에도 걸려요.
- combined 로그는 1초 단위라 0.1초 간격을 직접 재지 못해요.
가장 크게 배운 건 "쓸 수 있는 지표"보다 "버릴 지표"를 정하는 일이 더 어렵다는 점이에요. 근거를 줄 번호까지 남기니 판단을 설명하기가 훨씬 쉬워졌어요.
사용법
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
artex-hunt scan access.log \
--json out.json \
--html report.html \
--fail-on high
artex-hunt host \
--directory /srv/app \
--processes ps.txt \
--containers ctr.json
--fail-on high: 높음 이상이면 종료 코드 2--no-heuristics: UA만 검사- ps.txt는 ps 출력, ctr.json은 docker ps JSON 저장본
FAQ
ARTEX 코드가 들어 있나요?
아니요. 소스, 바이너리, 스크립트는 포함하지 않고 짧은 문자열과 줄 번호만 인용해요.
일치가 나오면 침해인가요?
아니요. 인가된 점검인지부터 확인하고, 앱 로그로 데이터 접근 여부를 따로 봐야 해요.
금융권 사건 로그로 검증했나요?
아니요. 공개된 사건 로그가 없어서 합성 데이터로만 테스트했어요.
마무리
검증된 것만 탐지하고, 버린 이유까지 남기는 데 집중한 프로젝트예요.
탐지 결과는 조사의 시작점으로만 써 주세요.