拾い物のコンパス

まともに書いたメモ

findしてlessする動作を自動化するシェル関数

基本的にfindコマンドで探したファイルはlessなどで閲覧することが多い.大きなプログラムだと,ディレクトリごとに名前が同じファイルがあったりするから,findして目grepしてパスをコピーしてlessするって動作を繰り返しているとうんざりしてくる.そこで自動化するシェル関数を作成した..zshrcに追記すれば使える.
やりたいことはless $(find -name "hoge").複数ヒットしたときもカバーしたいと思うと複雑になってくる.

code

zsh上では動作が確認できている.bashは知らない.

# findで見つけたファイルを表示し,lessする
function fl() {
  if [[ $# -gt 0 ]]; then
    list=`find -type f -name $1 2>/dev/null`
    if [[ $list = "" ]]; then
      line="0"
    else
      line=`echo $list | wc -l`
    fi
    case "$line" in
      "0") echo "No file" ;;
      "1") less $list ;;
      *)   echo -en $list | nl
           arr=(`echo $list | tr -s '\n', ' '`)
           echo -en "file: "
           read num
           less $arr[$num] 2>/dev/null ;;
    esac
  else
    echo "fn \"<file_name>\""
  fi
}

使用例

$ fl "*zshrc"
1  ./git/zsh/StartupFiles/zshrc
2  ./dotfiles/.zshrc
flie: 2 <Enter>

検索する対象はカレントディレクトリ以下.ダブルクウォートで囲めばワイルドカードでの検索もできる.候補が一つしか無いときはそれをlessする.
とりあえずこれを使って,不便なら繰り返し処理を加えるなりしてみようかな.
そもそもIntelIJみたいなIDE使えという話もあるが,手軽に探す分にはこれで十分だろう.コードが冗長な気がするから,気が向いたら書き直す.

参考

bash でカンマ区切り文字を split して変数に代入

既出の値を判定するコードについて思いついたこと(ビンゴゲームをネタに考える)

重複を許さないランダムな数値を一定数生成したいという機会は割りと遭遇する.このときの既出判定についてふと思いついたことを書き残す.
「そんなの一般的に使われている」というツッコミは置いておく.実装についての答えを示すものではなく,「簡単な用途であればこんな実装のほうが綺麗だな」って話.

きっかけ

去年開催された忘年会において,ビンゴゲームが開催された.その際に「白紙を渡すから,1-16の数字の中から,9個選んで3x3に適当に書け」ということになった.
自分で選ぶのも一興だが,時間がないときにはプログラムに任せて一瞬で作成してしまいたい.乱数を生成する事自体はAPIを使えば非常に簡単に生成できる.しかし,既出判定の部分が面倒臭いから簡単にできないものかと考えた.

以降のコードの目的は,ビンゴと同じ1-16の数値を被らないように9個選ぶこととする.変数や分岐などはC言語で考えている.

最初に思いつくやつ

出てきた数字を配列に格納し,生成した数字が出ているかを確認する.実装としては以下のような感じか.

  int buf[9] = {0};            
  int i, j, t, c=0;            
                               
  srand(time(NULL));                  
  for (i=0; i<9; i++) {               
    while (1) {                            // 生成    
      t = rand() % 16 + 1;            
      for (j = 0; j < 9; j++) {            // 既出チェック     
        if (buf[j] == t) c = 1;       
      }         
      if (c == 1) { c = 0; continue; }     // c=1なら,既出でもう一回
      break;                          
    }                        
    c = 0;                            
    buf[i] = t;
    // 出力処理
  }

というように,どうにも入り組んだものになりがちである.他の処理があったりすると,読みたくないコードになってくる.既出判定のオーダーは,配列長nの半分程度だから,O(n/2)かな?
このコードだと取り扱う数字・個数が大きくなっても使えるが,毎回線形探索するのはオーバヘッドばかりかかる.かといって配列を毎回ソートし,二分探索みたいなことするのも複雑だ.ビンゴ生成にそこまでやるかという問いはさておき.

楽そうなコード(フラグ管理してみる, 配列)

フラグ管理してしまえば簡単だなと思った.乱数を添字にして,フラグ配列に1が入っていれば既出,0なら未出扱い.

  int flag[16] = {0};              // フラグ管理

  for (i=0; i<9; i++) {
    tmp = rand() % 16 + 1;        // 1-16の値をランダム生成
    if (flag[i-1] == 1) continue; // 既出だから,もう一回
    flag[i-1] = 1;
    // 出力処理
  }

大分スッキリした.既出判定は一回しか参照しないから,オーダーはO(1)のはず.問題は,取り扱う数値・個数が多くなったときに使用するメモリ量が大きくなりがちなこと.
もうちょい改良してみる.

楽そうなコード(フラグ管理してみる, 省メモリ)

フラグは0/1しかないのに,何もわざわざintの配列を確保する必要はない.変数一個だけでも行けるなと気づいた.ブール変数の配列とどっちがいいんだろうか.

  unsigned int flag = 0;   // 64ビットOSだと,変数の大きさは64ビット
  int i, t;
 
  srand(time(NULL));                                                                                                    
  for (i=0; i<9; i++) {                                                                                                     
    while (1) {                                                                                                             
      t = rand() % 16 + 1;                                                                                                  
      if ( (flag & (1 << (t-1) )) == 0 ) break;  // t-1ビット目に1が立っていれば既出扱い,0なら未出                                                                       
    }                                                                                                                     
    flag |= (1 << (t-1));
    // 出力処理
  }

初見だと少し読みにくい印象.
これもオーダ的にはO(1)か.64個までしか数字を取り扱わないのなら,これでも十分にできる.しかし,64以上の整数や個数を取扱いにくいのが難点. 多倍長的なものを実装するのが良いが,これは実装コストが一気に上がる(といっても配列のどの要素を見るかの処理を加えるくらいだが).GMPを使えば楽にできそうだが,GMP使うと速度が下がりがちだから,要注意か.

完成版ビンゴジェネレータ

現実で使う機会が無さそうだから,ここでソースを挙げておく.

