G2H

보안 리서치 · 레드팀/블루팀 · DFIR · Cloud · Tooling

XPath Injection

· Web/Web Hacking Techniques

XPath?

XML이 과거부터 데이터 저장과 교환에 널리 사용되며, XML 문서 내 데이터를 검색 및 추출하기 위한 표준 질의 언어로 XPath가 도입되었다. SQL 뿐 아니라 XML을 통해서도 구성 정보, 데이터베이스, 외부 서비스 연동 등에 사용되며, 이러한 XML 문서에서 원하는 노드를 선택하기 위해 XPath 질의가 많이 사용된다.

XPath의 경우 XML의 데이터를 조회하고 필터링하기 위한 강력한 도구이며, 웹 개발에서는 데이터 처리 흐름의 일부로 여저히 활용되고 있다.

XPath 사용 목적과 필요성

  • 웹 서비스 통신(SOAP API 등)
  • 설정 파일 관리
  • 데이터베이스 대체
  • 인증/권한 관리 파일
  • SSO 연동

DB대신 XML 파일을 계정 정보 저장소로 활용하는 경량 시스템, 임베디드 장비, 구형 시스템등에서 자주 보인다.

XPath Injection

XPath Injection은  SQLI와 동일하게 사용자 입력을 사용하여 동적으로 XPath 질의 문자열을 생성할 때 발생한다.

String user = request.getParameter("user");
XPath xpath = XPathFactory.newInstance().newXPath();
String expr = "//User[Name='" + user + "']";
Node result = xpath.evaluate(expr, xmlDocument);

위와같이 java로 구성된 취약한 코드가 있다고 가정해보자.

사용자 입력값으로 받은 user 변수는 그대로 XPath 표현식에 삽입되게 된다. 이 때 쿼리 제어용 구문인 '(따옴표)나 논리 연산자를 통해 원래 의도와 다른 조작된 쿼리를 만들 수 있다.

 

여기서 주의할 점은 XPath 표준에는 SQL의 Prepared Statement 처럼 변수를 안전하게 바인딩하는 메커니즘이 존재하지 않는다.

XPath 질의는 경로와 조건식으로 구성된다. 예를 들어 위와 같은 코드에서는 //User[name'Alice']는 경로 //User 아래에서 Name 노드라 Alice인 User 노드를 찾는 질의 이다.

내부적으로 전체 질의에서 user인 사용자 입력값이 추가되는 형태로 문자열이 만들어지게 되는데,

