ブログ未満のなにか

ブログなのか誰にも分からない

Midnight Sun CTF Finals Flitbip writeup

はじめに

一人writeup advent calendarの7日目です。 1日1問分のwriteupを目標に頑張っていきます。 7日目の問題は、Midnight Sun CTF Finalsで出題された「Flitbip」。 初めての人にオススメです。

カーネルの情報 (セキュリティ機構など)

カーネルのバージョンは、4.17。 smep, smap、kaslr、kptiが無効になっている。

/ $ uname -a
Linux (none) 4.17.0 #1 Fri Jun 15 18:23:33 CEST 2018 x86_64 GNU/Linux
/ $ cat /proc/cpuinfo | grep flags
flags       : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx lm nopl cpuid pni cx16 hypervisor lahf_lm svm 3dnowprefetch vmmcall

解析

この問題では脆弱性を持つシステムコールが新しく追加されている。 親切にも、新しいシステムコールソースコードが存在するので、カーネルのバイナリを解析する必要は特にない。 システムコールは、引数で指定したアドレスの指定したビットを反転できる。 しかし反転できる回数に制限があり、flit_countがMAXFLIT(=1)以上の場合ビットを反転させることができない。

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/syscalls.h>

#define MAXFLIT 1

#ifndef __NR_FLITBIP
#define FLITBIP 333
#endif

long flit_count = 0;
EXPORT_SYMBOL(flit_count);

SYSCALL_DEFINE2(flitbip, long *, addr, long, bit)
{
        if (flit_count >= MAXFLIT)
        {
                printk(KERN_INFO "flitbip: sorry :/\n");
                return -EPERM;
        }

        *addr ^= (1ULL << (bit));
        flit_count++;

        return 0;
} 

exploit

kaslrがないため、カーネルがロードされいているアドレスは固定である。 よって、.text、.data、.bssといった領域が存在するアドレスは固定となっている。

まずは、そのままでは1度しかビット反転させることができないので、flit_countの最上位ビットを反転させ、ビット反転の回数の条件を解決する。 最上位ビットを反転させて1を立てることで、flit_countは負数となり、符号付の比較を突破でき、複数回のビット反転が可能になる。

任意の箇所に複数回のビット反転が行えるので、書き込み可能でグローバルな領域に存在する関数テーブルを書き換えていく。 最近のLinuxカーネルexploit問に対するテクニック集3 - HackMD によると、n_tty_opsという関数テーブルが書き込み可能でグローバルである。 smepが無効となっているので、ユーザ空間に諸々の処理を行う関数へとn_tty_opsのreadを向けさせる。

root権限を取るまでの流れだが、基本に忠実にcredのuid系の値を全て0で書き換える方針を取った。 current_taskは、現在実行しているタスクのtask_structを指していており、グローバル変数である。 current_task->cred->uidといった感じで参照していくことで、uidを書き換えられる。

gist.github.com

f:id:hama7230:20181219233317p:plain