[하루한줄] CVE-2021-41117: Keypair 모듈의 잘못된 랜덤 시드 생성

URL

https://securitylab.github.com/advisories/GHSL-2021-1012-keypair/

Target

  • GitKraken 7.6.x, 7.7.x, 8.0.0 버전의 SSH key를 발급받은 유저

Explain

JS에서 RSA 암호화 프리미티브를 구현한 keypair에서 SSH의 RSA 키를 생성하는 과정에서 잘못된 난수를 생성하는 취약점이 발견되었습니다.

Keypair는 운영체제에서 제공하는 CSPRNG(또는 CPRNG)에 완전히 의지하지 않고, 자체 카운터 기반의 CMAC 방식을 사용합니다.

CSPRNG(Cryptographically secure PRNG) : 난수를 흉내내기 위한 알고리즘으로 생성되는 유사난수 생성기(Pseudorandom number generator, PRNG) 중 암호학적으로 안전한 유사난수 생성기를 말합니다.

문제는 중복되지 않는 임의의 데이터로 CMAC을 구현하는 시드 선정 문제인데, AES-CMAC 생성기의 seed를 위해 Keypair는 NodeJS에서 두 가지 방법으로 접근합니다. 브라우저에서는 window.crypto.getRandomValues()를 사용하지만, NodeJS는 window라는 객체가 정의되지 않아 AES-CMAC을 사용할 수 없게 됩니다.

window.crypto.getRandomValues()를 사용할 수 없을 때, Lehmer LCG 난수 생성기가 CMAC 카운터 seed에 사용되고 LCG는 Math.random과 함께 seed를 만듭니다.

b.putByte(String.fromCharCode(next & 0xFF))
util.ByteBuffer.prototype.putByte = function(b) {
  this.data += String.fromCharCode(b);
};

// b.putByte(String.fromCharCode(String.fromCharCode(next & 0xFF)))

위 코드를 단순화하면 String.fromCharCode(String.fromCharCode(next & 0xFF)), 즉 이중(double) String.fromCharCode로 의도하지 않은 약한 시드의 원인이 됩니다. 그러나 이 과정에서 에러가 발생하지 않고 0xFF로 and 연산을 한 결과로 출력 버퍼의 97%에 0이 들어갑니다.

Untitled

출력에서 의미있는 값은 48~57밖에 되지 않습니다. 따라서 RNG 시드가 0일 확률이 97%라는 것을 의미하며, 만약 0이 아라면 바이트는 0에서 9까지라는 것을 알 수 있습니다.

결국 Keypair의 문제점은 다음과 같이 요약할 수 있습니다.

  • 안전한 CSPRNG가 아닌 취약한 LCG와 Math.random을 사용합니다.
  • CSPRNG를 사용할 수 있어도 NodeJS에서는 사용할 수 없습니다.
  • 시드의 대부분(약 97%)이 0이 되는 구현입니다.


본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.