シェルコードに挑戦

流れに流れて今回はシェルコードに挑戦してみようと思います。

まずシェルコードとは

ソフトウェアのセキュリティホールを利用するペイロードとして使われるコード断片である。
By Wikipedia

いわゆるfragmentだ。

とりあえず参考サイトのままに権限を取得するコードを試しでやってみた。
実行結果しか載せないけど、うまくいかずに結構時間はかかってる。
アセンブラむずかしい。

[takeken@32bittest]$ id                                                                                                           [/home/takeken/asm]
uid=500(takeken) gid=500(takeken)
[takeken@32bittest]$ ./shelltest.o
sh-4.1# zsh
[root@32bittest]$ id
uid=0(root) gid=500(takeken)
[root@32bittest]$

 

さくっとrootが取れちゃうような、こういうものらしいけど・・・、サンプルがこういうのしかなく試しただけであって、自分でアセンブラを使ってなにか作りたいのさ。

アセンブリに詳しくなりそうだし、まずはやっぱりHello Worldだわよね。

とりあえず前回作ったHello Worldのコードを修正する。
-nostdlibオプションを使うように、短いコードにする。

[takeken@32bittest]# cat hello2.s                                               [/home/takeken/asm]
.att_syntax noprefix
.global _start

_start:
   mov             $1, %ebx
   mov             $4, %eax
   mov        $msg, %ecx
   mov             $15,    %edx
   int        $0x80

   mov             $1,     %eax
   int               $0x80

.data
   msg:    .ascii "Hello, World!!\n"


[takeken@32bittest]# ./hello2                                                   [/home/takeken/as
Hello, World!!
[takeken@32bittest]#

 

実行可能となりました。
だけどこのままではダメなようで、シェルコードはヌルバイトをなくさないといけないらしい。

レジスタのことはまだいまいち分かってないので、パズル式にやってみる。
pushとaddを使って、ヌルにならないようにしてみた。

[takeken@32bittest]# cat hello2.s                                               [/home/takeken/asm]
.att_syntax noprefix
.global _start

_start:
   mov             $1, %ebx
   mov             $4, %eax
   mov     $msg, %ecx
   mov             $15,    %edx
   int     $0x80

   mov             $1,     %eax
   int     $0x80

.data
   msg:    .ascii "Hello, World!!\n"

[takeken@32bittest]# objdump -d hello                                                        

hello:     file format elf32-i386


Disassembly of section .text:

080480b8 <_start>:
 80480b8:       31 d2                   xor    %edx,%edx
 80480ba:       6a 01                   push   $0x1
 80480bc:       53                      push   %ebx
 80480bd:       83 c0 04                add    $0x4,%eax
 80480c0:       b9 d0 90 04 08          mov    $0x80490d0,%ecx
 80480c5:       83 c2 0f                add    $0xf,%edx
 80480c8:       cd 80                   int    $0x80
 80480ca:       b0 01                   mov    $0x1,%al
 80480cc:       cd 80                   int    $0x80
[takeken@32bittest]# ./hello2                                                 
Hello, World!!

 

ご覧のようにヌルバイトがなくなり、実行も可能な状態となった。

だが

Cの形式にしてみて実行しても

objdump -M att -d hello2 | grep '^ ' | cut -f2 | perl -pe 's/(\w{2})\s+/\\x\1/g'
#include <stdio.h>

char shellcode[] = "\x31\xd2\x6a\x01\x53\x83\xc0\x04\xb9\xd0\x90\x04\x08\x83\xc2\x0f\xcd\x80\xb0\x01\xcd\x80";

int main()
{
  printf("sizeof(shellcode) == %d\n", sizeof(shellcode));
  (*(void (*)())shellcode)();
}
$ ./aa.o                                                                                                       [/home/takeken/asm]
sizeof(shellcode) == 23
zsh: segmentation fault  ./aa.o

 

 

だめらしい。

うーん・・・。

どうしたもんかなぁ。

普通にデバッグしてみたものの

(gdb) next
Single stepping until exit from function shellcode,
which has no line number information.

Program received signal SIGSEGV, Segmentation fault.
0x0804966c in shellcode ()
(gdb) next
Single stepping until exit from function shellcode,
which has no line number information.

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.

 

 

うーん、分からん。

nasm式というのがあるらしいので、そっちでやってみることにした。

[takeken@32bittest nasm]$ cat hello.asm
section .text
global _start

BITS 32

_start:
   xor     edx, edx
   mov     ebx, 1    ; stdout
     mov             eax, 4          ; write
   mov     ecx, msg    ; address
     mov             edx, len
     int 0x80

   mov     eax, 1      ; sys_exit
     int 0x80

section .data
   msg     db      'hello, world',0xa
   len     equ     $ - msg

[takeken@32bittest nasm]$ ./hello
hello, world

 

 

とりあえず実行できるようにはなったものの。

[takeken@32bittest nasm]$ objdump -d hello.o

hello.o:     file format elf32-i386


Disassembly of section .text:

00000000 <_start>:
  0:   31 d2                   xor    %edx,%edx
  2:   bb 01 00 00 00          mov    $0x1,%ebx
  7:   b8 04 00 00 00          mov    $0x4,%eax
  c:   b9 00 00 00 00          mov    $0x0,%ecx
  11:   ba 0d 00 00 00          mov    $0xd,%edx
  16:   cd 80                   int    $0x80
  18:   b8 01 00 00 00          mov    $0x1,%eax
  1d:   cd 80                   int    $0x80

そりゃそうなんだが、ヌルヌルなのであった。

さっきの要領でヌル除去を進めていくんだが、うまくいかないらしい。

もうダメかなーなんて思ってたらNASMは変数タイプを保存しないというのを発見して閃いた。

[takeken@32bittest nasm]$ cat hello.asm
section .text
global _start

BITS 32

foo equ 1
bar equ 4

_start:
   xor     edx, edx
   mov       bl,foo    ; stdout
     mov             al,bar          ; write
   mov     ecx, msg    ; address
     mov             dl, len
     int 0x80

   mov     al, 1      ; sys_exit
     mov             ah, 0
     int 0x80

section .data
   msg     db      'hello, world',0xa
   len     equ     $ - msg

[takeken@32bittest nasm]$ nasm -f elf hello.asm && ld -s -o hello hello.o
[takeken@32bittest nasm]$ ./hello
hello, world
[takeken@32bittest nasm]$ objdump -d hello

hello:     file format elf32-i386


Disassembly of section .text:

08048080 <.text>:
 8048080:       31 d2                   xor    %edx,%edx
 8048082:       b3 01                   mov    $0x1,%bl
 8048084:       b0 04                   mov    $0x4,%al
 8048086:       b9 98 90 04 08          mov    $0x8049098,%ecx
 804808b:       b2 0d                   mov    $0xd,%dl
 804808d:       cd 80                   int    $0x80
 804808f:       b0 01                   mov    $0x1,%al
 8048091:       b4 00                   mov    $0x0,%ah
 8048093:       cd 80                   int    $0x80

おおおー。

 

[takeken@32bittest nasm]$ ./mike
セグメンテーション違反です orz

ダメか。

なにか違うところに原因があるのかもしれない。

 

参考サイト

技術メモ帖 shellcode を書く

The Netwide Assembler: NASM

ももいろテクノロジー  Linux x86用のシェルコードを書いてみる