简体   繁体   English

从可利用程序运行时出现Shellcode Segmentation Fault错误

[英]Shellcode Segmentation Fault error when run from exploitable program

BITS 64
section     .text
global      _start

_start:
jmp short two

one:
pop     rbx
xor     al,al
xor     cx,cx
mov     al,8
mov     cx,0755
int     0x80
xor     al,al
inc     al
xor     bl,bl                               
int     0x80

two:
call one
db  'H'`

This is my assembly code. 这是我的汇编代码。 Then I used two commands. 然后,我使用了两个命令。 "nasm -f elf64 newdir.s -o newdir.o" and "ld newdir.o -o newdir".I run ./newdir and worked fine but when I extracted op code and tried to test this shellcode using following c program . “ nasm -f elf64 newdir.s -o newdir.o”和“ ld newdir.o -o newdir”。我运行./newdir并工作正常,但是当我提取op代码并尝试使用以下c程序测试此shellcode时。 It is not working(no segmentation fault).I have compiled using cmd gcc newdir -z execstack 它不起作用(没有分段错误)。我已经使用cmd gcc newdir -z execstack进行了编译

#include <stdio.h>
char sh[]="\xeb\x16\x5b\x30\xc0\x66\x31\xc9\xb0\x08\x66\xb9\xf3\x02\xcd\x80\x30\xc0\xfe\xc0\x30\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x48";
void main(int argc, char **argv)
{
    int (*func)();
    func = (int (*)()) sh;
    (int)(*func)();
}

objdump -d newdir objdump -d newdir

newdir:     file format elf64-x86-64


Disassembly of section .text:

0000000000400080 <_start>:
  400080:   eb 16                   jmp    400098 <two>

0000000000400082 <one>:
  400082:   5b                      pop    %rbx
  400083:   30 c0                   xor    %al,%al
  400085:   66 31 c9                xor    %cx,%cx
  400088:   b0 08                   mov    $0x8,%al
  40008a:   66 b9 f3 02             mov    $0x2f3,%cx
  40008e:   cd 80                   int    $0x80
  400090:   30 c0                   xor    %al,%al
  400092:   fe c0                   inc    %al
  400094:   30 db                   xor    %bl,%bl
  400096:   cd 80                   int    $0x80

0000000000400098 <two>:
  400098:   e8 e5 ff ff ff          callq  400082 <one>
  40009d:   48                      rex.W

when I run ./a.out , I am getting something like in photo. 当我运行./a.out时,我得到的是类似照片的东西。 I am attaching photo because I cant explain what is happening. 我附上照片,因为我无法解释发生了什么。 image 图片

PS- My problem is resolved. PS-我的问题解决了。 But I wanted to know where things was going wrong. 但是我想知道哪里出了问题。 So I used debugger and the result is below ` 所以我用了调试器,结果如下

(gdb) list
1   char shellcode[] = "\xeb\x16\x5b\x30\xc0\x66\x31\xc9\xb0\x08\x66\xb9\xf3\x02\xcd\x80\x30\xc0\xfe\xc0\x30\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x48";
2   int main (int argc, char **argv)
3   {
4           int (*ret)();              
5           ret = (int(*)())shellcode; 
6                                      
7           (int)(*ret)();   
8           }           (gdb) disassemble main
Dump of assembler code for function main:
   0x00000000000005fa <+0>: push   %rbp
   0x00000000000005fb <+1>: mov    %rsp,%rbp
   0x00000000000005fe <+4>: sub    $0x20,%rsp
   0x0000000000000602 <+8>: mov    %edi,-0x14(%rbp)
   0x0000000000000605 <+11>:    mov    %rsi,-0x20(%rbp)
   0x0000000000000609 <+15>:    lea    0x200a20(%rip),%rax        # 0x201030 <shellcode>
   0x0000000000000610 <+22>:    mov    %rax,-0x8(%rbp)
   0x0000000000000614 <+26>:    mov    -0x8(%rbp),%rdx
   0x0000000000000618 <+30>:    mov    $0x0,%eax
   0x000000000000061d <+35>:    callq  *%rdx
   0x000000000000061f <+37>:    mov    $0x0,%eax
   0x0000000000000624 <+42>:    leaveq 
   0x0000000000000625 <+43>:    retq   
