0%

fslh-writeup

背景

在某次比赛中碰到一题很魔幻的题目,让我印象深刻,特别是赛后主办方提供的wp(见下图)看得我久久不能忘怀,机缘巧合之下,又碰到了这一题目,故来分析一下。

主办方的wp

正文

题目打开只有一个500报错

index页面

500报错

扫描源代码文件,发现index.php.swo备份文件(坑!少见的备份文件格式,其是vim打开文件后的缓存文件),以下为文件内容:

index.php.swo

index.php.swo

分析题目,猜测flag是藏在类的注释中,我们能够实例化任意类,并调用类方法,那么就可以利用PHP 内置类中的 ReflectionMethod来读取User类里面各个函数的注释,本地测试如下:

本地测试ReflectionMethod

构造成题目中的http参数则是:?rc=ReflectionMethod&ra=User&rb=a&rd=getDocComment

因为不知道是在哪个函数的注释中,所以逐个函数暴破,暴破rb的值a-z,可以发现flagq的注释中

遍历函数注释内容

小结

本题考察的是PHP反射ReflectionMethod构造User类中的函数方法,再通过getDocComment获取函数的注释,本例中使用__toString同样可以输出函数注释内容。

ReflectionClass API

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
$ref = new ReflectionClass(B::class);

//print_r(ReflectionClass::export(demo::class));
print_r($ref->getProperties()); // 获取一级属性, 可以传参数过滤, 返回ReflectionProperty 对象的数组。
var_dump($ref->getConstructor()); // 获取构造函数, 未定义返回null
var_dump($ref->inNamespace()); // 是否在命名空间中
var_dump($ref->getConstants()); // 获取所有定义的常量
var_dump($ref->getConstant('TEST_1')); // 获取某个常量
print_r($ref->getDefaultProperties()); // 获取默认属性, 返回数组, 包括父类的属性
var_dump($ref->getDocComment()); // 获取类文档注释, 不包含属性和方法的注释, 无注释返回false
var_dump($ref->getExtension()); // 获取获取最后一行的行数
var_dump($ref->getFileName()); // 获取定义类的文件名, 返回绝对路径
var_dump($ref->getInterfaceNames()); // 获取接口名称, 返回索引数组,值为接口名称, 未实现接口返回空数组
var_dump($ref->getInterfaces()); // 获取接口, 返回关联数组, name=>ReflectionClass实例, 未实现接口返回空数组
var_dump($ref->getMethods()); // 指获取类方法 ReflectionMethod。
var_dump($ref->getMethod('foo4')); // 获取一个类方法的 ReflectionMethod。如果方法不存在会抛出异常, 需要配合try catch一起用
var_dump($ref->getName()); // 获取类名, 包含命名空间
var_dump($ref->getNamespaceName()); // 获取命名空间的名称, 没有返回空
var_dump($ref->getParentClass()); // 获取父类reflectionClass的实例, 没有父类返回false
var_dump($ref->getProperty('prop3')); // 获取一个属性, 返回ReflectionProperty实例, 属性不存在会抛出异常, 需配合try catch使用
var_dump($ref->getShortName()); // 获取类名, 不包含命名空间
var_dump($ref->getStartLine()); // 获取起始行号
print_r($ref->getStaticProperties()); // 获取静态属性
print_r($ref->getStaticPropertyValue('prop_static')); // 获取静态属性值, 未定义的属性会报致命错误
print_r($ref->getTraitAliases()); // 返回 trait 别名的一个数组
print_r($ref->getTraitNames()); // 返回 trait 别名的一个数组
print_r($ref->getTraits()); // 返回这个类所使用的 traits 数组
var_dump($ref->hasConstant('AB')); // 检查常量是否已经定义
var_dump($ref->hasMethod('AB')); // 检查方法是否已经定义
var_dump($ref->hasProperty('AB')); // 检查属性是否已定义
var_dump($ref->implementsInterface('reflection\Abc')); // 检查是否实现了某个接口, 注意需要带上命名空间
var_dump($ref->isAbstract()); // 检查类是否是抽象类(abstract)
var_dump($ref->isAnonymous()); // 检查类是否是匿名类
var_dump($ref->isCloneable()); // 返回了一个类是否可复制
var_dump($ref->isFinal()); // 检查类是否声明为 final
var_dump($ref->isInstance($obj)); // 检查一个变量是否此类的实例
var_dump($ref->isInstantiable()); // 检查类是否可实例化
var_dump($ref->isInterface()); // 检查类是否是一个接口(interface)
var_dump($ref->isInternal()); // 检查类是否由扩展或核心在内部定义, 和isUserDefined相对
var_dump($ref->isIterateable()); // 检查此类是否可迭代, 实现了Iterator接口即可迭代
var_dump($ref->isSubclassOf(A::class)); // 是否是某一个类的子类
var_dump($ref->isTrait()); // 返回了是否为一个 trait
var_dump($ref->isUserDefined()); // 检查是否由用户定义的类 和isInternal相对

