出发
弱类型比较问题
文件包含漏洞
文件包含
开发人员将相同的函数写入单独的文件中,需要使用某个函数时直接调用此文件,无需再次编写,这种文件调用的过程称文件包含。
文件包含漏洞
开发人员为了使代码更灵活,会将被包含的文件设置为变量,用来进行动态调用,从而导致客户端可以恶意调用一个恶意文件,造成文件包含漏洞。
几乎所有脚本语言都会提供文件包含的功能,但文件包含漏洞在PHP Web Application中居多。
php文件包含函数:
include()
require()
inclue_once()
require_once()
incluce和require的区别:
include发生错误会跑出waring然后继续执行后面的内容
require发生错误会直接停止程序
本地文件包含
若include其中的变量能够被用户操控,则可能导致文件包含漏洞,导致任意代码执行或者任意文件读取。
1 |
|
读取同目录下的flag.php
同时,包含文件的内容只要符合php语法,就能够被当成php代码解析,无关后缀是什么
在同以目录下创建bg.jpg
文件
远程文件包含
远程文件包含就是允许攻击者包含一个远程的文件,一般是在远程服务器上预先设置好的脚本。
但需要在php.ini中将allow_url_fopen和allow_url_include设置为On
php支持的协议
- file:// — 访问本地文件系统
- http:// — 访问 HTTP(s) 网址
- ftp:// — 访问 FTP(s) URLs
- php:// — 访问各个输入/输出流(I/O streams)
- zlib:// — 压缩流
- data:// — 数据(RFC 2397)
- glob:// — 查找匹配的文件路径模式
- phar:// — PHP 归档
- ssh2:// — Secure Shell 2
- rar:// — RAR
- ogg:// — 音频流
- expect:// — 处理交互式的流
php中filter协议介绍
php://filter 是php中独有的一个协议,可以作为一个中间流来处理其他流,可以进行任意文件的读取。
主要是用来查看源码,因为包含php文件时会被解析,不能看到源码,所以用filter协议来读取敏感文件,但需要进行base64编码。
名称 | 描述 | 备注 |
---|---|---|
resource=<要过滤的数据流> | 指定要筛选过滤的数据流。 | 必选 |
read=<读链的筛选列表> | 可以设定一个或多个过滤器名称,以管道符(|)分隔。 | 可选 |
write=<写链的筛选列表> | 可以设定一个或多个过滤器名称,以管道符(|)分隔。 | 可选 |
- flag.php
1 |
|
若是直接读取的话,php代码会被解析,看不到源码,这时可以使用php伪协议进行文件读取
1 | ?file=php://filter/read=convert.base64-encode/resource=flag.php |
再进行base64解码就可以得到flag.php
的源码
1 |
|
弱类型比较问题
PHP是一门弱类型语言,存在弱类型比较问题
"=="
与"===”
看一道简单题
1
2
3
4
5
6
7
8
9
highlight_file(__FILE__);
$o = $_GET['o'];
if(is_numeric($o)){
die("no hack!!");
}
if($o==520){
echo file_get_contents('/flag');
}按照提示直接传入520会被die掉
但若传入520a之类数字加字母却成功
由于PHP是一门弱类型语言,存在弱类型比较问题。
PHP中
==
进行比较时候,当数据类型不一样的时候会将数据类型转换为相同的再进行比较,所以当我们传入字符串520a与整数520进行比较时,会强制转换520a为整数再与520比较要解决这个问题,用
===
比较即可,此时不仅会比较值,还会比较数据的类型0是特殊例子,弱类型与任何值比较都是true
switch弱类型比较问题
1 |
|
原理与上一致
MD5() 、sha1()哈希函数相等问题
1 |
|
isset()判断是否存在变量且非NULL,!意为取反值,因此该段代码想要get flag,要让两个不相等的值加密后他们相等
md5或者sha不能加密数组,所以在加密数组的时候会返回false
POST如下数据
1
2a[]=1&b[]=2
//用[]新建/修改一个数组
得到flag
HASH比较操作符问题
- 首先看几个问题
1 | "0e132456789"=="0e7124511451155" //true "0e1"=="0" //true |
在比较的时候,当出现xex模式,即匹配:
0e\d+
的 字符串将会当做科学计数法进行比较。
1 | if (isset($_GET['Username']) && isset($_GET['password'])) { $logined = true; $Username = $_GET['Username']; $password = $_GET['password']; if (!ctype_alpha($Username)) {$logined = false;} if (!is_numeric($password) ) {$logined = false;} if (md5($Username) != md5($password)) {$logined = false;} if ($logined){ echo "successful"; }else{ echo "login failed!"; } } |
ctype_alpha
函数功能就是检测是否是字符,也就是说我们username必须是数字,而password。必须是字母。再将传入的username和password进行md5加密后进行比较,这里就存在一个问题,如果md5加密后的值是0e开头这样就存在了弱类型比较问题就可以通过。- 常见以0e开头(可以用脚本写出,但是不会怎么写,所以只能在网上找到一些常用的)
QNKCDZO
0e830400451993494058024219903391
240610708
0e462097431906509019562988736854
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s155964671a
0e342768416822451524974117254469
s1184209335a
0e072485820392773389523109082030
s1665632922a
0e731198061491163073197128363787
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1091221200a
0e940624217856561557816327384675
s155964671a
0e342768416822451524974117254469
s1502113478a
0e861580163291561247404381396064
s155964671a
0e342768416822451524974117254469
s1665632922a
0e731198061491163073197128363787
s155964671a
0e342768416822451524974117254469
s1091221200a
0e940624217856561557816327384675
s1836677006a
0e481036490867661113260034900752
s1885207154a
0e509367213418206700842008763514
s532378020a
0e220463095855511507588041205815
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s214587387a
0e848240448830537924465865611904
s1502113478a
0e861580163291561247404381396064
s1091221200a
0e940624217856561557816327384675
s1665632922a
0e731198061491163073197128363787
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
0e545993274517709034328855841020
- 最后绕过payload是
1 | md5('240610708') == md5('QNKCDZO') |
- exp
?username=QNKCDZO&password=240610708
MD5碰撞问题
1 | highlight_file(__FILE__); $cmd=$_GET['cmd']; if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) { echo `$cmd`; } else { echo ("md5 is funny ~"); } md5 is funny ~ |
- 用
a[]=1&b[]=2
,发现不行,测试后发现数组被强制装换为字符串后都是Array - exp(a,b是两个MD5相同而内容不同的二进制文件url编码的结果)
1 | a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2 |
十六进制比较问题
1 | highlight_file(__FILE__); function noother_says_correct($number) { $one = ord('1'); $nine = ord('9'); for ($i = 0; $i < strlen($number); $i++) { $digit = ord($number{$i}); if ( ($digit >= $one) && ($digit <= $nine) ) { return false; } } return $number == '54975581388'; } $flag=file_get_contents("/flag"); if(noother_says_correct($_GET['key'])) echo $flag; else echo 'access denied'; access denied |
ord() 函数返回字符串的首个字符的 ASCII 值
- 这道题的意思就是要求输入一个key,然后这个key必须等于
54975581388
但是在自定义的函数里面又不允许出现数字,正好54975581388=0xccccccccc
这样就绕过了检测。
?key=0xccccccccc
PHP 组合比较运算符
组合比较运算符,符号为:<=>
1 | $c = $a <=> $b; |
相当于
1 | $c = $a > $b ? 1 : ( $a==$b ? 0 : -1 ); |
- 当 $a > $b 时,$c = 1
- 当 $a == $b 时,$c = 0
- 当 $a < $b 时,$c = -1
1 | $a = 1;$b = 2;$c = $a <=> $b;echo $c; |
-1
组合比较运算符同样存在 == 弱类型比较问题,原理同 “==”与“===”
1 | $a = '0e12';$b = '0e34';$c = $a <=> $b;echo $c; |
0
PHP strcmp() 函数
1 | int strcmp ( string $str1 , string $str2 ) |
根据ACSII表进行比较,从左到右依次进行,出现不同就停止比较,返回结果。
如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。
1 | // 二进制安全比较字符串(区分大小写)echo strcmp('Hello', 'hello');?> |
-1
因为’H’的ASCII值比’h’的小,所以返回负数
1 | $password="***";if(isset($_GET['password'])) { if (strcmp($_GET['password'], $password) == 0) { echo "flag"; } else { echo "Wrong"; }} |
PHP5.3以上版本的strcmp()存在漏洞,只要$_GET[‘password’]是一个数组或者一个object即可绕过。
1 | ?password[]=a |