
자체 포함형 SSH 허니팟으로, 공격자의 상호작용을 포착하여 구조화된 보안 인텔리전스로 전환합니다.
SSHintel은 Python과 Paramiko로 구축된 경량 SSH 허니팟입니다. 가짜 Linux 셸을 시뮬레이션하여 무단 접근 시도를 기록하고, 자격 증명을 캡처하며, 통제된 환경에서 공격자 행동을 분석합니다.
ls, cd, pwd, cat, echo, grep, find, tree, head, tail, wc, stat, ps, df, free, env, id, whoami 등)--tarpit 모드SSHintel은 완전한 Bash/Linux 셸이 아닙니다. 공격자 텔레메트리를 수집하기 위해 일반적인 명령의 그럴듯한 하위 집합을 시뮬레이션합니다. 명령은 경량 레지스트리에 의해 디스패치됩니다. 명령을 추가한다는 것은 작은 핸들러 함수를 추가한다는 의미입니다.
pip install -r requirements.txt
참고: SSH 호스트 키는 첫 실행 시 자동으로 생성됩니다. 수동 키 생성이 필요 없습니다.
특정 포트, 사용자 이름, 비밀번호로 허니팟 실행:
python3 run.py serve --port 2222 --username user1 --password pass123
기본 포트는
2222이고 호스트는0.0.0.0입니다.
허니팟은 첫 실행 시 static/server.key에 SSH 호스트 키가 없으면 자동으로 생성합니다. 수동 키 생성이 필요 없습니다.
타피트 모드 활성화:
python3 run.py serve --port 2222 --username user1 --password pass123 --tarpit
SQLite 텔레메트리 비활성화(JSONL만 사용):
python3 run.py serve --port 2222 --username user1 --password pass123 --no-db
SSHintel은 많은 동시 연결 또는 무기한 유지되는 연결로 인한 리소스 고갈을 방지합니다. 이는 CLI를 통해 구성할 수 있습니다:
예시:
python3 run.py serve --port 2222 --username user1 --password pass123 \
--max-connections 25 --auth-timeout 30 --session-idle-timeout 300
너무 많은 연결이 열려 있으면 초과 연결은 즉시 닫히고 connection_rejected 보안 이벤트(reason: connection_limit 포함)가 JSONL 로그에 기록됩니다. 지연된 인증은 reason: auth_timeout과 함께 연결 해제로 기록됩니다. 유휴 셸은 reason: idle_timeout으로 종료됩니다.
타피트 모드는 의도적으로 출력을 느리게 보내 공격자를 계속 붙잡아 두므로, 타피트 배너 루프는 비활성 타임아웃의 적용을 받지 않습니다 — 그러나 타피트 세션은 연결 제한에 포함됩니다.
SSHintel에는 SQLite에 저장된 보안 텔레메트리를 시각화하는 로컬 웹 대시보드가 포함되어 있습니다.
python3 run.py dashboard
그런 다음 브라우저에서 http://localhost:5000을 엽니다.
대시보드는 기본적으로
data/sshintel.db의 SQLite 데이터베이스를 읽습니다. 먼저 허니팟을 시작하여 텔레메트리가 캡처되도록 한 다음, 대시보드를 실행하여 데이터가 채워지는 것을 확인하세요.
세션 ID를 클릭하거나 /session/<session_id>로 이동하여 단일 공격을 시간순으로 재구성하는 세션 조사 보기를 엽니다:
$ command 보기로 표시# 터미널 1: 허니팟 시작
python3 run.py serve --port 2222 --username user1 --password pass123
# 터미널 2: 대시보드 시작
python3 run.py dashboard
# 터미널 3: 공격자 시뮬레이션
ssh user1@localhost -p 2222
# (일부 명령 실행 후 종료)
그런 다음 http://localhost:5000을 열어 캡처된 활동을 검사합니다.
대시보드는 실시간 텔레메트리를 지원합니다 — 허니팟을 2초마다 폴링하여 자동으로 업데이트합니다. 새 세션, 명령, 이벤트는 페이지를 새로 고치지 않고 실시간으로 나타납니다. 라이브 표시기(● Live)가 연결 상태를 보여줍니다.
두 번째 터미널을 열고 연결을 시도합니다:
ssh user1@localhost -p 2222
자격 증명이 일치하면 에뮬레이션된 셸로 들어가게 됩니다.
오래된 SSH 지문을 제거하려면:
notepad "%USERPROFILE%\.ssh\known_hosts"
localhost또는 허니팟의 IP가 포함된 관련 줄을 삭제하세요.
creds_logger에 기록됨funnel_logger를 통해 기록됨log_files/events.jsonl에 기록됨 — 줄마다 하나의 유효한 JSON 객체각 JSONL 이벤트에는 UTC ISO-8601 timestamp, event_type, 고유 session_id, source_ip가 포함됩니다. 연결, 인증 시도/결과, 명령 실행, 타피트 활성화, 연결 해제가 모두 구조화된 이벤트로 기록됩니다.
들어오는 각 SSH 연결은 고유한 session_id를 가진 독립적인 세션으로 추적됩니다. 세션은 소스 IP, 연결/해제 시간, 인증 결과, 연결 지속 시간을 기록하며, 해당 연결 내에서 생성된 모든 이벤트는 동일한 session_id를 전달합니다(따라서 인증 시도, 명령, 연결 해제를 단일 연결로 추적할 수 있음). 세션은 연결별로 격리됩니다 — 동시 클라이언트 간에 상태가 공유되지 않습니다.
또한 각 세션은 자체 격리된 인메모리 가짜 파일시스템을 받습니다 — 시뮬레이션된 파일시스템은 각 연결마다 새로 생성되고 연결이 종료되면 정리됩니다. 한 공격자가 생성하거나 변경한 파일, 디렉터리, 작업 디렉터리는 동시에 연결된 다른 공격자에게 절대 표시되지 않습니다. 전체 파일시스템은 Python 메모리에서 시뮬레이션되며 실제 호스트 파일시스템에 절대 접촉하지 않습니다.
현재 event_type 값: connect, auth_attempt, auth_success, auth_failure, command, disconnect, connection_rejected, tarpit.
SSHintel/
├── honeypot/ # 핵심 허니팟 로직
│ ├── __init__.py
│ ├── main.py # 수락 루프 + 연결 제한
│ ├── handlers.py # SSH 전송 설정 + 에뮬레이션된 셸
│ ├── server.py # Paramiko 서버 인터페이스(인증)
│ ├── session.py # 연결별 세션 추적
│ ├── fs.py # 인메모리 가짜 파일시스템(세션별 격리)
│ ├── shell.py # 가짜 셸: 명령 레지스트리 + 디스패처
│ ├── limits.py # 스레드 안전 동시 연결 제한
│ ├── logger.py # JSONL 이벤트 로깅 + SQLite 브리지
│ └── telemetry_store.py # SQLite 텔레메트리 저장소 + 쿼리 계층
│
├── dashboard/ # 로컬 웹 대시보드
│ ├── app.py # Flask 애플리케이션 + API 라우트
│ ├── templates/
│ │ ├── index.html # 메인 대시보드 템플릿
│ │ └── session.html # 세션 조사 템플릿
│ └── static/
│ ├── style.css # 대시보드 스타일
│ ├── dashboard.js # 메인 대시보드 JS(실시간 업데이트)
│ └── session.js # 세션 조사 JS
│
├── log_files/ # 런타임 로그(git 무시)
│ ├── creds_audits.log # 자격 증명 시도
│ ├── cmd_audits.log # 명령 감사 추적
│ └── events.jsonl # 구조화된 JSONL 보안 이벤트
│
├── data/ # SQLite 데이터베이스(git 무시)
│ └── sshintel.db
│
├── static/ # SSH 호스트 키(자동 생성)
│ └── server.key
│
├── .github/workflows/ # CI 구성
│ └── tests.yml
│
├── Dockerfile
├── README.md
├── requirements.txt # 런타임 의존성(paramiko, flask)
├── requirements-dev.txt # 테스트 의존성(pytest, pytest-cov)
└── run.py # CLI 진입점
컨테이너화된 환경에서 허니팟을 실행하려면 포함된 Dockerfile을 사용할 수 있습니다.
docker build -t sshintel .
이 명령은
sshintel이라는 Docker 이미지를 생성합니다.
docker run -p 2222:2222 sshintel
이 명령은 다음을 수행합니다:
static/server.key에 SSH 개인 키 자동 생성(아직 없는 경우)2222에서 허니팟 실행:username: user1, password: pass123두 번째 터미널을 열고 SSH로 연결합니다:
ssh user1@localhost -p 2222
자격 증명이 일치하면 시뮬레이션된 셸로 들어가게 됩니다.
컨테이너를 중지하려면:
docker ps # 컨테이너 ID 찾기
docker stop <container_id>
이미지를 제거하려면:
docker rmi sshintel
docker save -o sshintel.tar sshintel로 이미지를 내보내고 나중에docker load -i sshintel.tar로 로드할 수도 있습니다.
이 프로젝트는 MIT 라이선스에 따라 라이선스가 부여됩니다.
| 플래그 | 기본값 | 용도 |
|---|
--max-connections | 50 | 최대 동시 활성 연결 수; 초과 연결은 거부되고 connection_rejected 이벤트로 기록됨 |
--auth-timeout | 60 (초) | SSH 핸드셰이크/인증 완료에 허용되는 시간; 지연된 클라이언트는 연결 해제됨 |
--session-idle-timeout | 300 (초) | 인증된 셸의 비활성 타임아웃; 유휴 세션은 종료되지만, 활발히 입력 중인 공격자는 절대 종료되지 않음 |