End of assembler dump.
(gdb) b 7
Breakpoint 1 at 0x614: file test.c, line 7.
(gdb) run
Starting program: /root/Desktop/Progs/shell/a.out 

Breakpoint 1, main (argc=1, argv=0x7fffffffe2b8) at test.c:7
7           (int)(*ret)();   
(gdb) info registers rip
rip            0x555555554614   0x555555554614 <main+26>
(gdb) x/5i $rip
=> 0x555555554614 <main+26>:    mov    -0x8(%rbp),%rdx
   0x555555554618 <main+30>:    mov    $0x0,%eax
   0x55555555461d <main+35>:    callq  *%rdx
   0x55555555461f <main+37>:    mov    $0x0,%eax
   0x555555554624 <main+42>:    leaveq 
(gdb) s
(Control got stuck here, so i pressed ctrl+c)
^C
 Program received signal SIGINT, Interrupt.
 0x0000555555755048 in shellcode ()
(gdb) x/5i 0x0000555555755048 
=> 0x555555755048 <shellcode+24>:   callq  0x555555755032 <shellcode+2>
   0x55555575504d <shellcode+29>:   rex.W add %al,(%rax)
   0x555555755050:  add    %al,(%rax)
   0x555555755052:  add    %al,(%rax)
   0x555555755054:  add    %al,(%rax)

Here is the debugging information. 这是调试信息。 I am not able to find where the control goes wrong.If need more info please ask. 我找不到控件出了问题的地方。如果需要更多信息,请询问。

Below is a working example using x86-64; 以下是使用x86-64的工作示例; which could be further optimized for size. 可以进一步优化尺寸。 That last 0x00 null is ok for the purpose of executing the shellcode. 为了执行Shellcode,最后0x00 null可以。

assemble & link: 组装和链接:

$ nasm -felf64 -g -F dwarf pushpam_001.s -o pushpam_001.o && ld pushpam_001.o -o pushpam_001

Code: 码:

BITS 64
section     .text
global      _start

_start:
jmp short two

one:
        pop   rdi               ; pathname

        xor rax, rax
        add al, 85              ; creat syscall 64-bit Linux

        xor rsi, rsi
        add si, 0755            ; mode - octal
        syscall

        xor rax, rax
        add ax, 60
        xor rdi, rdi
        syscall

two:
call one
db  'H',0

objdump: objdump的:

pushpam_001:     file format elf64-x86-64
0000000000400080 <_start>:
  400080:       eb 1c                   jmp    40009e <two>
0000000000400082 <one>:
  400082:       5f                      pop    rdi
  400083:       48 31 c0                xor    rax,rax
  400086:       04 55                   add    al,0x55
  400088:       48 31 f6                xor    rsi,rsi
  40008b:       66 81 c6 f3 02          add    si,0x2f3
  400090:       0f 05                   syscall
  400092:       48 31 c0                xor    rax,rax
  400095:       66 83 c0 3c             add    ax,0x3c
  400099:       48 31 ff                xor    rdi,rdi
  40009c:       0f 05                   syscall
000000000040009e <two>:
  40009e:       e8 df ff ff ff 48 00               

             .....H.

encoding extraction: There are many other ways to do this. 编码提取:还有许多其他方法可以做到这一点。

$ for i in `objdump  -d pushpam_001  | grep "^ " | cut -f2`; do echo -n '\x'$i; done; echo
\xeb\x1c\x5f\x48\x31\xc0\x04\x55\x48\x31\xf6\x66\x81\xc6\xf3\x02\x0f\x05\x48\x31\xc0\x66\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdf\xff\xff\xff\x48\x00\x.....H.

C shellcode.c - partial C shellcode.c-部分

...
unsigned char code[] = \
"\xeb\x1c\x5f\x48\x31\xc0\x04\x55\x48\x31\xf6\x66\x81\xc6\xf3\x02\x0f\x05\x48\x31\xc0\x66\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdf\xff\xff\xff\x48\x00";  
...

