跳转至

业务逻辑漏洞

业务逻辑漏洞是指应用程序的设计和实现中存在的一些缺陷,这些缺陷使得攻击者能够引发非预期的行为。这可能会让攻击者操纵合法的功能以实现其恶意目的。这些漏洞通常是由于未能预见可能出现的不寻常应用程序状态,因此无法正确处理这些状态所导致的。

Note

在这种情况下,“业务逻辑”一词指的是定义应用程序运行方式的一系列规则。由于这些规则并不总是与具体业务直接相关,因此相关的漏洞也被称为“应用程序逻辑漏洞”或“逻辑缺陷”。

逻辑缺陷往往对于那些并非刻意寻找这些缺陷的人来说是看不见的,因为这类缺陷通常不会在应用程序的正常使用过程中被暴露出来。然而,攻击者或许可以通过以开发者未曾预料到的方式与应用程序交互,从而利用这些隐藏的缺陷进行攻击。

业务逻辑的主要目的之一,就是执行在设计应用程序或功能时所规定的规则和约束。广义来说,这些业务规则决定了在某种情况出现时,应用程序应该如何响应。这包括防止用户做出会对业务产生负面影响的操作,或者那些根本不合逻辑的操作。

逻辑中的漏洞使得攻击者能够绕过这些规则。例如,他们可能能够在不通过预期购买流程的情况下完成交易。在其他情况下,对用户提供的数据进行验证的方式存在缺陷或根本无法进行验证,这就使得攻击者能够随意修改对交易至关重要的数值,或者提交毫无意义的输入数据。通过将意外的值传递给服务器端逻辑,攻击者有可能让应用程序执行一些它不应该执行的操作。

基于逻辑的漏洞可能非常多样化,且往往具有针对特定应用程序及其功能的独特性。识别这些漏洞通常需要一定的专业知识,比如对业务领域的理解,或者了解攻击者在特定情境下可能追求的目标。因此,使用自动化漏洞扫描工具很难发现这些漏洞。正因如此,基于逻辑的漏洞成为了漏洞猎手和手动测试人员的重要目标。

业务逻辑漏洞如何产生的

业务逻辑漏洞的出现,通常是因为设计和开发团队对用户与应用程序交互方式的理解存在错误。这些错误的假设可能导致对用户输入的验证不够充分。例如,如果开发者认为用户只会通过网页浏览器来输入数据,那么应用程序可能会完全依赖一些薄弱的客户端控制机制来验证输入数据。而攻击者可以利用这些漏洞来绕过这些验证机制。

归根结底,这意味着当攻击者偏离了预期的用户行为时,应用程序无法采取适当的措施来防止这种情况的发生,因此也就无法安全地处理这一情况。

在那些过于复杂的系统中,逻辑错误尤为常见,甚至开发团队本身也未必完全理解这些系统的运作机制。为了避免出现逻辑错误,开发者需要全面理解整个应用程序的功能结构。这涉及到了解各个功能如何以意想不到的方式相互结合。在管理庞大代码库时,开发者可能无法深入了解应用程序各个部分的运作方式。如果某个开发者对另一个组件的运作方式持有错误的假设,那么就很有可能无意中引入严重的逻辑错误。如果开发者不明确记录下他们所做出的各种假设,那么这类漏洞就很容易潜入到应用程序中。

逻辑漏洞的危害

业务逻辑漏洞的影响有时可能相当微不足道。不过,这类漏洞属于广义范畴,其影响却变化很大。然而,任何非预期的行为都可能引发严重的安全威胁,因为如果攻击者能够正确地操控应用程序,那么就可能发起攻击。因此,即使你自己无法找到利用这些漏洞的方法,也应该尽量修复那些奇怪的逻辑设计。因为总有可能有人能够找到利用这些漏洞的方法。

从根本上来说,任何逻辑错误的影響程度取决于该错误涉及的具体功能。例如,如果错误出现在认证机制中,那么可能会对整体安全性产生严重的影响。攻击者可能会利用这一漏洞进行权限提升,或者完全绕过认证机制,从而获取对敏感数据和功能的访问权限。这也会为其他攻击行为提供更多的攻击面。

在金融交易中存在的逻辑错误,显然会导致企业因资金被盗、欺诈等问题而遭受巨大损失。

业务逻辑漏洞示例

