
PictShare < 3.7.1에서 rand()를 통한 예측 가능한 delete_code (CWE-338). PoC + 권고문 작성.
rand()를 통한 예측 가능한 delete_code허가된 보안 연구. 방어 및 교육 목적으로만 사용되는 PoC입니다.
PictShare는 delete_code
인가 토큰을 getRandomString()으로 생성하는데, 이 함수는 모든 문자를 PHP의
비암호학적 rand()에서 가져옵니다. rand()는 CSPRNG가 아니며 — 동일한 생성기가
모든 공유 URL에 표시되는 공개 파일 해시도 생성하기 때문에 — delete_code는 비밀이
아니라 예측 가능하며, info 엔드포인트에서 코드를 읽지 않고도 호스팅된 파일을
무단으로 삭제할 수 있습니다.
| CVE | CVE-2026-104356 |
| 제품 | PictShare (자체 호스팅 이미지/미디어 호스트) |
| 영향 받는 버전 | >= 2.0.0, < 3.7.1 |
| 수정된 버전 | v3.7.1 |
| 취약점 | 암호학적으로 취약한 PRNG 사용 (CWE-338) → 예측 가능한 인증 토큰 |
| 권한 | 없음 (인증 불필요) |
| CVSS 3.1 | 5.9 MEDIUM — AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H |
| CVSS 4.0 | 8.2 HIGH — AV:N/AC:H/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H |
| 발견자 | Alisher Qarshibayev |
| 권고문 | VulnCheck |
// src/inc/core.php (< 3.7.1)
function getRandomString($length=32, $keyspace='0123456789abcdefghijklmnopqrstuvwxyz')
{
$str = '';
$max = (function_exists('mb_strlen') ? mb_strlen($keyspace,'8bit') : strlen($keyspace)) - 1;
for ($i = 0; $i < $length; ++$i) {
$str .= $keyspace[rand(0, $max)]; // <-- rand() is NOT cryptographically secure
}
return $str;
}
이 하나의 함수가 둘 다 생성합니다:
delete_code.두 토큰 모두 동일한 비 CSPRNG 스트림에서 나오므로, 공개 해시를 관찰한 공격자는
생성기의 상태에 대한 정보를 얻습니다 — delete_code는 32자 길이가 시사하는 엔트로피를
더 이상 갖지 않습니다. rand()는 암호학적 예측 불가능성 보장을 제공하지 않으므로,
이를 보안 토큰에 사용하는 것이 결함(CWE-338)이며, 특정 상태 복구 기법과는 무관합니다.
random_int()로 전환합니다.rand()(Mersenne-Twister
경로로 별칭됨)를 자동 시드하므로, 즉시 사용 가능한 원격 예측기는 배포 환경(프로세스
재사용, 관찰 가능한 출력 수, PHP 빌드)에 따라 달라집니다. 따라서 이 PoC는 보편적인
단일 원격 공격을 주장하기보다는, 통제된 시드에서 예측 가능성 원리를 결정론적으로
입증합니다. 이것이 이 발견의 솔직한 범위이며 — 바로 이 때문에 벤더가 여전히 이를
취약점으로 취급하고 수정한 것입니다.demo.php는 벤더의 정확한 getRandomString()을 재현하며, PRNG 상태가 알려지거나
복구되면 첫 번째 출력(공개 해시)이 두 번째 출력(delete_code)을 완전히 결정한다는
것 — 즉 해시를 관찰하면 코드를 예측할 수 있다는 것을 보여줍니다:
php demo.php
예상 출력:
[*] Reproducing PictShare getRandomString() with rand()
[*] Seed used by victim process : 1337 (unknown to attacker a priori)
public file hash (output #1): k3f9... (visible in the shared URL)
secret delete_code(output #2): 8a1c...
[*] Attacker recovers seed by matching the OBSERVED public hash:
[+] Seed recovered: 1337
[+] Predicted delete_code: 8a1c...
[+] MATCH — delete_code predicted from the public hash alone (no info API needed)
demo.php를 참조하세요. 이는 자체 포함되어 있으며(서버도 네트워크도 필요
없음) rand()를 여기에 부적합하게 만드는 해시→코드 결정성을 입증합니다.
PictShare 3.7.1(수정 커밋 ce5fc47)로
업그레이드하세요. 이 버전은 rand()를 random_int()(CSPRNG)로 대체합니다. 일반 지침:
모든 보안 토큰(삭제 코드, 재설정 토큰, API 키, 솔트)을 암호학적으로 안전한 RNG —
PHP에서는 random_int() / random_bytes() — 로 생성하고, 절대 rand(), mt_rand()
또는 uniqid()를 사용하지 마세요.
참고: 수정 이전에 발급된 삭제 코드는 교체되지 않으므로, 수정 전 업로드물은 재업로드할 때까지 예측 가능한 상태로 남습니다.
| 날짜 | 사건 |
|---|---|
| 2026-10-01 | 공개 공개, CVE 예약 및 게시 (VulnCheck), v3.7.1에서 수정 |
벤더에 책임 있게 공개하고 VulnCheck를 통해 조율했습니다. 이 PoC가 릴리스되기 전에 수정되었습니다. 방어 및 교육 목적으로 게시됩니다.