Xpath injection 문제를 풀어보았습니다.
관리자 패스워드를 획득하는 것이 목적입니다.
아직 Xpath Injection에 대한 개념이 없어서 사전 검색을 먼저 해보았습니다.
Xpath Injection 이란?
- XML 구조에 악의적인 쿼리/구문을 삽입하여 정보를 탈취하는 공격
- XML DB 내용을 선택 및 조작
- XML : 데이터를 트리 구조의 노드로 표현하며, 사용자 정의로 데이터 분류가 가능함(=구조화된 데이터베이스 조작)
- 즉, XML이 DB, Xpath를 SQL 이라고 생각하면 된다.
Xpath 명령어
/ | 최상위 노드 |
// | 현재 노드로부터 모든 노드 조회 |
* | 모든 노드 조회 |
. | 현재 노드 |
.. | 현재 상위 노드 접근 |
parent | 현재 노드의 부모 노드 |
child | 현재 노드의 자식 노드 |
[] | 조건문 |
node() | 현재 노드로부터 모든 노드 조회 |
Xpath 함수
count() | 노드들의 개수를 반환하는 함수 |
string-length() | 문자열의 길이를 반환하는 함수 |
::* | 지정 노드의 모든 내용 |
name() | 노드의 이름을 반환하는 함수 |
substring() | 지정한 문자열을 반환하는 함수 |
position() | 노드의 위치를 반환하는 함수 |
string() | 인자로 받은 값을 문자열로 반환하는 함수 |
문제 풀이
문제에 들어가면, 메뉴가 3개가 보입니다.
Members 메뉴를 클릭하면 계정 리스트가 보입니다. 그 중에서 두 번째 열의 John이 관리자군요.
John의 패스워드를 획득해야 합니다.
Login 메뉴를 클릭하였더니, Username과 Password를 입력하는 창이 보입니다.
Xpath injection이 가능한지 여부를 판단하기 위해 특수문자를 입력해 보았습니다.
[Step1. 취약 여부 판단1]
특수문자를 입력해보았더니 XML 문법 관련 Error가 뜹니다. 여기가 구멍일 것 같다는 느낌이 듭니다.
[Step2. 취약 여부 판단2]
이번에는 username : admin / password : 'or '1'='1 값을 입력해보았더니, Steve 계정으로 로그인이 되었습니다.
응답 값에서 해당 계정의 username과 password 값이 확인됩니다.
username : admin / password : 'or '1'='2, 즉 쿼리 구문이 거짓인 경우에는 "Wrong credentials" 로그인 실패 구문이 출력되는 것을 확인 할 수 있습니다.
확실하게 Xpath Injection이 가능하다는 판단을 할 수 있습니다.
[Step3. 자신과 동일한 위치의 자식노드 개수 파악]
본격적으로 관리자의 패스워드를 알아내기 위해 인젝션을 시도해봐야겠죠.
우선, 동일한 위치의 자식노드 개수를 파악해보았습니다.
'and count(../chile::*)=3 or '1'='2
위의 결과(로그인 성공)로 인해 자식 노드가 3개 인것을 확인 할 수 있습니다.
(사실 members 메뉴에서 3개의 계정이 있다는 것을 미리 알고 있기 때문에 이 Step은 생략해도 무방합니다.)
[Step4. 부모 노드의 이름 길이가 몇 자인지 파악]
XML 데이터를 파악하려면 부모 노드의 이름을 알아야 합니다. 우선 길이부터 파악하는 것이 빠르겠죠.
'and string-length(name(parent::*))=8
string-length 함수를 사용하여 부모 노드의 이름 길이가 8자임을 알아냈습니다.
[Step5. 부모 노드의 이름 파악]
Blind Injection 공격을 이용하여 한 글자씩 파악해보았습니다.
'and substring(name(parent::*), 1, 1)='d' or '1'='2
부모 노드의 첫 글자가 d라는 것을 알 수 있습니다.
위와 같은 방법으로 두번째, 세번째, 8번째 길이까지 알아낸다면, 이름이 "database" 임을 알 수 있죠.
[Step6. 첫번째 자식 노드의 이름 파악]
부모 노드의 이름이 database 인 것을 알아냈으니 첫번째 자식 노드의 이름을 알아낼 차례입니다.
이 또한 substring 함수를 이용해 한 글자씩 알아낼 수 있습니다.
'and substring(name(../child::*[position()=1]), 1, 1)='u' or '1'='2
위의 결과로 첫번째 자식 노드의 이름인 "user" 를 획득하였습니다.
[Step7. 첫번째 자식 노드(user)의 자식 노드 개수 파악]
부모 노드(database) 밑에 자식노드(user)가 존재함을 알아냈고, 이번엔 user의 자식노드 개수를 알아 봅시다.
앞에서와 마찬가지로 count 함수를 이용해 쉽게 파악할 수 있습니다.
'and count(/database/user[1]/child::*)=5 or '1'='2
user의 자식 노드 개수는 5개네요.
[Step8. 첫번째 user 노드의 첫번째 자식노드 정보 파악]
user 노드에 5개의 자식노드가 존재함을 알아 내었으니, 해당 자식 노드들의 이름을 파악해야겠죠.
string-length, substring 함수들을 적절히 사용하여 첫 번째 자식노드가 "userid" 라는 것을 알아내었습니다.
[Step9. 첫번째 user 노드의 두번째 자식노드 정보 파악]
이번에는 position 값을 2로 설정하여 user의 두번째 자식 노드 이름이 "username" 임을 확인합니다.
[Step10. 첫번째 user 노드의 세번째 자식노드 정보 파악]
user의 세번째 자식 노드의 이름이 "password" 입니다!
이 노드를 이용하여 관리자의 패스워드를 파악할 수 있겠다는 생각이 듭니다.
[Step11. 첫번째 user 노드의 두번째 자식노드 username의 값 획득]
이제 전반적인 노드 구조를 파악했으니, 해당 노드에 매칭되는 값을 획득해야겠죠.
해당 노드에 들어있는 값을 가져오는 string 함수를 이용합니다.
'and string(/database/user[1]/username)='Steve' or '1'='2
위의 결과로 인해 첫번째 user 노드의 username 값이 Steve 임을 알아낼 수 있죠.
[Step12. 두번째 user 노드의 두번째 자식노드 username의 값 획득]
하지만 저희는 관리자의 패스워드가 필요합니다.(Steve의 패스워드는 이미 testing123임을 알 수 있음)
그래서 부모 노드(database)의 두번째 user 노드의 username 값을 확인하였더니 John, 즉 관리자가 들어 있는 것을 확인할 수 있습니다. 두번째 노드의 패스워드 값만 알면 되겠네요.
[Step13. 두번째 user 노드의 세번째 자식노드 password의 값 파악]
무턱대고 Blind Injection을 수행하기 전, 사전 정보인 길이를 확인했더니 23자임을 알 수 있네요.
substring 함수를 이용해 23자까지 값을 알아내보겠습니다. 우선 첫번째 값은 6이군요.
쉽게 인젝션을 하기 위해 저는 Burp Suite의 Intruder 기능을 이용하였습니다.
이로 인해 관리자의 패스워드를 획득할 수 있고, 이것이 Flag 값입니다^^
[ETC. 트리구조]
[ETC. XML 구조]
'Study > Wargame' 카테고리의 다른 글
[root-me] PHP - assert() (0) | 2020.01.02 |
---|---|
[root-me] JSON Web Token (JWT) - Introduction (0) | 2019.12.20 |
[root-me] SQL Truncation (0) | 2019.07.18 |
[root-me] CRLF (0) | 2019.07.18 |
[root-me] Javascript - Obfuscation1 (0) | 2019.04.15 |