final: 最后:

./shellcode 

--wxrw---t 1 david david     0 Jan 31 12:25 H   

If int 0x80 in 64-bit code was the only problem, building your C test with gcc -fno-pie -no-pie would have worked, because then char sh[] would be in the low 32 bits of virtual address space, so system calls that truncate pointers to 32 bits would still work. 如果只有64位代码中的int 0x80是唯一的问题,那么使用gcc -fno-pie -no-pie构建C测试就可以了,因为char sh[]会位于虚拟地址空间的低32位,因此将指针截断为32位的系统调用仍然有效。

Run your program under strace to see what system calls it actually makes. strace下运行程序,以查看其实际执行的系统调用。 (Except that strace decodes int 0x80 syscalls incorrectly in 64-bit code, decoding as if you'd used the 64-bit syscall ABI. The call numbers and arg registers are different.) But at least you can see the system-call return values (which will be -EFAULT for 32-bit creat with a truncated 64-bit pointer.) (除了strace在64位代码中无法正确解码int 0x80系统调用,就像您使用64位syscall ABI一样进行解码。调用编号和arg寄存器不同。)但是至少您可以看到系统调用返回值(对于带有截断的64位指针的32位creat ,将为-EFAULT 。)

You can also just gdb to single-step and check the system call return values. 您也可以只通过gdb单步执行并检查系统调用返回值。 Having strace decode the system-call inputs is really nice, though, so I'd recommend porting your code to use the 64-bit ABI, and then it would just work. 但是,使用strace解码系统调用输入确实非常好,因此我建议移植您的代码以使用64位ABI,然后它就可以工作了。

Also, it would actually be able to exploit 64-bit processes where the buffer overflow is in memory at an address outside the low 32 bits. 同样,它实际上将能够利用64位进程,其中缓冲区溢出在低32位之外的地址在内存中。 (eg like the stack). (例如,像堆栈一样)。 So yes, you should really stop using int 0x80 or stick to 32-bit code. 所以是的,您应该真正停止使用int 0x80或坚持使用32位代码。


You're also depending on registers being zeroed before your code runs , like they are on process startup, but not when called from anywhere else. 您还依赖在代码运行之前将寄存器清零 ,就像它们在进程启动时一样,但不从其他任何地方调用时。

xor al,al before mov al,8 is completely pointless, because xor-zeroing al doesn't clear upper bytes. xor al,almov al,8之前是完全没有意义的,因为xor调零al不会清除高位字节。 Writing 32-bit registers clears the upper 32, but not writing 8 or 16 bit registers. 写入32位寄存器会清除高32位,但不会写入8位或16位寄存器。 And if it did, you wouldn't need the xor-zeroing before using mov which is also write-only. 如果确实如此,则在使用mov (也是只写操作)之前,无需进行异或归零。

If you want to set RAX=8 without any zero bytes in the machine code, you can 如果要在机器代码中将RAX = 8设置为不包含任何零字节,则可以

  • push 8 / pop rax (3 bytes) push 8 / pop rax (3个字节)
  • xor eax,eax / mov al,8 (4 bytes) xor eax,eax / mov al,8 (4字节)
  • Or given a zeroed rcx register, lea eax, [rcx+8] (3 bytes) 或给定的rcx寄存器清零, lea eax, [rcx+8] (3个字节)

Setting CX to 0755 isn't so simple, because the constant doesn't fit in an imm8. 将CX设置为0755并不是那么简单,因为常量不适合imm8。 Your 16-bit mov is a good choice (or would have been if you'd zeroed rcx first. 您的16位mov是一个不错的选择(或者如果您rcx调零的话,也可以这样rcx

xor  ecx,ecx
lea  eax, [rcx+8]   ; SYS_creat = 8 from unistd_32.h
mov  cx, 0755       ; mode
int  0x80           ; invoke 32-bit ABI

xor  ebx,ebx
lea  eax, [rbx+1]   ; SYS_exit = 1
int  0x80

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM