← Jayverse

가짜 편지 — 오염된 메모리가 송금을 돌린다

Patlan 외(2025)는 ElizaOS 에이전트의 메모리에 Discord 로 올린 위조 '지난 대화'를 심었고, 나중에 X 에서 들어온 송금 요청이 공격자 주소로 갔습니다(Sepolia, 연구 데모). 아래는 그 흐름을 주인 @kindofblue91 로 다시 짠 시뮬레이션입니다. 모델 호출도, 서명도, 트랜잭션도 없습니다.

↓ 기술 노트 보기 (목적·동작 방식)

켜면 검증자가 '주인이 서명한 요청'의 수신자를 직접 확인합니다 (메모리는 입력이 아님).
공격자Discord@kindofblue91주인 · X공유 메모리깨끗함에이전트 (시스템)대기금고100 jUSD공격자 지갑0 jUSDAlice0 jUSDV1V2V3V4V5검증자 5 · 쿼럼 3

①부터 누르면 공격이, ①을 건너뛰고 ②부터 누르면 정상 송금이 진행됩니다. 방어를 켜고 다시 해 보세요.

무엇을 보여 주나

메모리는 출처 없는 입력이다

모든 플랫폼이 하나의 메모리 로그를 공유하면, 누구든 글 한 줄로 '주인이 예전에 한 말'을 만들 수 있습니다. 간접 프롬프트 인젝션(Greshake 외)이 한 번으로 끝나지 않고 저장되어 계속 작동합니다.

검증자 다섯이 같은 걸 보면 하나다

에이전트는 자기 키로 서명하므로 서명은 진짜이고, 금액도 한도 안입니다. 검증자가 그 둘만 보면 5/5 로 통과합니다 — Bybit 의 '유효한 서명 세 개'와 같은 구조입니다.

방어는 모델 밖에서, 원래 요청에 묶는다

방어를 켜면 각 검증자가 주인이 서명한 X 요청에서 수신자를 직접 꺼내 tx.to 와 비교합니다. 메모리는 이 검사의 입력이 아니므로, 메모리를 아무리 오염시켜도 결과가 바뀌지 않습니다.

실제 계정으로 하면: Discord 로 명령, X 로 영수증

시나리오: Discord 에서 "Alice 에게 10 jUSD 보내 줘", 영수증은 X 에. 계정마다 맡은 일이 하나씩 있습니다. Discord 는 지휘 채널입니다. 거기서 명령을 내리고, 시스템은 그 명령이 정말 내게서 왔다는 걸 증명할 수 있습니다. X 는 공개 게시판입니다. 에이전트는 거기에 올리기만 하고, 절대 명령을 받지 않습니다.

#어디서 어디로무슨 일이
①Discord → 서버내가 /pay alice 10 을 입력합니다. Discord 가 서명한 요청을 서버가 DISCORD_PUBLIC_KEY 로 검증합니다.
②서버보낸 사람의 사용자 id 가 jay 인지 확인합니다(허용 목록).
③서버 → Discord3초 안에 "처리 중…" 으로 먼저 답합니다(지연 응답).
④서버Cloud Tasks 작업에서 에이전트가 송금을 계획합니다. 받는 사람 "alice" 는 내 명령에서 가져오고, 메모리나 대화 기록에서는 절대 가져오지 않습니다.
⑤서버 → Discord[확인] [취소] 버튼이 달린 후속 메시지를 보냅니다.
⑥Discord → 서버확인을 누르면 서명을 다시 검증한 뒤에야 10 jUSD 를 보냅니다(테스트넷).
⑦서버 → Discord인터랙션 토큰으로 "완료, tx 0x…" 를 알립니다.
⑧서버 → X@kindofblue91 로 영수증을 게시합니다: "alice 에게 10 jUSD · tx 0x…"

각 키가 하는 일

키쓰이는 곳비밀?
DISCORD_PUBLIC_KEY①, ⑥: 요청이 정말 Discord 에서 왔는지 증명아니오
DISCORD_APPLICATION_ID/pay 등록, ⑤·⑦ 후속 메시지아니오
DISCORD_BOT_TOKEN명령 등록, 또는 인터랙션 없이 채널에 글쓰기예
X OAuth 2.0 user token (tweet.write)⑧, @kindofblue91 로 게시예

