Shi0shishi0

汐鹿生

SECCON 令和 CTF - Writeup

SECCON 令和 CTFに参加しました. 310点を獲得して53位でした.

f:id:echoha610:20190502041502p:plain

解いた問題と競技後に解いた問題についてメモを残します.

Misc

フラグの例は?

問題文にFLAGがある.

bREInWAck

Brainf*ck問題. 問題文より, Brainf*ckで使われる記号が 平成令和「」。 の文字に置き換えられていると推測する.

令和和和和和和和和和和和和和和和和「令和
和和和和令和和和和令和和和和和和和令和和
和和和和令和和平平平平平成」令和和和。令
和和和和和。成成。。平成成成成。成。令令
和和和和和和和和和和和。令和和。平平平和
和和和。令和和。和和和和。令令和和和和和
和和和和和和和。平平平和和和和和和和和和
和和和和。成成成成成成成成。令成成成成成
成成成。令令。成成成成成。成成成成成成。
令和。平平和和。令令令和和和和和和和和和
和。

置き換えを何パターンか適当に試して, 以下のコードをインタプリタに投げていたら当たった.

copy.sh

>++++++++++++++++[>+
++++>++++>+++++++>++
++++>++<<<<<-]>+++.>
+++++.--..<----.-.>>
+++++++++++.>++.<<<+
+++.>++.++++.>>+++++
+++++++.<<<+++++++++
++++.--------.>-----
---.>>.-----.------.
>+.<<++.>>>+++++++++
+.

SECCON{bREIn_WAnic!}

Forensic

元号発表

PDFファイルが与えられる. 元号が隠れて見えない.

PDFファイル内にJPGファイルが埋まっているのでそれを切り出して, 隠れている箇所(令和の形にくり抜かれたQRコード)に重ね合わせる.

出来たQRコードiPhoneのカメラで読むとFLAGが得られた.

SECCON{overlay_overlap_overera}

Binary

和暦の流れ

x86 ELFファイルが与えられる.

hamasho@hamasho-virtual-machine:~$ file reiwa 
reiwa: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.24, BuildID[sha1]=d0002617bfdf8f3834b03237412cc81b88e56bd0, not stripped

実行すると入力を求められる. Crackme系の問題?

hamasho@hamasho-virtual-machine:~$ ./reiwa 
Welcome to New Era! Please answer the name of New Era! a
Something is wrong...

解析してみると, 入力値とハードコードされた値(SHOWAとHEISEIだったと思う)を1文字ずつXORした値が特定の値になるかチェックしている.

1回目の入力で RAYWA , 2回目で HEYSAY と入力するとFLAGが得られる.

hamasho@hamasho-virtual-machine:~$ ./reiwa 
Welcome to New Era! Please answer the name of New Era! RAYWA
Welcome to New Era! Please answer the name of Old Era! HEYSAY
SECCON{M-T-S-H-R}

SECCON{M-T-S-H-R}

令和コード

競技時間中に解けなかったが, 終わった後で解いたので記載する.

runmeというファイル名のELFファイルが与えられる. アーキテクチャMatsushita MN10300 となっている.

hamasho@hamasho-virtual-machine:~$ file runme 
runme: ELF 32-bit LSB executable, Matsushita MN10300, version 1 (SYSV), statically linked, not stripped

Matsushita MN10300についてGoogle先生に聞いてみると, MN103というWikipediaの記事がヒットする. Panasonicマイコンらしい.

https://en.wikipedia.org/wiki/MN103

SECCON + 謎アーキテクチャということで, 昨年のSECCON CTFの時にも使用した仮想マシン(@kozossakaiさんが公開しているやつ)を用意する.

/usr/local/cross2-gcc494/bin配下を確認すると, MN10300に対応していると思われるobjdump等が確認できる.

これを使ってrunmeを逆アセンブルする. 逆アセンブル結果は以下の通り. 何となく既視感.

user@debian:/usr/local/cross2-gcc494/bin$ ./mn10300-elf-objdump -d /home/user/runme_mn10300 

