2SeC 팀 프로젝트에서 DVWA 기반 공격 로그를 수집하고 분석하는 SIEM 시스템을 만들었다
단순히 로그 모으는 게 아니라, 이걸 LLM이 학습해서 공격 패턴을 자동으로 분석하는 게 목표였다
그 과정에서 두 가지 큰 고민이 있었다
[DVWA 공격 시뮬레이션]
↓
[공격 로그 생성]
(raw text format)
↓
[C 로그 파싱 엔진] ← Part 1
↓
[JSON 변환]
(구조화된 데이터)
↓
[Logstash]
↓ (IAM 인증) ← Part 2
[OpenSearch]
↓
[LLM CTI 분석]
Python으로 10만 줄 로그 파싱하면 약 5~10초 걸린다
C로 만들면 1초 안에 끝난다
실제 측정
# Python 버전
time python3 parser.py attack.log output.json
real 0m8.342s
# C 버전
time ./log_parser attack.log output.json
real 0m0.721s
약 11배 차이 난다
나중에 실시간 로그 처리할 때 이 차이가 크다
malloc, realloc, free 직접 관리하니까 메모리 누수 걱정 없고
언제 얼마나 메모리 쓰는지 정확히 알 수 있다
실제 회사 가면 고성능 로그 처리 시스템은 대부분 C/C++로 되어 있다
지금 배워두면 나중에 도움된다
공격 로그가 이런 형식으로 들어온다
2025-12-12 10:39:15 | SQL_INJECTION | SUCCESS | ' AND SLEEP(3) | 192.168.1.100 | SESSION_ABC123
이걸 파이프(|) 기준으로 쪼개서 구조체에 담는다
지원하는 공격 타입
| 공격 타입 | enum 값 | 설명 |
|---|---|---|
| SQL Injection | ATTACK_SQL_INJECTION | DB 쿼리 조작 공격 |
| XSS | ATTACK_XSS | 스크립트 삽입 공격 |
| Command Injection | ATTACK_COMMAND_INJECTION | OS 명령어 삽입 |
| File Inclusion | ATTACK_FILE_INCLUSION | LFI/RFI 공격 |
| Brute Force | ATTACK_BRUTE_FORCE | 무차별 대입 공격 |
| CSRF | ATTACK_CSRF | 요청 위조 공격 |
공격 타입은 enum으로 관리해서 나중에 switch-case로 처리하기 편하게 만들었다
단순히 로그만 파싱하는 게 아니라 공격의 위험도를 자동으로 계산한다
심각도 계산 알고리즘
int calculate_severity(LogEntry *entry) {
int severity = 1; // 기본 점수
// 공격 성공 여부로 가중치
if (entry->success) {
severity += 3;
}
// 공격 유형별 가중치
switch(entry->attack_type) {
case ATTACK_SQL_INJECTION:
case ATTACK_COMMAND_INJECTION:
severity += 4; // 시스템 침투 가능한 공격
break;
case ATTACK_FILE_INCLUSION:
case ATTACK_XSS:
severity += 3; // 정보 탈취 가능한 공격
break;
case ATTACK_CSRF:
case ATTACK_BRUTE_FORCE:
severity += 2; // 상대적으로 낮은 위험도
break;
default:
severity += 1;
}
if (severity > 10) severity = 10; // 최대값 제한
return severity;
}
점수 기준
| 점수 | 의미 | 예시 |
|---|---|---|
| 1-3 | 낮음 | 실패한 Brute Force |
| 4-6 | 중간 | 실패한 SQL Injection |
| 7-9 | 높음 | 성공한 XSS |
| 10 | 치명적 | 성공한 Command Injection |
왜 이렇게 만들었냐면, 나중에 OpenSearch에서 심각도 기준으로 필터링하거나 알림 보낼 때 유용하기 때문이다
동적 배열로 로그 엔트리를 관리한다
typedef struct {
LogEntry *entries; // 동적 배열
int count; // 현재 저장된 개수
int capacity; // 현재 배열 크기
} LogCollection;
초기화
LogCollection* init_log_collection(void) {
LogCollection *collection = malloc(sizeof(LogCollection));
if (!collection) return NULL;
collection->capacity = 1000; // 초기 크기
collection->count = 0;
collection->entries = malloc(sizeof(LogEntry) * collection->capacity);
return collection;
}
초기 capacity를 1000으로 잡았다
테스트해보니 보통 한 번에 1000~5000개 정도 로그가 들어오더라
자동 확장
int add_log_entry(LogCollection *collection, LogEntry *entry) {
// 꽉 차면 2배로 확장
if (collection->count >= collection->capacity) {
collection->capacity *= 2;
LogEntry *new_entries = realloc(collection->entries,
sizeof(LogEntry) * collection->capacity);
if (!new_entries) return 0;
collection->entries = new_entries;
}
collection->entries[collection->count++] = *entry;
return 1;
}
capacity 넘어가면 자동으로 2배씩 늘어난다
realloc 실패하면 0 리턴해서 상위에서 에러 처리하게 만들었다
메모리 해제
void free_log_collection(LogCollection *collection) {
if (collection) {
if (collection->entries) {
free(collection->entries);
}
free(collection);
}
}
NULL 체크 꼭 해야 한다
안 하면 segfault 난다
LLM이 바로 학습할 수 있게 표준화된 JSON 포맷으로 변환한다
입력 로그
2025-12-12 10:39:15 | SQL_INJECTION | SUCCESS | ' AND SLEEP(3) | 192.168.1.100 | SESSION_ABC123
출력 JSON
{
"timestamp": "2025-12-12 10:39:15",
"attack_type": "SQL_INJECTION",
"success": true,
"payload": "' AND SLEEP(3)",
"source_ip": "192.168.1.100",
"session_id": "SESSION_ABC123",
"severity": 8
}
전체 출력 구조
{
"total_events": 3,
"events": [
{...},
{...},
{...}
]
}
total_events 필드 넣어서 나중에 로그 개수 확인할 때 편하게 만들었다
페이로드에 특수문자 들어가 있으면 JSON 깨진다
그래서 escape 처리 필수다
void escape_json_string(const char *input, char *output, int max_len) {
int j = 0;
for (int i = 0; input[i] && j < max_len - 2; i++) {
// 큰따옴표와 백슬래시 escape
if (input[i] == '"' || input[i] == '\\') {
output[j++] = '\\';
}
output[j++] = input[i];
}
output[j] = '\0';
}
처리 예시
| 원본 | 변환 후 |
|---|---|
' OR "1"="1 | ' OR \"1\"=\"1 |
C:\windows\system32 | C:\\windows\\system32 |
<script>alert("XSS")</script> | <script>alert(\"XSS\")</script> |
이거 안 하면 JSON 파싱 실패한다
.
├── log_parser.h # 헤더 파일 (구조체, 함수 선언)
├── log_parser.c # 핵심 로직 (파싱, 변환)
└── main.c # 메인 함수 (입출력 처리)
역할 분리 확실히 했다
나중에 라이브러리로 만들 수도 있게
LogEntry 구조체
typedef struct {
char timestamp[MAX_TIMESTAMP_LENGTH]; // 32 bytes
AttackType attack_type; // 4 bytes (enum)
char attack_type_str[MAX_ATTACK_TYPE_LENGTH]; // 64 bytes
int success; // 4 bytes
char payload[MAX_PAYLOAD_LENGTH]; // 2048 bytes
char source_ip[MAX_IP_LENGTH]; // 64 bytes
char session_id[MAX_SESSION_LENGTH]; // 128 bytes
int severity; // 4 bytes
} LogEntry;
총 크기: 약 2348 bytes
1000개면 약 2.3 MB
크게 부담 안 된다
strtok를 사용한 토큰 분리
int parse_log_line(const char *line, LogEntry *entry) {
char temp_line[MAX_LINE_LENGTH];
strncpy(temp_line, line, MAX_LINE_LENGTH - 1);
temp_line[MAX_LINE_LENGTH - 1] = '\0';
// 개행 문자 제거
char *newline = strchr(temp_line, '\n');
if (newline) *newline = '\0';
// 파이프 기준으로 분리
char *token = strtok(temp_line, "|");
if (!token) return 0;
// 공백 제거
while (*token == ' ') token++;
char *end = token + strlen(token) - 1;
while (end > token && *end == ' ') end--;
*(end + 1) = '\0';
strncpy(entry->timestamp, token, MAX_TIMESTAMP_LENGTH - 1);
// ... 이후 필드들도 동일하게 처리
return 1;
}
주의할 점
strtok는 원본 문자열을 수정한다
→ 복사본 만들어서 사용
공백 처리 꼭 해야 한다
→ 앞뒤 공백 제거 로직 추가
버퍼 오버플로우 방지
→ strncpy 사용하고 null 문자 보장
메모리 할당 실패
LogCollection *collection = init_log_collection();
if (!collection) {
fprintf(stderr, "Error: Failed to initialize log collection\n");
return 1;
}
파일 열기 실패
FILE *fp = fopen(input_file, "r");
if (!fp) {
fprintf(stderr, "Error: Cannot open input file '%s'\n", input_file);
return 1;
}
파싱 실패
if (parse_log_line(line, &entry)) {
// 성공
parsed_count++;
} else {
// 실패
fprintf(stderr, "Warning: Failed to parse line %d\n", line_number);
error_count++;
}
에러 나도 프로그램 죽지 않게 만들었다
로그 일부 파싱 실패해도 나머지는 처리한다
gcc -o log_parser main.c log_parser.c -Wall -O2
컴파일 옵션 설명
| 옵션 | 의미 |
|---|---|
-Wall | 모든 경고 표시 |
-O2 | 최적화 레벨 2 |
-o | 출력 파일명 지정 |
처음엔 -O3 썼는데 -O2랑 속도 차이 거의 없더라
./log_parser <입력파일> <출력파일>
예시
./log_parser data/raw_logs/attack.log data/parsed_logs/attack.json
Parsing log file: data/raw_logs/attack.log
[1] Parsed: 2025-12-12 10:39:15 | SQL_INJECTION | SUCCESS
[2] Parsed: 2025-12-12 10:40:22 | XSS | FAILURE
[3] Parsed: 2025-12-12 10:41:33 | COMMAND_INJECTION | SUCCESS
[4] Parsed: 2025-12-12 10:42:15 | BRUTE_FORCE | FAILURE
Parsing complete:
Total lines: 4
Successfully parsed: 4
Errors: 0
Writing JSON output to: data/parsed_logs/attack.json
Successfully wrote 4 events to JSON file
Log parsing engine completed successfully.
실시간으로 파싱 진행 상황 보여준다
나중에 큰 파일 처리할 때 멈춘 건지 아닌지 알 수 있어서 좋다
| 로그 개수 | 파싱 시간 | 메모리 사용량 |
|---|---|---|
| 1,000 | 0.01초 | 2.3 MB |
| 10,000 | 0.08초 | 23 MB |
| 100,000 | 0.72초 | 230 MB |
| 1,000,000 | 7.89초 | 2.3 GB |
100만 줄도 8초 안에 처리한다
Python 버전은 100만 줄 처리하는 데 약 80초 걸렸다
확장 횟수
초기 capacity: 1000
realloc 오버헤드가 있긴 한데 전체 시간에서 차지하는 비율은 5% 미만이다
처음에 free 제대로 안 해서 메모리 누수 났다
valgrind로 찾아냈다
valgrind --leak-check=full ./log_parser attack.log output.json
문제 코드
// 잘못된 코드
void free_log_collection(LogCollection *collection) {
free(collection); // entries 안 free
}
수정 코드
void free_log_collection(LogCollection *collection) {
if (collection) {
if (collection->entries) {
free(collection->entries); // 추가
}
free(collection);
}
}
valgrind 없었으면 찾기 힘들었을 듯하다
strtok는 원본 문자열을 수정한다
이거 몰라서 한참 헤맸다
// 문제 상황
char *line = "2025-12-12 | SQL | SUCCESS";
char *token1 = strtok(line, "|");
char *token2 = strtok(line, "|"); // 이미 수정된 line
// 해결책
char temp[MAX_LINE_LENGTH];
strcpy(temp, line);
char *token = strtok(temp, "|");
원본 복사해서 사용해야 한다
처음엔 escape 안 했더니 JSON 파싱 실패하더라
// 잘못된 출력
{
"payload": "' OR "1"="1"
}
// JSON 파서가 여기서 멈춤
// 올바른 출력
{
"payload": "' OR \"1\"=\"1\""
}
특히 큰따옴표랑 백슬래시 조심해야 한다
C 파서로 JSON 만들었으니 이제 OpenSearch에 넣어야 한다
여기서 새로운 고민이 생겼다
어떻게 안전하게 인증할 것인가?
Terraform → 랜덤 비밀번호 생성
↓
OpenSearch 프로비저닝 시 비밀번호 설정
↓
Secrets Manager에 저장
↓
ECS가 Secret ARN 참조해서 Logstash 컨테이너에 전달
이렇게 하면 될 것 같았다
상황
resource "random_password" "opensearch_admin" {
length = 16
special = true
}
resource "aws_opensearch_domain" "main" {
# ...
advanced_security_options {
master_user_options {
master_user_name = "admin"
master_user_password = random_password.opensearch_admin.result
}
}
}
이렇게 하면 Terraform state에 비밀번호가 평문으로 남는다
State 파일 확인해보니
{
"resources": [
{
"type": "random_password",
"instances": [
{
"attributes": {
"result": "MyS3cr3tP@ssw0rd" // 그냥 평문으로 박혀있음
}
}
]
}
]
}
물론 우리 state는 S3에 있고 외부 접근 불가긴 하다
근데 팀원이면 다 볼 수 있다
이게 Best Practice는 아닌 것 같았다
ECS는 편하다
resource "aws_ecs_task_definition" "logstash" {
container_definitions = jsonencode([{
secrets = [{
name = "OPENSEARCH_PASSWORD"
valueFrom = aws_secretsmanager_secret.opensearch.arn
}]
}])
}
ECS가 알아서 Secret ARN 참조해서 컨테이너한테 환경변수로 넘겨준다
근데 OpenSearch는 다르다
OpenSearch는 프로비저닝 할 때 관리자 비밀번호를 미리 설정해야 한다
나중에 바꿀 수는 있는데, 처음 만들 때는 무조건 넣어야 한다
그래서 Terraform이 비밀번호를 알아야 하고
결국 state에 남는다
만약 Logstash 컨테이너가 털리면?
admin 비밀번호가 노출된다
OpenSearch 전체가 위험해진다
Logstash가 실제로 하는 일
이게 다다
클러스터 설정 바꾸거나 유저 관리 같은 관리자 권한은 전혀 필요 없다
그런데 admin 계정을 준다는 건 과한 권한이다
OpenSearch가 IAM(SigV4) 인증을 공식으로 지원한다
ECS/Fargate 환경에서는 Task Role 기반 인증이 권장 방식이다
장점 비교
| 항목 | 비밀번호 방식 | IAM 방식 |
|---|---|---|
| Terraform state | 평문 저장됨 | 저장 안 됨 |
| 비밀번호 관리 | Secrets Manager 필요 | 불필요 |
| 권한 세분화 | 계정 단위 | IAM Policy 단위 |
| 자동 회전 | 수동 관리 | STS 자동 회전 |
| 감사 추적 | 제한적 | CloudTrail 통합 |
| 컨테이너 보안 | 비밀번호 노출 위험 | Task Role로 안전 |
SigV4 (AWS Signature Version 4)
AWS API 요청에 서명을 추가하는 방식이다
1. ECS Task가 Task Role 자격증명 획득
2. Logstash가 OpenSearch에 요청
3. 요청에 IAM 서명 추가 (SigV4)
4. OpenSearch가 IAM으로 검증
5. 인증 성공 시 요청 처리
핵심 개념: Task Role
ECS Task 자체가 신원이 된다
컨테이너 안에 비밀번호 같은 거 안 넣어도 된다
STS 임시 자격증명
Task Role은 임시 자격증명을 사용한다
Access Key ID: ASIAXXX...
Secret Access Key: wJalr...
Session Token: FwoGZXIv...
Expiration: 2025-12-15 12:00:00
보통 12시간마다 자동으로 만료되고 새로 발급된다
비밀번호처럼 평생 유효한 게 아니다
output {
elasticsearch {
hosts => ["https://opensearch-endpoint"]
index => "attack-logs-%{+yyyy.MM.dd}"
user => "admin"
password => "${OPENSEARCH_PASSWORD}" # 환경변수에서 가져옴
ssl_verification_mode => "none"
}
}
이렇게 하면 컨테이너 환경변수에 비밀번호가 있어야 한다
output {
elasticsearch {
hosts => ["https://opensearch-endpoint"]
index => "${PROJECT_NAME}-siem-%{+yyyy.MM.dd}"
auth_type => "aws_iam" # IAM 인증 활성화
region => "${AWS_REGION}"
ssl_verification_mode => "${SSL_VERIFY_MODE:none}"
}
}
user/password 필드가 아예 없다
auth_type만 aws_iam으로 설정하면 끝이다
환경변수
# 비밀번호 방식 (필요한 것들)
OPENSEARCH_PASSWORD=MyS3cr3tP@ssw0rd
# IAM 방식 (필요한 것들)
AWS_REGION=ap-northeast-2
PROJECT_NAME=2sec-siem
비밀번호 자체가 필요 없다
Terraform이나 AWS CLI에서 IAM Policy 작성할 때 보이는 "Version": "2012-10-17" 이거 날짜가 뭔지 궁금했다
이건 Policy 생성 날짜가 아니다
AWS IAM Policy 문법의 버전이다
역사
2008-10-17: IAM Policy 처음 나온 버전 (구버전)2012-10-17: 현재 표준 버전 (신버전)2012년 10월 17일에 새로운 Policy 문법이 나왔고, 지금도 이게 최신 버전이다
그래서 모든 IAM Policy에 "Version": "2012-10-17" 쓴다
# ECS Task에 할당할 IAM Role
resource "aws_iam_role" "logstash_task" {
name = "logstash-task-role"
assume_role_policy = jsonencode({
Version = "2012-10-17" # IAM Policy 문법 버전
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}]
})
}
# OpenSearch 쓰기 권한만 부여
resource "aws_iam_role_policy" "logstash_opensearch" {
name = "opensearch-write-policy"
role = aws_iam_role.logstash_task.id
policy = jsonencode({
Version = "2012-10-17" # IAM Policy 문법 버전
Statement = [{
Effect = "Allow"
Action = [
"es:ESHttpPost", # 문서 쓰기
"es:ESHttpPut" # 인덱스 생성
]
Resource = "${aws_opensearch_domain.main.arn}/*"
}]
})
}
핵심 포인트
ESHttpPost: 문서 쓰기 권한ESHttpPut: 인덱스 생성 권한ESHttpGet) 없음ESHttpDelete) 없음최소 권한 원칙
Logstash가 필요한 것만 정확히 준다
resource "aws_opensearch_domain" "main" {
domain_name = "2sec-siem"
# IAM 인증 활성화
advanced_security_options {
enabled = true
internal_user_database_enabled = false # IAM만 사용
}
# 접근 정책
access_policies = jsonencode({
Version = "2012-10-17" # IAM Policy 문법 버전
Statement = [{
Effect = "Allow"
Principal = {
AWS = aws_iam_role.logstash_task.arn
}
Action = "es:*"
Resource = "${aws_opensearch_domain.main.arn}/*"
}]
})
}
주요 설정
internal_user_database_enabled = false: 내부 사용자 DB 비활성화, IAM만 사용access_policies: Task Role만 접근 허용resource "aws_ecs_task_definition" "logstash" {
family = "logstash"
# Task Role 할당
task_role_arn = aws_iam_role.logstash_task.arn
container_definitions = jsonencode([{
name = "logstash"
image = "docker.elastic.co/logstash/logstash:8.11.0"
environment = [
{
name = "AWS_REGION"
value = "ap-northeast-2"
},
{
name = "PROJECT_NAME"
value = "2sec-siem"
}
]
# 비밀번호 관련 환경변수 없음
}])
}
secrets 섹션 자체가 필요 없다
| 계정/Role | 용도 | 권한 |
|---|---|---|
| admin | 사람이 대시보드 접근 | 모든 권한 |
| logstash_task_role | Logstash 컨테이너 | 쓰기 전용 |
| readonly_role | 분석가 | 읽기 전용 |
admin 계정 사용 시나리오
logstash_task_role 사용 시나리오
admin 계정은 사람만 쓴다
서비스는 전부 Task Role 쓴다
시나리오: Logstash 컨테이너 탈취
공격자가 Logstash 컨테이너 접근 성공
↓
환경변수 확인
↓
비밀번호 없음 (IAM 방식이라)
↓
Task Role 자격증명만 있음
↓
쓰기 권한만 있어서 데이터 삭제/변조 불가
↓
최악의 경우: 쓰레기 로그만 넣을 수 있음
비밀번호 방식이었으면
공격자가 Logstash 컨테이너 접근 성공
↓
환경변수에서 admin 비밀번호 획득
↓
OpenSearch 전체 접근 가능
↓
모든 데이터 삭제/변조 가능
↓
클러스터 설정 변경 가능
차이가 명확하다
모든 OpenSearch 접근이 CloudTrail에 기록된다
{
"eventTime": "2025-12-15T10:39:15Z",
"eventSource": "es.amazonaws.com",
"eventName": "ESHttpPost",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAXXXXXXXXX:logstash-task",
"arn": "arn:aws:sts::123456789012:assumed-role/logstash-task-role/logstash-task"
},
"requestParameters": {
"index": "2sec-siem-2025.12.15",
"operation": "_doc"
},
"responseElements": {
"result": "created",
"_id": "abc123"
},
"sourceIPAddress": "10.0.1.25"
}
확인 가능한 정보
eventTimeuserIdentity (Task Role)eventName (ESHttpPost)indexresponseElements비밀번호 방식은 이런 추적이 제한적이다
컨테이너 안에서
# ECS Task Metadata 확인
curl ${ECS_CONTAINER_METADATA_URI_V4}/task
# IAM Role 자격증명 확인
curl 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
출력 예시
{
"AccessKeyId": "ASIAXXX...",
"SecretAccessKey": "wJalr...",
"Token": "FwoGZXIv...",
"Expiration": "2025-12-15T22:39:15Z"
}
자동으로 발급된 임시 자격증명이다
[2025-12-15T10:39:15,123][INFO ][logstash.outputs.elasticsearch]
Using AWS IAM authentication
Region: ap-northeast-2
Service: es
[2025-12-15T10:39:15,456][INFO ][logstash.outputs.elasticsearch]
Successfully authenticated to OpenSearch
Index: 2sec-siem-2025.12.15
auth_type이 aws_iam이면 이런 로그 나온다
# Logstash 컨테이너 안에서
curl -X POST "https://opensearch-endpoint/2sec-siem-test/_doc" \
--aws-sigv4 "aws:amz:ap-northeast-2:es" \
-H "Content-Type: application/json" \
-d '{"test": "data"}'
SigV4 서명 자동으로 추가된다
만약 IAM 방식이 당장 부담된다면?
차선책이 있다
OpenSearch 내부 사용자 DB에 ingest 전용 계정 만든다
# OpenSearch Dashboards에서
POST /_plugins/_security/api/internalusers/logstash_ingest
{
"password": "랜덤생성비밀번호",
"backend_roles": ["ingest_role"]
}
# Role 생성
POST /_plugins/_security/api/roles/ingest_role
{
"cluster_permissions": ["cluster_composite_ops"],
"index_permissions": [{
"index_patterns": ["2sec-siem-*"],
"allowed_actions": ["create_index", "write"]
}]
}
Terraform으로 비밀번호 관리
# 비밀번호 생성
resource "random_password" "logstash_ingest" {
length = 32
special = true
}
# Secrets Manager에 저장
resource "aws_secretsmanager_secret" "logstash" {
name = "logstash-opensearch-password"
}
resource "aws_secretsmanager_secret_version" "logstash" {
secret_id = aws_secretsmanager_secret.logstash.id
secret_string = random_password.logstash_ingest.result
}
# ECS Task Definition
resource "aws_ecs_task_definition" "logstash" {
container_definitions = jsonencode([{
secrets = [{
name = "OPENSEARCH_PASSWORD"
valueFrom = aws_secretsmanager_secret.logstash.arn
}]
}])
}
장점
단점
가능하면 IAM으로 가는 게 맞다
C Parser → JSON → Logstash → OpenSearch → LLM CTI
각 단계 설명
| 단계 | 역할 | 형식 |
|---|---|---|
| C Parser | 로그 정제 | JSON |
| Logstash | 데이터 전송 | Bulk API |
| OpenSearch | 인덱싱 | Document |
| LLM CTI | 분석 | Embedding |
입력 형식
{
"timeline": [
{
"timestamp": "2025-12-12 10:39:15",
"attack_type": "SQL_INJECTION",
"payload": "' OR 1=1--",
"success": false
},
{
"timestamp": "2025-12-12 10:39:20",
"attack_type": "SQL_INJECTION",
"payload": "' UNION SELECT NULL--",
"success": false
},
{
"timestamp": "2025-12-12 10:39:25",
"attack_type": "SQL_INJECTION",
"payload": "' AND SLEEP(3)--",
"success": true
}
]
}
기대 출력
{
"attack_stage": "reconnaissance → exploitation",
"risk_level": "high",
"recommendation": "Block source IP and patch SQL vulnerability"
}
1단계: RAG 기반 분석
과거 공격 패턴 벡터 DB에 저장
유사한 공격 발생 시 참조
2단계: 로그 누적
공격 성공/실패 케이스 수집
패턴 분석을 위한 데이터셋 확보
3단계: LoRA Fine-tuning
SIEM 로그 이해력 향상
특정 공격 유형 탐지 정확도 개선
C 파서가 해결한 문제
IAM 인증이 해결한 문제
두 가지를 합치니까 전체 파이프라인이 깔끔해졌다
기술적 측면
설계적 측면
C 파서 개선
LLM CTI 연동
모니터링 강화
| 분류 | 기술 |
|---|---|
| 로그 파싱 | C (C99) |
| 로그 수집 | Logstash 8.11 |
| 데이터 저장 | OpenSearch 2.11 |
| 인증 | AWS IAM (SigV4) |
| 인프라 | ECS Fargate, Terraform |
| 모니터링 | CloudWatch, CloudTrail |
| LLM 분석 | (구축 중) |