XML/Xpath Injection

반응형

XML

- W3C에서 개발한 특수한 마크업 언어를 만드는데 사용하도록 권장되는 다목적 마크업 언어

 ※ 마크업 언어

   - 태그등을 이용하여 문서나 데이터의 구조를 기록할 수 있는 언어

   - 주로 다른 종류의 시스템, 특히 인터넷에 연결된 시스템 끼리 데이터를 쉽게 주고 받을 수 있게 하여 HTML의 한계를 극복할

     목적으로 만들어진 언어

 

- XML을 사용하면 메타에디어를 기술할 수 있다.

 XML 사용하지 않는 경우

web hacking site

 - 데이터 표기시 HTML에서 어느부분이 데이터명이고 어디까지 데이터 값인지 알수 없음

XML을 사용한 경우

<dataname>web hacking</dataname>
<datavalue>site</datavalue>

 - 데이터명과 데이터 값처럼 데이터에 의미를 부여할 수 있다.

 - 이와같이 데이터에 의미를 부여하는 것을 메타데이터라고 한다.

 - XML은 수많은 종류의 데이터를 유연하고 자유롭게 기술하는데 적용이 가능하다.

 - XML 사용시 인터넷으로 연결된 시스템끼리 식별 가능한 데이터를 주고 받을 수 있다.

 - XML은 TCP / IP 네트워크 동신 할 때도 많이 사용하고 있다.

 

XPath Injection

- 웹 사이트가 사용자에게 제공한 정보를 사용하여 XPath 쿼리를 보낼 때 발생되는 취약점 공격

- 공격자는 의도적으로 잘못된 형식의 정보를 웹 사이트로 전송하여 XML 구조를 알아낸다.

- 공격자는 XPath Injection 공격을 통해 평소에 접근할 수 없는 데이터에 접근할 수 있게 됨

- XML 데이터가 인증에 사용되는 경우, 공격자는 XPath Injection을 통해 공격자의 권한 상승을 할 수 있음

 

XPath Injection(Login Form)- low

XML / XPath Injection에 들어가면 다음과 같은 로그인 창이 나온다.

1. SQL 공격이 먹히는지 확인하기 - '

이 창에 인젝션 공격이 먹히는지 확인하기 위해 '(작은따옴표)를  입력한다.

'를 입력하면

다음과 같은 경고창이 있는데 XPath와 관련된 오류이다.

이제 XPath Injection을 해야하는데 하기 전에 알아야 하는 명령어가 있다.

명령어 설명
/ 최상위 노드
// 현재 노드로부터 모든 노드 조회
* 모든 노드 조회
. 현재 노드
.. 

현재 노드의 상위 노드 접근

parent 현재 노드의 부모 노드
child 현재 노드의 자식 노드
[] 조건문
node() 현재 노드로부터 모든 노드 조회

로그인 페이지가 XML 데이터 베이스로 전송될 때

2. XML Injection 수행 - ' or 1=1 or '

login = '[login 입력값]' and password= ' [password 입력값]'

이제 login에다가 ' or 1=1 or ' 입력하고 비밀번호에는 아무값이나 입력한다.

 

입력을 하면 다음과 같은 방식으로 DB에 들어가게 된다.

-> login = ' ' or 1=1 or ' ' and password = '123' 

and 연산자보다 or 연산자의 연산 순위가 높기 때문에 or문이 참이 되는 쿼리를 입력하게 되면 and 연산 결과에 상관없이 참이된다.

다음과 같이 입력하면

이러한 결과가 나오게 된다.

XML Injection으로 neo를 제외한 나머지 사용자에 접속할 때는 or 연산자에 1=1 대신에 사용자 이름을 넣어주면 된다.

ex) alice 정보를 알고 싶을때

alice ' or 'alice'='alice

alice ' or 'alice'='alice

다음과 같이 입력하면 alice의 정보가 나온다.

현재 페이지는 blind sql injection 처럼 참일경우와 거짓일 경우 때의 응답만 제공하고 있다.

이제 XML의 DB 구조를 알아내보자

3. 노드의 갯수 구하기 - count()

여기서 count 함수를 사용할건데 count() 함수는 인자로 받은 노드의 개수를 반환해주는 함수이다.

출처 : http://tcpschool.com/xml/xml_xpath_filterExpression

count를 이용해서 현재 노드의 부모 노드부터 자식노드가 몇개인지 파악한다.

