FLYTEAM第一届新年CTF Web方向部分wp
字数 3836
更新时间 2026-03-12 13:48:03

FLYTEAM 第一届新年CTF Web方向Writeup教学文档

题目一:ez_SSTI

1. 题目概览

此题目是一个模板注入(SSTI)挑战,目标是利用一系列逻辑和身份验证漏洞,最终在/ssti路由下执行模板注入,达成目标条件。

2. 核心漏洞与利用链分析

攻击流程可分解为以下关键步骤:

第一步:信息收集与目标确立

  1. 分析路由:首先审查应用的所有路由及其对应的处理函数。
  2. 明确目标:攻击的最终目标是访问/ssti路由,并利用其模板渲染漏洞。该路由设有前置条件:
    • 当前用户身份不能admin
    • 当前用户账户的金额必须大于等于10000元

第二步:创建非admin用户

  1. 利用注册功能:通过/register路由注册一个新用户。注意:需严格按照要求的输入格式提交注册信息。

第三步:获取足够资金(关键漏洞点1 - 转账逻辑漏洞)

  1. 分析转账路由:发现转账功能存在逻辑缺陷。
    • 漏洞细节非admin用户在向他人转账时,不会扣除自身账户的金额
    • 额外优势:转账操作无需知道收款方的密码。
  2. 漏洞利用思路:利用上述漏洞,让admin用户向刚刚注册的新用户转账,从而为新用户积累资金。
  3. 关键问题:如何以admin身份发起转账?这需要获取admin的登录凭证(Cookie)。

第四步:获取admin身份Cookie(关键漏洞点2 - 密码重置漏洞)

  1. 分析密码重置路由 (/reset_password):该功能存在身份验证绕过漏洞。
  2. 漏洞细节:重置密码时验证“旧密码”的逻辑存在缺陷。系统并非验证完整的旧密码,而是检查用户输入的旧密码的后三位,是否与真实密码按位与0xfff后的值相等。
  3. 利用方法:可以利用此宽松的比较条件,尝试碰撞出符合条件的输入,从而成功重置admin的密码,进而登录获取其Cookie。

第五步:执行转账与资金积累

  1. 登录admin账户:使用上一步获取的Cookie登录admin账户。
  2. 发起转账:以admin身份,向第二步注册的新用户账户发起多次转账,直至新用户的账户余额大于等于10000元

