内容简介:利用到的漏洞分别为OP_FORPREP/OP_FORLOOP、OP_CLOSURE中的类型混淆,这里以Redis 2.8.20版本进行分析。1) OP_FORPREP/OP_FORLOOPlua中对for循环生成的字节码如下:
1. 漏洞分析
利用到的漏洞分别为OP_FORPREP/OP_FORLOOP、OP_CLOSURE中的类型混淆,这里以 Redis 2.8.20版本进行分析。
1) OP_FORPREP/OP_FORLOOP
lua中对for循环生成的字节码如下:
可以看到for循环是由FORPREP、FORLOOP两条指令组合而来,对应的源码是deps/lua/src/lvm.c line 654-680:
在OP_FORPREP中,lua对参数进行类型检查,判断是否为number类型,不是则触发错误;然而在OP_FORLOOP中,因已做过类型检查,便假定参数为number类型,并对其执行idx = idx + step操作,这导致任意类型到number类型的混淆。
如下修改字节码中的FORPREP指令("\96%z%z\128")为JMP指令("\22\0\0\128"),跳过OP_FORPREP中的类型检查,直接进入OP_FORLOOP:
测试如下:
2) OP_CLOSURE
对CLOSURE指令的处理位于deps/lua/src/lvm.c line 723-742:
line 731-737是对闭包的处理,具体为在CLOSURE指令后后生成对应的MOVE指令,MOVE指令的第二个参数为闭包变量引用。正常情况下引用只能指向当前栈桢中的局部变量,但通过修改字节码,可以将其指向至任意位置。
如上,通过修改"(\100%z%z%z)...."(MOVE 0 0)为"%1\0\0\0\1"(MOVE 0 2),将middle函数中的magic引用指向middle函数自身(R2),所以输出的结果为middle函数。
对函数调用的处理位于deps/lua/src/lvm.c line 586-606:
对函数返回的处理位于deps/lua/src/lvm.c line 382-390:
line385将L->ci->func(当前函数指针)转换为Closure指针,由上文可知,通过修改字节码可以将闭包变量引用指向当前函数指针,导致任意类型到Closure类型的混淆。
基于此,结合number类型混淆,可以做任意地址读/写:
结尾处修改字节码,将middle/inner函数中的magic引用指向middle函数;inner函数中将magic赋值为字符串,这使得middle函数中的当前函数指针将被混淆为该字符串,函数返回; middle函数中读取闭包变量magic,读取闭包变量对应的源码为deps/lua/src/lvm.c line 427-431:
其实际上是去当前函数指针的upvals字段中获取相应引用,而当前函数指针已被混淆为字符串,对应的upvals字段可控。
TString类型与Closure类型的结构如下:
| TString |
CommonHeader |
reversed |
align |
hash |
len |
str |
||||
| 8 |
1 |
1 |
1 |
1 |
4 |
8 |
char[len] |
|||
| Closure |
CommonHeader |
isC |
nupvalues |
align |
gclist |
env |
p |
upvals |
||
| 8 |
1 |
1 |
1 |
1 |
4 |
8 |
8 |
8 |
UpVal* [n] |
|
变量upval为字符串,as_double(upval)获取其TString指针,偏移24获取到upval->str地址,也就是说cl->p、cl->upvals[0]都指向输入的字符串"commonhead16bits" .. p32(lo) .. p32(hi)。
UpVal结构如下:
| UpVal |
CommonHeader |
align |
v |
u |
||
| 8 |
1 |
1 |
6 |
8 |
… |
|
所以cl->upvals[0]->v指向构造的指针p32(lo) .. p32(hi),也即为addr。
以上,便将任意地址的前8字节读取出来,写操作也是同理,只需要在middle函数中对magic赋值,需注意的是写操作实际会写入8字节数值及4字节tt类型:
deps/lua/src/lvm.c line 451-456:
deps/lua/src/lobject.h line 161-164:
测试如下:
2. 漏洞利用
具备任意地址读/写能力后是一定可以做代码执行的,目前想到如下两种方案。
1) 覆写CClosure->f
在 lua 中可以使用coroutine.wrap创建C函数闭包对象CClosure,其结构如下:
| CClosure |
CommonHeader |
isC |
nupvalues |
align |
gclist |
env |
f |
upvals |
||
| 8 |
1 |
1 |
1 |
1 |
4 |
8 |
8 |
8 |
UpVal* [n] |
|
CClosure->f指向函数指针,调用其对应的源码为deps/lua/src/ldo.c 307-326:
2) 覆写got
Linux PWN常规思路,通过DynELF解析Binary,进一步解析libc,获取system地址并覆写至fputs.got;在lua中调用print("id")即可执行命令。
3.漏洞修复
两个漏洞都是因为加载字节码导致的,Redis中的修复简单粗暴,直接干掉了字节码加载:
https://github.com/antirez/redis/commit/49efe300af258e83f377cd8142d2c67d66fc2e3a
4. 参考
-
https://gist.github.com/corsix/6575486
-
https://apocrypha.numin.it/talks/lua_bytecode_exploitation.pdf
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
How to Solve It
Zbigniew Michalewicz、David B. Fogel / Springer / 2004-03-01 / USD 59.95
This book is the only source that provides comprehensive, current, and detailed information on problem solving using modern heuristics. It covers classic methods of optimization, including dynamic pro......一起来看看 《How to Solve It》 这本书的介绍吧!