Aggregator
DubheCTF 2024|倒计时1天!
上海 | 03.21-22 • Level-2 商业安全&隐私保护进阶课
七周年大活动 | 先抽911助助兴!
What a Cluster: Local Volumes Vulnerability in Kubernetes
Discover the 3 Trends Driving API Security Compliance
苹果Webkit安全机制分析
PHP之殇 : 一个IR设计缺陷引发的蝴蝶效应
鸟哥 (Laruence) [1]是所有国内PHPer应该都知道的一个人。鸟哥的博客是我早期学习PHP内核的时候经常会去的地方。在2020年的时候,鸟哥发了一篇《深入理解PHP7内核之HashTable》的文章[2],在文章的结尾提到了一个问题:
在实现zend_array替换HashTable中我们遇到了很多的问题,绝大部份它们都被解决了,但遗留了一个问题,因为现在arData是连续分配的,那么当数组增长大小到需要扩容到时候,我们只能重新realloc内存,但系统并不保证你realloc以后,地址不会发生变化,那么就有可能:
<?php $array = range(0, 7); set_error_handler(function($err, $msg) { global $array; $array[] = 1; //force resize; }); function crash() { global $array; $array[0] += $var; //undefined notice } crash();比如上面的例子, 首先是一个全局数组,然后在函数crash中, 在+= opcode handler中,zend vm会首先获取array[0]的内容,然后+$var, 但var是undefined variable, 所以此时会触发一个未定义变量的notice,而同时我们设置了error_handler, 在其中我们给这个数组增加了一个元素, 因为PHP中的数组按照2^n的空间预先申请,此时数组满了,需要resize,于是发生了realloc,从error_handler返回以后,array[0]指向的内存就可能发生了变化,此时会出现内存读写错误,甚至segfault,有兴趣的同学,可以尝试用valgrind跑这个例子看看。
但这个问题的触发条件比较多,修复需要额外对数据结构,或者需要拆分add_assign对性能会有影响,另外绝大部分情况下因为数组的预先分配策略存在,以及其他大部分多opcode handler读写操作基本都很临近,这个问题其实很难被实际代码触发,所以这个问题一直悬停着。
直到今天这个问题还是悬停着。对于普通PHP开发者而言,这可能确实不算是一个很大的问题,但对于做安全的人来说,这里可能隐藏一个很严重的安全问题。因为它是我见过为数不多出现在PHP VM中的问题,而不是平时出现在各种PHP native libraries中的问题。一旦可以被利用,影响将非常之大。所以这个问题一直就放在了我的心上,它也一直以crash.php [3] 在我的PHP-exploit repo中放了4年. 特别地,只要你用PHP7或者8运行它就会出现segmentfault,也不知道有没有人去尝试过。
1.2 修复该问题的阻力鸟哥出给的解释非常清晰明了,这里我试着用更加通俗的伪代码来进一步帮助不熟悉PHP内部的读者, 去理解PHP VM在第11行这里到底做了什么:
// array = [0, 1, 2, 3, 4, 5, 6, 7] arr_base = get_base_addr_of(array) elem_addr = get_addr_by_index(array_base, index) elem = get_elem_from_addr(elem_addr) // elem is ok check_var(var) // is elem ok? res = add(elem, var) assign_var_to_elem(elem, res)这里做了这样几件事:
- 首先我们获取这个array存储元素内存区域的起始地址;
- 根据index获取我们指定元素的内存地址;
- 从elem_addr读取元素到elem;
- 检查var的合法性, 更具体一点, 当var是一个PHP代码中显式变量(i.e., $a)的时候, 检查它是否被定义过。 如果var是一个未定义的PHP变量, 那么VM会将var的值初始化为null. 因为VM不能直接将undefined (类似JS中的特殊值), 暴露给用户代码;
- 对elem和var做算术加法得到结果res;
- 最后将var赋值给elem。
而问题出现在第6行这里,check_var(var)可能会产生副作用(side-effects),从而clobber the world。这个词我是从JavaScriptCore (WebKit的JS引擎) 中学到的,副作用的出现可能会导致之前的计算结果变得的不可信,在这种不确定地情况下,我们是不能直接使用这些计算结果的。这里的elem是否还依然正确地指向待写入的目标元素呢? 在第6行之后我们是不能确定的,因为它指向的内存地址可能已经被释放了,而正确的目标元素位置已经被搬到了其他内存上。
以上其实就是PHP opcode ZEND_ASSIGN_DIM_OP的大致解释过程,完整的解释过程你可以在[4]中找到。那么这个问题为什么一直没有被修复呢? 好问题。我们从几个直觉上可行的简单修复方法开始,来讲一下修复的阻力在哪里。这里我用array->arData表示指向第1个元素的内存地址,其余array其他元素都顺序地落在其后.
简单方法1: 在第6行之后检查elem是否还落在array->arData相对位置上
这样做只能确保array->arData没有发生变化,但是你如何保证ABA问题 ? 比如array存储元素区域被释放了,然后被其他内存结构抢占了,然后又被释放了,再被布置为原本array存储元素区域的布局 (另外一个和它结构相同的array2把这块区域抢占了)。
简单方法2: 把check_var放在最前面
那么你考虑如下形式:
$array['a']['b'] = $var;这段代码会被翻译成类似如下的中间代码:
L0 : V2 = FETCH_DIM_W CV0($array) string("a") L1 : ASSIGN_DIM V2 string("b") L2 : OP_DATA CV1($var)这里我们考虑不带二元运算的ZEND_ASSIGN_DIM。以上代码等同于:
V2 &= $array['a']; V2['b'] = $var;其中V2是指向$array中index为'a'元素的位置,所以这里我用&=,来强调V2不是$array['a']。那么问题来了,如果第2行中的副作用导致在$array被resized了,那么这个V2就指向的位置就不对了。
这个问题注定了不能简单地被修复。
1.3 unset 和 reassign你可以试着将前面的resize操作换成unset或者reassign,如下:
<?php $array = range(0, 7); set_error_handler(function($err, $msg) { global $array; // $array = 2; unset($array); }); function crash() { global $array; $array[0] += $var; //undefined notice } crash();两个情况有些不太一样:
- unset($array),只是将$array在当前function scope内给"清理"掉了,并不影响全局变量中的$array,所以这里没有问题。
- $array = 2会影响到所有引用到它的地方,因此这里产生了和resize一样的问题。
有趣地是,官方已经注意到这样的问题,比如它对undefined index (i.e., $arr[$undef_var] = 1)产生的副作用做出了检查。而对要写入的值没有做检查。
- 这里它首先让ht (HashTable是zend_array的别名) 引用计数加1,把这个array hold住。
- 等错误处理函数返回之后,再减去这个前面加上的引用计数,如果引用计数没有发生变化,说明array没有被释放。
将ZEND_ASSIGN_DIM或者ZEND_ASSIGN_DIM_OP (同时包括所有的array fetch操作) 改成支持multi-index, 是我觉得最直接的手法。比如前面的$array['a']['b'] = $var;会被翻译为
L0 : V2 = FETCH_DIM_W CV0($array) string("a") L1 : ASSIGN_DIM V2 string("b") L2 : OP_DATA CV1($var)那么现在直接翻译为
L0 : ASSIGN_DIM CV0($array) [string("b"), string("b")] L1 : OP_DATA CV1($var)并且再此之前把所有的indexs和带待写入的var对应的表达式全部计算完成。注意这并不会改变现在PHP求值顺序. 考虑如下代码
<?php function func1() { echo "func1\n"; return 1; } function func2() { echo "func2\n"; return 2; } $a = []; set_error_handler(function($err, $msg){echo $msg."\n";}); echo $a[func1()][func2()]; /* output at PHP 8.3.3: func1 func2 Undefined array key 1 Trying to access array offset on null */可以看到index也是全部是先计算完成的。
0x02 三只蝴蝶 (butterfly)TL;DR. 如果不想听故事可以跳过这一章节。
四年前,在知道了这个问题之后,我就开始了探索应该如何利用它。非常可惜,我不太聪明,四年都没有能想出个招。这四年,我的工作也和PHP紧密结合在一起,在PHP里面写了大概有40-50k行代码吧,以至于我近乎写出了一个全新的PHP解释器,很难想象这是一个做安全的人在做的事情。所以我对PHP要稍微了解那么多一点点。
我能完成这篇文章,是因为有三只蝴蝶。第一只蝴蝶,教会我了一些新的方法; 第二只蝴蝶,让我发现了新大陆; 第三只蝴蝶,带我走出了困境。
之前,我其实一直被困在一个误区里面。我的基本想法是:
- array会被resize。
- 然后我马上拿到array释放的内存,这样就可以造一个UAF出来。
这里没有问题。
这里贴一下前面的关于ZEND_ASSIGN_DIM_OP类似的ZEND_ASSIGN_DIM的伪代码:
// array = [0, 1, 2, 3, 4, 5, 6, 7] arr_base = get_addr_of(array) elem_addr = get_addr_of(array_base, index) elem = get_elem_from_addr(elem_addr) check_var(var) assign_var_to_elem(elem, var)但是问题来了,其中assign_var_to_elem只能像目标内存写一个特殊的null (前面提到var会被初始化为null)值, 并且过程中需要对elem进行检查。换句话说目标内存需要有比较苛刻的memory layout. 其次受鸟哥代码中的a[0] += $var影响,我觉得这个null只能在这块内存稍前的位置写入。这就是我的误区。结合以上原因一直让我找不到一个合适的structure来hold这块内存。
过去我逐渐地其实不太关注PHP里面的安全了,有时候写代码也会发现一些问题,但也觉得就那么回事。直到最近看见了关于LockBit的新闻,突然有了兴趣,才有了《CVE-2023-3824: 幸运的Off-by-one (two?)》[5] 一文。在文章写完后的几天,我又去逛逛了安全圈看看大家都在研究什么,在这过程中发现那三只蝴蝶。
首先发现了一篇《WebAssembly安全研究总结》[6]。 这篇文章中重要介绍了如何通过构造恶意的bytecode来攻击Wasm引擎,挺有趣的,也行PHP opcache中的也有类似的问题。我个人比较喜欢解释器和编译器上的一些安全研究,然后我就想去看看有没有关于Wasm更深入一点研究,搜索了一下作者其他的文章。
第一只蝴蝶
我又发现了作者有许多关于JavaScriptCore (jsc) 的研究,我之前是没有接触过jsc,只短暂接触过V8。感觉似乎挺有趣的,那就来感受一下吧。在文章[7]和系列文章[8]的帮助下,使得我的博客中又多了一篇《CVE-2018-4262: Apple Safari RegExp Match Type Confusion by JIT》。在这过程中积累了一点点关于jsc的姿势。特别地,里面的部分构造(box/unbox)让我大开眼界,可谓是相当之精彩,以至于后面在PHP的构造中我都想重现它。 jsc里面有一个用来作为存储JSObject的properties和elements特殊结构叫butterfly, 因为其内存结构像一只带翅膀的蝴蝶,顾名butterfly。ascii graph来自[9]
-------------------------------------------------------- .. | propY | propX | length | elem0 | elem1 | elem2 | .. -------------------------------------------------------- ^ | +---------------+ | +-------------+ | Some Object | +-------------+在jsc的利用中都频繁地使用到了这个结构,包含我前面提到的box/unbox技术。这是第一只蝴蝶。
第二只蝴蝶
在看[9]的过程中,我又看到了saelo(前google project zero成员, 目前V8 JS引擎的安全负责人)的博客中《Pwning Lua through 'load'》[10]。 真苦恼,都是我喜欢读的东西,那就看吧。让我比较惊讶的Lua竟然没有bytecode verifier,文章内容和第一篇攻击Wasm引擎的内容比较相似。然后我又想看看Lua上的一些安全研究,搜索了到一系列来自bigshaq关于LuaJIT方面的安全研究[11],在里面遇到了第二只蝴蝶。LuaJIT的jit complier会将收集到的trace翻译成的IR放在一个类似butterfly结构中。形如
----------------------------------------- | | | | | |const2|const1|inst1|inst2| | | | | | --------------------▲-----------─--------- │ │ ┌──────┐ │ │ ir_p ├─────────┘ └──────┘instructions在一边翅膀,而constants在另一边翅膀。在这短暂的LuaJIT之旅中,又积累了一些关于LuaJIT的知识,但是我觉得最后研究的安全问题太刻意,毕竟是CTF的题,可以理解嘛。不过利用JIT code中的guarded assertions来固定shellcode的技术确实不错。
最后一只蝴蝶
PHP 8中的JIT技术深受LuaJIT影响。以至于bigshaq博客在一篇关于PHP文章中,给PHP打了patch,就把LuaJIT上相关利用直接拿到PHP上。绕了一大圈我又回到了PHP,我突然发现Dmitry整出了一套JIT Compilation Framework [11],名字就叫IR。Dmitry是那个一个人写了PHP中几乎全部optimizers的男人,我对其从心里佩服。听闻IR之后,让我内心久久不能平静,依托IR的全新JIT compiler已经merge到了PHP-src的主线上,令人抓狂的DynAsm终于不见了。我又马上看了一眼Dmitry对其的介绍[13],未来我终于有机会不用在PHP bytecode上做优化了。我看到了类似V8 TurboFan中的Sea of Nodes,及其各种补全的优化算法。这一刻,我打算以后为它也做点什么。因为Dmitry写的那些optimizers曾经陪伴我了很多时候。
我又想到文中这个IR缺陷,我觉得它应该结束了。我又开始了审视它,目光又重新对准了PHP中zend_array,它那里不恰好也有一只蝴蝶吗? 下面ascii来自[14]:
/* * HashTable Data Layout * ===================== * * +=============================+ * | HT_HASH(ht, ht->nTableMask) | +=============================+ * | ... | | HT_INVALID_IDX | * | HT_HASH(ht, -1) | | HT_INVALID_IDX | * +-----------------------------+ +-----------------------------+ * ht->arData ---> | Bucket[0] | ht->arPacked ---> | ZVAL[0] | * | ... | | ... | * | Bucket[ht->nTableSize-1] | | ZVAL[ht->nTableSize-1] | * +=============================+ +=============================+ */PHP中有两种特殊的数组,packed array和mixed array,我在考虑它们的时候,突然想起了这只蝴蝶。原来不用在内存稍前的位置写入那个null,完全可以在内存的中间写入这个null. 甚至我都忘记了可以通过拨动index来控制写入这个null的位置,这一错就是四年。原来那只蝴蝶一直都在那里,都在那个我能看得见的枝头。
0x03 PHP前置知识之前我写PHP内核相关内容的时候,几乎不会去写相关的前置知识,因为我不太想复制粘贴大量的代码,观感不是很好。 但是这次我希望更多的人,能从这个文章中学到一些东西。这篇文章用到的前置知识不会太多,不用担心。如果有不懂的地方,都可以发我邮件问我,但我不能保证及时地回复。
3.1 zval 结构PHP中的变量都是以zval 的形式出现的,它是一个tagged union形式:
// Zend/zend_types.h typedef union _zend_value { zend_long lval; /* long value */ double dval; /* double value */ zend_refcounted *counted; zend_string *str; zend_array *arr; zend_object *obj; zend_resource *res; zend_reference *ref; zend_ast_ref *ast; zval *zv; void *ptr; zend_class_entry *ce; zend_function *func; struct { uint32_t w1; uint32_t w2; } ww; } zend_value; struct _zval_struct { zend_value value; /* value */ union { uint32_t type_info; struct { ZEND_ENDIAN_LOHI_3( zend_uchar type, /* active type */ zend_uchar type_flags, union { uint16_t extra; /* not further specified */ } u) } v; } u1; union { ... } u2; };这在编程语言设计中非常常见,比如JavaScriptCore里面对应的变量表示形式JSValue。所以在了解编程语言内部的时候,你需要提早关注它里面的变量表示形式。其中zval.value会存储变量对应的真正值,而zval.u1.type_info会存储变量对应的类型信息。
3.2 PHP基本类型PHP中基本类型有
// Zend/zend_types.h #define IS_UNDEF 0 #define IS_NULL 1 #define IS_FALSE 2 #define IS_TRUE 3 #define IS_LONG 4 #define IS_DOUBLE 5 #define IS_STRING 6 #define IS_ARRAY 7 #define IS_OBJECT 8 #define IS_RESOURCE 9 #define IS_REFERENCE 10 #define IS_CONSTANT_AST 11 /* Constant expressions */它们出现在zval.u1.v.type中。
- undefined, null, false 和 true 可以直接用类型信息区分;
- long 和 double直接以primitive value存储在zval.value.lval和zval.value.dval中;
- string, array, object, resource, reference和constant_ast都有对应的具体结构,其地址将以指针的形式存放在zval.value.str, zval.value.arr ... 中。
zend_string用于描述上面提到的string类型。其结构如下:
typedef struct _zend_refcounted_h { uint32_t refcount; /* reference counter 32-bit */ union { uint32_t type_info; } u; } zend_refcounted_h; struct _zend_string { zend_refcounted_h gc; zend_ulong h; /* hash value */ size_t len; char val[1]; };其中:
- zend_string.gc : 我通常叫它gc_info,里面有一个比较重要是zend_string.gc.refcount表示引用计数;
- zend_string.h : 用于缓存对该string计算过的hash值;
- zend_string.len : 用于表示该string表示字符串长度;
- zend_string.val: 用于表示string表示字符串具体内容,可以看到字符串实际存储在zend_string结构后面连续的地方上。
PHP中两种类型的数组:
- packed array : 用整数作为index连续存放的数组 i.e., $arr = [1,2,3,4];
- mixed array: 混合了以字符串以index作为的数组 i.e., $arr = [1, 'key1' => 'val1'];
我们来介绍一下在array中的butterfly. 首先是packed array:
+=============================+ | HT_INVALID_IDX | | HT_INVALID_IDX | +-----------------------------+ ht->arPacked ---> | ZVAL[0] | | ... | | ZVAL[ht->nTableSize-1] | +=============================+其中zend_array.arData 指向第1个元素,注意到它并不是指向申请的内存起始位置,前面还有两个index cells (一个cell大小为4字节),在其上都存放着HT_INVALID_IDX == -1。因为packed array, 不需要对index做hash, 直接根据index取值就行。那这两个invalid index在这里是干啥呢? 为了照顾未来使用非整数index来array fetch。我之前就困在packed array之上。
再一个就是mixed array:
+=============================+ | HT_HASH(ht, ht->nTableMask) | | ... | | HT_HASH(ht, -1) | +-----------------------------+ ht->arData ---> | Bucket[0] | | ... | | Bucket[ht->nTableSize-1] | +=============================+PHP数组中的元素顺序存储, 位于一块连续的内存上。为了解决hash冲突,PHP将hash冲突的元素用一张链表连接。那么为了在mixed array中找到正确的元素,会做这样以下操作:
- 对index做hash, 得到值h;
- 根据h计算它落在index table的位置 h | ht->nTableMask, 其中index table就是第一个元素中的那块区域。每一个index cell中都存储目标元素所在链表的头结点与ht->arData的offset;
- 因此会从ht->arData[h | ht->nTableMask]开始遍历链表,比照real index,找到目标元素。
在mixed array中,index table中index cells的个数是这个array可存储元素容量的2倍。在array扩正的过程中依然会保持这个关系,例如如果array可以存储8个元素,那么就有16 index cells。它们总计大小,即是对应butterfly区域内存大小。
无论是packed array或者mixed array它们的容量最小都是8个元素,每次扩容都是double。特别地是,PHP数组存储单个元素的结构为Bucket, 其定义如下:
typedef struct _Bucket { zval val; zend_ulong h; /* hash value (or numeric index) */ zend_string *key; /* string key or NULL for numerics */ } Bucket;- Bucket.val 存放元素对应的value;
- Bucket.h 存放整型的index;
- Bucket.key 存放元素对应的key。
这里讲一下两个zval *var, *val之间的赋值过程,它对应Zend/zend_execute.h中两个函数zend_assign_to_variable 和 zend_copy_to_variabl部分过程 。我用伪代码表示,因为适合突出一些重要的东西,并省略一些不太重要的信息。
// assign val to var if var is refcouted: var_value = get_value_from_zval(var); copy_zval(var, val) if (get_refcount(var_value) == 1) free_value(var_value) else copy_zval(var, val)它对应的两个函数明显会比我给出的伪代码复杂,但是我们不需要关注里面大多数cases。其中我们说一个zval是refcounted,意味它对应值需要额外分配内存,比如string, array和 object这些都是,而null,false, true,long 和double它们不是refcounted,因为它们的值是直接保存在zval中的。这里赋值过程的核心逻辑是我们特别需要注意var原本的值。
我来解释一下这里在做什么:
- 当var是refcounted时,我们做以下操作:
- 首先我们用var_value记录了var的原值;
- 我们直接通过copy_zval将val拷贝到var上;
- 判断var的原值的引用计数是否为1,如果是1则释放掉var的原值。
- 反之,我们直接通过copy_zval将val拷贝到var。
在1.3中var的原值的引用计数为1,意味着这个值只有var来用,当var被赋予新值之后,它的原值就没人用了,那么是可以释放掉的。其中copy_zval做了两件事情:
- 将val的值直接拷贝到var上;
- 按情况调整val所指向值的引用计数。
这里我们暂时不讨论是什么情况会调整引用计数。
3.6 Copy on Write它的中文名叫写时复制,是一种比较常见的优化。考虑如下代码
$a = 'aaaa'; $b = $a; echo $b; $b .= 'b'; echo $b;在第二行这里并不会直接复制字符串'aaaa' 给变量$b,而是把$a指向的string上引用计数加1. 在第4行这里才会将前面的字符串重新复制一份,用于连接字符串b,再将新的结果写入$b. 那么写时复制是如何判断的呢? 很简单,你只需要判断你指向的值的引用计数是否大于1.
以上就是这里我们需要知道的所有PHP里面的知识。
0x04 利用简述我们的大致路线是:
- 构造fakeZval原语;
- 泄露堆上某个地址;
- 构造addressOf原语;
- 构造第一阶段有条件的读/写原语;
- 构造第二阶段稳定的任意读/写原语。
参考jsc中经常会fakeObj和addressOf原语, 我们来构造PHP中独特的fakeZval和addressOf。这篇文章不讨论后续利用,因为相关利用方式比较模板化,常规PHP漏洞利用中都有提到,不再累述,节省篇幅。
0x05 构造fake zval这个技术的灵感来于jsc利用里面的fakeobj源语。
回忆一下,我们之前的想法
- 触发array的resize, 让array的butterfly被释放掉;
- 我们马上抢占这块butterfly对应的内存;
- 让null写在我们抢占这块内存所使用的结构上。
这里我们先搞清楚两个问题:
-
null会写在butterfly的哪里?
-
结合我们前面理解的两个zval直接的赋值过程,如何让写null这个操作顺利执行?
第1个问题,毫无意义,null写在你通过index指定的元素上. 例如我定义一个mixed array如下:
$a1_str = 'eeee' $victim_arr = array( 'a1' => $a1_str, 'a2' => 1, 'a3' => 1, 'a4' => 1, 'a5' => 1, 'a6' => 1, 'a7' => 1, 'a8' => 1, );它对应的memory layout如下(我们前面提到过,8个元素对应16个index cells):
┌───────────────┐ │ index_cell15 │ │ ├───────────────┤ │ │ ... │ │ ├───────────────┤ │ │ index_cell1 │ │ ├───────────────┤ │ │ index_cell0 │ │ addr $victim_arr['a1']──────► |───────────────┤ │ │ bucket0 │ │ ├───────────────┤ │ │ bucket1 │ │ ├───────────────┤ ▼ │ ... │ ├───────────────┤ │ bucket7 │ └───────────────┘如果要写这个数组的第1个元素 $a[0] = $undef_var,那么写入的位置相对于这块butterfly的其实地址的offset应该为4 * 16 = 64。
第二问题,当上面的butterfly区域被释放后,我们马上构造一个大小合适的string来把它抢占。例如:
$zend_array_burket_size = 0x20; $zend_table_index_size = 0x4; $zend_string_size = 0x20; $user_str_length = 16 * $zend_table_index_size + 8 * $zend_array_burket_size - $zend_string_size; set_error_handler(function() { $victim_arr['a9'] = 1; $user_str = str_repeat('b', $user_str_length); })对于一个string, 它的前0x18字节属于header, 具体来说:
- +0x0 : 引用计数;
- +0x4 : gc信息;
- +0x08 : hash值缓存,如果对这个string做过hash,得到的hash会放在这个地方;
- +0x16 : 字符串长度;
- 其余部分存储字符串内容。
那么很显然要写的地方0x40落在了我们可控的字符串内容上。那么可以伪造一个zval,来满足前面提到过的赋值过程中的check,让null顺利的写到这个fake zval上。
0x06 泄露某个地址绕过ASLR,或者是读写指定地址的内容,我们都需要先泄露一些地址,才能准确定位我们需要的地址。这里的过程比较trick,我们借助了PHP的弱类型转换。考虑如下代码:
$victim_arr['a1'] = true; $victim_arr['a1'] .= null; var_dump($victim_arr['a1']); // output: string(1) "1"在第3行这里,有一个string concact操作,会把$a['a1']和null连接起来。但是它们都不是string,所以这里会经历一个弱转,true会被转成字符串"1",而null会被转成empty string。 最后值为"1"的string写到$a['a1']上,所以$a['a1']会保存这个string的指针。通过前面UAF, $a['a1']实际位于我们可以控制的内存 (即$user_str)上,它对应我们使用fakeZval构造的zval。通过读取$user_str,我们就拿到了这个string的地址。
此时$user_str内存布局应该为
┌──────────────┐ │ │ │ string_header│ │ │ ├──────────────┤0x18 │ │ │ ... │ │ │ string: '1' fake_zval_with_null──────────►├──────────────┤0x40 ◄─────────────┬────────────┐ │ zval_value │ 0x0 │ │ gc_header │ ├────────────────►├──────────────┤0x48 ├────────────┤ │ zval_type │ 0x3 │ │ hash │ └────────────────►├──────────────┤ ├────────────┤ │ │ │ len │ │ │ ├────────────┤ │ │ │ content │ └──────────────┘ └────────────┘注意里面的0x3表示是这个fake zval是一个true。因为这个fake zval作为一个待赋值的zval,它只是一个null,非前面我们提到的refcounted类型的值。所以这里的赋值过程非常简单:
- 将string : "1"的地址复制到fake zval的zval.value.str中;
- 将fake zval类型修改为 is_string。
注意这里有一个小问题,你会发现上述泄露出来的string地址不在PHP自己管理的堆上,用于存放各种PHP运行时结构。而是在glibc通过malloc/free管理的堆上。这是因为PHP对于字符串的一个小优化,PHP会将常见的字符串对应的string事先分配,如果在运行时,有碰到这些字符串,直接返回之前分配好的就行,避免频繁分配。而这些字符串在PHP是以persistent string出现的, 它们内存都是通过malloc分配的。
true弱转对应的当个字符"1"恰好就是这已知字符串中的一个,并且它在这里连接是一个empty string。使得最后结果依然这个已知的string。如果我们想到得到PHP自己堆上的一个地址,我们就必须绕过它。很简单,我们可以用int或者double来作为fake zval的值就行。
这里我使用的是int : (100),最后我们就得到了string : "100"的地址。为什么使用100,后面会提到。
0x07 获取一块内存目前我们有string : "100"的地址str100_addr,我们先来看一下string : "100"的memory layout:
string : "100" ┌────────────────────────┐ │ 0x0000001600000001 │gc_info fake_string──►├────────────────────────┤ │ 0x0000000000000000 │hash ├────────────────────────┤ │ 0x0000000000000003 │len ├────────────────────────┤ fake_len─────►│ 0x00007fff00303031 │content ├────────────────────────┤ │ │ └────────────────────────┘在content这里的0x303031其实对应字符串"100"。试想,我们如果利用fakeZval原语构造一个zval, 让它的类型为string,让它的值指向str100_addr + 0x8,即上图的fake_string处的位置。从fake_string开始,我们构造了一个新的string, 它的长度为0x00007fff00303031。其中出现的7fff是堆上的一些随机数据,这里的0x303031它是大于一个PHP中memory chunk的容量0x200000的,以至于这个fake_string能盖住整个memory chunk,这就是我之前用int : (100)的原因。
我们的想法是,我能不能利用这个fake_string读到内存后面的内容? 那么我需要拿到这个fake_string,如下:
reset_victim_arr_and_user_str(); set_error_handler(function() { // resize global $victim_arr; global $user_str_length; global $user_str; global $first_elem_offset; global $zend_string_header; global $str100_addr; $victim_arr['a9'] = 1; $user_str = str_repeat('b', $user_str_length); // construct fake zval that contains a fake zend_string; // 1. zval.value.str <= $leak_addr + 0x8; // 2. zval.u1.type_info <= is_string_ex == (6 | (1 << 8)); writestr64($user_str, $first_elem_offset - $zend_string_header, $str100_addr + 0x8); writestr64($user_str, $first_elem_offset - $zend_string_header + 0x8, (6 | (1 << 8))); }); $heap = $victim_arr['a1'] .= $undef_var;- 第1行 reset_victim_arr_and_user_str() 表示重置$victim_arr 和 $user_str,以保证后面UAF的触发;
- 在error_handler里面我们构造了一个fake zval, 指向我们的fake_string;
- 注意第15行这里,我们用$heap hold了后面这个array assign的计算结果。后面array assign的计算结果是fake_string拼接一个empty string,那么这意味着$heap就是fake_string。
我们可以通过读取$heap来漫游PHP堆上的内容。这不算完,我们还可以修改$heap对应fake_string的内容,但不会触发copy-on-write。不会触发copy-on-write是这里最关键的。按道理,$heap hold了array assign的计算结果,即为fake_string,那么fake_string的引用计数是需要加1的,如果fake_string的引用计数大于1,在我们修改$heap的时候,就会发生copy-on-write,造成我们根本修改不到fake_string上的内容。再退一步说,我们可能会在copy-on-write的时候会导致PHP直接结束,因为fake_string的size可能会很大,你要拷贝一份fake_string显然就会失败,比如参考前面的0x00007fff00303031。
那么这里为什么不会发生copy-on-write,我们看fake_string的gc_info,它的值是原来string : "100"的hash,即为0x00。而PHP检查一个值是不是refcounted,就会检查gc_info是不是不为0x00。这就意味着PHP认为fake_string不是refcounted,即不是gc关注的对象。意味着array assign计算结果也不是refcounted,那么这里根本就不存在什么copy-on-write。以为copy-on-write只针对refcounted values。
0x08 构造addressOf现在我们就有一个可读可写,并且我们知道它位置的内存。实际做到一步,我们已经可以停手了。比如像[5]中的利用方式一样:
- 在堆上喷射大量我们想要读取的内存结构,拿到我们想要的地址。
- 在堆上喷射大量我们想要写入的内容结构,写入我们希望的值。
在第一版exploitation我是这样的利用的。但是这里还是有很多不确定性,比如我们喷射的内存结构不在我们可以漫游的memory chunk中,就可能会失败。这时候我们需要重新调整fake_string的位置,比如先喷射大量的string : "100",让我们迁移到全新的memory chunk上。
没人喜欢不确定性,我也一样。这里我们来构造一个更加稳定的addressOf来帮助我们定位想要的内存结构位置。比如
$num = 1111; $num_value = addressOf($num); $str = "aaaaaaa"; $str_addr = addressOf($func); $obj = new stdClass(); $obj_addr = addressOf($obj);它有如下功能 :
- 对于不是refcounted的值,我们直接可以通过addressOf来获取它的immediate value。比如上面的$num。
- 对于refcounted的值,我们可以通过addressOf来获取它的地址。比如上面的$str和$obj。
我们的想法是在前面这块内存上布置一个array : [0, 1, 2, 3, 4, 5, 6, 7] 。如下
array : [0, 1, 2, 3, 4, 5, 6, 7] ┌───────────────┐ │packed_arr_flag│ butterfly ├───────────────┤ ┌────────────────┐ │ ... │ │ invalid_idx │ │ ... │ ├────────────────┤ ├───────────────┤ │ invalid_idx │ │ arData ├─────────────────►├────────────────┤ ├───────────────┤ │ bucket0 │ │ ... │ ├────────────────┤ │ ... │ │ ... │ │ │ ├────────────────┤ │ │ │ bucket7 │ │ │ └────────────────┘ └───────────────┘我们的想法:
- 控制这个fake array的引用计数为1;
- 使用fakeZval原语包装这个fake_array;
- 触发前面的UAF,fake_array被释放,我们马上申请一个相同的array $hax,拿到这块内存;
- 假设你要读取的值为$val, 那么使得$hax[0] = $val ;
- 那么我们再去$heap指定位置读butterfly上第一个元素的内容,即可获得我们想要的。
需要注意的是,在free一个小内存的时候,PHP是先定位它所在page,来判定它属于什么size的bin,再投放正确的到free_list上。所以你构造fake array的位置要确定好。如果你想绕过这个限制,你可以申请一块超大内存,来自己伪造memory chunk,具体可以参考[16]。
0x09 任意读/写原语我目光对准了php://memory[15],PHP运行我们以文件操作的形式操作一块内存。控制这块内存大小的结构为,
typedef struct { char *data; size_t fpos; size_t fsize; size_t smax; int mode; } php_stream_memory_data;我们的想法:
- 在$heap上布置和sizeof(php_stream_memory_data)大小的string;
- 利用UAF释放掉这个string,确保fopen("php://memory")在创建php_stream拿到;
- 修改上面的data指针和fpos以及fsize来读写任意的区域。
同样地,要注意释放string所在的page。
0x0A 完整的利用暂时不提供,因为影响比较大,且没有修复。
0x0B 总结我们分析了PHP IR中存在的问题,以及为什么长时间没有被修复,最后提出了一个修复建议。写下了我在探索这个问题时,给过我帮助的3只蝴蝶。最后给大家分享了我的利用方式,将JS引擎利用中的常见原语尝试搬到了PHP上。当走出了误区之后,在构造exploitation过程中诞生了许多ideas,实际这不是一个特别难的利用,只是我比较笨而已。我觉得不同解释器或者编译器的利用中都有很多相同点,可以相互借鉴学习,也许能帮你找到更多的思路。
最后,题目中的"PHP之殇",更多是对过去的一种告别,未来我会更多关注PHP中可能马上会release的新的JIT complier,希望在未来给大家带来我关于它的一些有趣的故事。
0x0C 引用- 风雪之隅, https://www.laruence.com/
- 深入理解PHP7内核之HashTable, 深入理解PHP7内核之HashTable - 风雪之隅
- crash.php, php-exploit/crash.php at master · m4p1e/php-exploit · GitHub
- zend_assign_dim_op, php-src/Zend/zend_vm_def.h at master · php/php-src · GitHub
- CVE-2023-3824: 幸运的Off-by-one (two?), CVE-2023-3824: 幸运的Off-by-one (two?) | maplgebra
- WebAssembly安全研究总结, https://mp.weixin.qq.com/s/cPUaDQaCWpZiBEgZqbqvPg
- JavaScript engine exploit(二),JavaScript engine exploit(二)-安全客 - 安全资讯平台
- Browser Exploitation, Browser Exploitation - LiveOverflow
- Attacking JavaScript Engine, .:: Phrack Magazine ::.
- Pwning Lua through 'load', Pwning Lua through 'load'
- LuaJIT Internals: Intro, LuaJIT Internals: Intro
- dstogov/ir, GitHub - dstogov/ir: Lightweight JIT Compilation Framework
- https://www.researchgate.net/publication/374470404_IR_JIT_Framework_a_base_for_the_next_generation_JIT_for_PHP
- Zend/zend_types.h, php-src/Zend/zend_types.h at master · php/php-src · GitHub
- PHP memory wrapper PHP: php:// - Manual
- RWCTF2021 Mop 0day Writeup, RWCTF2021 Mop 0day Writeup | maplgebra
1 个帖子 - 1 位参与者
從 React 到 Vue 的心得感想
如果有看過我的部落格的話,應該會知道我一直都是寫 React,完全沒有碰過 Vue,也沒有碰過 Angular。自從 2015 年接觸到 React 後,工作上就一直是用 React 了。
然而,最近因為工作上的需求,所以開始寫 Vue 了,而剛好也有讀者來問我從 React 跳到 Vue 的心得,因此這邊就簡單寫一篇來分享。
How to share sensitive files securely online
原创 Paper | USB设备开发:从入门到实践指南(四)
火线Zone 24年社区规则升级,共建技术安全生态
安全日报(2024.03.13)
2024-03 补丁日: 微软多个漏洞安全更新通告
Cybercriminals Evolve Tooling for Remote Access Compromise
【补丁日速递】2024年3月微软补丁日安全风险通告
Where are they now? Starring: Atlassian's Confluence CVE-2023-22527
Можно ли предсказывать кибератаки?
The March 2024 Security Update Review
It’s the second Tuesday of the month, and Adobe and Microsoft have released a fresh crop of security updates. Take a break from your other activities and join us as we review the details of their latest advisories. If you’d rather watch the full video recap covering the entire release, you can check it out here:
Adobe Patches for March 2024
For March, Adobe released six patches addressing 56 vulnerabilities in Adobe Experience Manager, Premiere Pro, ColdFusion, Adobe Bridge, Lightroom, and Adobe Animate. Two of these bugs were submitted through the ZDI Program. The largest is the update for Experience Manager, which addresses 44 CVEs. However, all but two of these are simple cross-site scripting (XSS) bugs. The fix for Adobe Animate corrects four CVEs. Only one of these CVEs is rated Critical and could lead to arbitrary code execution if a user opens a specially crafted file on an affected system. The other three bugs are all memory leaks resulting from Out-of-Bounds (OOB) Read bugs. The patch for Premiere Pro fixes two Critical-rated bugs that also require user interaction to gain code execution.
For those still running ColdFusion, there’s a single Critical-rated arbitrary file system read bug getting fixed. Adobe also recommends updating your ColdFusion JDK/JRE LTS version to the latest update release. The fix for Adobe Bridge addresses three Critical rated and one Important severity bug. The worst could lead to code execution when opening a specially crafted file. The final patch fixes a single code execution bug in Lightroom. Adobe also made the odd decision to stop tweeting when its patches become available and limiting communication to just email subscriptions. Let’s hope they reverse that decision as many people (myself included) rely on the twitter feed for notifications.
And with this release, anyone targeting Adobe Reader at next week’s Pwn2Own Vancouver event can breathe a sigh of relief. It seems your exploits won’t be patched before the event.
None of the bugs fixed by Adobe this month are listed as publicly known or under active attack at the time of release. Adobe categorizes these updates as a deployment priority rating of 3.
Microsoft Patches for March 2024
This month, Microsoft released 59 new patches addressing CVEs in Microsoft Windows and Windows Components; Office and Office Components; Azure; .NET Framework and Visual Studio; SQL Server; Windows Hyper-V; Skype; Microsoft Components for Android; and Microsoft Dynamics. In addition to the new CVEs, multiple Chromium bugs are being incorporated into the release, bringing the total number of CVEs to 64. One of these bugs was reported through the ZDI program.
Of the new patches released today, two are rated Critical, and 57 are rated Important in severity. This is a relatively low volume for March, especially considering this is the last patch cycle before the Pwn2Own contest next week. Vendors usually try to patch as much as possible knowing we update all targets to the latest release. Considering Microsoft has several targets in the contest, it’s interesting to see such a small release.
None of the CVEs released today are listed as publicly known or under active attack, but that could change. After the February release, Microsoft revised multiple updates to indicate they were being actively exploited. For now, nothing is listed as in the wild. I’ll update this blog should that change.
Let’s take a closer look at some of the more interesting updates for this month, starting with a Critical-rated Hyper-V bug:
- CVE-2024-21407 – Windows Hyper-V Remote Code Execution Vulnerability
This is one of the two Critical-rated bugs for this month, and this is the only one that could result in code execution. This vulnerability would allow a user on a guest OS to execute arbitrary code on the host OS. This is often referred to as a guest-to-host escape and could be used to impact other guest OSes on the server. It’s a shame we won’t see this bug get exploited at Pwn2Own next week, where it could have won $250,000. Maybe next year.
- CVE-2024-26198 – Microsoft Exchange Server Remote Code Execution Vulnerability
It seems there are Exchange patches almost every month now, and March is no different. This bug is a classic DLL loading vulnerability. An attacker places a specially crafted file in a location they control. They then entice a user to open the file, which loads the crafted DLL and leads to code execution. Last month, Microsoft stated the Exchange bug was being actively exploited only after the release. This bug is currently NOT listed as exploited in the wild, but I’ll update this blog should Microsoft change its mind (again).
- CVE-2024-21334 – Open Management Infrastructure (OMI) Remote Code Execution Vulnerability
This bug rates the highest CVSS rating for this release with a 9.8. It would allow a remote, unauthenticated attacker to execute code on OMI instances on the Internet. It’s not clear how many of these systems are reachable through the Internet, but it’s likely a significant number. Microsoft gives this an “Exploitation less likely” rating, but considering this is a simple Use After Free (UAF) bug on a juicy target, I would expect to see scanning for TCP port 5986 on the uptick soon.
- CVE-2024-21400 – Microsoft Azure Kubernetes Service Confidential Container Elevation of Privilege Vulnerability
This bug allows an unauthenticated attacker to access the untrusted AKS Kubernetes node and AKS Confidential Container to take over confidential guests and containers. Successful exploitation would allow the attacker to steal credentials and affect other resources. While that’s bad enough, patching won’t be straightforward. Customers must ensure they are running the latest version of “az confcom” and Kata Image. The bulletin contains additional information on the commands needed. Be sure to check it out.
Here’s the full list of CVEs released by Microsoft for March 2024:
CVE Title Severity CVSS Public Exploited Type CVE-2024-21408 Windows Hyper-V Denial of Service Vulnerability Critical 5.5 No No DoS CVE-2024-21407 Windows Hyper-V Remote Code Execution Vulnerability Critical 8.1 No No RCE CVE-2024-21392 .NET and Visual Studio Denial of Service Vulnerability Important 7.5 No No DoS CVE-2024-26203 Azure Data Studio Elevation of Privilege Vulnerability Important 7.3 No No EoP CVE-2024-21421 † Azure SDK Spoofing Vulnerability Important 7.5 No No Spoofing CVE-2024-21431 Hypervisor-Protected Code Integrity (HVCI) Security Feature Bypass Vulnerability Important 7.8 No No SFB CVE-2023-28746 * Intel: CVE-2023-28746 Register File Data Sampling (RFDS) Important N/A No No Info CVE-2024-21438 Microsoft AllJoyn API Denial of Service Vulnerability Important 7.5 No No DoS CVE-2024-21390 Microsoft Authenticator Elevation of Privilege Vulnerability Important 7.1 No No EoP CVE-2024-21400 † Microsoft Azure Kubernetes Service Confidential Container Elevation of Privilege Vulnerability Important 9 No No EoP CVE-2024-20671 Microsoft Defender Security Feature Bypass Vulnerability Important 5.5 No No SFB CVE-2024-26164 Microsoft Django Backend for SQL Server Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-21419 Microsoft Dynamics 365 (on-premises) Cross-site Scripting Vulnerability Important 7.6 No No XSS CVE-2024-26198 Microsoft Exchange Server Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-26201 Microsoft Intune Linux Agent Elevation of Privilege Vulnerability Important 6.6 No No EoP CVE-2024-21451 Microsoft ODBC Driver Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-26159 Microsoft ODBC Driver Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-21440 Microsoft ODBC Driver Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-26162 Microsoft ODBC Driver Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-26199 Microsoft Office Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-26190 Microsoft QUIC Denial of Service Vulnerability Important 7.5 No No DoS CVE-2024-21426 Microsoft SharePoint Server Remote Code Execution Vulnerability Important 7.8 No No RCE CVE-2024-21448 † Microsoft Teams for Android Information Disclosure Vulnerability Important 5 No No Info CVE-2024-21441 Microsoft WDAC OLE DB provider for SQL Server Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-21444 Microsoft WDAC OLE DB provider for SQL Server Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-21450 Microsoft WDAC OLE DB provider for SQL Server Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-26161 Microsoft WDAC OLE DB provider for SQL Server Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-26166 Microsoft WDAC OLE DB provider for SQL Server Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-21434 Microsoft Windows SCSI Class System File Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-21446 NTFS Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-21330 Open Management Infrastructure (OMI) Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-21334 Open Management Infrastructure (OMI) Remote Code Execution Vulnerability Important 9.8 No No RCE CVE-2024-26204 Outlook for Android Information Disclosure Vulnerability Important 7.5 No No Info CVE-2024-21411 † Skype for Consumer Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-21418 Software for Open Networking in the Cloud (SONiC) Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-26165 Visual Studio Code Elevation of Privilege Vulnerability Important 8.8 No No EoP CVE-2024-26160 Windows Cloud Files Mini Filter Driver Information Disclosure Vulnerability Important 5.5 No No Info CVE-2024-26170 Windows Composite Image File System (CimFS) Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-26185 Windows Compressed Folder Tampering Vulnerability Important 6.5 No No Tampering CVE-2024-26169 Windows Error Reporting Service Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-21437 Windows Graphics Component Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-21436 Windows Installer Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-21427 Windows Kerberos Security Feature Bypass Vulnerability Important 7.5 No No SFB CVE-2024-26181 Windows Kernel Denial of Service Vulnerability Important 5.5 No No DoS CVE-2024-26182 Windows Kernel Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-26173 Windows Kernel Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-26176 Windows Kernel Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-26178 Windows Kernel Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-21443 Windows Kernel Elevation of Privilege Vulnerability Important 7.3 No No EoP CVE-2024-26174 Windows Kernel Information Disclosure Vulnerability Important 5.5 No No Info CVE-2024-26177 Windows Kernel Information Disclosure Vulnerability Important 5.5 No No Info CVE-2024-21435 Windows OLE Remote Code Execution Vulnerability Important 8.8 No No RCE CVE-2024-21433 Windows Print Spooler Elevation of Privilege Vulnerability Important 7 No No EoP CVE-2024-26197 Windows Standards-Based Storage Management Service Denial of Service Vulnerability Important 6.5 No No DoS CVE-2024-21439 Windows Telephony Server Elevation of Privilege Vulnerability Important 7 No No EoP CVE-2024-21432 Windows Update Stack Elevation of Privilege Vulnerability Important 7 No No EoP CVE-2024-21430 Windows USB Attached SCSI (UAS) Protocol Remote Code Execution Vulnerability Important 5.7 No No RCE CVE-2024-21429 Windows USB Hub Driver Remote Code Execution Vulnerability Important 6.8 No No RCE CVE-2024-21442 Windows USB Print Driver Elevation of Privilege Vulnerability Important 7.8 No No EoP CVE-2024-21445 Windows USB Print Driver Elevation of Privilege Vulnerability Important 7 No No EoP CVE-2024-26167 Microsoft Edge for Android Spoofing Vulnerability Unknown 4.3 No No Spoofing CVE-2024-2173 * Chromium: CVE-2024-2173 Out of bounds memory access in V8 High N/A No No RCE CVE-2024-2174 * Chromium: CVE-2024-2174 Inappropriate implementation in V8 High N/A No No RCE CVE-2024-2176 * Chromium: CVE-2024-2176 Use after free in FedCM High N/A No No RCE* Indicates this CVE had been released by a third party and is now being included in Microsoft releases.
† Indicates further administrative actions are required to fully address the vulnerability.
The only other Critical-rated bug is a Denial-of-Service (DoS) vulnerability in Hyper-V Server. Microsoft does not indicate how extensive the DoS is or if the system automatically recovers, but considering its rating, the bug likely shuts down the entire system.
Moving on to the other remote code execution bugs, as we saw last month, there are many impacting SQL clients that would require connecting to a malicious SQL server. Practical exploitation is unlikely without significant social engineering. That’s not the case for the bug in Django Backend for SQL Server. This vulnerability is a classic SQL injection via unsanitized parameters. There’s also a DLL loading bug for Windows OLE. The RCE bug in SharePoint requires user interaction in that the threat actor needs to convince the user to open a specially crafted file. Social engineering will also be required for the Skype for Consumer vulnerability. You’ll also need to manually download the latest version of Skype here as there doesn’t seem to be an automated upgrade option. The final two RCE bugs are a bit rare in that they require physical access to the target system. Both vulnerabilities rely on the attacker plugging a device into an open USB port. It’s uncommon to see patches for bugs with this physical attack vector, but it’s good to see Microsoft is willing to make updates for these types of issues.
Speaking of rarities, there is a single patch for a Tampering bug in the Windows compressed folder component. Microsoft doesn’t give any indication of how the vulnerability would manifest other than to say it requires a user to open a specially crafted file. After that, it’s not clear what is actually being tampered with, although the inclination is to believe an attacker could change file contents with this bug.
There are more than 20 elevation of privilege (EoP) patches in this month’s release. In most cases, a local attacker would need to run specially crafted code to elevate to SYSTEM. The bug in the telephony component would lead to the similar (but distinctly different) “NT AUTHORITY\Network Service” privilege. The bug in the Azure Data Studio would only elevate to the permission level of the user running the application. Another reminder to not do daily tasks with administrative privileged accounts. The bug in the Microsoft Intune Linux Agent bypasses compliance checks when using custom compliance scripts, thus altering the results. The bug in the Authenticator app sounds quite bad as it could bypass 2FA, but it requires a fair bit of user interaction to succeed. An attacker needs to be already executing code on the target and have the user close and re-open the Authenticator application. The vulnerability in the Windows Installer would allow an attacker to delete files. We recently blogged about a similar bug in the .NET framework. The bug in OMI is interesting in that an attacker could exploit it to communicate as Root with an OMI server. The final EoP patch for March affects the Software for Open Networking in the Cloud (SONiC) component. Successful exploitation would allow an attacker to escalate to Root in the Border Gateway Protocol (BGP) container and perform specific actions that enable them to escape the container.
There are three separate Security Feature Bypass (SFB) patches in this month’s release with the most impactful affecting Windows Defender. The good news is that you’ll likely need to take no action as the Defender engine automatically updates itself. The bad news is that if you’re in an isolated environment or have Defender disabled, you’ll likely need to manually verify the Defender version. Given that this bug allows attackers to prevent Defender from starting, it’s best to make sure you have that patch applied. The bug in the hypervisor-protected code integrity (HVCI) could allow an attacker to bypass code integrity protections, but it requires administrator-level permissions. Another rarity, as exploits that begin with admin permissions rarely get fixed. The final SFB update fixes a bug in Kerberos that could lead to impersonating other users.
The March release includes five information disclosure bugs, but unusually, only one leaks unspecified memory contents. The two bugs in the kernel could allow an attacker to view registry keys they would otherwise not be able to access. The bug in Teams for Android would allow the reading of files from the private directory of the app. You’ll also need to manually get this update from the Google Play Store. That’s also the case for Outlook for Android. That bug allows attackers to view the ineffable “file contents”. In addition to the one already documented, the March release includes fixes for five different denial-of-service (DoS) bugs in various. However, Microsoft provides no real information or details for them.
There are two spoofing bugs receiving patches this month, and the Microsoft Edge for Android is a strange one. It was actually published earlier this month but without an actual fix. Instead, it notes, “The security update for Edge for Android is not immediately available.” It seems odd that Microsoft would choose to publish information about the bug without also pushing a fix for the bug. Perhaps it will be updated soon. The other spoofing bug is in the Azure SDK, and you may or may not need to take extra steps to be fully protected. If you are running a deployment created before October 19, 2023, you will need to manually upgrade Azure-core to Azure Core Build 1.29.5 or higher. If you have a deployment from after October 19, you should receive the patch automatically.
There is one new advisory for this month as Microsoft announces the deprecation of Oracle’s libraries within Exchange. This is a long time coming and a welcome change, as Exchange was essentially 0-day’ed every time Oracle updated their libraries.
Finally, there is a single cross-site scripting (XSS) bug in Microsoft Dynamics fixed this month.
Looking Ahead
Be sure to look out for updates from Pwn2Own Vancouver, and if you’re at the CanSecWest conference, please stop by to say hello. I like it when people say hello. The next Patch Tuesday of 2024 will be on April 9, and I’ll return with details and patch analysis then. Until then, stay safe, happy patching, and may all your reboots be smooth and clean!