/home/user/runme_mn10300:     file format elf32-mn10300


Disassembly of section .text:

00001400 <_start>:
    1400:       fc dc 20 1c     mov     7200,a0
    1404:       00 00 
    1406:       f2 f0           mov     a0,sp
    1408:       dd 54 01 00     call    155c <_main>,[],0
    140c:       00 00 00 
    140f:       81              mov     d0,d1

00001410 <___r_exit>:
    1410:       80 01           mov     1,d0
    1412:       f0 c0           syscall 
    1414:       f0 fc           rets 

00001416 <___r_read>:
    1416:       80 04           mov     4,d0
    1418:       f0 c0           syscall 
    141a:       f0 fc           rets 

0000141c <___r_write>:
    141c:       80 05           mov     5,d0
    141e:       f0 c0           syscall 
    1420:       f0 fc           rets 

00001422 <___exit>:
    1422:       f8 fe f4        add     -12,sp
    1425:       81              mov     d0,d1
    1426:       80 00           mov     0,d0
    1428:       dd e8 ff ff     call    1410 <___r_exit>,[],0
    142c:       ff 00 00 

0000142f <___read>:
    142f:       f8 fe ec        add     -20,sp
    1432:       f1 e0           mov     d0,a0
    1434:       46 0c           mov     d1,(12,sp)
    1436:       58 20           mov     (32,sp),d0
    1438:       42 10           mov     d0,(16,sp)
    143a:       80 00           mov     0,d0
    143c:       f1 d1           mov     a0,d1
    143e:       dd d8 ff ff     call    1416 <___r_read>,[],0
    1442:       ff 00 00 
    1445:       df 00 14        ret     [],20

00001448 <___write>:
    1448:       f8 fe ec        add     -20,sp
    144b:       f1 e0           mov     d0,a0
    144d:       46 0c           mov     d1,(12,sp)
    144f:       58 20           mov     (32,sp),d0
    1451:       42 10           mov     d0,(16,sp)
    1453:       80 00           mov     0,d0
    1455:       f1 d1           mov     a0,d1
    1457:       dd c5 ff ff     call    141c <___r_write>,[],0
    145b:       ff 00 00 
    145e:       df 00 14        ret     [],20

00001461 <_exit>:
    1461:       f8 fe f4        add     -12,sp
    1464:       cd be ff 00     call    1422 <___exit>,[],0
    1468:       00 

00001469 <_read1>:
    1469:       f8 fe ec        add     -20,sp
    146c:       85 01           mov     1,d1
    146e:       46 0c           mov     d1,(12,sp)
    1470:       3c              mov     sp,a0
    1471:       20 13           add     19,a0
    1473:       f1 d1           mov     a0,d1
    1475:       cd ba ff 00     call    142f <___read>,[],0
    1479:       00 
    147a:       f8 b8 13        movbu   (19,sp),d0
    147d:       df 00 14        ret     [],20

00001480 <_write1>:
    1480:       f8 fe f0        add     -16,sp
    1483:       f8 96 18        movbu   d1,(24,sp)
    1486:       85 01           mov     1,d1
    1488:       46 0c           mov     d1,(12,sp)
    148a:       3c              mov     sp,a0
    148b:       20 18           add     24,a0
    148d:       f1 d1           mov     a0,d1
    148f:       cd b9 ff 00     call    1448 <___write>,[],0
    1493:       00 
    1494:       df 00 10        ret     [],16

00001497 <_getchar>:
    1497:       f8 fe f4        add     -12,sp
    149a:       80 00           mov     0,d0
    149c:       cd cd ff 00     call    1469 <_read1>,[],0
    14a0:       00 
    14a1:       df 00 0c        ret     [],12

000014a4 <_putchar>:
    14a4:       cf 80           movm    [d2],(sp)
    14a6:       f8 fe f4        add     -12,sp
    14a9:       82              mov     d0,d2
    14aa:       80 01           mov     1,d0
    14ac:       89              mov     d2,d1
    14ad:       15              extbu   d1
    14ae:       cd d2 ff 00     call    1480 <_write1>,[],0
    14b2:       00 
    14b3:       88              mov     d2,d0
    14b4:       df 80 10        ret     [d2],16

