【CTF-WEB】Web基础工具的使用(burpsuit抓包并修改数值)
题目Buy a lottery!
打开这个页面,查看源代码,发现buy.js这个唯一的js文件
代码
代码如下:
function buy(){$('#wait').show();$('#result').hide();var input = $('#numbers')[0];if(input.validity.valid){var numbers = input.value;$.ajax({method: "POST",url: "api.php",dataType: "json",contentType: "application/json", data: JSON.stringify({ action: "buy", numbers: numbers })}).done(function(resp){if(resp.status == 'ok'){show_result(resp);} else {alert(resp.msg);}})} else {alert('invalid');}$('#wait').hide();
}function show_result(resp){$('#prize').text(resp.prize);var numbers = resp.numbers;var win_numbers = resp.win_numbers;var numbers_result = '';var win_numbers_result = '';for(var i=0; i<7; i++){win_numbers_result += '<span class="number-ball number-ball-red">' + win_numbers[i] + '</span>';if(numbers[i] == win_numbers[i]){numbers_result += '<span class="number-ball number-ball-red">' + numbers[i] + '</span>';} else {numbers_result += '<span class="number-ball number-ball-gray">' + numbers[i] + '</span>';}}$('#win').html(win_numbers_result);$('#user').html(numbers_result);$('#money').text(resp.money);$('#result').show();$('#numbers').select()
}$(document).ready(function(){$('#btnBuy').click(buy); $('form').submit(function( event ) {buy();return false;});
})
漏洞原理
前端代码,关键点“numbers[i] == win_numbers[i]”
在php语言中,==
(宽松相等)操作符在比较不同类型的值时会进行类型转换,这是导致漏洞的关键原因。
这个漏洞源于服务器端(api.php
)在处理请求数据时,未对 numbers
字段进行严格的类型验证,导致类型转换错误被利用。以下是详细分析:
-
正常请求处理:
- 前端发送的数据应为
{"action":"buy","numbers":"1111111"}
(字符串),服务器期望numbers
是一个7位数字字符串。 - 服务器比较用户号码和中奖号码(如 “8180036”)时,会逐位比较字符(例如
'1' == '8'
),结果为布尔值。
- 前端发送的数据应为
-
修改请求后的异常处理:
- 当请求被修改为
{"action":"buy","numbers":[true,true,true,true,true,true,true]}
(布尔数组)时,服务器端(假设使用 PHP)的json_decode
会解析numbers
为一个布尔数组,而非字符串。 - 服务器代码可能未验证
numbers
的类型,直接进行中奖比较。在比较时(如$numbers[$i] == $win_numbers[$i]
),PHP 的松散类型比较(==
)会触发隐式类型转换:- 布尔值
true
转换规则:true
在比较时始终等价于整数1
或非空字符串。 - 中奖号码字符转换规则:
- 非零数字字符(如
'8'
)转换为布尔值时为true
(因为非空字符串)。 - 字符
'0'
转换为布尔值时为false
(因为'0'
在 PHP 中被视为“假值”)。
- 非零数字字符(如
- 比较逻辑变为:
true == <中奖字符的布尔值>
。
- 布尔值
- 当请求被修改为
-
中奖号码 “8180036” 的具体影响:
- 中奖号码 “8180036” 逐位转换布尔值:
- 位置 0:
'8'
→true
- 位置 1:
'1'
→true
- 位置 2:
'8'
→true
- 位置 3:
'0'
→false
- 位置 4:
'0'
→false
- 位置 5:
'3'
→true
- 位置 6:
'6'
→true
- 位置 0:
- 用户号码
[true,true,true,true,true,true,true]
逐位比较:- 位置 0:
true == true
→ 匹配 - 位置 1:
true == true
→ 匹配 - 位置 2:
true == true
→ 匹配 - 位置 3:
true == false
→ 不匹配 - 位置 4:
true == false
→ 不匹配 - 位置 5:
true == true
→ 匹配 - 位置 6:
true == true
→ 匹配
- 位置 0:
- 结果:7 位中 5 位匹配(位置 0、1、2、5、6),而正常字符串输入(如 “1111111”)仅位置 1 匹配(因为
'1' == '1'
)。更高的匹配率导致服务器错误地判定中奖。
- 中奖号码 “8180036” 逐位转换布尔值:
前端显示问题
- 在
show_result
函数中,前端使用resp.numbers
(服务器返回的用户号码)和resp.win_numbers
(中奖号码)进行比较:- 如果服务器返回的
resp.numbers
是布尔数组,numbers[i]
为true
,而win_numbers[i]
是字符(如'8'
)。 - JavaScript 的
==
比较true == '8'
:true
转换为数字1
'8'
转换为数字8
1 == 8
→false
,因此显示灰色球(不匹配)。
- 但实际服务器已判定中奖(因为服务器比较基于布尔转换),前端显示的匹配状态与服务器结果不一致。
- 如果服务器返回的
JavaScript计算true == '8’等于false,而php计算true == '8’等于true
抓包
对话框随便输入7个数字
拦截后,修改numbers
{"action":"buy","numbers":"1111111"}
修改为
{"action":"buy","numbers":[true,true,true,true,true,true,true]}
放行后,显示返回了很多奖金
重复几遍,就可以去兑奖了
最终答案:
ctf{187297e194574ceb9628c602d6cfbfc4ab8098fc}