// 从指定的参数创建一个新的类实例,创建类的新的实例。给出的参数将会传递到类的构造函数。
// 接受可变数目的参数,用于传递到类的构造函数,和 call_user_func() 很相似。
var_dump($ref->newInstance());
// 从指定的参数创建一个新的类实例,创建类的新的实例。给出的参数将会传递到类的构造函数。
//这个参数以 array 形式传递到类的构造函数。
var_dump($ref->newInstanceArgs([]));
var_dump($ref->newInstanceWithoutConstructor()); // 创建一个新的实例而不调用他的构造函数
$ref->setStaticPropertyValue ('prop_static', '222'); // 设置静态属性的值, 无返回值
var_dump($ref->__toString ()); // 返回 ReflectionClass 对象字符串的表示形式。
​```

ReflectionMethod API

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/*
ReflectionMethod::__construct — ReflectionMethod 的构造函数
ReflectionMethod::export — 输出一个回调方法
ReflectionMethod::getClosure — 返回一个动态建立的方法调用接口,译者注:可以使用这个返回值直接调用非公开方法。
ReflectionMethod::getDeclaringClass — 获取被反射的方法所在类的反射实例
ReflectionMethod::getModifiers — 获取方法的修饰符
ReflectionMethod::getPrototype — 返回方法原型 (如果存在)
ReflectionMethod::invoke — Invoke
ReflectionMethod::invokeArgs — 带参数执行
ReflectionMethod::isAbstract — 判断方法是否是抽象方法
ReflectionMethod::isConstructor — 判断方法是否是构造方法
ReflectionMethod::isDestructor — 判断方法是否是析构方法
ReflectionMethod::isFinal — 判断方法是否定义 final
ReflectionMethod::isPrivate — 判断方法是否是私有方法
ReflectionMethod::isProtected — 判断方法是否是保护方法 (protected)
ReflectionMethod::isPublic — 判断方法是否是公开方法
ReflectionMethod::isStatic — 判断方法是否是静态方法
ReflectionMethod::setAccessible — 设置方法是否访问
ReflectionMethod::__toString — 返回反射方法对象的字符串表达
*/
ReflectionMethod extends ReflectionFunctionAbstract implements Reflector {
/* 常量 */
const integer IS_STATIC = 1 ;
const integer IS_PUBLIC = 256 ;
const integer IS_PROTECTED = 512 ;
const integer IS_PRIVATE = 1024 ;
const integer IS_ABSTRACT = 2 ;
const integer IS_FINAL = 4 ;
/* 属性 */
public $name ;
public $class ;
/* 方法 */
public __construct ( mixed $class , string $name )
public static export ( string $class , string $name [, bool $return = false ] ) : string
public getClosure ( object $object ) : Closure
public getDeclaringClass ( ) : ReflectionClass
public getModifiers ( ) : int
public getPrototype ( ) : ReflectionMethod
public invoke ( object $object [, mixed $parameter [, mixed $... ]] ) : mixed
public invokeArgs ( object $object , array $args ) : mixed
public isAbstract ( ) : bool
public isConstructor ( ) : bool
public isDestructor ( ) : bool
public isFinal ( ) : bool
public isPrivate ( ) : bool
public isProtected ( ) : bool
public isPublic ( ) : bool
public isStatic ( ) : bool
public setAccessible ( bool $accessible ) : void
public __toString ( ) : string
/* 继承的方法 */
final private ReflectionFunctionAbstract::__clone ( ) : void
public ReflectionFunctionAbstract::getClosureScopeClass ( ) : ReflectionClass
public ReflectionFunctionAbstract::getClosureThis ( ) : object
public ReflectionFunctionAbstract::getDocComment ( ) : string
public ReflectionFunctionAbstract::getEndLine ( ) : int
public ReflectionFunctionAbstract::getExtension ( ) : ReflectionExtension
public ReflectionFunctionAbstract::getExtensionName ( ) : string
public ReflectionFunctionAbstract::getFileName ( ) : string
public ReflectionFunctionAbstract::getName ( ) : string
public ReflectionFunctionAbstract::getNamespaceName ( ) : string
public ReflectionFunctionAbstract::getNumberOfParameters ( ) : int
public ReflectionFunctionAbstract::getNumberOfRequiredParameters ( ) : int
public ReflectionFunctionAbstract::getParameters ( ) : array
public ReflectionFunctionAbstract::getReturnType ( ) : ReflectionType
public ReflectionFunctionAbstract::getShortName ( ) : string
public ReflectionFunctionAbstract::getStartLine ( ) : int
public ReflectionFunctionAbstract::getStaticVariables ( ) : array
public ReflectionFunctionAbstract::hasReturnType ( ) : bool
public ReflectionFunctionAbstract::inNamespace ( ) : bool
public ReflectionFunctionAbstract::isClosure ( ) : bool
public ReflectionFunctionAbstract::isDeprecated ( ) : bool
public ReflectionFunctionAbstract::isGenerator ( ) : bool
public ReflectionFunctionAbstract::isInternal ( ) : bool
public ReflectionFunctionAbstract::isUserDefined ( ) : bool
public ReflectionFunctionAbstract::isVariadic ( ) : bool
public ReflectionFunctionAbstract::returnsReference ( ) : bool
abstract public ReflectionFunctionAbstract::__toString ( ) : void
}

伪源码

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<?php
class User
{
private static $c = 0;

function a()
{
return ++self::$c;
}

function b()
{
return ++self::$c;
}

function c()
{
return ++self::$c;
}

function d()
{
return ++self::$c;
}

function e()
{
return ++self::$c;
}

function f()
{
return ++self::$c;
}

function g()
{
return ++self::$c;
}

function h()
{
return ++self::$c;
}

function i()
{
return ++self::$c;
}

function j()
{
return ++self::$c;
}

function k()
{
return ++self::$c;
}

function l()
{
return ++self::$c;
}

function m()
{
return ++self::$c;
}

function n()
{
return ++self::$c;
}

function o()
{
return ++self::$c;
}

function p()
{
return ++self::$c;
}
/**
* Increment counter
* @final
* @static
* @access publicflag{b5bd0ab820fd11eb8cf4fa163e83cb88}
* @return int
*/
function q()
{
return ++self::$c;
}

function r()
{
return ++self::$c;
}

function s()
{
return ++self::$c;
}

function t()
{
return ++self::$c;
}

}



$rc=$_GET["rc"];
$rb=$_GET["rb"];
$ra=$_GET["ra"];
$rd=$_GET["rd"];
$method= new $rc($ra, $rb);
var_dump($method->$rd());