[ Java ] XML Injection
포스트
취소

[ Java ] XML Injection

취약한 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package kr.co.openeg.lab.test.controller;

import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

@Controller
public class XPathController {
	
	@RequestMapping(value="/test/xpath_test.do", method = RequestMethod.POST)
	@ResponseBody
	public String testXpathInjection(HttpServletRequest request){
		StringBuffer buffer=new StringBuffer();
		String name=request.getParameter("name");
		TestUtil util=new TestUtil();
	    buffer.append(util.readXML(name));
        return buffer.toString();
		
	}

	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();
		  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/>");
		   }
    	   } catch (FileNotFoundException e) {
				   e.printStackTrace();
		   } catch (SAXException e) {
				   e.printStackTrace();
		   } catch (IOException e) {
				   e.printStackTrace();
		   } catch (ParserConfigurationException e) {
				   e.printStackTrace();
		   } catch (XPathExpressionException e) {
				   e.printStackTrace();
		   } catch (Exception e) {
				   e.printStackTrace();
		   }
		  if ( buffer.length() == 0) {
				   buffer.append("검색된 결과가 없습니다.");
		   }
		  return buffer.toString();
	   }
}

/**
 * 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>
 */	 



취약점 분석


1
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

해당 부분에서 사용자 입력값을 검증 없이 실행하고 있다. 이럴 경우 공격자가 의도한 악성 코드가 실행될 수 있다.


외부에서 입력된 값에 XPath 삽입을 일으킬수 있는 문자들을 제거하고 사용될 수 있도록 값을 필터링해야 한다.



안전한 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package kr.co.openeg.lab.test.controller;

import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

@Controller
public class XPathControllerSec {

	@RequestMapping(value = "/test/xpath_test.do", method = RequestMethod.POST)
	@ResponseBody
	public String testXpathInjection(HttpServletRequest request) {
		StringBuffer buffer = new StringBuffer();
		String name = request.getParameter("name");
		TestUtil util = new TestUtil();
		buffer.append(util.readXML(name));
		return buffer.toString();

	}

	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();

            /* 외부에서 입력된 name값에 XPath 삽입을 일으킬수 있는 문자들을 제거하고 사용될 수 있도록 값을 필터링하는 함수 사용*/
			String expression = "/addresses/address[@name='" + filter(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/>");
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (buffer.length() == 0) {
			buffer.append("검색된 결과가 없습니다.");
		}
		return buffer.toString();
	}

	public String filter(String input) {
		/* input값을 가공하여 XPath 삽입을 일으킬 수 있는 문자를 제거하는 메소드 */
		return input.replaceAll("[',\\[]", "");
	}
}

/**
 * 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>
 */