对客户端控制机制过度依赖

一个根本错误的假设是:用户只会通过提供的网络界面与应用程序进行交互。这种假设非常危险,因为它会导致进一步的错误判断——认为客户端验证能够防止用户提供恶意输入。但实际上,攻击者可以利用诸如 Burp Proxy 之类的工具,在数据被浏览器发送出去但尚未进入服务器端逻辑之前对其进行篡改。这样一来,客户端验证机制就完全失效了。

如果仅依据表面上的数据来接受输入,而不进行必要的完整性检查和服务端验证,那么攻击者就可以以相对较小的努力造成各种损害。攻击者能够实现什么程度的效果,取决于该功能的具体功能以及它处理的可控制数据的内容。在合适的情境下,这种缺陷可能会对业务功能以及网站的安全性产生毁灭性的影响。

无法处理非传统的输入数据

应用程序逻辑的一个目的是将用户输入的值限制在符合业务规则的范围内。例如,应用程序可以接受某种数据类型的任意值,但逻辑部分会判断这些值是否符合业务需求。许多应用程序在其逻辑中加入了数值限制机制。这些限制可能包括用于管理库存、实施预算限制、触发供应链中的特定阶段等功能。

我们以在线商店这个简单的例子来说明。当用户订购商品时,通常会指定他们想要订购的数量。虽然理论上任何整数都可以作为输入,但实际上,业务逻辑可能会限制用户订购的数量不超过当前库存的数量。

为了实现这样的规则,开发者需要预见所有可能的情景,并将处理这些情景的方法融入应用程序的逻辑中。换句话说,他们必须告诉应用程序是否允许某种输入,以及在不同情况下应该如何做出反应。如果缺乏处理特定情况的逻辑,就可能导致意外且可能被利用的行为。

例如,某种数值数据类型可以接受负值。不过,根据具体的业务逻辑,允许这种输入可能并不合适。然而,如果应用程序没有进行充分的服务器端验证来拒绝这种输入,那么攻击者就有可能输入负值,从而导致不良后果。

想象一下,在两个银行账户之间进行资金转账的情况。在完成转账之前,系统几乎肯定会检查收款人是否有足够的资金来进行转账。

JavaScript
$transferAmount = $_POST['amount'];
$currentBalance = $user->getBalance();

if ($transferAmount <= $currentBalance) {
    // Complete the transfer
} else {
    // Block the transfer: insufficient funds
}

但是,如果这种逻辑无法有效阻止用户在 amount 参数中输入负数值,那么攻击者就可以利用这一漏洞,绕过余额检查,并以“错误”的方向进行资金转移。如果攻击者向受害者的账户中注入-\(1000,那么受害者实际上可能会收到\)1000 的款项。因为逻辑始终会认为当前余额小于-1000,所以会批准这笔转账。

这种简单的逻辑缺陷,如果出现在关键的功能环节中,可能会带来严重的后果。而且,在开发和测试过程中,这类缺陷很容易被忽视,尤其是当这些输入被网页界面上的客户端控件所阻止时。

在审核某个应用程序时,应该使用诸如 Burp Proxy 和 Repeater 之类的工具来尝试输入一些非传统的数值。具体来说,可以尝试输入合法用户不太可能会使用的数值范围。这包括异常高的数值、异常低的数值,以及基于文本字段时异常长的字符串。甚至还可以尝试使用一些意想不到的数据类型。通过观察应用程序的响应情况,试着回答以下问题:

  • 对数据是否有任何限制呢?
  • 当达到那些极限时,会发生什么?
  • 您的输入数据是否经过了任何转换或标准化处理?

这可能会暴露出薄弱的输入验证机制,从而让攻击者能够以不寻常的方式操控应用程序。请记住,如果目标网站中的某个表单无法正确处理非传统的输入数据,那么其他表单也可能存在同样的问题。

对用户行为做出错误的假设

逻辑漏洞最常见的根源之一,就是对于用户行为做出了错误的假设。这可能会导致各种问题,因为开发者没有考虑到那些可能违反这些假设的危险情况。在本节中,我们将提供一些典型的、需要避免的假设示例,并说明这些假设如何导致危险的逻辑错误。

值得信赖的用户不会永远保持这种信任状态

