[하루한줄] CVE-2024-12085: rsync의 Uninitialized Stack Contents로 인한 Info Leak
URL
Target
- rsync < 3.4.0
Explain
최근 구글 연구원들이 발견한 rsync의 취약점 6개가 공개되었습니다. 오늘은 그 중 CVE-2024-12085 취약점에 대해 공부해보려고 합니다.
CVE-2024-12085 취약점은 초기화하지 않은 스택 변수를 통해 메모리를 유출할 수 있는 취약점입니다.
/* sum2를 스택 위에 선언하지만, 아래에서 일부 길이만 채워질 수도 있음 */
char sum2[MAX_DIGEST_LEN];
...
int done_csum2 = 0;
...
if (!done_csum2) {
map = (schar *)map_ptr(buf, offset, l);
get_checksum2((char *)map, l, sum2);
done_csum2 = 1;
}
/* 바로 아래와 같은 비교를 할 때, s->s2length가 실제 sum2가 계산된 길이보다 크면
미초기화된 sum2의 나머지를 참조하게 됨 */
if (memcmp(sum2, sum2_at(s, i), s->s2length) != 0) {
false_alarms++;
continue;
}
sum2 버퍼를 충분히(또는 전혀) 초기화하지 않은 상태에서 memcmp(sum2, sum2_at(s, i), s->s2length) 비교를 수행해, s->s2length 값이 실제 체크섬 길이보다 크게 세팅될 경우(조작될 경우) sum2 배열의 미초기화 영역을 참조하게 됩니다. 이 과정에서 스택에 남아 있던 1바이트(또는 그 이상이 될 수 있다고 생각하는데 취약점 설명에는 1바이트만 leak된다고 합니다)가 비교 과정에서 밖으로 leak될 수 있습니다.
char sum2[MAX_DIGEST_LEN];
로 선언된 지역 배열이 전부 0으로 초기화되지 않습니다.get_checksum2()
함수가 실제로 해시를 계산해sum2
에 쓰는 길이는 해시 알고리즘에 따라 고정되거나 제한됩니다(예: 16바이트, 20바이트 등).get_checksum2()는 다음 과정을 거칩니다:
/* The "sum" buffer must be at least MAX_DIGEST_LEN bytes! */ void get_checksum2(char *buf, int32 len, char *sum) { /* ... */ switch (xfer_sum_nni->num) { case CSUM_XXH64: /* 8바이트만 씀 */ SIVAL64(sum, 0, XXH64(buf, len, checksum_seed)); break; case CSUM_XXH3_128: /* 16바이트만 씀 (low64 + high64) */ XXH128_hash_t digest = XXH3_128bits_withSeed(buf, len, checksum_seed); SIVAL64(sum, 0, digest.low64); SIVAL64(sum, 8, digest.high64); break; case CSUM_MD5: /* md5_result()로 16바이트 작성 */ md5_result(&m5, (uchar *)sum); break; /* ... */ } }
- 해시 알고리즘에 따라 고정된 길이 혹은 그에 준하는 길이의 데이터를 sum 버퍼에 쓴다. 예를 들어 XXH64면 8바이트, XXH3_128이면 16바이트, MD5나 MD4도 16바이트를 결과값으로 씁니다.
- 그 뒤 나머지 바이트(버퍼의 최대 길이인 MAX_DIGEST_LEN에서 실제 해시 길이를 뺀 나머지)는 전혀 초기화하지 않습니다.
s->s2length
가 조작되거나 의도치 않게 실제 해시 길이보다 클 경우,memcmp(sum2, sum2_at(s, i), s->s2length)
호출 시sum2
배열 중 계산된 해시가 쓰이지 않은 나머지 영역(미초기화 영역)을 비교하게 됩니다.- 결국 스택에 남아 있던 데이터(최대 s2length - 해시 실제 길이만큼)가 유출될 가능성이 생깁니다.
해당 취약점은 함수 시작 부분에 sum2 변수를 초기화하는 코드를 추가하는 것으로 패치되었습니다.
@@ -147,6 +147,9 @@ static void hash_search(int f,struct sum_struct *s,
int more;
schar *map;
+ // prevent possible memory leaks
+ memset(sum2, 0, sizeof sum2);
/* want_i is used to encourage adjacent matches, allowing the RLL
* coding of the output to work more efficiently. */
want_i = 0;
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.