neo' and count(../child::*)=1 or 'a'='b

다음과 같은 코드는 ..(현재 노드의 상위 노드)/child(자식노드)::*(전체 노드 조회) = 1(노드의 갯수) or 'a'='b

다음과 같이 뒤에 false인 값을 집어 넣어 (true / false) or false를 맞춰준다.

만약 뒤에 참값이 나오게 되면 앞에 있는 내용이 거짓이어도 항상 참값이 나오기 때문이다.

node == 1일경우

다음과 같이 'Invalid credentials'라는 문구가 나온다.

이와 같은 방식을 계속 반복하다보면 노드가 6일 경우

다음과 같은 결과가 나온다.

이로써 현재 노드의 부모노드의 자식의 노드 수는 6개인 것을 알 수있다.

노드의 개수를 알아냈으니 다음으로 부모 노드명을 확인해보자

4. 부모 노드명 길이 알아내기 - string-length(), name()

부모 노드명을 알아내기위해 먼저 부모노드의 길이부터 알아내자

길이를 알아내기 위해 string-length 함수와 name 함수를 사용하였다.

neo' and string-length(name(parent::*))=1 or 'a'='b

다음과 같이 입력하면

일단 부모노드의 길이는 1은 아니라는 것이다. 이제 이와같은 방식으로 길이를 늘려가면

길이가 6일 때 

 

다음과 같이 참인 결과가 나오게 된다.

이로써 부모 노드의 이름은 6글자라는 것을 알 수 있다.

5. 부모 노드 이름 알아내기 - name(), substring()

부모 노드의 길이가 6글자인것을 알아냈으니 다음으로는 부모 노드의 이름을 추측해 보도록 하겠다.

부모 노드의 이름을 알아내기 위해서 name함수와 substring 함수를 사용하겠다.

neo' and substring(name(parent::*),1,1)='a' or 'a'='b

substring 와 name 함수를 사용해 첫번째 글자의 이름을 알아내고 있다.

위의 코드를 해석하면 parent 노드의 첫번째 글자가 a인가?라는 뜻이다.

결과를 확인해보면

거짓의 결과가 나온다

이 행위를 반복하다 보면 'h'에서 참인결과가 나오게 된다.

이로써 부모노드의 이름은 'h○○'라는 것을 알 수 있다.

만약 두번 째 단어를 알고 싶으면substring(name(parent::*),1,1) -> substring(name(parent::*),2,1)로 바꿔주면 된다.

이 방식을 통해 전체 부모노드 이름을 알아내면 heroes라는 것을 알 수 있게 된다.

neo' and substring(name(parent::*),1,6)='heroes' or 'a'='b

5. 자식 노드의 글자수 알아내기 - position()

부모 노드를 알아냈으니 이제 자식 노드의 이름을 알아내자

자식 노드의 이름을 알아내기 전에 자식 노드의 글자 수 부터 알아 보자

neo' and string-length(name(../child::*[position()=1))=1 or 'a'='b

position함수는 MYSQL에서 limit 함수를 의미한다.

다음 코드의 뜻은 heroes(부모노드)의 자식노드 중 첫번째 노드(position()=1)의 길이가 1인가 이다.

당연히 결과는 false이다.

이 과정을 계속 반복하다 보면 글자가 글자수가 4일 때 true의 결과가 나온다.

neo' and string-length(name(../child::*[position()=1))=4 or 'a'='b

이로써 첫번째 노드의 길이는 4글자라는 것을 알게 되었다.

※ 현재 노드의 길이 알아내기

우리는 현재 hero라는 자식 노드안에 있다. 만약 현재 노드의 길이를 알아내려면 다음과 같이 입력하면 된다.

neo' and string-length(name(.))=4 or 'a'='b

6. 자식 노드의 이름 알아내기 - substring(), position()

첫번째 자식 노드의 길이가 4글자인것 까지 알아냈으니 다음으로 첫번째 자식노드의 이름을 알아보자

neo' and substring(name(../child::*[position()=1]),1,1)='a' or 'a'='b

다음 코드를 해석하면 첫번째 자식 노드의 첫번째 글자가 a인가?이다.

당연히 결과는

기대 안했다.

다음과 같은 방식을 계속 반복하다보면

'h'일때 true의 결과가 나온다.

이로써 첫번째 자식 노드는 'h○'라는 것을 알 수 있다.

