# 2025ISCTF

2025ISCTF

WEB

b@by n0t1ce b0ard

这里根据题目提示已经知道了这个是什么漏洞

image-20251202191352645

搜一下编号

image-20251202191607586

看来就是一个简单的文件上传漏洞

看下注册页面

image-20251202191815737

看见可以上传文件了,直接传

这里要把信息填写完全才能传

上传之后去审一下代码看眼图片的上传位置

image-20251202192144384

这里也可以分析出来了

马在images/123456789@qq.com/1.php

image-20251202192257811

直接寻找然后cat

image-20251202192327369

ezrce

image-20251202192449544

这里可以发现这里只能用字母()_;

所以这里就可以联想到无参数rce的绕过直接打payload

print_r(scandir(getcwd()));

这个就等同于ls

image-20251202195621739

看一下上级目录

print_r(scandir(dirname(getcwd())));

image-20251202195702036

这里有一个localhost的文件或者文件夹

使用readfile()尝试一下看看

image-20251202202135454

报错了,看来是一个文件夹

这里接着往前翻,翻到flag了

image-20251202204341695

太阴了

print_r(scandir(dirname(dirname(dirname(getcwd())))));

接下来获取就行

这里使用next会变得异常艰难,因为next返回的是一个值而不是·数组所以这里不能嵌套使用

这里得先更换目录,然后再读取flag

code=chdir(dirname(dirname(dirname(getcwd()))));readfile(flag);

image-20251202212654652

难过的bottle

这里打开源代码不难发现

他过滤完了基本上所有的字母就剩下flag

这里一开始想的是直接打软链接但是因为输入的内容最后是会被模板渲染

会导致模板渲染的错误

这里软链接就打不了了

这里就涉及到bottle的模板注入

因为是一个新知识点,这里其实就是简单的用到了斜体注入

image-20251204000220209

这里再打包上传查看文件就可以了

image-20251204000250525

虽然这里写的很少,但是其中辛酸只有我自己知道

flag到底在哪

打开环境进入robots.txt

image-20251204100616807

访问之后会跳转到一个登录口

image-20251204100840674

再结合题目提示,这里需要在密码位置做一个简单的sql注入

' OR '1'='1

用户名么就是admin了

登录之后就是上传php了

image-20251204100954056

image-20251204101057529

蚁剑链接一下

image-20251204101429767

flag的位置可以在start.sh里面看见

flag?我就借走了

打开环境

image-20251204102529119

这里很明显要打软链接,但是之前尝试的时候直接弄成了flag所以软链接打不了

这里我们需要弄成flag.txt因为只有这个还有png不会默认给你下载下来,才能在页面加载出来

这里去kali里面打包

ln -s /flag flag.txt
tar cf flag.tar flag.txt

打包之后上传

再访问就可以了

image-20251204102740528

Who am I

打开环境

image-20251204102913788

是一个登录界面

image-20251204103033718

可以发现密码最少是6个字符

先注册一个登录看一下

这里是可以直接注册admin用户

这里又注册了一个blueshark抓包的时候发现了type参数改成0之后登录到了管理员界面

image-20251204105325066

里面有一个main.py

from flask import Flask,request,render_template,redirect,url_for
import json
import pydash

app=Flask(__name__)

database={}
data_index=0
name=''

@app.route('/',methods=['GET'])
def index():
    return render_template('login.html')

@app.route('/register',methods=['GET'])
def register():
    return render_template('register.html')

@app.route('/registerV2',methods=['POST'])
def registerV2():
    username=request.form['username']
    password=request.form['password']
    password2=request.form['password2']
    if password!=password2:
        return '''
        <script>
        alert('前后密码不一致,请确认后重新输入。');
        window.location.href='/register';
        </script>
        '''
    else:
        global data_index
        data_index+=1
        database[data_index]=username
        database[username]=password
        return redirect(url_for('index'))

@app.route('/user_dashboard',methods=['GET'])
def user_dashboard():
    return render_template('dashboard.html')

