본문 바로가기
CS/시큐어 코딩

[시큐어 코딩] XPath 삽입(Injection)

by J-rain 2024. 5. 26.

 

XPath 삽입

XPath는 XML 문서에서 특정 요소(element)나 속성(attribute)까지 도달하기 위한 경로를 요소의 계층을 이용해서 표현하는 것이다. 파일경로와 비슷하며, 파일 경로에 절대 경로와 상대 경로가 있는 것처럼, XPath도 루트 요소(Root element)부터 특정 요소까지의 경로를 지정하거나 현재 위치하고 있는 요소부터 특정 요소까지의 경로를 지정할 수 있다.

아래 사진을 보는 것처럼 XML 문서는 트리 구조로 모델링해 표시할 수 있다. 예를 들어서 prduct id=3인 제품의 가격정보를 출력하기 위한 XPath 표현식은 /catalog/product[@id=3]/price 이다.

 

 

발생 원인

외부로부터 입력된 값을 이용해 XML 파일의 내용을 검색하기 위해 XPath 쿼리문을 생성할 때, 입력값에 대한 검증 작업을 수행하지 않고 동적으로 생성되는 XPath문에 삽입해 사용하는 경우 취약점이 발생한다.

 

 

취약점을 진단하기 전에 XML문서를 조회하기 위한 XPath사용법을 먼저 확인해 보자. 회원정보를 저장하고 있는 XML파일(src/config/address.xml) 내용이 다음과 같다.

<?xml version="1.0" encoding="UTF-8"?>
<addresses>
   <address name="김영숙" type="admin">
      <passwd>1234</passwd>
      <phone>010-1111-2222</phone>
      <email>hong@openeg.co.kr</email>
      <ccard>3111-0022-3333-9444</ccard>
   </address>
   <address name="홍길동" type="admin">
      <passwd>1234</passwd>
      <phone>010-1111-2222</phone>
      <email>hong@openeg.co.kr</email>
      <ccard>3333-0022-3333-9444</ccard>
   </address>
   <address name="이순신" type="general">
      <passwd>5678</passwd>
      <phone>010-3333-2222</phone>
      <email>lee@openeg.co.kr</email>
      <ccard>1115-2266-7733-4144</ccard>
   </address>
   <address name="강감찬" type="general">
      <passwd>9876</passwd>
      <phone>010-7777-2222</phone>
      <email>kang@openeg.co.kr</email>
      <ccard>3331-5553-3333-8884</ccard>
   </address>
</addresses>

 

이 XML 문서를 조회하기 위한 XPath 문법은 다음과 같다.

 

≫ XML 정보 중 email 정보를 추출

/addresses/address/email/text()

 

≫ name 값이 '홍길동'인 사용자의 정보를 얻기 위한 조건식 설정

/addresses/address[@name='홍길동']

 

≫ name 값이 '홍길동'이며 password 값이 '1234'인 사용자의 ccard 정보 추출

/addresses/address[@name='홍길동' and password/text()='1234']/ccard/text()

 

≫ name 값이 '홍길동'인 사용자의 패스워드를 모르는 경우 password 값을 조작해 ccard 정보를 추출

/addresses/address[@name='홍길동' and password/text()='' or 'a'='a']/ccard/text()

 

 

이전시간에 수행했던 SQL인젝션과 같은 환경에서 실습

 

 

공격기법

입력값이 XML 쿼리의 조회 키로 사용되는지 체크

 

아래 페이지는 XML 파일의 데이터를 검색할 때 XPath에서 회원의 이름을 검색해 해당 회원의 카드번호를 추출하는 쿼리를 수행하여 그 결과를 화면에 출력한다.

시큐어코딩테스트 > XPath 인젝션

 

정상적으로 [홍길동]을 입력하여, 홍길동의 CCARD정보를 화면에 출력한다.

다음, XPath 쿼리문을 조작해 허가되지 않은 파일의 정보 조회가 가능한지 시도해 본다.

 

≫ 입력값: ' 또는 '--

 

≫ 입력값: or 1=1 또는 ' or ''='

 

' or ''=' 을 입력했을 때 모든 사용자의 CCARD정보를 유출하는 것을 볼 수 있다.

 

해당 소스 코드를 확인해 보면 외부로부터 입력받은 값을 XPath 인자로 사용하면서 입력값에 대한 필터링 작업이 생략되어 쿼리문이 조작되는 것을 알 수 있다.

 

TestUtil.java

public String readXML(String name)  {

	 StringBuffer buffer=new StringBuffer();
     
	 try {
	   InputStream is =
			   this.getClass().getClassLoader().getResourceAsStream("config/address.xml");
       DocumentBuilderFactory builderFactory = 
    		    DocumentBuilderFactory.newInstance();
	   DocumentBuilder builder =  builderFactory.newDocumentBuilder();
	   Document xmlDocument = builder.parse(is);
	   XPath xPath =  XPathFactory.newInstance().newXPath();
	 
	   System.out.println("ccard 출력");
	   String expression = "/addresses/address[@name='"+name+"']/ccard";

	   NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
	   for (int i = 0; i < nodeList.getLength(); i++) {
		   buffer.append("CCARD[ "+i+ " ]  "+nodeList.item(i).getTextContent()+"<br/>");
	   }
       ...

 

String expression = "/addresses/address[@name='"+name+"']/ccard"; 
여기서 외부에서 입력된 name을 검증 없이 XPath 쿼리문에 삽입하고 있다.

 

 

방어기법

XPath에서 사용되는 외부 입력값에 대해 안전한 값으로 필터링해서 사용

엄격한 입력값 검증을 통해 필요한 항목만 받아서 사용할 수 있도록 프로그램을 작성한다.

 

입력값 검증 방식을 사용하는 경우 사용자가 입력한 값에서 입력값 허용정책 목록을 이용해 허용된 문자만 입력받아서 처리하거나, 허용되지 않는 문자열 점검정책을 적용하여 XPath 삽입을 발생 시킬 수 있는 문자 ( ) = ' [ ] : , * / 를 필터링해 제거한 뒤 커리문을 수행한다.

 

수정 TestUtil.java

	public static String XPathFilter(String input)
	{
		return input.replaceAll("[ ',\\[]","");
	}
	
	public String readXML(String name)  {

	 StringBuffer buffer=new StringBuffer();
     
	 try {
	   InputStream is =
			   this.getClass().getClassLoader().getResourceAsStream("config/address.xml");
       DocumentBuilderFactory builderFactory = 
    		    DocumentBuilderFactory.newInstance();
	   DocumentBuilder builder =  builderFactory.newDocumentBuilder();
	   Document xmlDocument = builder.parse(is);
	   XPath xPath =  XPathFactory.newInstance().newXPath();
	 
	   System.out.println("ccard 출력");
       
	   //String expression = "/addresses/address[@name='"+name+"']/ccard";
	   String expression = "/addresses/address[@name='"+XPathFilter(name)+"']/ccard";

	   NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
	   for (int i = 0; i < nodeList.getLength(); i++) {
		   buffer.append("CCARD[ "+i+ " ]  "+nodeList.item(i).getTextContent()+"<br/>");
	   }
       ...
안전하지 않은 값들을 다 제거하고 사용할 수 있도록 XPathFilter 메서드를 정의하여 외부 입력값을 사용하기 전에 먼저 필터링하도록 코드를 수정한다.

 

 

 

 

 

 

 

 

 

 

참고: 해킹 방어를 위한 JAVA 시큐어코딩(실무에 바로 적용하는)(개정판 4판) 김영숙

 

댓글