이 때 user자리에 싱글쿼터(')나 예약어 등을 넣어 새로운 조건이나 경로를 삽일할 수 있게된다.

XPath에는 접근 제어(ACL)의 개념이 없어 한 번 취약점이 발생하면 XML 문서의 전체구조와 모든 노드에 접근이 가능하다.

XPath Injection Mechanism

취약점 진단시 로그인 폼 혹은 검색필드와 같은 엔드포인트에 싱글쿼터(')를 넣어 제출시, 어플리케이션이 XPath 구문 오류를 반환하는지 확인할 수 있다. Invaiild Predicate..와 같은 XML 파서 오류 경고 메시지가 나타난다면, 이는 XPath 지릐에 직접 포함되고 있음을 의미한다.

 

이후 취약점이 식별되면, 논리 연산자와 True 조건을 조합하여 질의를 변조하는 형식이 존재한다.

//Employee[UserName/text()='{사용자입력}' and Password/text()='{비밀번호}']

위와 같은 XPath 질의가 있다고 가정해보자.

이경우 UserNmae과 Password 노드가 일치하는 Employee 노드를 찾는 질의문이다.

이 때  SQLI와 동일한 공격 구문인 'or 1=1 or 'a'='a 를 Username에 입력하고 password에 임의의 값을 넣어볼 수 있다.

//Employee[UserName/text()='test' or 1=1 or 'a'='a' And Password/text()='test']

그 결과 위와 같이 최종 질의가 완성된다.

순서대로 해석하면 'a'='a' and password(test) = 0(False) 가되지만 이후 앞에서 부터 or 조건을 통해 결국 1(True)가 되어 모든 Employee 노드가 조회 된다.

이 외에도 문자열 비교 대신 contains() 나 starts-with() 함수를 이용하거나, Union기능인 | 연산자를 사용해서XPath의 경로 자체를 조작할 수 있다.

 

//test[name='{input}']의 형태가 있을 때 '] //User/Password/text() | //test[name=' 와 같은 페이로드를 입력하게되면 최종적으로

//test[name='' ] | //User/Password/text() | //test[name=''] 와 같이 분리된다. 결과적으로 모든 사용자  비밀번호 노드를 기존의 test 결과와 합쳐 반환하게 된다.

 

XPath Inejction도 다른 인젝션들과 유사하게 블라인드(Blind) 기법으로 데이터 추출이 가능하다. 예를 들어 sting-length() 함수를 사용하여 특정 노드 값을 맞추거나, starts-with()을 통한 앞부분부터 한 글자씩 맞춰볼 수 있다.

사용자 입력을 통한 XPath 쿼리 적용 흐름

클라이언트는 웹 페이지의 데이터 입력 폼에서 데이터를 제출하면 HTTP 요청을 통해 서버로 전달된다.

전달받은 입력값은 서버측에 미리 정의된 XPath 쿼리 템플릿에 삽입되어 하나의 완성된 XPath를 구성한다.

이후 XML 파서/엔진에 전달하여 XML 문서를 탐색하기 시작한다.

서버는 일반적으로 XML 파일을 DOM 등으로 메모리에 로드한 후 해당 DOM에 대해 특정 함수를 호출하여 XPath질의를 실행하며, 이 과정에서 XML 파서가 쿼리를 해석하고 XML 문서 내에서 지정된 경로에 매칭되는 노드를 검색한다.

 

또한 서버의 오류 처리 방식으로인한 영향도 존재한다.

XPath 인젝션으로 인해 XPath 구문 오류가 발생하는 경우, 취약한 어플리케이션은 에러 메시지를 그대로 반환하거나 스택 트레이스를 노출한다. 이러한 메시지에는 종종 쿼리 문자열의 일부 혹은 XML 경로 정보가 포함되어, XML 구조를 유추할 수 있다.

 

예를 들어, 따옴표로 인한 오류시, 어느 위치에서 문제가 생겼는지 표시되며, 이는 Blind 기반의 공격 기법으로 이어질 수 있다.

  1. 항상 참이 되는 조건식
    1. 흔한 Injection 기법으로 ' or 1=1 ' 와 같이 참/거짓을 활용한 공격으로 필터 조건을 무력화 할 수 있으며, 주로 로그인 패스워드 체크를 무용지물로 만들고 모든 노드에 대한 접근 권한을 획득하는데 사용된다.
  2. XPath 함수 악용
    1. position(), contains()등의 함수들이 존재하며, 이를 활용한 인젝션으로 특정 노드 선택이나 패턴 매칭을 수행한다.
    2. user 라는 입력에 ' || posistion()=1 or '을 주입하게 되면, 첫 번쨰 사용자 노드만 참으로 간주되어 특정 위치의 사용자 계정으로 로그인 되게 된다.
    3. 혹은 contaion(.,"admin")과 같이 사용하게 될 경우 admin 문자열이 포함된 경우만 참이 되므로 사용자명에 admin이 들어간 계정만 골라느내는 것도 가능하다. 이러한 방법은 원하는 조건에 맞춰 노드를 선별적으로 탈취하는데 사용된다.
  3. 경로 조합 연산자 및 와일드카드 악용
    1. | (union)을 사용하여 여러 XPath 질의를 한 번에 실행할 수 있다. 이를 활용하여 새로운 경로 질의를 통한 공격과 // 이중 슬래시를 통해 루트부터 하위 모든 경로를 탐색하는데 사용된다. @*나 node()와 같은 와일드카드를 주입하면 특정 속성이나 노드 타입을 가리지 않고 모든 노드/속성을 긁어오도록 쿼리를 확장할 수 있다. 
    2. 이는 필요한 데이터만 빼오거나 전체 데이터를 한꺼번에 노출시킬 때 사용된다.

언어별 XPath 처리 방식

1. Java ( JAXP - javax.xml.xpath 패키지 )

String uid = request.getParameter("uid");            
XPath xp = XPathFactory.newInstance().newXPath();
String expr = "/Users/User[@id='" + uid + "']";       
String result = xp.evaluate(expr, xmlDoc);

해당 코드의 경우 uid에 악의적인 입력이 들어오게 될 경우 expr 에 최종적으로 XPath 쿼리문이 완성되어 인젝션으로 이어진다.

Java의 경우 SQL의 PrearedStatement처럼 매개변수를 바인딩하는 표준 방법이 없어 개발자가 직접 입력값 검증을 수행해야한다.

Python ( xml.etree.ElementTree, lxml 등  )

import xml.etree.ElementTree as ET
tree = ET.parse('users.xml')
root = tree.getroot()
username = request.args.get('username')            
query = f"./users/user[@name='{username}']/location"
elements = root.findall(query)

pythond의 경우 기본 XML 파서는 C 언어 기반 libxml2를 사용하므로, 잘못된 XPath가 들어가면 예외를 던지거나 무시하게 된다.

lxml의 경우 etree.XPath나 element.xpath() 메서드를 제공하며, 추가로 xpath(query, **kwargs) 형태로 변수 바인딩 기능을 지원한다. 하지만 안전한 기능의 사용방법을 모르는 개발자들은 여전히 형식화 연산자나 f-문자열로 동적 쿼리를 만들며, 이 구간에서 취약점이 발생된다.

PHP (DOMDocument, SimpleXML 등)

$xml = simplexml_load_file("users.xml");
$user = $_POST['user'];
$pass = $_POST['pass'];

$result = $xml->xpath("//user[username='$user' and password='$pass']");

위 코드에서 $user나 $pass에 ' 등이 포함되면 XPath 구문이 깨져서 의도치 않은 결과를 초래다. 예를 들어 $pass에 admin" or "a"="a라는 값을 넣으면 최종 질의는 //user[username='...' and password='admin" or "a"="a']가 되어 or "a"="a 부분이 항상 참(True)이므로 비밀번호 검증을 우회된다.

.NET (C#, VB 등)

// 취약한 C# 코드 예시 (XPath 인젝션)
string title = Request.Query["title"];
XmlDocument doc = new XmlDocument();
doc.LoadXml("<Books>...</Books>");
string xpath = $"//book[title='{title}']";
XmlNode bookNode = doc.SelectSingleNode(xpath);

이 코드에서 title이 '] or '1'='1 같은 페이로드라면, 최종 XPath는 //book[title=''] or '1'='1']로 변환된다.

이 때 XPath 엔진은 //book[title='']와 '1'='1' 두 부분을 OR 연산으로 해석하여, 결과적으로 모든 <book> 노드를 반환할 수 있다.

더 심각한 경우, XML 문서에 책 이외에 <user> 노드 등이 섞여 있다면, 공격자는 "] | //user/password | //*[@any='와 같이 경로를 끝까지 탈출해 전혀 다른 데이터 노드들을 추출할 수도 있다,


XPath Injection 우회기법은 다양한 인젝션 종류의 공격들과 유사하지만, 

널 바이트 삽입으로 인한 우회가 가능하다 이는 파일업로드 취약점 우회 방안과 매우 유사하며 

예를 들어 원래 쿼리가 ... [username='{input}']/username이라 뒷부분에 /username 노드 지정이 붙는 상황에서, 공격자가 입력에 %00를 넣으면 나머지 ']/username이 잘려나가 질의가 조기 종료된다. 앞서 예시로 든 페이로드에 널 바이트를 응용하면 ' or '1'='1']%00 같은 형태가 되는데, PHP의 libxml은 이를 '] 이후를 읽지 않으므로 최종적으로 //user[username='' or '1'='1']만 실행된다.

 

가장 완벽한 보안대책은 화이트리스트 기반의 필터링과, 특수문자 이스케이프 그리고 ,아예  XML 사용을 지양하고 더 안전한 데이터 저장소를 활용하는 것이 권장된다.


참고자료

https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/on-null-byte-poisoning-and-xpath-injection/#:~:text=,true%2C%20we%20do%20have%20an 
https://dojo-yeswehack.com/learn/vulnerabilities/xpath
https://hakre.wordpress.com/2013/07/11/mitigating-xpath-injection-attacks-in-php/#:~:text=injected%20any%20longer%20into%20the,that%20the%20document%20gets%20changed
https://docs.aws.amazon.com/codeguru/detector-library/python/xpath-injection/
https://www.cobalt.io/blog/pentesters-guide-to-xpath-injection#:~:text=2,a%E2%80%99%20in%20the%20login%20field
https://www.vaadata.com/blog/xpath-injections-exploitations-and-security-tips/#:~:text=If%20the%20imagined%20XPath%20query,d%3D%E2%80%98positive%E2%80%99%20or%20%E2%80%981%E2%80%99%3D%E2%80%981%E2%80%99%5D%2Fdescription%60%2C%20returns%20%60true
https://owasp.org/www-community/attacks/XPATH_Injection#:~:text=Username%3A%20blah%27%20or%201%3D1%20or,a%27%3D%27a%20Password%3A%20blah
https://www.imperva.com/learn/application-security/xpath-injection/#:~:text=%2F%2FCustomer,%E2%80%99lol%E2%80%99
https://www.hostduplex.com/blog/how-to-prevent-code-injection-attacks/#:~:text=,redirecting%20users%20to%20other%20sites