Skip to content

Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

シミュレーションによる FIA

前回は FIA の仕組みを簡単に解説しました. そのうち,電圧を用いた FIA は,一時的に電圧を落とすことで,命令を実行させない仕組みであることを言及しました. 今回は,gdb を使って手動でこれを再現します.

ターゲットプログラム

今回攻撃するプログラムは以下です. 暗証番号が 1234 で,入力が合致していれば 1, 間違っていれば 0 となります.

#include <stdio.h>
#include <stdlib.h>

int check_pin(int entered) {
    int correct = 1234;
    if (entered == correct) {
        return 1;                 /* 認証OK */
    } else {
        return 0;                 /* 拒否   */
    }
}

int main(int argc, char **argv) {
    int entered = (argc > 1) ? atoi(argv[1]) : 0;
    if (check_pin(entered)) {
        printf("GRANTED (entered=%d)\n", entered);
    } else {
        printf("DENIED  (entered=%d)\n", entered);
    }
    return 0;
}

正常系を確認しましょう.

u@u-VirtualBox:~/FIA$ gcc -O0 -g -no-pie -o victim victim.c
u@u-VirtualBox:~/FIA$ ./victim 9999
DENIED  (entered=9999)
u@u-VirtualBox:~/FIA$ ./victim 1234
GRANTED (entered=1234)

これから狙うのは,./victim 999 なのに GRANTED と出させることです.

まず gdb を触る前に,アセンブリで何をしているのかを見ておきます.

u@u-VirtualBox:~/FIA$ objdump -d ./victim

0000000000401156 <check_pin>:
  401156: f3 0f 1e fa          endbr64
  40115a: 55                   push   %rbp
  40115b: 48 89 e5             mov    %rsp,%rbp
  40115e: 89 7d ec             mov    %edi,-0x14(%rbp)
  401161: c7 45 fc d2 04 00 00 movl   $0x4d2,-0x4(%rbp)
  401168: 8b 45 ec             mov    -0x14(%rbp),%eax
  40116b: 3b 45 fc             cmp    -0x4(%rbp),%eax
  40116e: 75 07                jne    401177 <check_pin+0x21>
  401170: b8 01 00 00 00       mov    $0x1,%eax
  401175: eb 05                jmp    40117c <check_pin+0x26>
  401177: b8 00 00 00 00       mov    $0x0,%eax
  40117c: 5d                   pop    %rbp
  40117d: c3  

まず 40116b で入力と 1234 を比較しています. 次に 40116e で前の比較結果を確認し,値が等しくなければ拒否されます. 拒否した場合は 401177 へ飛びます. このとき,仮に何らかの方法で 40116e がスキップされれば,次に401170 が実行されます. 401170 は return 1 なので,認証が合格しています. なので,攻撃者としては,jne という命令を飛ばすことで,入力した PIN が何であっても認証が通るようにしたいと分かります.

gdb によるスキップ

間違った PIN で gdb を起動します.

u@u-VirtualBox:~/FIA$ gdb -q ./victim
Reading symbols from ./victim...

check_pin にブレークポイントを設置します.

(gdb) b check_pin
Breakpoint 1 at 0x401161: file victim.c, line 8.

9999 を PIN として入力します.

(gdb) r 9999
Starting program: /home/u/FIA/victim 9999
Breakpoint 1, check_pin (entered=9999) at victim.c:8
8	    int correct = 1234;

次に disassemble でスキップしたい命令の場所を確認します.

(gdb) disassemble
Dump of assembler code for function check_pin:
   0x0000000000401156 <+0>:	endbr64
   0x000000000040115a <+4>:	push   %rbp
   0x000000000040115b <+5>:	mov    %rsp,%rbp
   0x000000000040115e <+8>:	mov    %edi,-0x14(%rbp)
=> 0x0000000000401161 <+11>:	movl   $0x4d2,-0x4(%rbp)
   0x0000000000401168 <+18>:	mov    -0x14(%rbp),%eax
   0x000000000040116b <+21>:	cmp    -0x4(%rbp),%eax
   0x000000000040116e <+24>:	jne    0x401177 <check_pin+33>
   0x0000000000401170 <+26>:	mov    $0x1,%eax
   0x0000000000401175 <+31>:	jmp    0x40117c <check_pin+38>
   0x0000000000401177 <+33>:	mov    $0x0,%eax
   0x000000000040117c <+38>:	pop    %rbp
   0x000000000040117d <+39>:	ret
End of assembler dump.

先ほども見たように,今回は jne をスキップしたいので,アドレス 40116e と,その次の命令である 401170 を記憶します. さらに,分岐命令の直前まで実行を進めます.

(gdb) break *0x40116e
Breakpoint 2 at 0x40116e: file victim.c, line 9.
(gdb) c
Continuing.

Breakpoint 2, 0x000000000040116e in check_pin (entered=9999) at victim.c:9
9	    if (entered == correct) {

今 jne を実行する直前で止まっていることが分かります. C プログラムとしても if (entered == correct) で止まっていることが分かります. 次に,プログラムカウンタ(次に実行される命令の場所)を確認します.

(gdb) print/x $pc
$1 = 0x40116e
(gdb) set $pc=0x401170
(gdb) print/x $pc
$2 = 0x401170

プログラムカウンタはもともと 0x40116e だったので,次に実行されるのは jne のはずでした. ここで,FIA による命令スキップを模擬する操作として,jne がスキップされ,次の命令が実行されるように変更を加えています. このまま実行してみます.

(gdb) c
Continuing.
GRANTED (entered=9999)
[Inferior 1 (process 4642) exited normally]

入力したのは 9999 で間違った PIN なのに,GRANTED となり,認証と突破できたことが分かります.