000014b7 <_gets>:
    14b7:       cf 20           movm    [a2],(sp)
    14b9:       f8 fe f4        add     -12,sp
    14bc:       f1 e2           mov     d0,a2
    14be:       f0 42           movbu   (a2),d0
    14c0:       a0 00           cmp     0,d0
    14c2:       c8 14           beq     14d6 <.L10>

000014c4 <.L12>:
    14c4:       cd d3 ff 00     call    1497 <_getchar>,[],0
    14c8:       00 
    14c9:       a0 0a           cmp     10,d0
    14cb:       c8 0b           beq     14d6 <.L10>
    14cd:       f0 52           movbu   d0,(a2)
    14cf:       49              inc     a2
    14d0:       f0 42           movbu   (a2),d0
    14d2:       a0 00           cmp     0,d0
    14d4:       c9 f0           bne     14c4 <.L12>

000014d6 <.L10>:
    14d6:       80 00           mov     0,d0
    14d8:       df 20 10        ret     [a2],16

000014db <_puts>:
    14db:       cf 20           movm    [a2],(sp)
    14dd:       f8 fe f4        add     -12,sp
    14e0:       f1 e2           mov     d0,a2
    14e2:       f0 42           movbu   (a2),d0
    14e4:       81              mov     d0,d1
    14e5:       15              extbu   d1
    14e6:       a5 00           cmp     0,d1
    14e8:       c8 11           beq     14f9 <.L15>

000014ea <.L17>:
    14ea:       14              extbu   d0
    14eb:       cd b9 ff 00     call    14a4 <_putchar>,[],0
    14ef:       00 
    14f0:       49              inc     a2
    14f1:       f0 42           movbu   (a2),d0
    14f3:       81              mov     d0,d1
    14f4:       15              extbu   d1
    14f5:       a5 00           cmp     0,d1
    14f7:       c9 f3           bne     14ea <.L17>

000014f9 <.L15>:
    14f9:       80 00           mov     0,d0
    14fb:       df 20 10        ret     [a2],16

000014fe <_random_init>:
    14fe:       fc 81 00 18     mov     d0,(1800 <_gp>)
    1502:       00 00 
    1504:       de 00 00        retf    [],0

00001507 <_get_random_value>:
    1507:       fc a4 00 18     mov     (1800 <_gp>),d0
    150b:       00 00 
    150d:       81              mov     d0,d1
    150e:       f8 c1 0d        asl     13,d1
    1511:       f2 24           xor     d1,d0
    1513:       81              mov     d0,d1
    1514:       f8 c5 11        lsr     17,d1
    1517:       f2 24           xor     d1,d0
    1519:       81              mov     d0,d1
    151a:       f8 c1 0f        asl     15,d1
    151d:       f2 24           xor     d1,d0
    151f:       fc 81 00 18     mov     d0,(1800 <_gp>)
    1523:       00 00 
    1525:       de 00 00        retf    [],0

00001528 <_decode>:
    1528:       cf 20           movm    [a2],(sp)
    152a:       f8 fe f4        add     -12,sp
    152d:       f1 e2           mov     d0,a2
    152f:       f0 42           movbu   (a2),d0
    1531:       a0 00           cmp     0,d0
    1533:       c8 24           beq     1557 <.L22>

00001535 <.L24>:
    1535:       cd d2 ff 00     call    1507 <_get_random_value>,[],0
    1539:       00 

0000153a <_destruct_code>:
    153a:       22 fc           add     -4,a2
    153c:       21 ff           add     -1,a1
    153e:       22 ff           add     -1,a2
    1540:       f8 cb 02        asr     2,d3
    1543:       20 ff           add     -1,a0