一些应用程序看起来很安全,因为它们采用了看似有效的措施来强制执行业务规则。然而,不幸的是,有些应用程序错误地认为,一旦通过了这些严格的控制措施,用户及其数据就可以被无限期地信任。结果就是,从那时起,对这些控制的执行就变得相对宽松了。

如果业务规则和安全措施在整个应用程序中不能得到一致的执行,那么就会存在潜在的危险漏洞,这些漏洞可能会被攻击者利用。

用户不会总是提供必要的输入信息

一个常见的误解是,用户总是会为必填的输入字段提供值。虽然浏览器可以阻止普通用户在没有必要输入的情况下提交表单,但正如我们所知,攻击者可以在传输过程中篡改参数,甚至完全删除这些参数。

这在需要在同一服务器端脚本中实现多个功能的情况下是一个特别重要的问题。在这种情况下,某个参数的存在或缺失可能会决定执行哪段代码。删除某些参数值可以让攻击者访问那些本应无法访问的代码片段。

在检查逻辑错误时,应该尝试依次移除每个参数,然后观察这对系统响应产生的影响。务必确保做到这一点:

  • 每次只修改一个参数,以确保能够覆盖所有相关的代码路径。
  • 试着同时删除该参数的名称和其值。通常情况下,服务器对这两种情况的处理方式是不同的。
  • 遵循多阶段流程直至完成。有时,在某一步骤中修改某个参数,可能会影响到后续步骤的效果。

这适用于 URL 参数和 POST 参数,但别忘了也要检查一下 cookie 值。这个简单的过程可以揭示一些奇怪的应用程序行为,这些行为可能被利用来进行攻击。

用户不会总是按照预期的顺序进行操作

许多交易都依赖于预定义的工作流程,这些流程由一系列步骤组成。网页界面通常会引导用户完成每个步骤,然后将其带到流程的下一个步骤。然而,攻击者并不一定遵循这种预定的顺序。如果不考虑这种可能性,就可能会引入一些危险的漏洞,而这些漏洞可能相对容易就被利用掉。

例如,许多实施双因素认证(2FA)的网站要求用户在登录页面完成注册流程后,才能在另一个页面输入验证码。假设用户始终按照这一流程操作,但实际上并没有真正完成验证过程,那么攻击者就有可能完全绕过双因素认证的步骤进行攻击。

对事件发生的顺序做出假设,即使在相同的工作流程或功能中,也可能引发各种问题。使用诸如 Burp Proxy 和 Repeater 这样的工具,一旦攻击者截获了某个请求,他们就可以随意重新执行该请求,并通过强制浏览的方式以任意顺序与服务器进行交互。这样,他们就可以在应用程序处于意外状态的情况下执行不同的操作。

为了发现这类漏洞,你应该使用强制浏览的方式,以非预期的顺序发送请求。例如,可以跳过某些步骤,多次访问某个步骤,或者返回到之前的步骤等。注意不同步骤是如何被访问的。虽然通常只是向某个特定 URL 发送 GET 或 POST 类型的请求,但有时可以通过向同一个 URL 发送不同的参数集来访问不同步骤。就像处理所有逻辑漏洞一样,试着找出开发者所做的假设,以及攻击面所在的位置。然后,就可以寻找破坏这些假设的方法了。

需要注意的是,这种测试方式往往会引发异常,因为预期中的变量可能包含空值或未初始化的数据。当应用程序处于部分定义或不一致的状态时,也可能会出现各种问题。在这种情况下,务必仔细关注任何出现的错误信息或调试数据。这些信息可以成为揭示漏洞的重要线索,有助于你优化攻击策略,并了解有关后端行为的关键细节。

特定领域的缺陷

在许多情况下,你会遇到一些特定于某个业务领域或网站目的的逻辑缺陷。

在线商店的折扣计算功能是寻找逻辑漏洞时的一个典型攻击点。在折扣计算的方式上,可能会出现各种基本的逻辑错误,而这些错误对于攻击者来说可能是一个巨大的机会。

例如,假设有一个在线商店,对金额超过 1000 美元的订单提供 10%的折扣。但如果业务逻辑无法确保在应用折扣后订单内容没有发生变化,那么这个机制就可能存在被滥用风险。在这种情况下,攻击者可以不断向购物车中添加商品,直到订单金额达到 1000 美元为止,然后再删除不需要的商品,最后完成订单。这样,他们就能享受到折扣优惠,而实际上订单已经不再符合折扣条件了。