/* bingo.c */
#include <stdio.h>                                                                                                          
#include <stdlib.h>                                                                                                         
#include <time.h>                                                                                                           
                                                                                                                          
int main(void)                                                                                          
{                                                                                                                
  unsigned int flag = 0;                                                                                                
  int i, t=0;                                                                                                               
                                                                                                                            
  printf("=== Bingo Card Generator ===\n"                                                                                 
         "================\n|");
  srand(time(NULL));                                                                                                    
  for (i=0; i<9; i++) {                                                                                                     
    while (1) {                                                                                                             
      t = rand() % 16 + 1;                                                                                                  
      if ( (flag & (1 << (t-1) )) == 0 ) break;                                                                             
    }                                                                                                                     
    flag |= (1 << (t-1));                                                                               
                                                                                                                 
    if ( ((i % 3) == 0) && (i != 0) ) printf("\n|");                                                                    
    if (t >= 10) printf(" %d |", t);                                                                                        
    else         printf("  %d |", t);                                                                                       
  }                                                                                                                       
  printf("\n================\n");
  printf("Good Luck!\n");                                                                                                   
  return 0;                                                                                                                 
}                                                                                                                         
/* E.O.F. */
$ gcc bingo.c -o bingo
$ ./bingo
=== Bingo Card Generator ===                                                                                                
================                                                                                                            
|  2 | 16 |  8 |                                                                                                            
|  1 | 15 | 14 |                                                                                                            
| 13 |  5 |  4 |                                                                                                            
================                                                                                                          
Good Luck!

おわり

そこまで大きな数字・個数を取り扱わないものであれば,既出判定はフラグ管理するのがスッキリしててわかりやすそう.

Linuxで突然startxが起動しなくなったときの対処

startxが上手くいかないときの対処はググると色々出てくるが,ややこしいやつばかり出てきた.そして,それを試しても成功しなかった.最終的に上手くいったことを書き残す.

症状

それまで普通に使えたUbuntuを再起動した途端にGUIで起動しなくなった.$ startxしてみると,
waiting for X server to shut down error setting MTRR (base = 0xd0000000, size = 0x04000000, type = 1) Invalid argument(22)
とでて,終了してしまう.
$ sudo Xorg -configure
してみると,セグフォが起こっていると言われた.

対処

いくつか試してみたが,結局
$ cp /etc/X11/xinit/xinitrc ~/.xinitrc
した後に$ startxしたら動いた.
下手にビデオカードやら何やら入れていないなら,.xinitrcはいじらないほうが良いようだ.

使えれば良いやradare2(静的解析編)

バイナリ関連の機能を一通り提供するフレームワークradare2,通称r2(d2はない模様)による静的解析をざっくりと紹介する.CLIベースの使い方はいろんなサイトが詳しくやっているから,Visual Modeの使い方を中心に解説する.

radare2について

radare2について軽く説明する.
radare2は64ビットオフセット対応の16進バイナリエディタとして,2006年から開発が始まった.途中でportable reversing frameworkになり,更に,いつの間にかradareからradare2へとバージョンアップしている.経緯は知らない.バイナリエディタとして使われているところを見たこともやったこともない・・・.
x86からxtensaなどのマニアックなアーキテクチャまで対応し,基数変換ツール(rax2)やアセンブラ(rasm2),バイナリ比較プログラム(radiff2)など細かい所まで提供してくれる,とても素晴らしいプラットフォーム!
といえば聞こえは良いが,現実には挙動にビックリな地雷を抱えているところがあり,全面的に信用して使うと痛い目にあうというのが正直なところ(個人的な感想).
今までに出会った例としては,デバッグ中のプログラムがセグフォを起こすと,出力が無限ループ起こした.他には,巨大なプログラムを解析しようとすると,関数フラグなどの収集が終わらない.この状態だとCtrl+cしても終了できない.Ctrl+zで停止させて,改めてkillする必要があった.まだ他にもある.

インストール

radare2をインストールしていない場合は,ubuntuならapt/apt-get install radare2,arch linuxならpacman -S radare2で簡単に見つかるはず.ただし,ubuntuのパッケージはバージョンが古いことがあり,機能の一部が実装されていない可能性もある.不安な人は自前ビルドするとよい.自前ビルドは

$ git clone https://github.com/radare/radare2
$ cd radare2 && sys/install.sh
$ sys/install.sh
rootでインストールしたくない人はホームディレクトリにインストールする方法もある.
$ sys/user.sh

で行う.

環境

$ uname -a
Linux poppycompass 4.7.6-1-ARCH #1 SMP PREEMPT Fri Sep 30 19:28:42 CEST 2016 x86_64 GNU/Linux
$ r2 -v
radare2 0.10.2-git 10613 @ linux-little-x86-64 git.0.10.1-156-gcb11996
commit: cb119968dd590fbb0719243eadd7344c7293b996 build: 2016-03-16

現行のバージョンは0.10.6(2016/10/26現在)だから,少し古いバージョンでの解説となる.
カーネルバージョンから,先日発表されたDirty COW(CVE-2016-5195)の餌食になりそうな雰囲気.この後無茶苦茶更新した.

解析対象

解析対象はカラフルな出力を行うプログラム.

/* color.c */
#include <stdio.h>

void red(void)
{
  printf("\x1b[31m");
  puts("red");
}
void blue(void)
{
  printf("\x1b[34m");
  puts("blue");
}
void green(void)
{
  printf("\x1b[32m");
  puts("green");
}
int main(void)
{
  int i=1;
  if (i) {
    red();
    blue();
    green();
  } else {
    return 1;
  }
  return 0;
}
/* E.O.F. */

変なif文が入っているのは,コードに分岐を作りたかったから.
コンパイルは,
$ gcc -o color color.c -Wall

解析

いよいよr2を起動する.r2の操作性を一言で表現するなら,vim + gdb.この辺りは素晴らしい.コマンド体系は独自すぎて少しわかりにくい.

$ r2 ./color
[0x00400450]>

といった感じのシェルが起動する.?でヘルプが見られる.
checksecのような情報が欲しいときは,

[0x00400450]> iI
pic      false
canary   false
nx       true
crypto   false
va       true
intrp    /lib64/ld-linux-x86-64.so.2
bintype  elf
class    ELF64
lang     c
arch     x86
bits     64
machine  AMD x86-64 architecture
os       linux
minopsz  1
maxopsz  16
pcalign  0
subsys   linux
endian   little
stripped false
static   false
linenum  true
lsyms    true
relocs   true
rpath    NONE
binsz    6605

今回はa(nalyze)コマンドで関数部のフラグを特定し,Visual Mode上で解析していく.最低限の解析のみならaa,プログラム中の文字列などを扱いたいなら,aaaコマンドを使う.ただし,stripされているプログラムだとaaaでは非常に時間がかかる.適宜使い分けるとよい.

[0x00400450]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[*] Use -AA or aaaa to perform additional experimental analysis.
[x] Construct a function name for all fcn.* (.afna @@ fcn.*)
fcns: 16
xref: 39
sect: 514
code: 590
covr: 114 %

[0x00400450]> afl # 解析した関数一覧を表示
0x00400000  60  2  sym.imp.__libc_start_main
0x00400400  23  3  sym._init
0x00400430  16  2  sym.imp.puts
0x00400440  16  2  sym.imp.printf
0x00400450  43  1  entry0
0x00400480  50  4  sym.deregister_tm_clones
0x004004c0  58  4  sym.register_tm_clones
0x00400500  28  3  sym.__do_global_dtors_aux
0x00400520  38  4  sym.frame_dummy
0x00400546  32  1  sym.red
0x00400566  32  1  sym.blue
0x00400586  32  1  sym.green
0x004005a6  50  4  sym.main
0x004005e0  101  4  sym.__libc_csu_init
0x00400650  2  1  sym.__libc_csu_fini
0x00400654  9  1  sym._fini

main関数から解析していく.

[0x00400450]> s sym.main # アドレス移動
[0x004005a6]> VV         # Visual Mode起動
[0x004005a6]> VV @ sym.main (nodes 4 edges 4 zoom 100%) BB-NORM mouse:canvas-y movements-speed:5                                 

                                              =-----------------------------------=
                                              | [0x4005a6]                        |
                                              |   ;-- main:                       |
                                              | (fcn) sym.main 50                 |
                                              | ; var int local_0h     @ rbp-0x0  |
                                              | ; var int local_4h     @ rbp-0x4  |
                                              | push rbp                          |
                                              | mov rbp, rsp                      |
                                              | sub rsp, 0x10                     |
                                              | mov dword [rbp - local_4h], 1     |
                                              | cmp dword [rbp - local_4h], 0     |
                                              | je 0x4005d1 ;[a]                  |
                                              =-----------------------------------=
                                                    t f
                                                .---' '-------------------.
                                                |                         |
                                                |                         |
                                          =----------------=      =----------------------=
                                          |  0x4005d1      |      |  0x4005bb            |
                                          | mov eax, 1     |      | call sym.red ;[b]    |
                                          =----------------=      | call sym.blue ;[c]   |
                                              v                   | call sym.green ;[d]  |
                                              |                   | mov eax, 0           |
                                              |                   | jmp 0x4005d6 ;[e]    |
                                              '------------.      =----------------------=
                                                           |          v
                                                           .----------'
                                                           |
                                                           |
                                                       =----------------=
                                                       |  0x4005d6      |
                                                       | leave          |
                                                       | ret            |
                                                       =----------------=

                            

上記のような表示になったと思う.分岐を起点としたブロックに分割されて,表示される.実際にはt(true),f(false)の線は色がついていて,非常に有り難い.vimと同様hjklで移動する.スペースキーを押すと,16進ダンプに跳ぶ.もう一回スペースキーを押すと戻る.
:を押すことでr2のコマンドを実行できる.sym.red関数をテキストとしてディスアセンブルする.

Press <enter> to return to Visual mode.
:> pdf@sym.main # pdf: 関数のディスアセンブル,'@'以降は引数に関数名やアドレスを与える
/ (fcn) sym.red 32
|           ; CALL XREF from 0x004005bb (sym.red)
|           0x00400546      55             push rbp
|           0x00400547      4889e5         mov rbp, rsp
|           0x0040054a      bf64064000     mov edi, str._e_31m         ; str._e_31m
|           0x0040054f      b800000000     mov eax, 0
|           0x00400554      e8e7feffff     call sym.imp.printf
|           0x00400559      bf6a064000     mov edi, 0x40066a
|           0x0040055e      e8cdfeffff     call sym.imp.puts
|           0x00400563      90             nop
|           0x00400564      5d             pop rbp
\           0x00400565      c3             ret

Visual Modeのままsym.redに移動したいときは

Press <enter> to return to Visual mode.
:> s sym.red<Enter><Enter>
[0x004005a6]> VV @ sym.red (nodes 1 edges 0 zoom 100%) BB-NORM mouse:canvas-y movements-speed:5                                  
                                                  =---------------------------=
                                                  | [0x400546]                |
                                                  | (fcn) sym.red 32          |
                                                  | push rbp                  |
                                                  | mov rbp, rsp              |
                                                  | mov edi, str._e_31m       |
                                                  | mov eax, 0                |
                                                  | call sym.imp.printf ;[a]  |
                                                  | mov edi, 0x40066a         |
                                                  | call sym.imp.puts ;[b]    |
                                                  | nop                       |
                                                  | pop rbp                   |
                                                  | ret                       |
                                                  =---------------------------=

uでmain関数に戻る.

終了

qを2回押して,元のプロンプトに戻り,Ctrl+dexitで終了.

最後に

radare2のVisual Modeでの解析を中心とした使い方をざっくり説明した.機能としてはもっと膨大であるが,そのへんの解説はbookやヘルプに譲りたい.objdumpよりも直感的なコードを提供してくれるため,静的解析には非常に心強い.
今年の9月に世界初のradare2カンファレンスr2con-2016が開催されたらしい.来年は是非行って,radare2は「ラダレツゥー」と読んでいいのか確かめたい.しかし,金と言語の壁が厚い.