00001545 <_restruct_code>:
    1545:       cb              nop 
    1546:       cb              nop 
    1547:       cb              nop 
    1548:       cb              nop 
    1549:       cb              nop 
    154a:       f0 46           movbu   (a2),d1
    154c:       f2 21           xor     d0,d1
    154e:       f0 56           movbu   d1,(a2)
    1550:       49              inc     a2
    1551:       f0 42           movbu   (a2),d0
    1553:       a0 00           cmp     0,d0
    1555:       c9 e0           bne     1535 <.L24>

00001557 <.L22>:
    1557:       80 00           mov     0,d0
    1559:       df 20 10        ret     [a2],16

0000155c <_main>:
    155c:       cf 80           movm    [d2],(sp)
    155e:       f8 fe f4        add     -12,sp
    1561:       fc cc a2 8c     mov     -1831433054,d0
    1565:       d6 92 
    1567:       cd 97 ff 00     call    14fe <_random_init>,[],0
    156b:       00 
    156c:       fc cc b1 15     mov     5553,d0
    1570:       00 00 
    1572:       cd 69 ff 00     call    14db <_puts>,[],0
    1576:       00 
    1577:       fc cc 45 15     mov     5445,d0
    157b:       00 00 
    157d:       cd 3a ff 00     call    14b7 <_gets>,[],0
    1581:       00 
    1582:       fc ce 04 18     mov     6148,d2
    1586:       00 00 
    1588:       88              mov     d2,d0
    1589:       cd 9f ff 00     call    1528 <_decode>,[],0
    158d:       00 
    158e:       fc cc c7 15     mov     5575,d0
    1592:       00 00 
    1594:       cd 47 ff 00     call    14db <_puts>,[],0
    1598:       00 
    1599:       88              mov     d2,d0
    159a:       cd 41 ff 00     call    14db <_puts>,[],0
    159e:       00 
    159f:       fc cc c5 15     mov     5573,d0
    15a3:       00 00 
    15a5:       cd 36 ff 00     call    14db <_puts>,[],0
    15a9:       00 
    15aa:       80 00           mov     0,d0
    15ac:       cd b5 fe 00     call    1461 <_exit>,[],0
    15b0:       00 

もう少し調べてみると, 坂井さん(と他著者の方々)が書かれている書籍("31バイトでつくるアセンブラプログラミング: アセンブラ短歌の世界")の情報が見つかった.

この本によると, MN10300には以下のような特徴があるらしい. d1レジスタとd0レジスタを気にすると良さそう?

* 第一引数はd1レジスタで扱う
* 第二引数, 第三引数はスタック上で扱う(SP+12が第二引数, SP+16が第三引数)
* d0レジスタはシステムコール番号の格納等や戻り値に使用する

大熱血!アセンブラ入門のp.447以降にもMN10300についての記載があった. この辺を参考にコードを(雰囲気で)読んで行く.

www.shuwasystem.co.jp

main関数から読んで行く.

0000155c <_main>:
    155c:       cf 80           movm    [d2],(sp)
    155e:       f8 fe f4        add     -12,sp
    1561:       fc cc a2 8c     mov     -1831433054,d0  
    1565:       d6 92 
    1567:       cd 97 ff 00     call    14fe <_random_init>,[],0
    156b:       00 
    156c:       fc cc b1 15     mov     5553,d0
    1570:       00 00 
    1572:       cd 69 ff 00     call    14db <_puts>,[],0
    1576:       00 
    1577:       fc cc 45 15     mov     5445,d0
    157b:       00 00 
    157d:       cd 3a ff 00     call    14b7 <_gets>,[],0
    1581:       00 
    1582:       fc ce 04 18     mov     6148,d2
    1586:       00 00 
    1588:       88              mov     d2,d0
    1589:       cd 9f ff 00     call    1528 <_decode>,[],0
    158d:       00 
    158e:       fc cc c7 15     mov     5575,d0
    1592:       00 00 
    1594:       cd 47 ff 00     call    14db <_puts>,[],0
    1598:       00 
    1599:       88              mov     d2,d0
    159a:       cd 41 ff 00     call    14db <_puts>,[],0
    159e:       00 
    159f:       fc cc c5 15     mov     5573,d0
    15a3:       00 00 
    15a5:       cd 36 ff 00     call    14db <_puts>,[],0
    15a9:       00 
    15aa:       80 00           mov     0,d0
    15ac:       cd b5 fe 00     call    1461 <_exit>,[],0
    15b0:       00 