이 방식을 반복하다 보면 첫 번째 노드의 이름은 'hero'라는 것을 알 수 있다.

neo' and substring(name(../child::*[position()=1]),1,4)='hero' or 'a'='b

이제 6개의 노드 중 한개 찾았다 이제 5,6번을 반복해서 6개의 노드를 전부다 알아내면 노드 이름이 전부 'hero'라는 것을 알 수 있다.

대충 구도를 그려보면

heroes라는 부모노드 밑에 6개의 hero라는 자식노드가 있는 구조이다.

 

※ 현재 노드의 이름 알아내기

우리는 현재 hero라는 자식 노드안에 있다. 만약 현재 노드의 이름을 알아내려면 다음과 같이 입력하면 된다.

neo' and substring(name(.),1,1)='a' or 'a'='b

7. 자식노드의 이름 알아내기 - [], string-length()

이제 hero의 자식 노드의 갯수를 알아보자

neo' and string-length(name(//hero[1]/child::*[position()]=1))=1 or 'a'='b

다음 코드를 해석하면 현재 노드서부터 모든 노드까지(//)에서 첫번째 hero 노드(hero[1]) 첫번째 자식 노드([position()]=1)이 한글자(=1)인가?이다.

당연히 결과는 false이다.

계속 반복해보면 노드의 길이가 2일 경우 True 결과가 나온다.

8. hero의 자식 노드 이름 알아내기

이제 2글자인 것 까지 알아냈으니까 이름을 알아내자

neo' and substring(name(//hero[1]/child::*[position()]=1),1,1)='a' or 'a'='b

코드를 해석하면 hero의 자식노드의 첫번째 글자가 a인가? 이다.

당연히 결과는 False이다.

b부터 계속 대입 해보면 'i'에서 True 결과가 나온다.

neo' and substring(name(//hero[1]/child::*[position()]=1),2,1)='a' or 'a'='b

이로써 자식 노드의 첫번째는 'i○'라는 것을 알 수 있다.

두번째 단어를 알아보자

간단하다. ),1을 2로 바꿔준다.

당연히 'a'는 False이다.

계속 반복하다보면 'd'일 때  True의 결과가 나온다.

neo' and substring(name(//hero[1]/child::*[position()]=1),2,1)='d' or 'a'='b

이제 첫번째 자식 노드의 이름이 'id'가 맞는지 확인해 보자

neo' and substring(name(//hero[1]/child::*[position()]=1),1,2)='id' or 'a'='b

현재 노드들의 관계를 그려보면

다음과 같이 나온다.

9. 자식 노드(id) 값의 길이 알아내기 - string-length(), string()

이제 자식 노드의 값을 알아보자

우선 자식 노드의 값의 길이 알아내기 위해 string을 사용한다.

string안에 input 값으로 길이를 알아낼 노드의 값을 적는다.

neo' and string-length(string(//hero[1]/id))=1 or 'a'='b

다음과 같이 입력하면

다음과 같이 나온다 

10. 자식 노드(id) 알아내기 - substring(),string()

첫번째 노드의 길이가 한글자라는 것을 알아냈다. 

이제 단어를 알아내자

neo' and substring(string(//hero[1]/id),1,1)=1 or 'a'='b

다음과 같이 입력하면 True 값이 나온다.

이로써 hero의 첫번째 값은 1이라는 것을 알 수 있다.

이제 9번과 10번을 반복하다 보면 노드들의 값을 알아낼 수 있다.

 

XPath Injection(Login Form)- medium, high

로그인 창에 '를 입력해보자

low때와 다른 결과이다.

이유를 확인하기 위해 xmli_1.php 페이지 코드를 확인해보자

난이도 (중)과 (상)은 xmli_check_1 함수가 적용되어 있다.

functions_external.php 페이지에서 xmli_check_1 함수를 확인해보면

모든 특수문자를 빈 문자로 치환해준다.

그로인해 특수문자가 먹히지 않아 공격이 먹히지 않는다.

반응형

'웹해킹 > bee-box' 카테고리의 다른 글

Broken Auth - Insecure Login Form  (0) 2019.10.21
XML/XPATH Injection(Search)  (0) 2019.10.21
XML / XPATH 인젝션 - Login Form  (0) 2019.08.07
SQL Injection - Blind - Time Based  (0) 2019.08.06
Blind SQL Injection - Boolean Based  (0) 2019.08.05