游客发表
渗透测试员老周把键盘推给我时,渗透屏幕上一行行绿色字符正滚过某个内网服务器的编程登录日志。他指着一串十六进制编码说:“你看,渗透这就是编程语言本身的漏洞——不是业务逻辑的锅,是渗透语法糖让人放松了警惕。”
那天下午我们正测试一家银行的编程移动端应用。老周没去翻接口文档,渗透反而打开了Python的编程官方手册,翻到列表推导式那一页。渗透“很多渗透工具都是编程Python写的,但攻击者更爱看开发者怎么写Python。渗透”他输入了一段极简代码,编程利用异常处理机制里`except`语句的渗透隐式类型转换,绕过了WAF的编程过滤规则。我盯着那几行代码,渗透忽然意识到所谓“渗透编程语言”,从来不是指某种神秘的黑客语言,而是指——当开发者过度信任语言特性时,那些特性就成了后门。
拿PHP举例。十年前我见过一个老系统,登录验证用`==`比较密码哈希。攻击者根本不需要破解哈希,只要构造一个以`0e`开头的字符串,PHP的弱类型比较会把两边都当作科学计数法,结果都趋近于零——相等成立。这不是算法缺陷,是语言设计时留下的“魔法”。程序员写`if ($hash == $input)`时觉得天经地义,可渗透测试的本质,就是去质疑这些“天经地义”。
JavaScript更离谱。原型链污染攻击至今还能打穿不少Node.js应用。攻击者往`Object.prototype`塞一个属性,所有对象就都“继承”了它。很多开发者用`lodash.defaultsDeep`合并配置时,根本没想过深拷贝会沿着原型链爬上去。我见过一个团队用TypeScript重写了整个服务,自信满满地说“类型系统挡住了90%的漏洞”——结果渗透测试时,我们往JSON里塞了个`__proto__`字段,直接改写了全局配置。类型检查发生在编译期,可攻击发生在运行时。
但你别以为只有动态语言才危险。C语言的缓冲区溢出是经典,可现代开发者早就不碰指针了。真正有趣的是Rust——它号称内存安全,可如果你用`unsafe`块包住一段代码,或者用`unwrap()`粗暴处理错误,编译器就撒手不管了。去年某云厂商的漏洞,就是开发者为了省事,在解析不可信输入时用了`String::from_utf8_unchecked`。语言给了你安全网,你偏要自己剪个洞。
说回老周。他最后绕进那个银行系统,靠的不是零日漏洞,而是开发者在日志里打印了完整的SQL语句——用`f-string`拼接的。Python的f-string本来很安全,可如果拼接的内容来自用户输入,等于把引号递给了攻击者。老周说:“语言特性就是一把刀,厨师用它切菜,疯子用它砍人。渗透测试员要做的,是找出那些把刀当玩具的人。”
我后来自己写工具时,开始刻意避开“便捷功能”。不用`eval`,不用动态导入,连正则表达式都尽量写死。倒不是矫枉过正,而是见过太多“方便”背后藏着的钩子。渗透编程语言,说白了就是用语言的思维方式去思考它的薄弱面——不是背CVE,而是理解每个语法糖背后的假设。那些假设一旦被打破,语言就从工具变成了帮凶。
现在老周退休了,但他留给我一句话,我一直贴在显示器边上:“别问代码能做什么,问它不能做什么。不能做的部分,才是攻击者住的地方。”
友情链接