参考

radare2レポジトリ: GitHub - radare/radare2: unix-like reverse engineering framework and commandline tools

端末出力の色変更方法: C言語でターミナルで表示される文字をカラー表示させる : Serendip - Webデザイン・プログラミング

radare2 book: http://www.radare.org/get/radare.pdf

Dirty COW: Dirty COW (CVE-2016-5195)

csawctf 2016 writeup(forensics)

書くことがないから,最近参加したCSAWCTF 2016のWriteupをメモしておく.分野はforensics.ExploitやWebに比べると日本では人気がない印象があるのが残念.

evidence

evidence.zipという壊れたZIPファイルが与えられる.

$ unzip -l evidence.zip
Archive:  evidence.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
1384576462406025012  1980-10-01 06:01   out/rxo802ayx4
1384576462406025012  1980-10-01 06:01   out/zhatnf5maf
1384576462406025012  1980-10-01 06:01   out/50lshu1ue6
1384576462406025012  1980-10-01 06:01   out/14o803swdq
1384576462406025012  1980-10-01 06:01   out/nppjdd2wdg
1384576462406025012  1980-10-01 06:01   out/e9ydsxwetg
1384576462406025012  1980-10-01 06:01   out/0eetjrxpdm
1384576462406025012  1980-10-01 06:01   out/otuj3jrezt
1384576462406025012  1980-10-01 06:01   out/m70ohrycax
1384576462406025012  1980-10-01 06:01   out/mpi3z4x5jt
1384576462406025012  1980-10-01 06:01   out/meoflsxv4v
1384576462406025012  1980-10-01 06:01   out/8y10tp04b2
1384576462406025012  1980-10-01 06:01   out/eui6ltoijl
---------                     -------
17999494011278325156                     13 files
以下,どうでも良い情報
1384576462406025012  -> 0x133700f4ee13ff34
17999494011278325156 -> 0xf9cb0c701703f5a4
全く意味のない数字のようだ.

作成日時も特に情報は見当たらない.
とりあえず
$ zip -FF evidence.zip --output fix.zip
で修復してみる.先に言っておく,これやった人はさようなら.
この後,
- unzip
- ファイル名がハッシュになっているかと検索
- base64で復号
- とりあえず,文字列を反対にしてみる
- 適当にXORしてみる
- コメントを期待して,unzip -z
- ..etc
をやってみたが,どうにも出てこない. 疲れて,基本に戻ってみようとZIP情報の詳細を見てみる.

$ unzip -vv ./evidence.zip
Archive:  ./evidence.zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
1384576462406025012  Unk:169     908 100% 1980-10-01 06:01 666c6167  out/rxo802ayx4
1384576462406025012  Unk:047     294 100% 1980-10-01 06:01 7b746833  out/zhatnf5maf
1384576462406025012  Unk:158     377 100% 1980-10-01 06:01 5f766931  out/50lshu1ue6
1384576462406025012  Unk:231     552 100% 1980-10-01 06:01 3169346e  out/14o803swdq
1384576462406025012  Unk:223     249 100% 1980-10-01 06:01 5f77335f  out/nppjdd2wdg
1384576462406025012  Unk:238     503 100% 1980-10-01 06:01 6e333364  out/e9ydsxwetg
1384576462406025012  Unk:045     150 100% 1980-10-01 06:01 5f236672  out/0eetjrxpdm
1384576462406025012  Unk:200     273 100% 1980-10-01 06:01 65656c65  out/otuj3jrezt
1384576462406025012  Unk:099     684 100% 1980-10-01 06:01 6666656e  out/m70ohrycax
1384576462406025012  Unk:205     112 100% 1980-10-01 06:01 7daaaaaa  out/mpi3z4x5jt
1384576462406025012  Unk:174     287 100% 1980-10-01 06:01 aaaaaaaa  out/meoflsxv4v
1384576462406025012  Unk:193     172 100% 1980-10-01 06:01 aaaaaaaa  out/8y10tp04b2
1384576462406025012  Unk:199     485 100% 1980-10-01 06:01 aaaaaaaa  out/eui6ltoijl
--------          -------  ---                            -------
17999494011278325156             5046 100%                            13 files

ああ,なるほどと気づいたときには笑った.こんなやり方もあるのか.CRCがASCIIだ. 666c61677b7468335f7669313169346e5f77335f6e3333645f23667265656c656666656e7d
-> flag{th3_vi11i4n_w3_n33d#freeleffen}
flag: flag{th3_vi11i4n_w3_n33d
#freeleffen}
復元して頑張っても無駄だという問題.よくCRCにねじこんだな・・・.

watchword

Canned epic hidden snek flavored cookies have shy gorilla.  
Hint: http://domnit.org/stepic/doc/
Hint: It's not base64, but it uses the Python 3 base64 module
password = password

問題文は知らない単語が多くて理解を投げた.ゴリラが何かしたらしい.
ヒントに沿って,http://domnit.org/stepic/doc/から,stepic-0.3.tar.gzをダウンロード.

$ tar xvf ./stepic-0.3.tar.gz
$ cd stepic-0.3
$ sudo ./setup.py install
動作するためにImageが必要だったため,
$ sudo pip install Image
Image関連でエラーを吐くため,
$ sudo vim /usr/bin/stepic
[-] import Image
[+] from PIL import Image

これで準備は完了.
動画ファイルをダンプしてみると,<data...>というbase64があった.
"aHR0cDovL3N0ZWdoaWRlLnNvdXJjZWZvcmdlLm5ldC8="
-> http://steghide.sourceforge.net/base64
どうやら,steghideが一枚噛んでいるようだ.
配られた動画ファイルを問答無用でforemost.動画は結局見なかった.
$ foremost powpow.mp4
PNGファイルが出てきた.
f:id:poppycompass:20160927151138p:plain
8月にひっそりと日本での販売が終了したオレオの写真.foremostしても何も出てこない.
また,PNGにはsteghideが使えない.よって,stepicの出番.
$ stepic -d -i output/png/00001069.png -o out.jpg --debug
out.jpgというJPG画像を取得する.画像は親子の微笑ましい会話.
f:id:poppycompass:20160927135920j:plain
以下,文章の訳.間違っていたら指摘してください.

> ねえ,お母さん.どうしていとこの名前はダイヤモンドなの?
< おばさんが好きだからよ
> 僕の名前は?
< 私が好きなゴリラよ,Harambe

Harambeは先日子供を助けるために仕方なく射殺されたゴリラの名前.
Harambeはまだ良いが,おばさん,いくら好きでもダイヤモンドはキラキラしすぎじゃないかな・・・
< 閑話休題 >
JPGファイルはsteghideでいける.パスワードが要求されるが,これは問題文で与えられたものを使う.

$ steghide extract -sf ./out.jpg
Enter passphrase:password
wrote extracted data to "base64.txt"
$ cat ./base64.txt
W^7?+dsk&3VRB_4W^-?2X=QYIEFgDfAYpQ4AZBT9VQg%9AZBu9Wh@|fWgua4Wgup0ZeeU}c_3kTVQXa}eE

