
CVE-2025-40019에 대한 개념 증명(PoC)
CVE-2025-40019는 Encrypted Salt-Sector Initialization Vector 메커니즘(crypto/essiv.c)에서 Associated Authentication Data(AAD) 길이에 대한 검증이 초기화 벡터(IV) 크기와 비교하여 충분하지 않아, 메모리 경계를 넘어 접근하는 취약점입니다. 이 저장소에 제공된 코드는 단순한 버그 트리거이지만, 이 버그는 쉽게 악용될 수 있습니다.
이 버그를 kernelctf 스프레드시트 여기에서 발견했습니다. 이 버그에 대한 블로그 포스트나 PoC가 없었기 때문에, 리눅스 커널의 암호 서브시스템을 탐구하여 문제의 원인을 알아보기로 결정했습니다.

ESSIV의 필요성을 이해하려면 디스크에 데이터가 어떻게 저장되는지 살펴봐야 합니다. 디스크 암호화(LUKS 또는 dm-crypt 등)는 일반적으로 섹터 단위로 작동합니다. 각 섹터는 독립적으로 암호화되어야 하므로, 하나의 섹터를 읽기 위해 전체 디스크를 읽을 필요가 없습니다.
표준 CBC(Cipher Block Chaining) 모드에서는 각 암호화 작업에 초기화 벡터(IV)가 필요합니다. 초기 디스크 암호화 구현에서는 섹터 번호를 IV로 사용했습니다.
하지만 섹터 번호는 예측 가능하기 때문에, 공격자는 "워터마킹 공격"을 수행할 수 있습니다. 알려진 섹터에 특별히 조작된 데이터를 기록함으로써, 공격자는 암호문에서 특정 파일의 존재를 드러내는 패턴을 관찰할 수 있으며, 이는 암호화의 기밀성을 효과적으로 우회합니다.
ESSIV(Encrypted Salt-Sector Initialization Vector)는 IV를 예측 불가능하게 만들기 위해 설계되었습니다. 다음과 같이 작동합니다:
이렇게 하면 공격자가 섹터 번호를 알고 있더라도, 비밀 키를 모르면 IV를 예측할 수 없습니다.
모든 알고리즘(기본 암호인 AES나 래퍼인 ESSIV 등)은 커널이 호출을 라우팅하는 데 사용하는 구조를 구현합니다:
struct skcipher_alg {
int (*setkey)(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen);
int (*encrypt)(struct skcipher_request *req);
int (*decrypt)(struct skcipher_request *req);
// ...
struct skcipher_alg_common co; // Contains ivsize, chunksize, etc.
};
이 취약점은 사용자가 제공한 메타데이터(AAD 길이)가 항상 암호화 변환의 내부 요구 사항을 만족할 것이라고 가정하는 전형적인 사례입니다. "in-place" 또는 복호화(!enc) 경로에서 코드는 오프셋: req->assoclen - crypto_aead_ivsize(tfm)을 계산합니다. 그러나 req->assoclen < ivsize인지 확인하지 않습니다. 즉, 이 오프셋이 음수가 될 수 있습니다.
static int essiv_aead_crypt(struct aead_request *req, bool enc)
{
struct crypto_aead *tfm = crypto_aead_reqtfm(req);
const struct essiv_tfm_ctx *tctx = crypto_aead_ctx(tfm);
struct essiv_aead_request_ctx *rctx = aead_request_ctx(req);
struct aead_request *subreq = &rctx->aead_req;
struct scatterlist *src = req->src;
int err;
crypto_cipher_encrypt_one(tctx->essiv_cipher, req->iv, req->iv);
/*
* dm-crypt embeds the sector number and the IV in the AAD region, so
* we have to copy the converted IV into the right scatterlist before
* we pass it on.
*/
rctx->assoc = NULL;
if (req->src == req->dst || !enc) {
scatterwalk_map_and_copy(req->iv, req->dst,
req->assoclen - crypto_aead_ivsize(tfm), // <------- bug !
crypto_aead_ivsize(tfm), 1);
} else {
u8 *iv = (u8 *)aead_request_ctx(req) + tctx->ivoffset;
int ivsize = crypto_aead_ivsize(tfm);
int ssize = req->assoclen - ivsize;
struct scatterlist *sg;
int nents;
.
.
.
scatterwalk_map_and_copy 함수를 살펴보면, 단순히 scatterlist sg에 memcpy를 수행하는 것을 알 수 있습니다:
static inline void scatterwalk_map_and_copy(void *buf, struct scatterlist *sg,
unsigned int start,
unsigned int nbytes, int out)
{
if (out)
memcpy_to_sglist(sg, start, buf, nbytes);
else
memcpy_from_sglist(buf, sg, start, nbytes);
}
이 버그에 대한 패치는 매우 간단합니다. assoclen이 ivsize보다 작은지만 확인합니다.
diff --git a/crypto/essiv.c b/crypto/essiv.c
index d003b78fcd855a..a47a3eab693519 100644
--- a/crypto/essiv.c
+++ b/crypto/essiv.c
@@ -186,9 +186,14 @@ static int essiv_aead_crypt(struct aead_request *req, bool enc)
const struct essiv_tfm_ctx *tctx = crypto_aead_ctx(tfm);
struct essiv_aead_request_ctx *rctx = aead_request_ctx(req);
struct aead_request *subreq = &rctx->aead_req;
+ int ivsize = crypto_aead_ivsize(tfm);
+ int ssize = req->assoclen - ivsize;
struct scatterlist *src = req->src;
int err;
+ if (ssize < 0)
+ return -EINVAL;
+
crypto_cipher_encrypt_one(tctx->essiv_cipher, req->iv, req->iv);
/*
@@ -198,19 +203,12 @@ static int essiv_aead_crypt(struct aead_request *req, bool enc)
*/
rctx->assoc = NULL;
if (req->src == req->dst || !enc) {
- scatterwalk_map_and_copy(req->iv, req->dst,
- req->assoclen - crypto_aead_ivsize(tfm),
- crypto_aead_ivsize(tfm), 1);
+ scatterwalk_map_and_copy(req->iv, req->dst, ssize, ivsize, 1);
} else {
u8 *iv = (u8 *)aead_request_ctx(req) + tctx->ivoffset;
- int ivsize = crypto_aead_ivsize(tfm);
- int ssize = req->assoclen - ivsize;
struct scatterlist *sg;
int nents;
- if (ssize < 0)
- return -EINVAL;
-
nents = sg_nents_for_len(req->src, ssize);
if (nents < 0)
return -EINVAL;
assoclen < ivsize를 제공하면 이 크래시가 발생합니다. 이 버그는 힙에서 객체를 가공하여 scatterlist 옆에 배치함으로써 악용될 수 있습니다.