CTF中常见的 PHP 弱类型漏洞总结
作者:ZERO 所属团队:Arctic Shell
参考资料:
http://archimesan.me/2017/12/21/php%E5%BC%B1%E7%B1%BB%E5%9E%8B%E6%BC%8F%E6%B4%9E/
https://www.cnblogs.com/Mrsm1th/p/6745532.html
https://blog.spoock.com/2016/06/25/weakly-typed-security/
0x1::弱类型与强类型
通常语言有强类型和弱类型两种,强类型指的是强制数据类型的语言,就是说,一个变量一旦被定义了某个类型,如果不经过强制类型转换,这个变量就一直是这个类型,在变量使用之前必须声明变量的类型和名称,且不经强制转换不允许两种不同类型的变量互相操作。我们称之为强类型,而弱类型可以随意转换变量的类型例如可以这样:
$text=1; $text=”string”
也就是说php并不会验证变量的类型,可以随时的转换类型,估计开发者的意图是让程序员可以进行更高效的开发,所以在大量内置函数以及基本结构中使用了很多松散的比较和转换,防止程序中的变量因为程序员的不规范而报错,虽然提升了效率,但是引发了很多安全问题。
类型转换问题
类型转换最常见的就是int转String,String转int。
Int转String:
$num = 5;
方式1:$item =
(string)$num;
方式2:$item = strval($num);
String转int:
intval() 函数。(取整函数)
主要问题就出现在这个intval()函数上了。
例子:
var_dump(intval(4))// var_dump(intval(‘1asd’))// var_dump(intval(‘asd1’))//
上面三个例子说明了intval()函数在转换字符串的时候即使碰到不能转换的字符串的时候它也不会报错,而是返回0。
例子(来自于大佬的博客http://archimesan.me/2017/12/21/php%E5%BC%B1%E7%B1%BB%E5%9E%8B%E6%BC%8F%E6%B4%9E/)
<?php
if($_GET[id]) {
mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
mysql_select_db(SAE_MYSQL_DB);
$id = intval($_GET[id]);
$query = @mysql_fetch_array(mysql_query("select content from ctf2 where id='$id'"));
if ($_GET[id]==1024) {
echo "<p>no! try again</p>";
}
else{
echo($query[content]);
}
}
?>
主要问题就是你输入一个1024.1这样就可以利用取整性质进行绕过了。
0x2:比较操作符
在编程中类型转换是不可避免的一个事情,比如说网络编程中get方法或者post方法传入一个需要转换成int的值,再比如说变量间进行比较的时候,需要将变量进行转换,鉴于php是自动进行类型转换,所以会引发很多意想不到的问题。
“= =”与“= = =”比较操作符问题
php有两种比较方式,一种是“= =”一种是“= = =”这两种都可以比较两个数字的大小,但是有很明显的区别。
“= =”:会把两端变量类型转换成相同的,在进行比较。
“= = =”:会先判断两端变量类型是否相同,在进行比较。
这里明确说明,在两个相等的符号中,一个字符串与一个数字相比较时,字符串会转换成数值。
例如:
<?php
var_dump("name"==0); //true
var_dump("1name"==1); //true
var_dump("name1"==1) //false
var_dump("name1"==0) //true
var_dump("0e123456"=="0e4456789"); //true
?>
观察上述代码,很有意思,按照我们前面讲的规则,name与0相比较的时候,因为name是字符串,所以说转换成数字如果是0的话,0与0相比较自然是true,那末问题来了,下一句中的1name正常来说也是字符串,按照上一句的成立方式,1name应该是0,与1相比较的时候应该为false才对,为什莫为true了呢?我们可以假设一下,带数字的字符串不会变成0,会变成1,这样前三条逻辑就解释的清楚了,但是到第四条就又错了,为此我查了一下php的官方文档,文档是这样说的:当一个字符串当作一个数值来取值,其结果和类型如下:如果该字符串没有包含'.','e','E'并且其数值值在整形的范围之内,该字符串被当作int来取值,其他所有情况下都被作为float来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。
上述的代码为什莫出现那种奇怪的情况也就解释的清楚了。当不同类型的变量进行比较的时候就会存在变量转换的问题,在转换之后就有可能会存在问题。
hash比较操作符问题
在hash比较的时候也会出现问题例如:
"0e132456789"=="0e7124511451155" //true
"0e1abc"=="0" //true4219903
当出现xex模式的时候代表科学计数法,比如1e3=1*10三次方,在进行比较运算时,如果遇到了0e\d+(意思就是0e就是0e,d+的意思是后面全部是数字)这种字符串,就会将这种字符串解析为科学计数法。所以上面例子中2个数的值都是0因而就相等了。如果不满足0e\d+这种模式就不会相等。这个题目在攻防平台中的md5 collision就有考到。
例子:《MD5碰撞》源于南邮攻防平台题目(https://cgctf.nuptsast.com/challenges#Web)
<?php
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!";
}
}
?>
这一段代码的大致意思是输入一个数字和一个字符串,并且让他们的MD5值相同,才可以得到successful, 上文提到过,0e在比较的时候会将其视作为科学计数法,所以无论0e后面是什么,0的多少次方还是0。所以我们只需要输入一个数字和字符串进行MD5加密之后都为0e的即可得出答案。md5('240610708') == md5('QNKCDZO')成功绕过!。
十六进制转换问题
首先我们看一下例子:
"0x1e240"=="123456" //true
"0x1e240"==123456 //true
"0x1e240"=="1e240"//false
php在接受一个带0x的字符串的时候,会自动把这行字符串解析成十进制的再进行比较,0x1e240解析成十进制就是123456,并且与字符串类型的123456和int型的123456都相同。
例子:《起名字真难》源自源于南邮攻防平台题目(https://cgctf.nuptsast.com/challenges#Web)
<?php
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='*******';
if(noother_says_correct($_GET['key']))
echo $flag;
else
echo 'access denied';
?>
题目大致的意思就是输入一串key,key呢不可以是数字的形式,但是却要求与54975581388相等,看完题目就知道要求字符串和数字进行比较,想到的就是弱类型,54975581388与之匹配的十六进制的字符串是0xccccccccc。这就很巧了,全不是数字,自然就绕过了,得到flag。
布尔值转换问题
举例:
<?php
If ( true=“name”){
echo
“success”;
}
布尔值可以和任何字符串相等。
0x3:总结
1、字符串和数字比较,字符串会被转换成数字。
2、混合字符串转换成数字,看字符串的第一个。
3、字符串开头以xex开头,x代表数字。会被转换成科学计数法(注意一定要是0e/d+的模式)。但是也有例外如:-1.3e3转换为浮点数是-1300。
4、0x开头的字符串会先解析成十六进制再进行比较
5、布尔值跟任意字符串都弱类型相等。
php内置函数的参数的松散性
主要意思就是php内部函数在调用时给函数传递函数无法接受的参数类型但是却没有报错的情况
json绕过(这个不符合松散型)
首先我们介绍一下什莫是json:JSON概念很简单,JSON 是一种轻量级的数据格式,他基于 javascript 语法的子集,即数组和对象表示。由于使用的是 javascript 语法,因此JSON 定义可以包含在javascript 文件中,对其的访问无需通过基于 XML 的语言来额外解析。
例子:
<?php
if (isset($_POST['message'])) {
$message = json_decode($_POST['message']);
$key ="*********";
if ($message->key == $key) {
echo "flag";
}
else {
echo "fail";
}
}
else{
echo "~~~~";
}
?>
输入一个数组进行json解码,如果解码后的message与key值相同,会得到flag,主要思想还是弱类型进行绕过,我们不知道key值是什莫,但是我们知道一件事就是它肯定是字符串,这样就可以了,上文讲过,两个等号时会转化成同一类型再进行比较,直接构造一个0就可以相等了。最终payload message={"key":0}。
MD5 ,sha1绕过
首先还是介绍一下这两个函数,这俩都是加密函数,分别进行的时给字符串进行MD5加密和计算字符串的 SHA-1 散列。
但是这个函数都有着缺陷,就是不能处理数组。
这样就很容易绕过了
例子:
<?php
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
die('Flag: '.$flag);
else
print 'Wrong.';
}
?>
直接构造数组就可以绕过了payload: a[]=1&b[]=2
switch绕过
缺陷原理相同,绕过姿势相同,如果switch是数字类型的case的判断时,switch会将其中的参数转换为int类型。如下:
<?php
$i ="3name";
switch ($i) {
case 0:
case 1:
case 2:
echo "this is two";
break;
case 3:
echo "flag";
break;
}
?>
strcmp绕过这个时候程序输出的是,类型转换的i,结果为3返回flag
strcmp()函数在PHP官方手册中的描述是int strcmp ( string $str1 , string $str2 ),需要给strcmp()传递2个string类型的参数。如果str1小于str2,返回-1,相等返回0,否则返回1。strcmp函数比较字符串的本质是将两个变量转换为ascii,然后进行减法运算,然后根据运算结果来决定返回值。
例子:
<?php
$password="***************
if(isset($_POST['password'])){
if (strcmp($_POST['password'], $password) == 0) {
echo "Right!!!login success";n
exit();
} else {
echo "Wrong password..";
}
?>
在这个题目中我们需要自己输入一个password的值和$password相比较但是我们不知道这个password的值,有可能时字符串有可能时数字,这个时候怎末办呢,依然时相同的绕过姿势,试一试数组绕过假设如果传入一个数组会怎末样呢?我们传入password[]=xxx ,绕过成功。
原理是因为函数接受到了不符合的类型,将发生错误,函数返回值为0,所以判断相等。
array_search()、in_array()绕过
首先介绍一下什莫是array_search()函数, array_search() 函数在数组中搜索某个键值,并返回对应的键名。in_array() 函数搜索数组中是否存在指定的值。基本功能是相同的,也就是说绕过姿势也相同。Array系列有两种安全问题,一种是正常的数组绕过,一种是“= =”号问题。先讲第一个数组绕过。
举例:
<?php
if(!is_array($_GET['test'])){exit();}
$test=$_GET['test'];
for($i=0;$i<count($test);$i++){
if($test[$i]==="admin"){
echo "error";
exit();
}
$test[$i]=intval($test[$i]);
}
if(array_search("admin",$test)===0){
echo "flag";
}
else{
echo "false";
}
?>
这段代码的意思就是先判断是不是数组,然后在把数组中的内容一个个进行遍历,所有内容都不能等于admin,类型也必须相同,然后转化成int型,然后再进行比较如果填入值与admin相同,则返回flag,如何绕过呢?
基本思路还是不变,因为用的是三个等于号,所以说“= =”号这个方法基本不能用,那就用第二条思路,利用函数接入到了不符合的类型返回“0”这个特性,直接绕过检测。所以payload:test[]=0。
第二个 “= =”的问题
在PHP手册中,in_array()函数的解释是bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ),如果strict参数没有提供或者是false(true会进行严格的过滤),那么in_array就会使用松散比较来判断$needle是否在$haystack中。当strince的值为true时,in_array()会比较needls的类型和haystack中的类型是否相同。
例子:
$array=[0,1,2,'3'];
var_dump(in_array('abc', $array)); //true
var_dump(in_array('1bc', $array)); //true
通过例子我们就知道了,这个松散的判断就是等于号,所以出现了“= =”号的特性“abc”==0、“1bc”==1,如果不加true的话就可以利用“= =”轻松绕过。array_search同理。
0x4:结束—时刻防备弱类型
作为一个程序员,弱类型确实提升了程序员书写代码的效率,但是也带来了严重的安全问题,可以说一切输入都是有害的,一切输入的类型也是可疑的,永远都要带有怀疑精神去看待弱类型的php下任何比较函数,任何数学运算!
CTF中常见的 PHP 弱类型漏洞总结的更多相关文章
- c#中的强类型、弱类型和泛型
强类型和弱类型的变量都有两个属性:类型和值. 强类型的变量类型是不能改变的,弱类型的变量类型是随需改变的,这是强弱的真正含义. 我们在编写c#代码时,变量类型是明确的,不可更改的,如string就是s ...
- PHP弱类型漏洞学习
简介 PHP在使用双等号(==)判断的时候,不会严格检验传入的变量类型,同时在执行过程中可以将变量自由地进行转换类型.由于弱数据类型的特点,在使用双等号和一些函数时,会造成一定的安全隐患 eg: &l ...
- CTF中常见Web源码泄露总结
目录00x1 .ng源码泄露 00x2 git源码泄露 00x3 .DS_Store文件泄漏 00x4 网站备份压缩文件 00x5 SVN导致文件泄露 00x6 WEB-INF/web.xml泄露 ...
- CTF中常见密码题解密网站总结
0x00.综合 网站中包含大多编码的解码. http://web2hack.org/xssee/ https://www.sojson.com/ http://web.chacuo.net/ 0x01 ...
- CTF中常见密码学
前言 参考,我们任课老师的WORD和PPT,结合自己的理解,在结合网上文章的理解. 一.BASE64编码 BASE64编码中,特征和所拥有的字符字母:A-Z a-z;数字:0-9;符号:+ / ,然后 ...
- ctf中常见注入题源码及脚本分析
1.代码审计发现 这里没有用escape_string,因此存在注入. function show($username){ global $conn; $sql = "select role ...
- CTF中常见的加解密(经典)
今天一早起来,就要去做早操,心里苦呀! 但是不影响我为未来的学弟学妹整理资料的心情呀!希望我的一些拙见能够帮助到学弟学妹! 永远爱你们的 ---- 新宝宝 ASCII编码 ASCII 码使用指定的7 ...
- CTF中常见文件的文件头(十六进制)
jpg/jpeg FF D8 FF E0 或 FF D8 FF E1 或 FF D8 FF E8 png 89 50 4E 47 bmp 42 4D 36 5D gif 47 49 46 38 zip ...
- PHP弱类型hash比较缺陷
成因分析: == 在进行比较的时候,会先将两边的变量类型转化成相同的,再进行比较 0e在比较的时候会将其视作为科学计数法,所以无论0e后面是什么,0的多少次方还是0. 因此CTF比赛中需要用到弱类型H ...
随机推荐
- SpringMVC 中xml 配置多数据源
1,配置jdbc.properties jdbc.driver_one=... jdbc.url_one=..... jdbc.username_one=... jdbc.password_one=. ...
- 【HTTP请求】、详解
一.协议介绍 HTTP(HyperText Transfer Protocol,超文本传输协议)是一套计算机通过网络进行通信的规则,使HTTP客户端能够从HTTP服务器端请求到信息和服务,目前的版本号 ...
- POJ2533 最长递增子序列
描述: 7 1 7 3 5 9 4 8 输出4 最长递增子序列为1 3 5 9,不必连续. 解法: 三种思路: 转化为最长公共子序列(n^2),动态规划(n^2),不知叫什么解法(nlogn). 解法 ...
- Python中numpy.apply_along_axis()函数的用法
numpy.apply_along_axis(func, axis, arr, *args, **kwargs): 必选参数:func,axis,arr.其中func是我们自定义的一个函数,函数fun ...
- CoderForces 689A Mike and Cellphone (水题)
题意:给定一个手机键盘数字九宫格,然后让你判断某种操作是不是唯一的,也就是说是不是可以通过平移也能实现. 析:我的想法是那就平移一下,看看能实现,就四种平移,上,下,左,右,上是-3,要注意0变成8, ...
- BCD码转换为十进制或者十进制转为BCD码
BCD码其实就是之前在数字电路中说的 用4位二进制数值 来表示一个0-9中的数字,例如: 0000=0 0001=1 0010=2 0011=3也就是说如果把一个数字作为一个BCD码,例如: 11 2 ...
- 微信小程序 tp5上传图片
test.wxml页面 <view class="title">请选择要反馈的问题</view> <view> <picker bindc ...
- linux计划任务(一)
一次性计划任务 at /etc/init.d/atd [root@localhost ~]# at : at> /bin/ls /etc |wc -l > /tmp/yimiao_demo ...
- Centos环境下手动设置-网络参数配置-网络挨排错顺序-设置网卡为上网模式的设定
Linux中网络参数大致包含以下内容: IP地址 子网掩码 网关 DNS服务器 主机名(默认 localhost) 历来Linux系统中修改这些参数的方式通常有:命令.文件两种.其中通过命令设置可以立 ...
- B-spline Curves 学习之B样条曲线的移动控制点、修改节点分析(7)
B-spline Curves: Moving Control Points 本博客转自前人的博客的翻译版本,前几章节是原来博主的翻译内容,但是后续章节博主不在提供翻译,后续章节我在完成相关的翻译学习 ...