第六步:触发SSTI(关键漏洞点3 - 模板注入与过滤绕过)

  1. 登录新用户:使用新用户的账号密码登录。
  2. 访问/ssti路由:此时已满足“非admin”且“金额>=10000”的条件,可以进入存在SSTI漏洞的页面。
  3. 分析过滤机制:页面存在针对SSTI payload的过滤,看似复杂,实为“障眼法”。
  4. 利用方法(打法1 - 全量URL编码绕过)
    • 关键在于服务器端在处理输入时,会调用unquote函数。此函数会将URL编码(如%7b)解码还原为原始字符(如{)。
    • 因此,攻击者可以将包含{{}等关键字符的SSTI payload进行全量URL编码后提交。服务器在unquote解码后,这些字符将恢复原状,从而绕过前端的过滤检测,成功注入。
  5. 利用方法(打法2 - 使用Fenjing工具中转)
    • 这是一种应对复杂过滤的更通用方法。
    • 步骤
      a. 在攻击者本地或可控服务器上搭建一个Web应用作为“中转站”。
      b. 该中转站的作用是:接收来自攻击者客户端的明文payload,自动将其转换为符合靶机过滤规则的格式(例如,自动进行全量URL编码),然后代表攻击者向靶机发送请求。
      c. 这样,攻击者可以直接使用Fenjing等SSTI自动化利用工具,向本地中转站发送明文的测试payload,由中转站负责编码和转发,极大简化了手工测试和绕过的过程。

题目二:mongo

1. 题目概览

本题涉及Pickle反序列化漏洞和一个辅助性的MongoDB服务。

2. 源码分析与漏洞定位

  1. 审查源码 (app.py):应用主要包含两个路由。
  2. 定位漏洞:其中一个路由的代码存在明显的pickle.loads()调用,且参数用户可控,构成了Pickle反序列化漏洞

3. 背景逻辑分析

  1. 自定义函数与定时任务:代码中定义了一个函数,并设置其被周期性调用。
  2. 函数作用:该函数会每10秒执行一次,其功能是清空MongoDB数据库中的某个集合,然后向其中写入大量包含admin用户密码的文档

4. 攻击思路

  1. 思路解析:题目给出了两个靶机。第一个是存在Pickle反序列化漏洞的Web应用(靶机1),第二个是一个开放的MongoDB数据库服务(靶机2)。根据代码逻辑,靶机2的数据库中会周期性地被写入admin的密码。
  2. 攻击步骤
    a. 连接MongoDB:首先,编写脚本连接第二个靶机(MongoDB服务)。
    b. 查询密码:从指定的集合中查询出admin用户的当前密码。
    c. 访问Admin路由:使用查询到的密码,访问Web应用(靶机1)的/admin路由,完成正常的登录认证。
    d. 利用反序列化漏洞:在获得admin权限后,寻找或触发存在pickle.loads()的点,提交构造好的恶意序列化数据,实现远程代码执行。

题目三:ez_php

1. 题目概览

本题是一个PHP反序列化题目,核心难点在于绕过__wakeup魔术方法的限制。

2. 源码分析与核心矛盾

  1. 审查源码:存在一个包含__wakeup, __construct, __destruct, execute等方法的类。
  2. 关键限制
    • __wakeup()方法:在反序列化时自动调用,此方法会将对象的handle属性设置为null
    • execute()方法:是命令执行的关键函数,但它会检查handle属性是否不为空。如果handlenull,则无法继续。
  3. 矛盾点:直接反序列化一个该类的对象,会首先触发__wakeup(),导致handle被置null,进而使得后续的__destruct()或其它路径调用的execute()方法失效。

3. 漏洞利用链构造

为了绕过__wakeup的限制,需要精心构造一个对象链,控制对象销毁和属性引用的顺序。核心思路是利用PHP对象引用和销毁顺序

以下是具体的构造步骤(以三个对象为例):

  1. 对象1 ($obj1)

    • 类型:目标类(即存在漏洞的类)。
    • 作用“炮灰”与“触发器”。它的handle属性指向对象3
    • 销毁时机:我们希望它最先被销毁。
  2. 对象2 ($obj2)

    • 类型:一个简单的容器类(例如getHandler),其内部有一个属性(如$a引用着对象3(即$obj2->a = &$obj3)。
    • 作用“载体”。它的唯一目的是在内存中持有一个对对象3的引用,保证在对象1销毁时,对象3不会被一同销毁。
  3. 对象3 ($obj3)

    • 类型:目标类。
    • 关键设置
      a. 其handle属性指向对象1(即$obj3->handle = $obj1)。
      b. 对象2的属性$a引用了它(即$obj2->a = &$obj3),形成引用关系,延长其生命周期。
    • 作用最终的执行者

4. 漏洞触发流程

假设将$obj1, $obj2, $obj3放入一个数组进行序列化,反序列化后,其销毁顺序与触发链如下:

  1. $obj1 率先销毁:触发$obj1__destruct()方法。假设其__destruct()会调用$this->handle->someMethod()
  2. 触发 $obj3__construct():由于$obj1->handle指向$obj3,这相当于调用$obj3->someMethod()。如果someMethod__construct(),则此时$obj3__construct()被调用。注意__construct()在反序列化时不会自动调用,但可以通过其他对象手动触发。此时$obj3handle属性被(在__construct中)设置为一个有效值(例如指向$obj1),而绕过了__wakeup()的置空
  3. $obj2 销毁:由于$obj2->a$obj3的一个引用,$obj2销毁本身通常不会直接触发关键操作,但它维护了对$obj3的引用计数。
  4. $obj3 最后销毁:当所有其他引用消失,$obj3自身需要销毁时,触发其__destruct()方法。
  5. 最终命令执行$obj3__destruct()方法调用$this->execute()。由于在步骤2中,$obj3handle属性已被__construct()正确设置(非null),因此execute()方法中的检查被通过,成功执行内部预设的命令。

总结:通过构造复杂的对象引用关系,我们手动触发了目标对象$obj3__construct()方法来初始化关键属性,从而避开了由反序列化自动触发的、具有破坏性的__wakeup()方法,最终在$obj3销毁时完成了利用链。

相似文章
相似文章
 全屏