base64编码补位的坑,导致数据验证被绕过,也绕过了系统黑名单的检测。
背景
突然,测试人员找上我,说篡改某对象ID的值会绕过系统的黑名单检测!
我非常不相信,因为该对象ID生成有随机因素,而且它的校验也有hash判断,只要校验不通过,立马会拒绝。
他把那个对象ID发给我,是这样的
NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD1=
是基于下面这个值改的。
NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD0=
第一个是篡改后的,第二个是原始生成的。第一个和第二个的差别是倒数第二个字符由0改为1
定位过程
原始的字符串是经过base64编码生成。用于base64解码一下
>>> origin = "NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD0="
>>> modified = "NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD1="
>>> origin == modified
False
>>> origin_decode = base64.b64decode( origin )
>>> modified_decode = base64.b64decode( modified )
>>> origin_decode == modified_decode
True
>>> origin_decode
b'5a70d310aaf2851e1e7d09f69af8a9f3-2e38b1ecee5d453692a86401a5af94350002-LwA1u8jWX/3zS6MKt4oimmjg5D8='
>>> modified_decode
b'5a70d310aaf2851e1e7d09f69af8a9f3-2e38b1ecee5d453692a86401a5af94350002-LwA1u8jWX/3zS6MKt4oimmjg5D8='
WTF, 这是怎么回事,为什么两个解码的结果是一样的?再继续修改看看是怎么回事?
>>> modified = "NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD2="
>>> modified_decode = base64.b64decode( modified )
>>> origin_decode == modified_decode
True
>>> modified = "NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD3="
>>> modified_decode = base64.b64decode( modified )
>>> origin_decode == modified_decode
True
>>> modified = "NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD4="
>>> modified_decode = base64.b64decode( modified )
>>> origin_decode == modified_decode
False
同样的步骤重复下去,会发现倒数第二个值竟然是以4单位的区间解码出来的结果是一样的。
为什么会这样呢?
看wiki百科关于base64的说明
由于Base64是6位编码,并且在现代计算机上,解码值被分成8位八位字节,因此Base64编码文本的每四个字符(4个六位字节=46=24位)代表三个八位字节的未编码文本或数据(3个八位字节=38=24位)。这意味着,当未编码输入的长度不是3的倍数时,编码输出必须添加填充,以便其长度是4的倍数。填充字符是=,这表示不需要更多的位来完全编码输入。
原因终于明白了,这是base64编码补位的问题。
在系统上,这个对象ID的校验是64位解码后,变成A-B-C三段,然后对C和f(A,B)进行比较,f是运算过程。由于补位,导致上面篡改的对象ID都可以解成同一个,所以通过校验。而系统的黑名单又是对象ID作为key,没有这个key,就认为是不存在黑名单,所以就绕过了黑名单的检测。
解决方法
由于黑名单的key有一些格式限制,不能使用base64解码出来的内容,所以,只能继续用对象ID作为key。黑名单的key生成改成这种方式,经验证OK
import base64
bs = base64.b64decode( obj_id )
obj_id = base64.b64encode( bs ).decode('utf-8')
原文始发于微信公众号(debugeeker):base64的天坑
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论