PHP特性总结上

1.MD5和hash

无限制

在php里面,如果要判断相等的话,有==和===,前面的是弱相等,只要数值相等就ok了,但是后者的话呢,就是强比较了,需要类型和数值都要相等,在弱类型比较中,如果是字符串和数字判断,会把字符串转为数字再来判断是否相等,转换规则如下:

当一个字符串当作一个数值来取值,其结果和类型如下:如果该字符串没有包含’.',‘e’,'E’并且其数值值在整形的范围之内
该字符串被当作int来取值,其他所有情况下都被作为float来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0

例:

<?php
var_dump("admin"==0); // admin开始不为数字,转为0 true
var_dump("1admin"==1); // 1admin开始为数字1,转为1 true
var_dump("admin1"==1) //admin1开始不为数字,转为0 false
var_dump("admin1"==0) //admin1开始不为数字,转为0 true
var_dump(NULL==0) //true
var_dump(0==false); //true
var_dump(NULL==false); //true
var_dump("0e123456"=="0e4456789"); //true
?>

最后一个var_dump("0e123456"=="0e4456789"),在比较时,双方都被转为数字,由于有e,所以会被看成 0123456和04456789,结果都是0,自然相等;这个特性常被用于MD5函数的绕过,当然如果是弱相等的话,可以使用数组绕过(强相等也是可以的)了,以及一些以0e开头的md5字符串

但是总有一些恶心的

string强转:

<?php
highlight_file(__FILE__);
$a=(string)$_GET['a'];
$b=(string)$_GET['b'];
var_dump($a);
if ($a!=$b &&md5($a)===md5($b) )
{
echo 'yes!';
}
?>

这里把a和b都强制转为了字符串,数组过不了前面的条件,只能利用md5的缺陷,用fastcoll工具碰撞出md5值相同的但本身不同的字符串 即可

用法:先新建一个txt,里面写入任意字符串,然后把这个txt拖到fastcoll即可,就会生成两个文本文件,里面分别就是值不同但md5相同的字符串

再用一个php脚本输出这两个值的url编码,传给a,b即可:

<?php 
function readmyfile($path){
$fh = fopen($path, "rb");
$data = fread($fh, filesize($path));
fclose($fh);
return $data;
}
$a = urlencode(readmyfile("test_msg1.txt"));
$b = urlencode(readmyfile("test_msg2.txt"));
if(md5((string)urldecode($a))===md5((string)urldecode($b))){
echo "a=".$a;
}
echo "\n";
if(urldecode($a)!=urldecode($b)){
echo "b=".$b;
}

然后就生成相同的字符串了(由于字符串过大,这里就不列举了)

a==md5(a):

这就要求自己是0e,且md5后还是0e,上网找到两个

0e215962017
0e251288019

NULL也是可以被md5的,当md5(a)==md5(md5(b))时,可以把a设为NULL,b设为数组

双MD5后0e

CbDLytmyGm2xQyaLNhWn
770hQgrBOjrcqftrlaZk
7r4lGXCH2Ksu2JNT3BYM

MD5sql注入:

<?php include 'conn.php'; 
highlight_file(__file__);
if (isset($_GET['user']))
{
$query = "SELECT flag FROM here_is_flag WHERE password = '" . md5($_GET["user"],true) . "'";
$result = $conn->query($query);
$row = $result->fetch_assoc();
var_dump($row);
$result->free();
$conn->close();
}
else{
echo "Please input the user!<br>";
}

md5函数第二个参数设为true时,会把得到的32为十六进制字符串转为ascii字符串,如果能构造出 类似abc’ or ‘6就能造成sql注入,查询出flag

129581926211651571912466741651878684928
ffifdyop(例如:万能密码)

其他:

md4后是0e的字符串:

0e001233333333333334557778889
0e00000111222333333666788888889

sha1后是0e的字符串:

aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m
0e1290633704
10932435112

2.intval:

https://blog.csdn.net/wangyuxiang946/article/details/131156104 (强烈推荐)

记一下刷题经常遇到的考点:

科学表达式字符串:

php<7.2.25时,intval函数不能正常解析字符串形式的科学表达式,会返回底数,如

<?php 
var_dump(intval(1e2)); #返回100
var_dump(intval('1e2'));#返回1
?>

数组:

$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;

正常来说,字符串被一般intval转化都是0,就算转为非0,要在字符串开头含有数字,无法绕过正则

但是这里可以传数组,intval处理数组会返回数字1,传?num[]=1即可

自动进制转换:

0x开头 0开头 正常
16进制 8进制 10进制
var_dump(intval('0x64',0));
var_dump(intval('0144',0));
var_dump(intval('100',0));
#结果都是100

浮点数处理:

intval转换浮点数,有小数位会直接把小数位去掉,没有四舍五入,如intval(3.9)=3intval(‘+0x64’,0)=100,数字前可以加+不影响解析

3.preg_match正则:

这里就要讲一下在php里面关于正则的东西了

