수억 대의 라우터, 스위치, 프린터, 게이트웨이, 산업용 컨트롤러에 장치 관리용으로 탑재된 임베디드 웹서버 GoAhead에서 TLS 구현과 관련된 결함 세 건이 공개됐다. 보안업체 FoxIO의 분석에 따르면 GoAhead는 Palo Alto Networks, Comcast, D-Link, HP, Canon, Honeywell, GE, Oracle, Siemens, Rockwell, Lockheed Martin, Boeing 등 다수 벤더의 IoT·방화벽·OT·군용 장비에서 쓰인다. 이번 결함들의 공통점은 셋 다 TLS 핸드셰이크 자체를 막지 못한다는 데 있다. 즉 연결은 정상적으로 맺어지고, 그 과정에서 검증이 조용히 무력화된다. 어떤 결함에 노출되는지는 장비가 어떤 TLS 라이브러리와 검증 설정으로 빌드됐는지에 따라 달라진다.
백엔드에 따라 갈리는 노출 면
GoAhead는 기본값인 OpenSSL과 MbedTLS 두 가지 TLS 백엔드를 지원하며, OpenSSL 백엔드는 LibreSSL 같은 호환 라이브러리와도 동작한다. 검증 동작은 컴파일 타임 설정으로 결정된다. VERIFY_PEER=1은 클라이언트 인증서를 요구하고, AUTHORITY는 신뢰 CA 번들을, VERIFY_ISSUER=1은 체인이 그 번들에 대해 발급자 신뢰를 성립시키도록 요구하며, REVOKE는 폐기 목록(CRL)을 가리킨다. 세 결함은 바로 이 설정들이 실제 라이브러리 동작과 어긋나는 지점에서 발생한다.
가장 심각한 것은 CVSS 9.1의 Critical 등급을 받은 CRL 미검증 문제(GOAHEAD-SA-2026-0006, CVE-2026-115881)다. GoAhead의 OpenSSL 백엔드는 설정된 CRL을 인증서 저장소에 로드하기는 하지만 폐기 검사를 켜지 않는다. OpenSSL에서 실제 폐기 확인을 하려면 X509_V_FLAG_CRL_CHECK 플래그가 필요한데, 파일을 저장소에 적재하는 것만으로는 이 플래그가 설정되지 않기 때문이다. 결과적으로 폐기된 클라이언트 인증서가 발급자 검증이 켜진 mTLS 환경에서도 그대로 핸드셰이크를 완료한다. 과거에 신뢰했다가 폐기한 신원이 접근 권한을 유지하는 셈이다. 6.1.0에 OpenSSL 3.6.2를 붙인 테스트에서 폐기된 CA 서명 인증서가 오류 없이 통과했고, 로드 시점에 경고조차 뜨지 않았다. 영향 범위는 CRL을 설정한 OpenSSL 호환 백엔드의 3.4.5부터 6.1.0까지이며, 6.1.1에서 로드 이후 검사하도록 수정됐다.
체인 깊이 우회와 DH 파라미터 절단
두 번째는 LibreSSL 레거시 검증기에서 나타나는 체인 깊이 우회(GOAHEAD-SA-2026-0004, CVE-2026-115884, CVSS 7.4 High)다. GoAhead는 체인 깊이 한계를 10으로 두지만, LibreSSL 3.2.x 및 3.3.2~3.3.6은 기본적으로 레거시 X.509 검증기를 쓰는데 이 검증기의 체인 구성 루프가 깊이 초과를 깊이 오류가 아니라 발급자 오류(오류 20, UNABLE_TO_GET_ISSUER_CERT_LOCALLY)로 보고한다. VERIFY_ISSUER=0 설정에서는 GoAhead 콜백이 이 발급자 오류를 관대하게 수용하기 때문에, 한계를 넘는 체인이 그대로 통과한다. 실제로 LibreSSL 3.3.6에 링크된 6.1.0에서 공격자가 통제하는 자가서명 CA를 뿌리로 하는 17개짜리 인증서 체인이 핸드셰이크를 완료하고 보호된 자원이 HTTP 200을 반환했다. 같은 소스를 LibreSSL 4.3.1로 빌드하면 깊이 10에서 오류 22로 거부된다. 대응책은 신뢰 CA 번들과 함께 VERIFY_ISSUER=1을 켜거나 OpenSSL 혹은 LibreSSL 4.x를 쓰는 것이다.
세 번째는 MbedTLS 백엔드의 DH 파라미터 절단(GOAHEAD-SA-2026-0005, CVSS 5.9 Medium)이다. 2048비트 DH 모듈러스를 전달하는 코드에서 길이 인자로 256바이트짜리 dhm_p 대신 1바이트짜리 dhm_g의 크기를 넘기는 오타 때문에, MbedTLS가 모듈러스의 첫 바이트(0xFF, 255)만 읽고 나머지 255바이트를 버린다. 그 결과 TLS 1.2 DHE_RSA 핸드셰이크의 ServerKeyExchange에 1바이트 값만 실린다. 가능한 공유 비밀값은 여덟 개뿐이어서, 수동 관찰자가 캡처한 트래픽을 후보로 하나씩 복호화해보며 세션을 완전히 오프라인으로 풀 수 있다. PoC에서는 다섯 번 만에 공유 비밀이 재현됐다. 다만 이 공격은 깨진 파라미터를 받아들이는 클라이언트가 있어야 성립한다. 테스트한 OpenSSL 클라이언트는 "modulus too small"로 거부했지만, 최소 크기 검사가 없는 커스텀·임베디드 구현은 복호화 가능한 세션을 맺는다. ECDHE 세션과 TLS 1.3은 이 유한체 그룹 결함의 영향을 받지 않는다. 이 오타는 2018년 바이너리 파라미터 API로 이전하면서 들어와 2024년 4월 리팩터링에서도 살아남았고, DHE 스위트를 제거한 MbedTLS 4.x는 영향권 밖이다.
실무적 함의
세 결함 모두 GoAhead의 코드 결함이라기보다 TLS 라이브러리와의 연동 가정이 어긋난 사례라는 점이 운영자에게 중요하다. CRL 적용은 검증 플래그에, DH 파라미터는 버퍼 길이에, 깊이 강제는 오류 처리 방식에 각각 의존하는데, 어느 쪽도 핸드셰이크를 멈추지 못한다. 따라서 "연결이 되니 검증도 된다"는 직관이 그대로 깨진다. LibreSSL로 빌드한 장비는 OpenSSL 호환 백엔드의 CRL 결함과 레거시 검증기의 깊이 문제를 동시에 물려받을 수 있고, MbedTLS 백엔드는 별도 경로를 쓰지만 클라이언트가 깨진 파라미터를 수용할 때 DH 절단에 노출된다. 즉 자산 점검 시 버전뿐 아니라 빌드된 백엔드와 VERIFY_ISSUER, REVOKE 같은 컴파일 타임 설정까지 확인해야 실제 위험이 드러난다.
대응 원칙은 비교적 분명하다. CRL 문제는 6.1.1 이상으로 올리면 해소되고, 깊이 우회는 발급자 검증을 켜거나 최신 LibreSSL·OpenSSL로 바꾸면 되며, DH 절단은 6.1.1 업그레이드, 유한체 DHE 스위트 비활성화, 또는 OpenSSL 백엔드 사용으로 막을 수 있다. 다만 임베디드 장비 특성상 즉시 펌웨어 교체가 어려운 경우가 많다는 점이 한계다. 공개된 정보는 외부에 노출된 서버를 백엔드별로 식별하기 위한 JA4Scan TLS 핑거프린트와 JA4S 로그 탐지 쿼리 힌트, 그리고 세션 복호화를 재현하는 PoC 코드를 함께 제공한다. 분석은 특정 장비 집합이 설정에 따라 서로 다른 결함에 노출된다는 점을 반복해서 강조하는데, 이는 역으로 운영 환경의 구체적 빌드를 모르면 영향 여부를 단정할 수 없다는 뜻이기도 하다. 노출 여부를 추정에 맡기기보다 실제 핸드셰이크 동작과 설정값을 직접 확인하는 것이 안전하다.