poc
1 | <script> |
patch
https://github.com/WebKit/webkit/commit/d0ac97f994f0145715402be4d4a24b54440beb02
1 | - { |
在for (auto& control : xxx)里,把form.associatedElements()改成了protectedAssociatedElements。
前者直接是使用HTMLFormElement中的m_associatedElements引用。
1 | const Vector<FormAssociatedElement*>& associatedElements() const { return m_associatedElements; } |
后者使用Ref
poc分析
poc的一些知识点
<body onload=jsfuzzer()>
在页面加载完成后就调用jsfuzzer()函数。textarea2.autofocus = true;
在jsfuzzer()里通过autofocus来改变了textarea2,从而因为onchange事件发生来回调了eventhandler2()函数。
这在webkit poc里很常见,都是这么写来触发的。
javascript是单线程的,回调就类似于内核里的trap,用来弥补不能多线程的缺陷。eventhandler2
1
2
3
4for(var i=0;i<100;i++) {
var e = document.createElement("input");
form.appendChild(e);
}appendChild调用setForm,触发内存重新分配,set之后free。
form.submit();
在submit里调用到了updateLayout。
在submit时需要保持页面布局是最新的. 导致有了一个时机通过回调事件访问目标对象.
UAF
free
在 lldb 中确认. 可以看到 eventhandler2 中使用的 appendChild() 方法经过层层调用释放了 WebCore::FormAssociatedElement*
1 | #0 0x103670044 in __sanitizer_mz_free (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5a044) |
use
前面我们说过了,在submit的时候,真正的触发页面的update
1 | (lldb) bt |
在for (auto& control : form.associatedElements()) 执行的时候, use after free。
其他
free和use的地点因为回调的缘故还是有点难找,我调试有点问题,这个洞暂时就这样了,不知道理解的对不对,看其他的时候加深理解吧。
参考的是我师傅之前写的文章(虽然现在删掉了)
主要学到的东西:
触发内存重新分配和真正的重新update,其实还是分离的,两者并不同时发生。
就像webkit里面页面里的一个元素被删除了,在cpp层不一定被释放掉了,不是同步的。
其实感觉webkit里UAF多的原因还是,程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存。
回调追着太复杂了。。还是easy,需要很多经验呀。