base64.txtが得られる.文字列の途中に=が入っていたりと,問題通り普通のbase64ではないようだ.
ヒントにpython3のbase64のモジュールを使っているとあったため,
http://docs.python.jp/3/library/base64.html#module-base64
を参考に一通り試していった.結局,base85でエンコードされた文字列だった.

# decrypt.py, python3で実行推奨
# coding: utf-8

import base64
print(base64.b85decode("W^7?+dsk&3VRB_4W^-?2X=QYIEFgDfAYpQ4AZBT9VQg%9AZBu9Wh@|fWgua4Wgup0ZeeU}c_3kTVQXa}eE"))
$ python3 decrypt.py
b'flag{We are fsociety, we are finally free, we are finally awake!}'

base85はASCII内でprintableな文字を全部使って限界に挑戦した符号化のようだ.データ量の膨張が6/4(1.5倍)のbase64に対し,base85は5/4(1.25倍)と,効率が良い.現在ではgitで使われているらしい.

最後に

forensicsはPwnやWebのようにきらびやかなハッカー・クラッカーっぽいものを感じにくい分野だが,解析のためにコマンドの細かいオプションを知ることができるといった面がある.
セキュリティをやっている理由が,色んなことを知りたいからという自分にとってはやってて楽しい分野.
もう少しくらいなら闇が深くても構わない.
ふと思ったが,IT業界って光という単語をあまり使わない気がする.闇の言語(C++とか)は存在するが,光の言語は聞いたことがない(Haskellとか純粋なものがある).その内出るかもしれないが,キャッチコピーが胡散臭すぎて使う気が起きなさそうだ.
そう思ってしまう辺り,IT業界に毒されているのだろう.

参考文献

Stepic - Python image steganography

19.6. base64 — Base16, Base32, Base64, Base85 データの符号化 — Python 3.5.2 ドキュメント

base85について - StoryEdit 開発日誌

OSX(mac)にXmonadをインストールする方法