まず最初に<_random_init>の直前のd0レジスタに変な値をmovしている箇所(mov -1831433054,d0)が気になる.

ここでmovされている値(16進数表記で 0x92D68CA2)で検索すると, 昨年のSECCON CTFのWriteupが幾つかヒットする. これは xorshift で使用されるSeed値らしい. 何となく見覚えがあった.

ファイル名とかその他諸々から薄々思っていたけれど, この問題は昨年のSECCON CTFのRev問の亜種っぽい.

ということは<random_init>と<get_random_value>はxorshiftで擬似乱数を生成するのかな?と予測しつつコードを読む.

Wikipediaの記載と少し違う箇所がある(0x151aの処理"asl 15,d1"が本来は"asl 5,d1"になる?)が, xorshiftっぽいことをやっているという推測で合ってそう.

https://ja.wikipedia.org/wiki/Xorshift

000014fe <_random_init>:
    14fe:       fc 81 00 18     mov     d0,(1800 <_gp>)
    1502:       00 00 
    1504:       de 00 00        retf    [],0
    
00001507 <_get_random_value>:
    1507:       fc a4 00 18     mov     (1800 <_gp>),d0       ; 0x1800 = <_random_init>でコピーした値(0x92D68CA2)を指す
    150b:       00 00 
    150d:       81              mov     d0,d1
    150e:       f8 c1 0d        asl     13,d1   ; 13で算術左シフト
    1511:       f2 24           xor     d1,d0
    1513:       81              mov     d0,d1
    1514:       f8 c5 11        lsr     17,d1   ; 17で算術右シフト
    1517:       f2 24           xor     d1,d0
    1519:       81              mov     d0,d1
    151a:       f8 c1 0f        asl     15,d1   ; 15で算術左シフト
    151d:       f2 24           xor     d1,d0
    151f:       fc 81 00 18     mov     d0,(1800 <_gp>)
    1523:       00 00 
    1525:       de 00 00        retf    [],0

更に見ていくと, <_gets>の実行前にd0に5445( = 0x1545)を格納している.

0x1545は<_restruct_code>の先頭(NOP命令)に当たる. 0x1545以降に標準入力から受け取った値を書き込むと思われる.

00001545 <_restruct_code>:
    1545:       cb              nop
    ...
    ...

重要そうな雰囲気の<_decode>の直前では, 6148( = 0x1804)をd2にmovし, 更にそれをd0にmovしている.

バイナリエディタで 0x1804 - 0x1000 = 0x804 を確認すると, 意味ありげなデータが格納されている. 上記のハードコードされた値を<_decode>で処理すると推測する.

f:id:echoha610:20190502033200p:plain

※ 自分のコメントの中では0x1804以降のデータをenc_dataとしています

<_decode>以降は, 以下のコードでループすると思われる.

00001528 <_decode>:
    1528:       cf 20           movm    [a2],(sp)
    152a:       f8 fe f4        add     -12,sp
    152d:       f1 e2           mov     d0,a2       ; d0 = 0x1804 = enc_data
    152f:       f0 42           movbu   (a2),d0
    1531:       a0 00           cmp     0,d0
    1533:       c8 24           beq     1557 <.L22>

00001535 <.L24>:
    1535:       cd d2 ff 00     call    1507 <_get_random_value>,[],0 ; d0 = xorshift's result
    1539:       00 

0000153a <_destruct_code>:
    153a:       22 fc           add     -4,a2
    153c:       21 ff           add     -1,a1
    153e:       22 ff           add     -1,a2
    1540:       f8 cb 02        asr     2,d3
    1543:       20 ff           add     -1,a0