안전하게 만드는 규칙 세 가지

1. 권한은 Discord 가 서명한, 내 id 에서 온 명령에만 있다

다른 Discord 메시지나 X 멘션은 전부 데이터일 뿐입니다. 에이전트가 읽을 수는 있어도, 그것으로 송금이 일어날 수는 없습니다. 위 시뮬레이션의 ① 오염 메시지가 바로 이 "다른 메시지"입니다.

2. 받는 사람은 내가 보낸 명령에서 온다

메모리에서 가져오지 않습니다. 서명은 내가 아니라 Discord 가 하고(①), 보낸 사람이 나인지는 id 로 따로 확인합니다(②). 위 시뮬레이션에서 "방어"를 켰을 때 검증자들이 하는 일이 이것입니다.

3. 돈은 확인 버튼 뒤에만, X 는 쓰기 전용

⑥ 전에는 아무것도 움직이지 않습니다. 에이전트가 X 멘션을 명령으로 읽으면, 전 세계 누구나 트윗으로 명령을 내릴 수 있게 됩니다.

더 단순한 변형

  • Discord 만: ①–⑦ 단계. 플랜의 T4 는 이 중 읽기 전용 부분(/ask, /status)만 만듭니다. /pay 는 계획에 없습니다.
  • X 만: 크론 작업으로 매일 "에이전트 상태" 트윗. 들어오는 경로가 없으니 주입할 틈도 없습니다.

이 페이지는 Discord 나 X 에 연결되지 않습니다. 위의 흐름은 설명이고, 실제 봇은 아직 없습니다. T4 는 읽기 전용 봇을 계획합니다. X 게시는 X API 요금을 먼저 확인해야 합니다.

기술 노트

각 항목이 실제로 무엇을 보여주고 어떻게 동작하는지 — 위 카드보다 자세한 기술 설명입니다.

가짜 편지 — 오염된 에이전트 메모리가 송금을 돌린다라이브

목적: 에이전트 보안 논의는 대개 모델의 판단을 다룹니다. Patlan 외(2025)는 더 싼 길을 보였습니다. ElizaOS 에이전트는 모든 플랫폼에 메모리 로그 하나를 쓰기 때문에, Discord 의 공격자가 올린 글이 나중에 '주인이 한 말'로 다시 읽히고, 주인이 X 로 요청한 송금이 공격자에게 갑니다(Sepolia, 연구 데모). 이 데모는 두 가지를 눈에 보이게 합니다. 메모리는 신뢰할 수 없는 입력이고, 저장되는 순간 간접 프롬프트 인젝션이 지속성을 얻습니다. 그리고 검증자들이 모두 같은 성질, 즉 에이전트 자신의 서명만 본다면 아무것도 더하지 못합니다. 오염된 에이전트도 그 서명은 정직하게 만들어 내니까요.

동작 방식: 다섯 단계짜리 클라이언트 상태 기계이고 LLM 은 호출하지 않습니다. ①은 출처 표시 없이 위조 줄 하나를 공유 메모리에 씁니다. ②는 주인의 X 요청 "Alice 에게 10 jUSD 보내 줘"입니다. ③에서 에이전트가 메모리로 수신자를 정하므로 오염된 경우 0xBAD 로 계획합니다. ④ 검증자 다섯이 쿼럼 3으로 투표합니다. 기본 모드에서는 각자 에이전트 서명과 금액 한도를 보는데 둘 다 참이라 5/5 입니다. 방어 모드에서는 각자 주인이 서명한 요청에서 수신자를 다시 꺼내 tx.to 와 비교합니다. 메모리는 입력이 아니므로 오염된 계획은 0/5 입니다. ⑤는 쿼럼일 때만 돈을 옮깁니다. 가리키는 실제 해법은 의도 바인딩입니다. 무엇을 지불해도 되는지는 에이전트의 문맥이 아니라 사람이 서명한 요청이 정합니다.

기본 vs 방어 — 수신자가 어디서 오나