r21
r17
1[[분류:the seed]] [[분류:보고서]]
r1

(새 문서)
2
3[목차]
4[clearfix]
5
6== 개요 ==
r5
72025년 9월 19일에 [[사용자:hyonsu]]에 의해 최초 발견되어 2025년 9월 23일 01시 (GMT +9)에 v4.28.0.897에서 해결된 the seed engine의 XSS 취약점.
r1

(새 문서)
8
9== 배경 설명 및 원인 ==
r17
10the seed engine에서는 문서 및 이름공간마다 사용자의 접근을 제한할 수 있는 [[ACL]] 시스템을 제공하고 있다. 일례로 [[더시드위키:대문]] 문서의 [[https://theseed.io/acl/더시드위키:대문|ACL]] 설정은 문서의 경우 편집, 토론 생성, ACL이, 더시드위키: 이름공간의 경우 편집, 이동, 삭제, ACL 권한이 '''aclgroup:관리자'''로 설정되어 있어, 해당 ACLGroup에 속한 사용자가 아니면 상술된 동작이 차단된다.
r1

(새 문서)
11
r17
12이 ACL을 적용할 대상은 권한, 사용자, 아이피, GeoIP, 그리고 ACLGroup의 이름을 적음으로서 설정할 수 있는데, 사용자, 아이피, GeoIP와 다르게 권한[* 시기 불명의 업데이트 이후로 F12를 통한 단순 이름 수정은 저장 시점에 원래 이름으로 롤백되나, 요소를 복제 후 수정하거나 내부 API 요청을 하는 방식으로 우회가 가능함.]과 ACLGroup 엔티티[* 존재하는 ACLGroup만 적을 수 있으나 그 이름으로 된 ACLGroup을 만들면 그만이므로]는 이름의 제약이 사실상 없다.
r1

(새 문서)
13
r17
14또한 문서나 토론에서 특정 동작을 실행 시 ACL 조건에 미달하면 거부 후 경고 메시지[* 오류: (동작) 권한이 부족합니다. '''특정 조건''' 이어야 합니다.]를 출력하는 과정에서 '''특정 조건'''에 대한 이스케이핑을 하지 않아 사용자가 지정한 내용이 그대로 출력되었다. 따라서 이는 ACL을 조정 가능한 사용자가 임의의 HTML 태그를 삽입하여 문서를 편집 및 편집 요청 시도하는 사용자를 대상으로 XSS 공격을 할 수 있는 취약점으로 이어졌다.
r1

(새 문서)
15
16== 예시 ==
17[[사용자:39.7.59.164]] 문서의 [[https://theseed.io/acl/사용자:39.7.59.164|ACL]] 탭을 보면, {{{perm:<script>alert('xss');</script>}}}과 {{{perm:<script>window.location.assign('https://theseed.io/thread/RipeLuckyBrashWood')</script>}}}이 편집 조건으로 걸려있는 것을 볼 수 있다.
18
r17
19이는 엔진에 존재하지 않는 권한이기 때문에 문서 편집을 시도 시 편집이 거부되며 편집 요청으로 넘어가는데, 패치 전에는 '''편집 권한이 부족합니다. perm: OR perm: 이어야 합니다.''' 라는 메시지가 뜨며 ACL에 삽입한 내용이 textContent가 아니라 innerHTML로 출력되는 현상이 발생했었다.
r1

(새 문서)
20
r20
21다행히 [[https://www.w3.org/TR/2008/WD-html5-20080610/dom.html#innerhtml0|W3C HTML5]] 스펙 상 차후에 {{{.innerHTML}}} 등으로 포함된 inline script는 차단되는 것이 표준이라 위키 내부 링크를 타고 들어온 경우 별 일이 없었으나, 그 상태에서 페이지를 새로고침하거나 애초에 외부 링크를 타고 들어온 경우 SSR을 사용하므로 해당 제한의 대상이 아니었다. 때문에 {{{alert('xss')}}}가 실행되며 경고 창이 뜨고, 이후 [[https://theseed.io/thread/RipeLuckyBrashWood]] 페이지로 강제 납치되었다.
r1

(새 문서)
22
r21
23[[https://revi.wiki/wiki/파일:더시드_XSS_20250919.mp4|(외부 링크) 해당 취약점 시연 영상]][* [[사용자:revi]]에 의해 [[https://theseed.io/thread/AboundingGlibLittleBait#318|제공]]됨.][* 파이어폭스뿐만 아니라 크롬 웹브라우저(143.0.7499.193 기준)에서도 영상 시청이 불가능하다. mp4가 맞지만 mp3처럼 소리만 들리고 재생바만 덩그러니 보인다. 이렇게 된 원인은 알 수 없지만 크롬이 mp4 파일 영상과 호환되지 않는 웹브라우저일 가능성도 있다.]
r1

(새 문서)
24== 위험성 ==
r20
25하나의 ACL 엔티티는 그 길이를 최대 255자까지밖에 지정할 수 없기는 하나, 추가할 수 있는 ACL 엔티티의 개수에는 제한이 없기에 사실상 무용지물로, 내부에 값을 잔뜩 추가한 다음 eval()을 해버리면 간단히 우회가 가능했었다. 따라서 공격자가 공격 코드의 길이 제한을 간단하게 없애버릴 수 있었다. 이 때문에 길이 제한 없이 공격자가 사실상 자유롭게 공격 가능한 환경이 조성되었다.
r1

(새 문서)
26
r20
27다행히 the seed engine을 사용하는 위키에는 [[2017년 나무위키 XSS 공격 보고서]]에서도 언급한 바 있듯이 Content Security Policy가 별도로 적용되어 있고, 쿠키에는 모두 Secure 및 HttpOnly 플래그가 붙어있어 공격자가 쿠키를 획득한다든가, 외부 XHR을 생성한다든가 할 수는 없었다.
r1

(새 문서)
28
r20
29== 대응 ==
r1

(새 문서)
30=== 더시드위키에서 ===
r18
31ACL 조정은 [[admin]] 권한이 있어야 가능하나, 이 권한은 대부분의 사용자들이 아무 제약 없이 받을 수 있는 권한이기에 발견 즉시 모든 사용자의 ACL 조정이 [[https://theseed.io/thread/AwfulAdDeliciousTurn|제한]]되었고, v4.28.0.897에서 해결된 것을 확인 후 ACL 조정 제한이 해제되었다.
r1

(새 문서)
32
33=== 기타 the seed engine을 사용하는 위키에서 ===
r19
34[[admin]] 권한은 관리자에게만 부여되는 것이 정책이고, 취약점 자체도 최초 발견자가 더시드위키[* 정확히는 [[namu]]에게]에만 자세한 내용을 공개하였기 때문에 [[https://theseed.io/thread/RipeLuckyBrashWood]] 토론을 열람할 수 있었던 (= [[틀:정식 관리자 목록]]) 사용자를 제외하고는 해결 전까지 해당 취약점의 존재 자체를 알지 못했을 것으로 추정된다.[* 취약점 검증을 위한 ACL 조정은 사용자 문서에서 이루어졌기 때문에 최근 변경에 표시되지 않았고, 검증을 한 정식 관리자의 문서 기여 목록을 방문한 후, 해당 문서의 편집 창을 수동으로 접속하지 않은 이상 (상술했다시피 편집 버튼을 누르는 것만으로는 편집 요청으로 이동되므로 XSS 대상이 되지 않는다.) 취약점을 발견할 수 없었을 것이다.]
35
36== 여담 ==
37이 취약점을 발견하고 고치는 과정에서 [[이 문서 ACL 탭에서 이상한 것 찾아보세요]] 문서의 ACL 탭을 확인해보면
38
39> * 입력 (문서 역사 참고)
40> 1. {{{insert,edit,gotons,perm:constructor}}}
41> 1. {{{insert,edit,gotons,perm:__proto__}}}
42> 1. {{{insert,edit,gotons,perm:valueOf}}}
43> * 출력 (ACL)
44> 1. [object Object] 이름공간ACL 실행
45> 1. function Object() { [native code] } 이름공간ACL 실행
46> 1. function valueOf() { [native code] } 이름공간ACL 실행
47으로 입력값과 다른 값이 나오는 것이 뒤늦게 발견되었다. 이 문제는 프론트엔드에서 내부적으로 ACL 조건이 '''perm'''이라면 원래 값인 any, member 등을 보기 좋게 변환하기 위해 권한 이름이 매핑된 객체에 접근하면서 생긴 것[* Javascript에서 객체의 값에 접근하는 것은 {{{object.key}}}뿐 아니라 {{{object['key']}}}로도 가능한데, 기본 정의된 키뿐 아니라 constructor에 정의된 내장 메서드의 이름 (예: {{{object['toString']}}})을 집어넣으면 그곳에도 접근이 가능해진다.]으로 추정된다.
48
49상술한 취약점과 다르게 이 문제를 악용하여 악의적 페이로드를 주입할 수는 없으나, 어쨌든 의도되지 않은 동작인 것은 분명하기 때문에 v4.28.0.897 업데이트 이후 다시 한 번 버전명 변경 없이 프론트엔드 업데이트가 되며 해결되었다. Map을 하나 생성하여 .get()으로 확실히 그 안에 있는 키만 가져오는 것으로 변경된 모양.