
https://portswigger.net/web-security/all-labs#api-testing今年年初4月打的靶场了发到csdn上lab-1-发现 API 端点并利用挂上 burp suite 代理不断点击站点内功能点查看 history发现在更新 email 功能处存在调用 api 接口。发送到 repeater 进行测试不断缩短接口路径/api/user/wiener-/api/user-/api同时切换请求方法为 GET尝试是否出其他数据。通过测试当使用 GET 方法访问/api接口时站点将返回接口文档下图内容包括当前 api 接口支持的通信方法 API 端点(Endpoint)参数响应值等。当然发现这个 API 接口文档还有其他手法直接对站点进行接口字典扫描。使用 burpsuite-intruder 模块对路径设置 payload 位置导入常见接口字典注意一定要关闭 url 编码不然接口路径中的/会被编码为%2f根本无法测试开启了 url 编码就是下面这样下图是未进行 url 编码的爆破测试我们可以发现接口为/api时出现了 302 跳转那么我们直接在浏览器上拼接接口路径尝试请求。https://0a6500a90488b291822a15fa00ac00c9.web-security-academy.net/api发现可以访问接口文档非常不错根据实验要求我们需要调用这个接口来删除用户carlos我们先来使用 get 方法请求一下这个接口https://0a6500a90488b291822a15fa00ac00c9.web-security-academy.net/api/user/carlos接口回显无权限。说明后端对于这个 API 接口也是存在一定的鉴权的我们当前是未登录状态没有任何权限但是如果我们登录这个系统的一个账户获取一定的权限能否调用 API 接口来试一试使用实验给出的账密wiener:peter登录系统成功登录再次尝试调用接口https://0ab9007c04ac6dc6831f46c3000600fa.web-security-academy.net/api/user/carlos直接返回了用户名和绑定邮箱。那我们来尝试删除carlos用户这一步需要来抓个包我们直接把刚刚请求的数据包发送到 repeater。修改一下请求方法GET-DELETE发包。成功删除用户carlos。再次在浏览器上请求用户信息接口回显用户不存在已经被删除了实验完成总结本次实验总体分为两个步骤发现接口调用接口本实验存在接口文档可以直接扫出来然后根据接口文档调用接口进行测试。如果没有接口文档我们可以挂着 burp 四处点击功能点然后回来看 history 中的数据包可能会存在一些被调用的接口。这一步的目的主要是为了找到 baseurl请求到后端。找到 baseurl 之后再去字典爆破接口名或者去翻找 js 中存在的接口信息尝试调用。本实验测试的接口也是存在一定的鉴权但是当我们登录到系统获取了调用接口的权限之后就能够调用接口删除其他用户这是一种思路漏洞挖掘的时候也可以这样干。lab-2-寻找并利用隐藏的 API 端点实验描述利用 api 端点实现物品购买实际上是使用 API 端点实现支付绕过。先登录账号接下来是寻找 API 端点常规手法是开启 burp 代理点功能查看请求中具有 API 端点特征的 url发现在添加物品到购物车时会调用一个 api/api/products/1/price发送到 repeater 中进行测试尝试使用 OPTIONS 方法查看 API 端点支持的 HTTPS 通信方法。根据返回可以看到允许 GETPATCH 两种。如果存在 PATCH并且 GET 请求中返回的 json 格式数据存在price可以尝试直接修改 price 实现支付绕过。先修改通信方法为 PATCH返回的错误信息表示请求体只支持 json 数据那么就在数据包内添加一个字段Content-Type:application/json并且需要将 API 端点之前 GET 方法正常返回的 json 格式数据稍微修改一下拼接到当前数据包下。正常 GET 方法请求到的数据拼接过来之后需要把 message 参数删掉因为这个参数与我们的目的无关没有修改的必要。需要把那个,逗号删除否则会因为 json 格式错误造成通信错误返回信息表示需要 price 参数为有效的非负整数。price:$1337.00当前的参数值是字符串那么就修改为一个非负整数。price:1发送后发现成功执行将后端物品价格修改为 0.01当然我们账户上分币没有还需要修改为 0。刷新靶场页面可以看到价格已经被修改为 0。直接加到购物车购买即可实验完成。总结本实验先由 brup 挂上代理点击 web 站点功能点发现 API 端点使用 OPTIONS 方法对端点请求发现端点支持的 HTTPS 通信方法API 端点通信方法中允许 PATCH并且存在参数price尝试直接使用 PATCH 方法修改 price 参数值根据返回错误信息一步一步修改数据包内容最终实现参数修改造成支付绕过 0 元购。lab-3-批量赋值漏洞依然挂 burp 代理点功能查找 api 端点调用处。发现在点击购物车增减物品数量时会 GET 方法调用/api/checkoutAPI 端点。在place order 下订单时会 POST 方法调用/api/checkoutAPI 端点。GETPOST通过对比这两个数据包可以看到 GET 方法获取到的数据量大于 POST 方法获取的数据量目前已知API 端点/api/checkout的通信数据有{ chosen_discount: { percentage: 0 }, chosen_products: [ { product_id: 1, name: Lightweight \l33t\ Leather Jacket, quantity: 1, item_price: 133700 } ] }其中包含两个主要的属性chosen_discount选择的折扣chosen_products选择的产品但是 POST 方法只上传了chosen_products属性那么可以尝试添加chosen_discount属性测试 API 接口是否存在赋值漏洞。即 POST 提交的chosen_discount属性是否能修改后端的chosen_discount值。后端鉴权不完善就可以实现。先完全赋值过来发送请求包响应包依然显示资金不足。没有报错至少说明后端允许在 POST 请求时json 数据中携带chosen_discount属性。因为这个属性表示的是产品折扣值。我们需要购买这个产品尝试将折扣值改为 100后端一般是最终价原价-原价*折扣价再将用户账户金额-最终价。可以看到将折扣值修改为 100 时可以实现 0 元购绕过支付。实验完成。总结对于 web 站点功能要点完找到每一个调用 API 端点的功能点。最好去把 API-sword 配置一下。本实验本质上是批量赋值漏洞但是利用起来就是一个增加数据造成 0 元购的支付绕过漏洞。lab-4-服务器端参数污染依旧挂上 burp 代理四处点功能但是没有发现明显的 API 端点发现在登录页面存在忘记密码操作。那么要登录 administrator 账户可以在这里想想办法。抓取这个数据包发送到 repeater 进行测试根据 web 响应发现忘记密码功能存在邮箱验证并且 POST 请求中包含username参数那么我们对请求包进行测试。测试参数截断在 username 参数后面拼接%23即#截断字符响应Field not specified字段未定义。修改 payload 为%2响应Invalid username无效用户名。那么说明%23可以对后端参数造成截断。但是这里貌似没有深入利用手法继续测试参数注入。注入无效参数%26xy回显参数不支持。那么表示后端接收了这个参数并且解析了参数发现参数不支持。如果后端可以解析参数那么可以尝试注入有效参数试试能不能出货。测试有效参数与无效参数的响应页面区别无效参数x有效参数username可以清晰看到当提交无效参数时响应为{error: Parameter is not supported.}提交有效参数时响应为{type:ClientError,code:400,error:Invalid username.}可以根据这个进行爆破测试出其他隐藏的有效参数。爆破有效字段定位 payload添加 burp 内置的Form field names - long大字典。发现存在有效参数field。继续对field参数值进行爆破。我们调用了 burp 内置的字典发现爆破无效那么我们使用 ai 来现场写一个对于忘记密码功能点的参数字典。再对字典进行处理删除多余空行文字描述。导入到 burp 爆破模块尝试。发现存在reset_token参数并且返回了结果。那么我们就将这个参数注入到忘记密码的请求数据包内看到返回了 result这个参数名为 reset_token意思是密码重置的凭证我们猜测在完成了邮箱验证之后会自动调用这个参数实现密码重新设置。那么我们直接将这个参数包括它的值添加到原数据包中。我们先去 http-history 中抓取一个 GET 类型请求发送到 repeater对其添加request query parameters(request 查询参数)。这里需要将%26修改为参数间隔符并且在路径后添加?查询开始符。csrfNJgfJ6qnXKVod4Alen5YhG75GJJajvEJusernameadministratorreset_tokenufopy4z496pyxvow1j1qxe5vrie16x7l发送请求响应 200在 response 中渲染一下出现密码重置页面执行成功。那我们就直接在 web 上把请求的参数写到链接上发送请求。可以看到成功跳转密码重置页面。然后随便输一个密码重置 administrator 的密码再使用该密码登录 administrator 的账号删除carlos用户即可完成实验。补充分析关于 field 字段这个字段是本实验的关键他使我们能够获取其他字段的更详细信息。本实验可以获取的字段信息有emailusernamereset_token。并且当我们不使用 field 字段请求数据时响应的数据与使用 field 字段值为 email 时响应的数据完全相同。我们有理由怀疑后端代码中的 field 字段默认值为 email。并且当我们使用%23对参数进行截断时响应包中的Field not specified.字段未定义就是指%23截断符将 username 字段之后的默认字段 fieldemail 截断失效。后端未接收到 field 字段值(field 值未定义)无法返回有效内容。reset_token 隐藏参数发现我们在上方已经说明了可以使用 ai 构造场景参数字典对参数进行爆破的方法这个方法具有普适性。但是本实验还存在一种方法JS 文件审计。我们将 http-history 中filter by file extension按文件拓展名过滤的HIde隐藏选项关闭。这一步的目的是让我们可以看到 js 文件的通信。最好的方法是将 js 写到上方的show only中js 文件还是有作用的。我们点忘记密码功能点之后查看 http-history发现请求了一个 js 文件forgotPassword.js也可以 f12 查看站点源码但是在我实验时源码里没有 forgetPassword.js 这个文件很奇怪。我们将文件路径拼接起来访问这个 JS 文件发现其中存在一个forgotPwdReady 的函数并且存在字段reset_token。这样就找到了隐藏字段 reset_token。总结在 http-history 中要开启 js 文件的通信点击对应功能点时注意调用的 js 文件其中可能存在隐藏信息API 接口参数加密方法……服务器端参数污染测试方法%23#截断根据返回包分析是否有效。%26参数连接符添加无效参数测试后端是否解析添加的参数。%26参数连接符添加有效参数并寻找该功能点隐藏的其他有效参数。寻找隐藏参数可以使用 ai 生成特定场景下的参数字典或者在 js 文件中寻找突破lab-5-REST 路径中测试服务器端参数污染依旧挂上 burp 代理四处点功能数据包 url 中没有发现明显的 API 端点调用。依然存在forgot-password功能点对该功能点进行测试发送到 repeater。尝试参数污染设置%23 截断参数响应返回Invalid route. Please refer to the API definition 无效的路由(路径)请参考API定义。表示%23 截断了 username 字段后方的其他参数导致 API 路径拼接错误。这种可能是将 username 拼接到了后端 API 路径上。继续测试注入参数。注入无效参数%26xy响应The provided username \administratorxy\ does not exist 提供的username参数XXX不存在观察响应的参数administratorxy\我们输入的%26xy被后端拼接到原参数值上。并且自动在输入的参数后方添加了\路径分隔符。前方也存在一个\路径分隔符。更印证了我们的判断后端将接收 username 拼接到后端代码中写好的 API 路径中。有效参数污染%26usernamecarlos%26email1gmail.com两个有效参数都被拼接到路径中那么后端应该是将这里写死了无法注入参数。尝试其他突破口。如果这里是直接拼接路径那么可以思考使用路径穿越是否可以访问其他文件或端口。测试正斜杠拼接../后端会直接过滤正斜杠。尝试反斜杠拼接..\后端会双写反斜杠。添加%23截断符对后方参数进行截断。../../../../../../../hostname%23返回 意外的 API 服务响应没有找到你请求的这个 URL 没有找到。表示成功进行了目录穿越但是当前根目录下不存在 hostname 这个路径。对 hostname 设置 payload 发送到 intruder 使用 burp 内置的目录穿越字典爆破服务器端常见文件但是没出货。使用自己构造的 API 接口文档字典爆破。发现服务器端存在一个名为openapi.json的文件。转发到 repeater 详细分析Unexpected response from API server:\n{\n \openapi\: \3.0.0\,\n \info\: {\n \title\: \User API\,\n \version\: \2.0.0\\n },\n \paths\: {\n \/api/internal/v1/users/{username}/field/{field}\: {\n \get\: {\n \tags\: [\n \users\\n ],\n \summary\: \Find user by username\,\n \description\: \API Version 1\,\n \parameters\: [\n {\n \name\: \username\,\n \in\: \path\,\n \description\: \Username\,\n \required\: true,\n \schema\: {\n ...我们使用 ai 对 API 文档进行美化{ openapi: 3.0.0, info: { title: User API, version: 2.0.0 }, paths: { /api/internal/v1/users/{username}/field/{field}: { get: { tags: [ users ], summary: Find user by username, description: API Version 1, parameters: [ { name: username, in: path, description: Username, required: true, schema: {} } ] } } } }在响应中存在一个暴露的 API 端点/api/internal/v1/users/{username}/field/{field}\可以看到我们的 username 参数正是拼接到该端点上。尝试修改目录穿越 payload 调用 API 端点。administrator/field/x%23响应由于安全原因该版本 API 仅支持 email 字段。对于这种有限制的 API 端点存在路径遍历绕过手法称为路径规范化绕过。通常需要一步一步测试。获取到了后端接收参数拼接的 API 路径/api/internal/v1/users/{username}/field/{field}\从传递的参数 username 入手对前方的路径使用路径遍历序列进行相对路径覆盖。username../users/administrator/field/passwordResetToken%23username../../v1/users/administrator/field/passwordResetToken%23当使用第二种 payload 进行路径规范化绕过时服务器返回数据{ type: passwordResetToken, result: ih99sntv8f6fk8903y136e5uwliigabx }成功绕过 API 端点字段限制。这是对本质原理的猜测不一定正确但是一般情况下一个系统内可能存在其他版本的 API 端点并且暴露的 API 文档中路径/api/internal/v1/users/{username}/field/{field}存在版本关键字v1还有internal关键字表示内部版本。很大概率存在其他版本 API 端点。对于这个路径/api/是它的 baseurl 基础路径/internal/v1/是 API 的内部版本标识后面的/users/{username}/field/{field}是资源路径。那么我们来尝试切换 API 版本。username../../v1/users/administrator/field/passwordResetToken%23获取到 passwordResetToken 数据之后直接构造一个 GET 请求即可绕过忘记密码邮箱验证直接进入重置密码阶段。这一步的原理在 lab4 已经分析过了。总结本实验考察服务器端参数污染参数截断参数注入。主要使用参数截断。rest-ful 接口文档获取通过服务器端参数污染-参数注入并观察响应包回显判断出后端使用 rest-ful 类型 API 接口。根据 username 参数进行路径遍历攻击配合 burp-intruder 爆破出服务器端 api 文档获取到该接口的路径通过 JS 文件审计或者 snow-eyes 找到 passwordResetToken 参数路径规范规范化绕过 API 端点对于字段的请求限制