护网杯ltshop引发的思考

题目描述

题目开始需要注册账号,登陆进去后是一个商场,每个账号初始有20RMB5RMB可以买1包大辣条,5包大辣条换1包辣条之王,9999999包辣条之王可以换Flag

img

img

做题思路

全页面就一个输入点,选择你需要越换辣条之王的数量,发现我们的钱连一个辣条之王都买不了,联想到之前CTFtime上的某个比赛(忘了),第一步条件竞争,Burp200线程买大辣条,成功买到16包大辣条!!!,但是因为要9999999包辣条之王才能换flag

思路1:查看此题有没有换回去的方法,如果存在可以通过条件竞争一直来回换辣条和RMB,导致RMB或辣条越来越多从而买到Flag,遗憾的是并没有反向兑换

思路2:查看是否可以通过注册账号功能或者其他功能,让每个账号的资产全部移植到同一个账号上去。Burp抓全部包,一一查看,发现逻辑全部在后台处理,除了cookie没有任何输入点,放弃

思路3:可以通过php弱类型类似于0.999999=1的方法,发现小数点被过滤,放弃

然后我就被卡在这了,常规思路走不通,走不常规思路,首先Fuzz输入框,发现除了数字几乎全部被过滤,但是;(分号)并没有被过滤,但也没有利用的方法,扫目录扫文件泄露没有任何发现,cookie发现后台是用golang写的,百度一波golang的漏洞,看了一波文章没有任何发现。。。

然后后面几乎一直都在人肉Fuzz了,啥字符啥payload都输了一遍,没有任何反应,然后突然刘师傅说了一句会不会是数值溢出因为go是强类型。发现新大陆,查看go数据类型最大值准备fuzz poc。

但打到比赛结束都没有做出来,赛后看wp发现要除以5才能拿,之前Web没做过溢出的题目,研究了一会儿,大概推测后台代码大致如下(不会Go用伪代码代替一下)

#伪代码
uint64 number = input()    //用户输入的兑换辣条之王的数字
uint64 max = math.uint64max //无符号整型的最大值
uint64 biglt = 16     //用户的大辣条包数,如上假如为16包
uint64 kinglt = 0    //用户拥有的辣条之王

if(number不是数字){
    error
}
else if(number > max){
    error
}
else if( (biglt - 5 * number) < 0 ){
    print 买不起那么多
}
else if( (biglt - 5 * number) >= 0 ){
    print 购买成功
    biglt = biglt - 5 * number
    kinglt = number
}

Go的64位无符号整型最大值为18446744073709551615(简称max)

对于无符号整型的溢出,我们知道

  • max + 1 = 0
  • max + 2 = 1
  • max + n = n -1
  • max * 2 = max - 1
  • max * 3 = max - 2
  • max * n = max - n

此时我们需要绕过判断让我们购买超多的辣条之王,所以上诉代码

biglt - 5 * number必须大于0

此时5 * number必须小于16

如上公示得知直接输入max是行不通的(比赛时候疯狂输入max,max-1,max+1......),我们需要变换一下思路,比如(max + 1) / 5, (max + 2) / 5, (max + 3) / 5……

只要max+n是5的倍数就行,我们选用(max+5) / 5, 此时biglt - 5 * number,等价于16 - max+5 等价于 16 - 4 等价于12,绕过了之前的所有判断,kinglt被赋值为(max+5)/5,大于9999999,我们成功买到了flag

img

img

后记

没做出来有点可惜,本来其实可以想到的,但由于最后做出来的人太多了:) 可能有点慌一直在Fuzz了,没有想到去猜后台代码。归根结底还是自己太菜了,膜师傅们