您应该特别留意那些价格或其他敏感信息根据用户操作所确定的标准进行调整的情况。试着了解应用程序使用了哪些算法来进行这些调整,以及这些调整是在什么时刻发生的。这通常涉及对应用程序进行一定的操作,使得调整后的结果不再符合开发者最初的设定。

为了识别这些漏洞,你需要仔细思考攻击者可能拥有的目标,并尝试利用所提供的功能找到实现这些目标的不同方法。这可能需要一定领域专业知识,才能理解在特定情况下什么方式可能具有优势。以一个简单的例子来说,你需要了解社交媒体,才能明白强制大量用户关注你的行为所带来的好处。

如果没有对该领域的了解,你可能会忽视一些危险的行为,因为你根本不知道这些行为可能带来的连锁后果。同样,你也可能会难以发现两个功能如何以有害的方式结合在一起。无论你是负责漏洞搜索、渗透测试,还是仅仅想编写更安全的代码,你有时可能会遇到来自不太熟悉的领域的应用程序。在这种情况下,你应该尽可能阅读相关的文档,如果有可能的话,还可以咨询该领域的专家以获取他们的见解。这听起来可能很麻烦,但领域越陌生,其他测试人员就越有可能忽略了许多漏洞。

电子邮件地址解析工具存在差异性问题

一些网站会解析电子邮件地址,以提取域名并判断邮件发送者所属的组织。虽然这个过程看起来很简单,但实际上它非常复杂,即使是对于符合 RFC 标准的有效地址来说也是如此。

电子邮件地址的解析方式存在差异时,就会破坏这一逻辑的有效性。当应用程序的不同部分以不同的方式处理电子邮件地址时,就会产生这些差异。

攻击者可以利用编码技术来利用这些差异,从而伪装电子邮件地址的部分内容。这样一来,攻击者就能创建出一些电子邮件地址,这些地址虽然通过了初步的验证检查,但会被服务器的解析逻辑以不同的方式处理。

电子邮件地址解析工具出现错误的主要影响就是导致未经授权的访问。攻击者可以使用来自受限制域名的看似有效的电子邮件地址来注册账户。这样一来,他们就能访问应用程序中的敏感区域,比如管理员面板或受限制的用户功能。

Note

可以参考https://portswigger.net/research/splitting-the-email-atom

如何防御业务逻辑漏洞

简而言之,预防业务逻辑漏洞的关键在于:

  • 确保开发人员和测试人员能够理解该应用程序所服务的领域。
  • 避免对用户行为或应用程序其他部分的行为做出隐含的假设。

你们需要明确自己对服务器端状态的假设,并编写相应的逻辑来验证这些假设是否成立。此外,在继续操作之前,还必须确保任何输入的值都是合理的。

同样重要的是,要确保开发人员和测试人员都能完全理解这些假设,以及应用程序在不同情况下应该如何响应。这有助于团队尽早发现逻辑缺陷。为了做到这一点,开发团队应尽可能遵循以下最佳实践:

  • 维护清晰的设计文档和数据处理流程,涵盖所有交易和工作流程。同时,记录每个阶段所做出的假设或预期情况。
  • 请尽可能清晰地编写代码。如果难以理解代码的预期行为,那么就很容易发现逻辑上的错误。理想情况下,编写良好的代码不需要任何文档来解释其含义。在不可避免地出现复杂情况的时候,制作清晰的文档非常重要,这样其他开发人员和测试人员就能明白代码中所依据的假设是什么,以及预期的行为究竟是什么。
  • 请注意任何提及使用各个组件的代码引用。考虑到如果这些依赖关系被恶意方以不寻常的方式操纵,可能会产生什么样的副作用。

由于许多逻辑缺陷都具有相对独特的特性,因此很容易将这些缺陷视为人为错误而无需处理。然而,正如我们所展示的那样,这些缺陷往往出现在应用程序构建的初始阶段,是由于不良实践导致的。分析为何会出现逻辑缺陷,以及团队是如何忽略这些缺陷的,可以帮助你发现流程中的弱点。通过进行微小的调整,你可以提高在开发过程中预防类似缺陷的能力,或者更早地发现这些缺陷。