@app.route('/272e1739b89da32e983970ece1a086bd',methods=['GET'])
def A272e1739b89da32e983970ece1a086bd():
    return render_template('admin.html')

@app.route('/operate',methods=['GET'])
def operate():
    username=request.args.get('username')
    password=request.args.get('password')
    confirm_password=request.args.get('confirm_password')
    if username in globals() and "old" not in password:
        Username=globals()[username]
        try:
            pydash.set_(Username,password,confirm_password)
            return "oprate success"
        except:
            return "oprate failed"
    else:
        return "oprate failed"

@app.route('/user/name',methods=['POST'])
def name():
    return {'username':user}

def logout():
    return redirect(url_for('index'))

@app.route('/reset',methods=['POST'])
def reset():
    old_password=request.form['old_password']
    new_password=request.form['new_password']
    if user in database and database[user] == old_password:
        database[user]=new_password
        return '''
        <script>
        alert('密码修改成功,请重新登录。');
        window.location.href='/';
        </script>
        '''
    else:
        return '''
        <script>
        alert('密码修改失败,请确认旧密码是否正确。');
        window.location.href='/user_dashboard';
        </script>
        '''

@app.route('/impression',methods=['GET'])
def impression():
    point=request.args.get('point')
    if len(point) > 5:
        return "Invalid request"
    List=["{","}",".","%","<",">","_"]
    for i in point:
        if i in List:
            return "Invalid request"
    return render_template(point)

@app.route('/login',methods=['POST'])
def login():
    username=request.form['username']
    password=request.form['password']
    type=request.form['type']
    if username in database and database[username] != password:
        return '''
        <script>
        alert('用户名或密码错误请重新输入。');
        window.location.href='/';
        </script>
        '''
    elif username not in database:
        return '''
        <script>
        alert('用户名或密码错误请重新输入。');
        window.location.href='/';
        </script>
        '''
    else:
        global name
        name=username    
        if int(type)==1:
            return redirect(url_for('user_dashboard'))
        elif int(type)==0:
            return redirect(url_for('A272e1739b89da32e983970ece1a086bd'))

if __name__=='__main__':
    app.run(host='0.0.0.0',port=8080,debug=False)

拿去trae里面看

这里可以分析一下/impression路由下是存在一个SSTI的

但是这里有一个问题,首先限制太多基本上打不了了,又是符号又是长度限制的

所以这里这个SSTI就可以放弃了,往前找找到了一个特殊的路由/operate

这个路由里面有一个特殊的函数pydash.set_

这个搜索一下就可以知道这个可以拿来打原型链污染

里面有一个类似waf的东西,先污染它,restricted_key把他污染成123

/operate?username=helpers.RESTRICTED_KEYS&password=123

image-20251204212313895

但是这里可以发现污染没成功因为这里还有一个限制

if username in globals() and "old" not in password:

因为restricted_key是一个常量不属于全局变量的范畴,所以这里我们要想办法绕过

这里直接找一下变量

database={}
data_index=0
name=''
app=Flask(__name__)

这里我们可以操控的有database来设置这个全局变量

这里根据代码里面可以知道

image-20251204212630700

我们创建之后就会把用户名和密码存进去

这里就创建一下

/operate?username=database&password=helpers.RESTRICTED_KEYS&confirm_password=123

image-20251204213232463

这里也是显示污染成功了

但是这里之后并不能进行后续操作

具体的原因可能要等到我再深入学习之后才能了解

这里要污染的是另一个

MISC

木林森

打开文本里面是两段base64

image-20251205212654876

第一段很明显的是png图片的base64

image-20251205212726927

加上前缀直接去浏览器里面看

data:image/png;base64,

image-20251205212815170

是一个二维码,扫一下看看

image-20251205213351794

再去把另一段base64解开

image-20251205213415453

是一串16进制的东西

再去看下题目,搜索那一串英文

image-20251205213509037

使用RC4解一下看看

posted @ 2025-12-07 23:44  crook666  阅读(1)  评论(0)    收藏  举报