preg_match(’/php/’,php)
  参数1 模式
  参数2 字符串

正则表达式中的元素:

介绍

  1、正则表达式中包含三种元素分别为:量词、元字符、修饰符
  2、前导字符串:就是符号前面的一个字符或字符串

量词:

+ 匹配任何至少包含一个前导字符串
* 匹配任何包含零个或多个前导字符串
? 匹配任何包含零个或1个前导字符串
. 匹配任意一个字符串
{x} 匹配任何包含x个前导字符串
{x,y} 匹配任何包含 x 到 y 个前导字符串
{x,} 匹配任何包含至少x个前导字符串
^ 匹配字符串的行首
$ 匹配字符串的行尾
| 选择符 匹配字符串的左边或者右边
() 分组,提取

元字符:

[a-z] 匹配任何包含小写字母a-z的字符串
[A-Z] 匹配任何包含大写字母A-Z的字符串
[0-9] 匹配任何包含0-9的字符串
[abc] 匹配任何包含小写字母a,b,c的字符串
[^abc] 匹配任何不包含小写字母a,b,c的字符串
[a-zA-Z0-9_] 匹配任何包含a-zA-Z0-9和下划线的字符串
\w 匹配任何包含a-zA-Z0-9和下划线的字符串
\W 匹配任何不包含a-zA-Z0-9和下划线的字符串
\d 匹配任何包含数字字符
\D 匹配任何非数字字符
\s 匹配任何空白字符
\S 匹配任何非空白字符
\b 匹配是否到达了单词边界
\B 匹配没有到达了单词边界
\ 匹配正则中的特殊字符

修饰符:

i 完全不区分大小写
m 可以采用多行识别,遇到换行也承认匹配规则
x 忽略掉规则模式中的空白字符
A 强制从头开始匹配
U 禁止贪婪匹配,只跟踪到最近的一个匹配符并结束

正则常用的函数:

1、preg_grep()

  搜索数组中的所有元素,返回与某个模式匹配的字符串数组

2、preg_match()

  搜索模式,匹配返回true,不匹配返回false

3、preg_match_all()

  在字符串匹配模式的所有出现,然后将所有匹配的全部放入数组

4、preg_quote()

  将特殊字符转义
  特殊字符包含 $ ^ * () + = {} [] | \ : <>
  定界正则,在每一个对于正则表达式语法而言有特殊含义的字符前插入一个反斜杠

5、preg_replace()

  替换模式的所有出现,然后替换成想要的字符串返回出来

6、preg_split()

  以不区分大小写将字符串划分不同的元素

换行绕过

绕过.

类似于preg_match('/^.*(flag).*$/',$a)在关键词前后+了.,可以使用换行符%0a,绕过检测,.不匹配换行符

举个栗子:a=%0aflag;b=flag;print(a==b);true

绕过$

$是匹配文本结束的字符,会忽略结尾的换行

举个栗子:a=abc;b=abc%0a;print(a==b);true

preg_replace也可以用同样的方法来绕过

if(preg_match('^flag$/im'),$a){if(preg_match('^flag$/i'){echo I like flag}} a=%0aflag
这个是/m修饰符会开启多行匹配,以换行符分割各行,有一行符合就返回true

最大回溯绕过:

php的正则回溯次数是1000000,超过这个次数就会返回false

这里简单记录一下大概什么时候可以用这个绕过,感觉不止是回溯,似乎往前寻找的次数超过了限制也能触发

在关键词左右使用了贪婪匹配 *或 +

if (isset($_POST['Y'])){
$N = (string)$_POST['Y'];
if (preg_match('/.*myon/is', $N))
{
die("相见很容易");
}
if (stripos($N, 'swctfmyon') === FALSE)
{
die("再见却很难");
}
else
{
echo $flag."\n";
var_dump(preg_last_error() === PREG_BACKTRACK_LIMIT_ERROR);//检测最后一次正则表达式操作中的错误是不是回溯次数超出了限制

这里要求字符串中有swctfmyonsnert,却不能有myon,在myon前有.*,如果放1000000个其他字符a,让php正则往后寻找次数超过限制,也是可以的

import requests
url=''
data = {'Y':'a'*1000000+'swctfmyonsnert'}
re = requests.post(url=url,data=data)
print(re.text)

另外:preg_match无法处理数组,直接传数组会返回false,strcmp以及其他处理字符串的函数也无法处理数组,会返回NULL或false,

而对于strcmp,在如果比较的两个字符串相等也是返回0,使用弱类型比较会造成漏洞

常见的处理字符串的函数:

stripos 用于查找字符串中第一次出现子字符串的位置(不区分大小写)查找不到返回false
strpos 类似stripos,但 strpos 是区分大小写的
strcmp 比较两个字符串,相同返回0
stristr 用于在字符串中查找首次出现的子字符串(不区分大小写),并返回该子字符串及其后面的所有内容。如果没有找到子字符串,则返回 false