普段はLinuxXmonadを入れて作業をしているが,短期間だけOSX(mac)を使うこととなった(知っている企業や大学ってやたらとLet's NoteとMacbookが好き.ThinkPadも選択肢に入れて欲しい).普段通りに作業するためにOSXXmonadを入れる方法を模索し,動かすところまではいけた.しかしLinuxほどの快適さはないため,OSX(mac)ではXmonadを使わないほうが良い.代わりにXmonadを意識して作られたAmethystがとりあえずおすすめ.インストールの流れをメモ代わりに書き残す.

環境

$ uname -a
後で入力する(確かDarwinのx64)

OSXXmonadを動作させる仕組み

XmonadX11で動作するよう実装されている.どれくらい依存しているかというと,ソースコードのMain部にガッツリX11関連の関数を使っていて置き換えることが面倒くさそうな位依存している.
このためOSXで動作させるためのアプローチとしてはX11OSX版であるXQuartzをインストールし,このX11上で動かすデスクトップマネージャ(DM)にXmonadを選択し,起動させる流れとなる.

インストール方法

brewのインストール

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

XQuartzのインストール

https://www.xquartz.orgからXQuartz-2.7.9.dmgをダウンロード,インストール(ダブルクリックとContinueを押し続けるだけ).
インストールに成功すると,/Applications/Utilities/XQuartz.app/opt/X11/bin/quartz-wmなどができているはず.デスクトップの検索マークからXQuartzを探し,起動するとDock(デフォルトだと画面下にあるショートカットキーが並んでいるところ)に大文字のXが中心のマークが現れるはず.
XQuartzを選択した状態でデスクトップの左上のXQuartz -> Preferencesで各種設定を変更する.WinodowタブでFull Screen Modeを設定する以外は特にいじらなかった.
~/.xinitrcがあるとエラーが出ることもあるから,あるならrenameしておくほうが良い.

Haskellをインストールする

XmonadをインストールするためにHaskellコンパイラなどをインストールする.She is Haskell.って笑うやつは表に出ろ.すごいH本を朗読してやる.
https://www.haskell.org/platform/mac.htmlからHaskellインストーラを落とす.ダブルクリックしてインストール.
インストールが終わったら,端末から
$ cabal update
でパッケージリストを最新に更新する.

xmonadのインストール

いよいよXmonadをインストールする.
$ cabal install xmonad
でできるはずだが,今回の環境ではXssライブラリがないとエラーが出た.原因は先ほどインストールしたXQuartzが持つX11のヘッダやライブラリ関係がパスに含まれていなことだった.よって今回のコマンドのときだけパスを追加してやれば良い.
$ LIBRARY_PATH=/opt/X11/lib:$LIBRARY_PATH cabal install xmonad
これでXmonadがインストールできるはず.これでも駄目なら,
$ LIBRARY_PATH=/opt/X11/lib:$LIBRARY_PATh cabal install X11
も試して,デバッグしていく.頑張れ.

Xmonad関連インストール

エラーが出るのを防ぐためにX11関連のヘッダを検索パスに含めておく.今回はシンボリックリンクを張る方法を選んだ.

$ ln -s /opt/X11/include/X11 /usr/local/include/X11
$ LIBRARY_PATH=/opt/X11/lib:$LIBRARY_PATH cabal install xmonad-contrib xmobar

XQuarzの設定作成

X11(XQuartz)起動時に使われるWMを設定する.設定は環境変数USERWMに使いたいWMのバイナリパスを入れてやる.

$ mkdir $HOME/.xinitrc.d/
$ vim $HOME/.xinitrc.d/90-xmonad.sh
  #!/bin/sh
  USERWM=$HOME/Library/Haskell/bin/xmonad # xmonadのバイナリが入っているところを指すようにする

xmonadのパスがわからないときは
$ find $HOME -name xmonad -type f 2>/dev/null
で検索する.
あとはX11が起動時に実行されるようにスクリプトに実行権限を付与する.
$ chmod +x ~/.xinitrc.d/90-xmonad.sh
X11で起動するWMをデフォルトのquartz-wmに戻したければ
$ chmod -x ~/.xinitrc.d/90-xmonad.sh
で実行権限をなくせば良い. 以上で基本的な作業は終了.
後は各自xmonad.hs.xmobarrcなどを調整する.

起動

XQuartzが起動した状態で左上のXQuartzタブを押して,Toggle Full Screenを押す.全画面がX11になり,Xmonadが使えるようになる(全画面にしなくても使えるが,全画面のほうがおすすめ).

戻り方

Command+Option+a

問題点

ワークスペースの切り替え,複数の端末操作は通常通り行えるが,/Applications以下のアプリ(ブラウザやメーラ)をXmonad上で実行する方法がわからないといった致命的な問題にぶち当たり,解決策を思いつかなかった.少し使ってみての問題としては以下が挙げられる.

  • OSXではXmonadの相棒urxvtclearコマンドが動かない
  • Chromeなどの/Applications以下にインストールされているアプリを起動させるとXmonadからホストのWMに戻り,そちらで起動してしまう
  • デフォルトで起動するxtermはマルチバイト文字読めない・入力できない
  • xmonad.hs.xmobarrcOSX用に調整しないといけない
  • デバッグしたくない

問題が山積みでここまでしてXmonadに固執する必要もない気がしてきたから,Amethystも試してみた.そしたら比較的快適だったから今回はこっちを使うことにした.

Amethystのインストール

$ brew cask install amethyst
とても簡単.

Amethyst設定

System Preferencesを立ち上げ,Security & Privacy -> Accessibility,左下のロックボタンを押してロックを解除し,'+'を押す.Applications -> Amethystを選択し,権限を与える.詳しくはAmethystのgithubを見るのがわかりやすい.

Amethystの起動

デスクトップの検索からAmethystを検索して起動するだけ. 提供する機能はウィンドウ同士の配置を自動でXmonad風に整形,ウィンドウの選択・入れ替えといったシンプルな部分のみ.
ワークスペースの切り替えはMission Controlによるものになるが,どうにも無駄な動作が多くて疲れる.xmobarを入れて使いたいが,それっぽいものは今の所無いようだ.OSXは余程追い詰められない限り使わないと決心した.
他にはhttps://github.com/arnihermann/osxmonadといったものもあるようだったが,ワークスペースの実装がされていないと書いてあったため使わなかった.

最後に

Linuxはさいきょう

参考サイト

Xmonad on Apple OSX: Xmonad/Using xmonad on Apple OSX - HaskellWiki

xmonad Q&A: Can't build X11 due to ld linking error on OSX 10.9.2 · Issue #24 · xmonad/X11 · GitHub

彼女はちょっと純情すぎるだけなんだ: She is Haskell. : プログラマーが覚えておくべき10の英語フレーズ - NAVER まとめ

Amethyst: GitHub - ianyh/Amethyst: Automatic tiling window manager for OS X à la xmonad and i3.

ELFバイナリのデバッグ時の関数名を読めなくする方法

Defconとかでは問題のELFバイナリをデバッグすると,ユーザ定義関数名だけでなく,ライブラリ関数名も読めなくなっている.どうやるとこんなバイナリを作れるのかを調べてみた.結論から言えば,静的リンクでコンパイルして,stripでデバッグシンボルを消すだけだった.

ライブラリ関数名の読めないプログラム

例えば,/bin/idexecve関数で実行するだけのid.cを考える.

/* id.c */
#include <stdio.h>                                                                                                           
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(void)
{
    char *env[] = {NULL, NULL};
    char *args[] = {"/bin/id", NULL};
    execve("/bin/id", args, env);
    return 0;
}
/* E.O.F. */

ここで使われているライブラリ関数はexecveだけ.これの関数名消去がうまく行くと,以下のようなアセンブラになる.

$ r2 -c 'aa;pdf@main' ./id
[x] Analyze all flags starting with sym. and entry0 (aa)
fcns: 4
xref: 54
sect: 550756
code: 1139
covr: 0 %
/ (fcn) main 68
|           ; var int local_8h     @ rbp-0x8
|           ; var int local_10h    @ rbp-0x10
|           ; var int local_18h    @ rbp-0x18
|           ; var int local_20h    @ rbp-0x20
|           ; DATA XREF from 0x0040082d (main)
|           0x0040092e      55             push rbp
|           0x0040092f      4889e5         mov rbp, rsp
|           0x00400932      4883ec20       sub rsp, 0x20
|           0x00400936      48c745f00000.  mov qword [rbp - local_10h], 0
|           0x0040093e      48c745f80000.  mov qword [rbp - local_8h], 0
|           0x00400946      48c745e04477.  mov qword [rbp - local_20h], str._bin_id
|           0x0040094e      48c745e80000.  mov qword [rbp - local_18h], 0
|           0x00400956      488d55f0       lea rdx, [rbp - local_10h]
|           0x0040095a      488d45e0       lea rax, [rbp - local_20h]
|           0x0040095e      4889c6         mov rsi, rax
|           0x00400961      bf44774800     mov edi, str._bin_id        ; "/bin/id" @ 0x487744
|           0x00400966      e8450e0300     call fcn.004317b0           ; !!! 本来 "call sym.imp.execve"とでる !!!
|           0x0040096b      b800000000     mov eax, 0
|           0x00400970      c9             leave
\           0x00400971      c3             ret

0x00400966の行を見ると,sym.imp.execveと普通にコンパイルしたら出るはずがfcn.004317b0になっている.
普段こんなバイナリが来ると,引数と演算結果を見てどの関数かを特定しているが(それしか知らない),ライブラリ関数を20種類以上使われていると心にヒビが入ってくる.
こいつの関数名復元方法を知るためにまずは作り方を調べた.

この方法では消えない

関数名の削除はデバッグシンボルの削除でできると考えて,まずは以下のコマンドを試してみた.

$ gcc -o id id.c
$ strip --strip-all ./id  # シンボル情報の全削除
$ r2 -c 'aa;pdf@main' ./id
[x] Analyze all flags starting with sym. and entry0 (aa)
fcns: 13
xref: 22
sect: 434
code: 470
covr: 108 %
            ;-- main:
/ (fcn) sym.main 68
|           ; var int local_8h     @ rbp-0x8
|           ; var int local_10h    @ rbp-0x10
|           ; var int local_18h    @ rbp-0x18
|           ; var int local_20h    @ rbp-0x20
|           ; DATA XREF from 0x0040040d (sym.main)
|           0x004004e6      55             push rbp
|           0x004004e7      4889e5         mov rbp, rsp
|           0x004004ea      4883ec20       sub rsp, 0x20
|           0x004004ee      48c745f00000.  mov qword [rbp - local_10h], 0
|           0x004004f6      48c745f80000.  mov qword [rbp - local_8h], 0
|           0x004004fe      48c745e0b405.  mov qword [rbp - local_20h], str._bin_id
|           0x00400506      48c745e80000.  mov qword [rbp - local_18h], 0
|           0x0040050e      488d55f0       lea rdx, [rbp - local_10h]
|           0x00400512      488d45e0       lea rax, [rbp - local_20h]
|           0x00400516      4889c6         mov rsi, rax
|           0x00400519      bfb4054000     mov edi, str._bin_id        ; "/bin/id" @ 0x4005b4
|           0x0040051e      e8adfeffff     call sym.imp.execve         ; !!! 消えてない !!!
|           0x00400523      b800000000     mov eax, 0
|           0x00400528      c9             leave
\           0x00400529      c3             ret

0x0040051eを見ると,関数名が消えてない.
そもそもstripで消えているものはあるかを確認すると

$ gcc -o id id.c
$ file ./id
id: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=55cd7bdcff079fbbad7be7450777c5645fcd4f41, not stripped # stripされてない
$ readelf -a ./id > nonstrip
$ strip --strip-all ./id
$ file ./id
./id: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=55cd7bdcff079fbbad7be7450777c5645fcd4f41, stripped # stripされた
$ readelf -a ./id > stripall
$ diff nonstrip stripall
13c13
<   Start of section headers:          4784 (bytes into file)
---
>   Start of section headers:          2592 (bytes into file)
19c19
<   Number of section headers:         31
---
>   Number of section headers:         29
81,86c81,82
<   [28] .shstrtab         STRTAB           0000000000000000  000011a4
<        000000000000010c  0000000000000000           0     0     1
<   [29] .symtab           SYMTAB           0000000000000000  00000928
<        0000000000000660  0000000000000018          30    48     8
<   [30] .strtab           STRTAB           0000000000000000  00000f88
<        000000000000021c  0000000000000000           0     0     1
---
>   [28] .shstrtab         STRTAB           0000000000000000  00000921
>        00000000000000fc  0000000000000000           0     0     1
170,240d165
< 
< Symbol table '.symtab' contains 68 entries:
<    Num:    Value          Size Type    Bind   Vis      Ndx Name
<      0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
<      1: 0000000000400200     0 SECTION LOCAL  DEFAULT    1 
<      2: 000000000040021c     0 SECTION LOCAL  DEFAULT    2 
<      3: 000000000040023c     0 SECTION LOCAL  DEFAULT    3 
<      4: 0000000000400260     0 SECTION LOCAL  DEFAULT    4 
<      5: 0000000000400280     0 SECTION LOCAL  DEFAULT    5 
<      6: 00000000004002e0     0 SECTION LOCAL  DEFAULT    6 
<      7: 0000000000400320     0 SECTION LOCAL  DEFAULT    7 
<      8: 0000000000400328     0 SECTION LOCAL  DEFAULT    8 
<      9: 0000000000400348     0 SECTION LOCAL  DEFAULT    9 
<     10: 0000000000400360     0 SECTION LOCAL  DEFAULT   10 
<     11: 0000000000400390     0 SECTION LOCAL  DEFAULT   11 
<     12: 00000000004003b0     0 SECTION LOCAL  DEFAULT   12 
<     13: 00000000004003e0     0 SECTION LOCAL  DEFAULT   13 
<     14: 00000000004003f0     0 SECTION LOCAL  DEFAULT   14 
<     15: 00000000004005a4     0 SECTION LOCAL  DEFAULT   15 
<     16: 00000000004005b0     0 SECTION LOCAL  DEFAULT   16 
<     17: 00000000004005bc     0 SECTION LOCAL  DEFAULT   17 
<     18: 00000000004005f0     0 SECTION LOCAL  DEFAULT   18 
<     19: 00000000006006e8     0 SECTION LOCAL  DEFAULT   19 
<     20: 00000000006006f0     0 SECTION LOCAL  DEFAULT   20 
<     21: 00000000006006f8     0 SECTION LOCAL  DEFAULT   21 
<     22: 0000000000600700     0 SECTION LOCAL  DEFAULT   22 
<     23: 00000000006008d0     0 SECTION LOCAL  DEFAULT   23 
<     24: 00000000006008d8     0 SECTION LOCAL  DEFAULT   24 
<     25: 0000000000600900     0 SECTION LOCAL  DEFAULT   25 
<     26: 0000000000600910     0 SECTION LOCAL  DEFAULT   26 
<     27: 0000000000000000     0 SECTION LOCAL  DEFAULT   27 
<     28: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS init.c
<     29: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
<     30: 00000000006006f8     0 OBJECT  LOCAL  DEFAULT   21 __JCR_LIST__
<     31: 0000000000400420     0 FUNC    LOCAL  DEFAULT   14 deregister_tm_clones
<     32: 0000000000400460     0 FUNC    LOCAL  DEFAULT   14 register_tm_clones
<     33: 00000000004004a0     0 FUNC    LOCAL  DEFAULT   14 __do_global_dtors_aux
<     34: 0000000000600910     1 OBJECT  LOCAL  DEFAULT   26 completed.6945
<     35: 00000000006006f0     0 OBJECT  LOCAL  DEFAULT   20 __do_global_dtors_aux_fin
<     36: 00000000004004c0     0 FUNC    LOCAL  DEFAULT   14 frame_dummy
<     37: 00000000006006e8     0 OBJECT  LOCAL  DEFAULT   19 __frame_dummy_init_array_
<     38: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test.c
<     39: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
<     40: 00000000004006e0     0 OBJECT  LOCAL  DEFAULT   18 __FRAME_END__
<     41: 00000000006006f8     0 OBJECT  LOCAL  DEFAULT   21 __JCR_END__
<     42: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 
<     43: 00000000006006f0     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_end
<     44: 0000000000600700     0 OBJECT  LOCAL  DEFAULT   22 _DYNAMIC
<     45: 00000000006006e8     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_start
<     46: 00000000004005bc     0 NOTYPE  LOCAL  DEFAULT   17 __GNU_EH_FRAME_HDR
<     47: 00000000006008d8     0 OBJECT  LOCAL  DEFAULT   24 _GLOBAL_OFFSET_TABLE_
<     48: 00000000004005a0     2 FUNC    GLOBAL DEFAULT   14 __libc_csu_fini
<     49: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
<     50: 0000000000600900     0 NOTYPE  WEAK   DEFAULT   25 data_start
<     51: 0000000000600910     0 NOTYPE  GLOBAL DEFAULT   25 _edata
<     52: 00000000004005a4     0 FUNC    GLOBAL DEFAULT   15 _fini
<     53: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
<     54: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND execve@@GLIBC_2.2.5
<     55: 0000000000600900     0 NOTYPE  GLOBAL DEFAULT   25 __data_start
<     56: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
<     57: 0000000000600908     0 OBJECT  GLOBAL HIDDEN    25 __dso_handle
<     58: 00000000004005b0     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used
<     59: 0000000000400530   101 FUNC    GLOBAL DEFAULT   14 __libc_csu_init
<     60: 0000000000600918     0 NOTYPE  GLOBAL DEFAULT   26 _end
<     61: 00000000004003f0    42 FUNC    GLOBAL DEFAULT   14 _start
<     62: 0000000000600910     0 NOTYPE  GLOBAL DEFAULT   26 __bss_start
<     63: 00000000004004e6    68 FUNC    GLOBAL DEFAULT   14 main
<     64: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
<     65: 0000000000600910     0 OBJECT  GLOBAL HIDDEN    25 __TMC_END__
<     66: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
<     67: 0000000000400390     0 FUNC    GLOBAL DEFAULT   11 _init

サイズは明らかに減っているし,.symtabシンボル情報が消されているようだ.しかし,関数名は消えない.
なんでだろうと思って,知り合いに聞いてみたらあっさり答えが返ってきた.
それによると,execveなどのライブラリ関数は./id外の共有ライブラリに実体がある../idが共有ライブラリの関数を使う場合,プログラム起動時に関数名などのシンボル名を使ってアドレス解決をする.だから,共有ライブラリを使う限りシンボル名が残ってしまう.つまり,静的リンクでコンパイルすればシンボル名を消してもアドレス解決できるようになるということだった.
これがわかれば話は早い.

消し方

$ gcc -static -o id id.c
$ strip -strip-all ./id
$ r2 -c 'aa;pdf@main' ./id
[x] Analyze all flags starting with sym. and entry0 (aa)
fcns: 4
xref: 54
sect: 550756
code: 1139
covr: 0 %
/ (fcn) main 68
|           ; var int local_8h     @ rbp-0x8
|           ; var int local_10h    @ rbp-0x10
|           ; var int local_18h    @ rbp-0x18
|           ; var int local_20h    @ rbp-0x20
|           ; DATA XREF from 0x0040082d (main)
|           0x0040092e      55             push rbp
|           0x0040092f      4889e5         mov rbp, rsp
|           0x00400932      4883ec20       sub rsp, 0x20
|           0x00400936      48c745f00000.  mov qword [rbp - local_10h], 0
|           0x0040093e      48c745f80000.  mov qword [rbp - local_8h], 0
|           0x00400946      48c745e04477.  mov qword [rbp - local_20h], str._bin_id
|           0x0040094e      48c745e80000.  mov qword [rbp - local_18h], 0
|           0x00400956      488d55f0       lea rdx, [rbp - local_10h]
|           0x0040095a      488d45e0       lea rax, [rbp - local_20h]
|           0x0040095e      4889c6         mov rsi, rax
|           0x00400961      bf44774800     mov edi, str._bin_id        ; "/bin/id" @ 0x487744
|           0x00400966      e8450e0300     call fcn.004317b0           ; !!! 消えた !!!
|           0x0040096b      b800000000     mov eax, 0
|           0x00400970      c9             leave
\           0x00400971      c3             ret

というわけで読めなくなった.
読めないバイナリはこうやって作られるのだろう.

最後に

作成したバイナリは静的リンク時に共有ライブラリに含まれていたのであろう文字列が色々含まれて,本来のユーザ定義文字列が探しにくくなる.これはどうにかできないのだろうか.共有ライブラリを片っ端から--strip-allすればいいのだろうか.
次は消した情報を復元する方法を調べたい.情報提供募集中.

参考

ありがとう: 知人