00001545 <_restruct_code>:
    1545:       cb              nop 
    1546:       cb              nop 
    1547:       cb              nop 
    1548:       cb              nop 
    1549:       cb              nop 
    154a:       f0 46           movbu   (a2),d1         ; copy enc_data[i] to d1
    154c:       f2 21           xor     d0,d1           ; xor
    154e:       f0 56           movbu   d1,(a2)
    1550:       49              inc     a2              ; i++
    1551:       f0 42           movbu   (a2),d0         ; copy enc_data[i] to d0
    1553:       a0 00           cmp     0,d0            ; if enc_data[i] == 0x00, exit
    1555:       c9 e0           bne     1535 <.L24>

00001557 <.L22>:
    1557:       80 00           mov     0,d0
    1559:       df 20 10        ret     [a2],16

動きは何となくイメージできるが, 以下の部分はよく分からない. <_destruct_code>はレジスタの値を操作しており, これを実行すると0x1804に格納されているデータを先頭から処理できなくなる.

<gets>で受け取った値で<restruct_code>以降が上書きされることから, <destruct_code>で行った処理と逆の処理を<restruct_code>の先頭で行うのでは?と推測する(関数名も destruct_code と restruct_code なので)

<_gets>から何が入力されるのか分からなかったので, 以下のコードは一旦無視して考えることにした.

これを除くと, <_get_random_value>を実行し, 実行結果とenc_data[i]とXORしているだけに見える.

0000153a <_destruct_code>:
    153a:       22 fc           add     -4,a2
    153c:       21 ff           add     -1,a1
    153e:       22 ff           add     -1,a2
    1540:       f8 cb 02        asr     2,d3
    1543:       20 ff           add     -1,a0

00001545 <_restruct_code>:
    1545:       cb              nop 
    1546:       cb              nop 
    1547:       cb              nop 
    1548:       cb              nop 
    1549:       cb              nop 

Wikipediaに載っているxorshift32の5を15に変えたものを使ってSolverを書きたい.

uint32_t xor(void) {
  static uint32_t y = 2463534242;
  y = y ^ (y << 13); y = y ^ (y >> 17);
  return y = y ^ (y << 5);
}

ctftimeから去年のSECCON CTFのWriteupを色々眺めて, 以下のWriteupを参考にさせてもらいつつ, Solverを書いた.

github.com

#!/usr/bin/env python3

import sys

seed = 0x92d68ca2
mask = 0xffffffff

# xorshift32
def get_rand():
    global seed
    seed ^= (seed << 13) & mask
    seed ^= (seed >> 17) & mask
    seed ^= (seed << 15) & mask
    return seed

# enc_data[] => from 0x804 to 0x81d
enc_data = [0x50, 0x77, 0x1f, 0xd3, 0x1e, 0x1f, 0xfb, 0xb4, 0x20, 0x5e, 0xc5, 0xa5, 0xdc, 0x2e, 0xf7, 0x62, 0xd3, 0xae, 0x16, 0x1e, 0x82, 0x44, 0x09, 0x72, 0xba, 0x39]

for i in range(len(enc_data)):
    key = get_rand() & 0xff
    tmp_flag = enc_data[i] ^ key
    sys.stdout.write(chr(tmp_flag))

FLAGは以下の通り.

C:\Users\hamasho\Desktop>python solver_seccon_reiwactf_runme.py
SECCON{MachineCodeOfREIWA}

<_gets>で受け取る入力とかは結局何だったんだろう?と思っていたら@kusano_kさんのブログで当該箇所について言及されていた.

qiita.com

標準入力から REIWA と入力するとNOPの箇所が上書きされて, 良い感じに実行可能なコードになるっぽい. 予想通り, 入力されたコードが<_destruct_code>と逆の動きをすると思われる.

仮想マシンでrunmeを実行してREIWAを与えてみたらFLAGが出力された.

user@debian:/usr/local/cross2-gcc494/bin$ ./mn10300-elf-run /home/user/runme_mn10300 
Input restruct code.
REIWA
FLAG: SECCON{MachineCodeOfREIWA}