tls

[TOC] 参考文章

对于tcache

  • 修改线程tcache变量
  • 在tls区域,有一个线程变量tcache,如果能用largebin attack修改tcache变量,也可以控制tcache的分配。其实这个地方指向的位置本身就是heapbase+0x10,就是tcache_perthread_struct结构从counts开始的地方
  • 具体做法:
pwndbg> search -p 0x55555555b010  0x55555555b010这个值实际上是heapbase+0x10
Searching for value: b'\x10\xb0UUUU\x00\x00'
pwn             0x555555558260 0x55555555b010
[anon_7ffff7fc2] 0x7ffff7fc7538 0x55555555b010

pwndbg> hex 0x7ffff7fc7538-0x7ffff7ddc000
+0000 0x1eb538

stack_guard

找的方法和pointer guard一样

pointer guard

  • 结构体的类型为struct pthread,我们称其为一个thread descriptor,该结构体的第一个域为tchhead_t类型,其定义如下:
typedef struct
{
  void *tcb;        /* Pointer to the TCB.  Not necessarily the
               thread descriptor used by libpthread.  */
  dtv_t *dtv;
  void *self;       /* Pointer to the thread descriptor.  */
  int multiple_threads;
  int gscope_flag;
  uintptr_t sysinfo;
  uintptr_t stack_guard; 0x28
  uintptr_t pointer_guard; 0x30
  unsigned long int vgetcpu_cache[2];
  /* Bit 0: X86_FEATURE_1_IBT.
     Bit 1: X86_FEATURE_1_SHSTK.
   */
  unsigned int feature_1;
  int __glibc_unused1;
  /* Reservation of some values for the TM ABI.  */
  void *__private_tm[4];
  /* GCC split stack support.  */
  void *__private_ss;
  /* The lowest address of shadow stack,  */
  unsigned long long int ssp_base;
  /* Must be kept even if it is no longer used by glibc since programs,
     like AddressSanitizer, depend on the size of tcbhead_t.  */
  __128bits __glibc_unused2[8][4] __attribute__ ((aligned (32)));

  void *__padding[8];
} tcbhead_t;

  • 可以看到这两个宏利用pointer_guard分别对指针进行了加密和解密操作,加密由一次异或以及一次bitwise rotate组成。加密使用的key来自fs:[offsetof(tcbhead_t, pointer_guard)], 利用pointer_guard进行加密的过程可以表示为rol(ptr ^ pointer_guard, 0x11, 64),解密的过程为ror(enc, 0x11, 64) ^ pointer_guard
  • 因此我们写入数据的时候用这个加密方式就可以了 eg:
#bin会给数字转化为2进制,但是会带上0b,因此要取[2:]
def ROL(content, key):
    tmp = bin(content)[2:].rjust(64, '0')
    return int(tmp[key:] + tmp[:key], 2)
ROL(gadget_addr ^ pointer_guard, 0x11)

远程爆破tls

参考此篇博客 在有些情况下,我们需要知道 TLS 的位置以便于修改其中的一些值,例如 canary、tcache struct ptr、key。

但是在大多数情况下,远程的ld.so距离 libc 基址的位置不确定与本地 patchelf 之后的不一致,这时候优先可以考虑起一个 docker 来看看偏移(因为题目大多数都是用 docker 搭建的), 推荐这个项目

如果还是不正确,则说明我们需要爆破偏移,这里的爆破偏移和往常的不同,因为这个偏移值的量在每次连接的时候都是固定的,所以我们只要循环执行足够多的次数,那么就一定能够攻击成功。而且偏移的变化值往往在地址末尾的第四个、第五个数(末三位不变),我们只需要考虑爆破这两个数字即可。

这里提供一个爆破的模版,可以参考一下

for x in range(0x10):
    for y in range(0x10):
        try:
            libc_base = 0x1234
            offset = 0x6 << 20
            offset += x << 16
            offset += y << 12
            ld_base = libc_base + offset
            log.success("try offset:\t" + hex(offset))
            # your code
            sh.interactive()
        except EOFError:
            sh.close()