flawopen.com/AI Security/ghostjacking-ai-agent-poisoned-observability-logs-august-2026
GhostJacking: 로그와 AI 에이전트
연구는 조작된 텔레메트리가 연동 에이전트에 영향을 주는 방식을 보여 줍니다. CVE나 확인된 피해자는 없습니다.[1], [2]
접수 담당자가 수상한 배송을 막으면서 발신자의 메모를 사건 로그에 그대로 옮겨 적었다고 생각해 보세요. 나중에 담당자는 실제 티켓 번호와 테스트 구성의 DNS 정책에서 실제로 확인된 누락을 확인한 뒤, 같은 메모에 적힌 공격자 지정 주소까지 믿고 브라우저와 이메일에 회사 서비스의 위치를 알려 주는 공개 주소록(DNS)을 바꿉니다. 로그는 증거이지 변경 권한이 아닙니다. 별도의 정책 검사와 승인이 변경을 통제해야 합니다.[1]
핵심 개념 및 용어
GhostJacking- Tenet Security가 관측 가능성 이벤트의 공격자 제어 콘텐츠가 해당 도구와 연결된 AI 에이전트에 영향을 주는 시연에 붙인 이름입니다. 연구 명칭이며 CVE가 아닙니다.[1], [2]
프레임워크 매핑
- MITRE CWE에 대한 FlawOpen의 개념적 매핑입니다. 공급업체나 CVE 레코드가 이 연구 시연에 해당 약점 ID를 지정한 것은 아닙니다. CWE-1427
- OWASP GenAI 2025 — LLM01:2025 프롬프트 인젝션
- OWASP GenAI 2025 — LLM06:2025 과도한 에이전시
단계별 공격 실행 흐름
에이전트에 조사 권한이 부여됩니다
Tenet의 연구는 Cloudflare, Datadog, Sentry 같은 도구에 연결된 개발 또는 운영 에이전트를 설명합니다. 중요한 전제는 에이전트가 외부 이벤트 콘텐츠를 읽고 기존 권한으로 중요한 작업을 수행할 수 있다는 점입니다.[1]
기존 권한이 영향으로 이어집니다
Tenet은 통제된 시연에서 연동된 Cloudflare 도구를 통해 에이전트가 DNS를 변경했다고 보고합니다. 결과는 에이전트 권한과 구성에 따라 달라지며 Cloudflare 자체가 침해되었거나 고객 시스템이 공격받았다는 증거는 아닙니다.[1]
소스 코드 비교: 취약한 구현 vs 보안 패치
이 코드는 애플리케이션을 설명하기 위한 예시이며 언급한 제품의 취약하거나 수정된 실제 소스 코드가 아닙니다. 취약한 형태는 신뢰할 수 없는 이벤트 텍스트를 읽는 에이전트에 변경 도구를 직접 제공합니다. 보호 형태는 제안과 실행을 분리합니다. 독립 서비스가 주체와 정확한 변경 사항에 묶인 일회성 승인을 사용하고 공급자를 호출하기 전에 정책을 적용합니다. 주체 ID는 호출자가 제어하는 필드가 아니라 인증된 서버 컨텍스트에서 가져오며 모델이 이를 선택할 수 없습니다. 프롬프트 지시만으로는 권한 부여가 되지 않습니다. 실제 코드에서는 스키마, 테넌트 및 리소스 소유권, 승인 만료, 재사용 방지, 감사 데이터의 영속성, 공급자 오류도 검증해야 합니다.[1], [2], [5]
// 안전하지 않은 연동의 예시이며 Cloudflare, Datadog, Sentry, Tenet의 소스 코드가 아닙니다.
async function investigate(event: EventRecord) {
return agent.run({
context: event.message,
tools: [cloudflare.updateDnsRecord]
});
}
// 제어 경계의 예시입니다. 모델은 제안할 수 있지만 승인하거나 적용할 수 없습니다.
type DnsProposal = { zoneId: string; name: string; value: string };
async function proposeDnsChange(event: EventRecord): Promise<DnsProposal> {
return agent.propose({ evidence: markUntrusted(event.message) });
}
async function applyDnsChange(
proposal: DnsProposal,
approvalId: string
) {
const actorId = auth.requireAuthenticatedActorId();
const validatedProposal = DnsProposalSchema.parse(proposal);
const approval = await approvals.consumeOnce(approvalId, { actorId });
if (!approval || approval.actorId !== actorId ||
approval.zoneId !== validatedProposal.zoneId ||
approval.name !== validatedProposal.name ||
approval.value !== validatedProposal.value ||
!policy.canChangeDns(actorId, validatedProposal.zoneId, validatedProposal.name)) {
throw new Error('DNS change is not independently authorized');
}
// 변경 전에 권한 부여 결정을 기록할 수 없으면 작업을 거부합니다.
await audit.record({ actorId, approvalId, action: 'dns-change', decision: 'authorized' });
try {
await cloudflare.updateDnsRecord(validatedProposal);
} catch (error) {
await audit.record({ actorId, approvalId, action: 'dns-change', outcome: 'provider-failed' });
throw error;
}
await audit.record({ actorId, approvalId, action: 'dns-change', outcome: 'succeeded' });
}
엔지니어링 및 시스템 보안 강화 체크리스트
- ✓에이전트 컨텍스트를 구성할 때 외부에서 영향을 줄 수 있는 로그, 경보, 추적, 오류, 티켓 필드를 모두 신뢰할 수 없는 것으로 표시하고 변환 후에도 출처를 보존합니다.[1], [2]
- ✓조사 에이전트에는 기본적으로 읽기 전용 자격 증명을 제공합니다. DNS, 클라우드, 저장소, 배포 변경은 별도 정책 적용 서비스 뒤에 둡니다.[1], [5]
- ✓인증된 주체, 테넌트, 리소스, 정확한 변경 사항, 현재 정책에 따라 모든 동작을 모델 밖에서 승인합니다. 모델 신뢰도나 도구 사용 가능성을 승인으로 간주하지 않습니다.[2], [5]
- ✓영향이 큰 변경에는 정확한 diff, 리소스, 주체, 짧은 만료 시간에 묶인 사람의 승인을 요구합니다. 승인은 한 번만 사용하고 재사용이나 변경된 제안은 거부합니다.[2], [5]
- ✓그럴듯한 참조와 사실, 그리고 상충하는 미검증 변경 제안을 포함한 비신뢰 텔레메트리로 회귀 테스트를 추가하세요. 에이전트가 이벤트를 요약할 수는 있어도 독립적인 승인 없이는 변경 API를 호출하지 않는지 확인하세요.[1]
- ✓조사 작업에서 발생한 쓰기 시도를 경고하고 사고 검토를 위해 원본 이벤트, 모델 제안, 정책 결정, 승인자, 공급자 응답을 보존합니다.[1]
- ✓각 연동의 실제 권한과 토큰 범위를 확인합니다. 공개 클라이언트 식별자가 반드시 특권 비밀은 아닙니다. 노출된 자격 증명이라고 부르기 전에 해당 토큰이 할 수 있는 작업을 시험합니다.[1], [2]