HackIT CTF 2017 rev200 writeup

問題

Description: You haxor, come on you little sciddie… debug me, eh? You fucking little lamer… You fuckin’ come on, come debug me! I’ll get your ass, you jerk! Oh, you IDA monkey! Fuck all you and your tools! Come on, you scum haxor, you try to reverse me? Come on, you asshole!!

rev200.efiというefiファイルが与えられる。

$ file rev200.efi 
rev200.efi: PE32+ executable (DLL) (EFI application) x86-64 (stripped to external PDB), for MS Windows

解く

シンボル付きのバイナリなので読みやすい。efi_main()を見ると、InitializeLib()でライブラリの初期化とInput()で入力の受け付けを行っているのが分かる。Inputの直後でalgo()をcallしていてあやしい。

f:id:TAKEmaru:20170904044949p:plain

algo()を見るとCorrect、Wrongを出力していて、ここで入力値のチェックをしていると分かる。algo()をhopperでデコンパイルすると以下のようになった。

int algo(int arg0) {
    rsp = rsp - 0x180;
    rbp = rsp + 0x80;
    arg_32 = arg0;
    for (arg_-1 = 0x0; arg_-1 <= 0x13; arg_-1 = arg_-1 + 0x1) {
            *(int32_t *)(rbp + sign_extend_32(arg_-1) * 0x4 + 0x90) = *(int8_t *)(arg_32 + sign_extend_64(arg_-1)) & 0xff & 0xff;
    }

    for (arg_-1 = 0x0; arg_-1 <= 0x13; arg_-1 = arg_-1 + 0x1) { // check input
            *(int32_t *)(rbp + sign_extend_32(arg_-1) * 0x4 + 0x90) = (((*(int32_t *)(rbp + sign_extend_32(arg_-1) * 0x4 + 0x90) ^ 0xc) + 0x6 ^ 0xd) + 0x7 ^ 0xe) + 0x8;
            *(int32_t *)(rbp + sign_extend_32(arg_-1) * 0x4 + 0x40) = (((*(int32_t *)(rbp + sign_extend_32(arg_-1) * 0x4 + 0x40) ^ 0xf) + 0x9 ^ 0x10) + 0xa ^ 0x11) + 0xb;
    }

    for (arg_28 = 0x0; arg_28 <= 0x13; arg_28 = arg_28 + 0x1) {
            *(int32_t *)(rbp + sign_extend_32(arg_28) * 0x4 + 0xffffffffffffffa0) = *(int32_t *)(rbp + sign_extend_32(arg_28) * 0x4 + 0x90);
    }

    for (arg_29 = 0x14; arg_29 <= 0x27; arg_29 = arg_29 + 0x1) {
            *(int32_t *)(rbp + sign_extend_64(arg_29 + 0xffffffffffffffec) * 0x4 + 0x40) = *(int8_t *)(arg_32 + sign_extend_64(arg_29)) & 0xff & 0xff;
    }
    for (arg_-1 = 0x14; arg_-1 <= 0x27; arg_-1 = arg_-1 + 0x1) {
            *(int32_t *)(rbp + sign_extend_32(arg_-1) * 0x4 + 0xffffffffffffffa0) = *(int32_t *)(rbp + sign_extend_32(arg_-1 - 0x14) * 0x4 + 0x40);
    }

    if (memcmp(&var_-96, correct, 0xa0) == 0x0) {
            rax = Print(u"\nCorrect\n", correct, 0xa0, r9);
    }
    else {
            rax = Print(u"\nWrong\n", correct, 0xa0, r9);
    }
    return rax;
}

入力値をxorしたあと、memcmpでcorrectと比較している。 総当りで解くsolverを書いた。

# coding: UTF-8

correct = [104, 60, 121, 113, 99, 124, 129, 146, 146, 101, 101, 147, 146, 73, 121, 146, 56, 108, 60, 111, 123, 135, 88, 85, 137, 90, 89, 126, 126, 107, 135, 108, 87, 108, 107, 88, 89, 90, 90, 111];
flag = ""

for i in range(20):
    for j in range(256):
        if ((((((j ^ 0xc) + 6) ^ 0xD) + 7) ^ 0xe) + 8) == correct[i]:
            flag += chr(j)

for i in range(20):
    for j in range(256):
        if (((((j ^ 0xf) + 9) ^ 0x10) + 10) ^ 0x11) + 11 == correct[i+20]:
            flag += chr(j)
   
print(flag)

z3pyを使っても解くことができる

# coding: UTF-8

from z3 import *

FLAG_LENGTH = 40
correct = [104, 60, 121, 113, 99, 124, 129, 146, 146, 101, 101, 147, 146, 73, 121, 146, 56, 108, 60, 111, 123, 135, 88, 85, 137, 90, 89, 126, 126, 107, 135, 108, 87, 108, 107, 88, 89, 90, 90, 111];

s = Solver()
text = []

for i in range(FLAG_LENGTH):
    text.append(BitVec(i, 8))
    s.add(And(text[i] >= 0x20, text[i] < 0x7f)) # in printable ascii

for i in range(20):
    s.add(((((((text[i] ^ 0xC) + 6) ^ 0xD) + 7) ^ 0xE) + 8) == correct[i])

for i in range(20, 40):
    s.add((((((text[i] ^ 0xF) + 9) ^ 0x10) + 10) ^ 0x11) + 11 == correct[i])

if s.check() == sat:
    m = s.model()
    flag = ''
    for i in range(FLAG_LENGTH):
        flag += chr(int(str(m[text[i]])))

    print(flag)
$ python solver.py 
h4ck1t{ff77af3cf8d4e1e67c4300aeb5ba6344}

HackIT CTF 2017 rev150 writeup

問題

Description: Looks like this packer can not unpack what has been packed :( There are 2 mistakes in unpacking procedure. It leads to the error. Try to fix unpacker and figure out what is inside.

packerとpackedという2つの64bitのELFが与えられる。

解く

packedを動かすと0x6b5489でSegmentation faultで落ちる。

$ ulimit -c unlimited
$ strace ./packed 
execve("./packed", ["./packed"], [/* 74 vars */]) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (コアダンプ)
$ gdb ./packed core
Reading symbols from ./packed...(no debugging symbols found)...done.
[New LWP 10428]
Core was generated by `./packed'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00000000006b5489 in ?? ()
gdb-peda$ 

落ちた0x6b5489周辺を見てみると、0x6b548fでraxがindexのカウンタとして使われているのに0x6b5489ではrdxをindexにしていておかしいような気がしてくる。 0x6b548fでraxに+2しているのもあやしい。

f:id:TAKEmaru:20170902220558p:plain

0x6b548f、0x6b5489を以下のようにhopperで変更する。

00000000006b5489         xor        byte [rax], dl                              ; CODE XREF=_6b5468+46
00000000006b548b         ror        rdx, 0x8
00000000006b548f         add        rax, 0x1
00000000006b5493         cmp        rax, rcx

実行するとまたも Segmentation faultで落ちる。

$ ulimit -c unlimited
$ strace ./modify_packed 
execve("./modify_packed", ["./modify_packed"], [/* 74 vars */]) = 0
uname({sys="Linux", node="ubuntu", ...}) = 0
brk(0)                                  = 0x25ec000
brk(0x25ed1c0)                          = 0x25ed1c0
arch_prctl(ARCH_SET_FS, 0x25ec880)      = 0
readlink("/proc/self/exe", "/mnt/hgfs/ctf2/hackit/rev150/Bro"..., 4096) = 55
brk(0x260e1c0)                          = 0x260e1c0
brk(0x260f000)                          = 0x260f000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 9), ...}) = 0
write(1, "The hardest part is overcome.\n", 30The hardest part is overcome.
) = 30
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (コアダンプ)
$ gdb ./modify_packed core
Reading symbols from ./modify_packed...(no debugging symbols found)...done.
[New LWP 10774]
Core was generated by `./modify_packed'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000000000042194a in ?? ()
gdb-peda$ bt
#0  0x000000000042194a in ?? ()
#1  0x0000000000400bde in ?? ()
#2  0x0000000000400e63 in ?? ()
#3  0x00000000004010ee in  ()
#4  0x00000000004009ba in  ()

The hardest part is overcomeと出力された。やったぜ。 バックトレース結果にある0x400bdbを見ていく。

gdb-peda$ x/19i 0x400bdb
=>0x400bdb:  idiv   edi
   0x400bdd:    inc    DWORD PTR [rbp+0x481375c0]
   0x400be3:    mov    eax,DWORD PTR [rbp-0x10]
   0x400be6:    add    rax,0x10
   0x400bea:    mov    rax,QWORD PTR [rax]
   0x400bed:    mov    rdi,rax
   0x400bf0:    call   0x400aae <">
   0x400bf5:    mov    rax,QWORD PTR [rbp-0x10]
   0x400bf9:    add    rax,0x8
   0x400bfd:    mov    rax,QWORD PTR [rax]
   0x400c00:    lea    rsi,[rip+0x88fa9]        # 0x489bb0
   0x400c07:    mov    rdi,rax
   0x400c0a:    call   0x400370
   0x400c0f:    test   eax,eax
   0x400c11:    jne    0x400c18
   0x400c13:    call   0x400b39
   0x400c18:    mov    eax,0x0
   0x400c1d:    leave  
   0x400c1e:    ret    

0x400c00でrsiに0x489bb0のstringを入れている。

gdb-peda$ x/s 0x489bb0
0x489bb0:   "decr"

直後でcallされている0x400b39があやしいので見る。

gdb-peda$ x/33i 0x400b39
   0x400b39:    push   rbp
   0x400b3a:    mov    rbp,rsp
   0x400b3d:    sub    rsp,0x10
   0x400b41:    lea    rdi,[rip+0x2b1558]        # 0x6b20a0
   0x400b48:    call   0x417bb0
   0x400b4d:    mov    DWORD PTR [rbp-0x8],eax
   0x400b50:    mov    eax,DWORD PTR [rbp-0x8]
   0x400b53:    mov    DWORD PTR [rbp-0x4],eax
   0x400b56:    cmp    DWORD PTR [rbp-0x4],0x0
   0x400b5a:    js     0x400ba6
   0x400b5c:    mov    eax,DWORD PTR [rbp-0x4]
   0x400b5f:    movsxd rdx,eax
   0x400b62:    lea    rax,[rip+0x2b1537]        # 0x6b20a0
   0x400b69:    movzx  ecx,BYTE PTR [rdx+rax*1]
   0x400b6d:    mov    eax,DWORD PTR [rbp-0x8]
   0x400b70:    sub    eax,0x1
   0x400b73:    sub    eax,DWORD PTR [rbp-0x4]
   0x400b76:    movsxd rdx,eax
   0x400b79:    lea    rax,[rip+0x2b1520]        # 0x6b20a0
   0x400b80:    movzx  eax,BYTE PTR [rdx+rax*1]
   0x400b84:    xor    eax,ecx
   0x400b86:    xor    eax,0xffffff80                # 実質0x80
   0x400b89:    mov    BYTE PTR [rbp-0x9],al
   0x400b8c:    mov    eax,DWORD PTR [rbp-0x4]
   0x400b8f:    movsxd rdx,eax
   0x400b92:    lea    rax,[rip+0x2b1507]        # 0x6b20a0
   0x400b99:    movzx  ecx,BYTE PTR [rbp-0x9]
   0x400b9d:    mov    BYTE PTR [rdx+rax*1],cl
   0x400ba0:    sub    DWORD PTR [rbp-0x4],0x1
   0x400ba4:    jmp    0x400b56
   0x400ba6:    nop
   0x400ba7:    leave  
   0x400ba8:    ret    

0x400b62、0x400b79、 0x400b92でraxに代入されているアドレス 0x6b20a0 はフラグの一部を含んでいた。この関数内でxorすることでフラグを生成しているようである。

gdb-peda$ x/s 0x6b20a0
0x6b20a0:   "\225\300Ӆ\302\307ˉ\260\201\302ޚ\255\223\223\263\204\227\300\264\214گ_no_se1ler_sn0rom{t1kc4h"

フラグ生成処理を書き起こす。

# coding: UTF-8

flag_parts = bytearray(open("packed","rb").read())
offset = 0xb20a0

first_flag = ""
latter_flag =  str(flag_parts[offset+0x30/2: offset + 0x30])

for i in range(0x30/2):
  first_flag += chr(flag_parts[offset+i] ^flag_parts[offset+0x2f-i] ^ 0x80)
    
print((first_flag + latter_flag)[::-1])

フラグが出た

$ python solver.py 
h4ck1t{mor0ns_rel1es_on_p4ck3r5_vari0rs_d03sn0t}

最初のバイナリを修正するのがちょっとエスパーなかんじがする。意図的に動作しないバイナリが与えられる問題は珍しい。

TomoriNaoから同人誌を出してコミケで頒布した。

所属しているCTFチームのTomoriNaoから同人誌を出してコミケで頒布しました。僕はMetasploitから学ぶ複アーキ シェルコード入門という題で記事を書きました。

f:id:TAKEmaru:20170813233314p:plain

無事完売した模様。

まさか200部も売れないだろうな〜と思っていたのでよかったですね。

複アーキ環境をシュッと使えるシェルスクリプトを書いた

リバースエンジニアリングであったりシェルコード開発であったりで、複数のアーキテクチャのバイナリが動作する環境が必要になることがある。

その環境構築と使い勝手をいいかんじにしてくれるシェルスクリプトを書いた。qcow2イメージの取り回しをよくする。

github.com

install.shとssh_config.sh、launch.shという3つのファイルがあって、名前通りinstall.shがインストールを、ssh_config.shがsshのconfigの追加を、launch.shがVMの起動を行う。launch.shを使うことで以下のようにqcow2イメージを起動できる。この例ではmipsbeのVMを立ち上げている。sshでログインしてmipsbeの環境を使える。

$ ./launch.sh mipsbe
When it will be ready, just connect (credentials: root/root): ssh root@localhost -p2222
Launching Mipsbe machine

qcow2イメージ毎にポートを変えているが、これはSSHWARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!を避けるためである。 QEMUの起動時に-net nic -net user,hostfwd=tcp::2222-:22を指定することで、ポートフォワーディングの設定をやり、ポートをイメージ毎に変えている。

qemu-system-mipsel -M malta -kernel ~/qcow/mipsle/vmlinux-3.2.0-4-4kc-malta -hda ~/qcow/mipsle/debian_wheezy_mipsel_standard.qcow -append "root=/dev/sda1 console=tty0" -net nic -net user,hostfwd=tcp::2222-:22

ssh_config.shであらかじめconfigを追加しておくので、ssh アーキ名VMに接続できる。

#!/bin/bash

echo '
Host mipsle
  HostName localhost
  User root
  Port 2222

Host mipsbe
  HostName localhost
  User root
  Port 2223

Host armle
  HostName localhost
  User root
  Port 2224

Host aarch64
  HostName localhost
  User root
  Port 2225
' >> ~/.ssh/config

echo 'Add VM host to SSH config'

これで複アーキ環境をシュッと使えるようになって便利になった。

gitの使ってないブランチをガッと消すコマンド書いた

バイトで開発するときブランチ切ってプルリクを送る感じで開発してて、ブランチ多いのが気になってたけど、ひとつひとつ消すのもだるいので書いた。

alias rmbr="git branch --merged | grep -v '*' | grep -v 'master' | xargs -I % git branch -d %"
alias rmbrall="git branch | grep -v '*' | grep -v 'master' | xargs -I % git branch -D %"

rmbr はマージ済みのブランチを、rmbrall は全てのブランチを消す。 git branchでブランチ名をゲットしたあと、grep -v で使用中のブランチと master ブランチを除外して、git branch -d/D で消す。 git branch -d はマージ済みのブランチを消して、-D はマージされたかどうかにか関わらず消す。

便利。

ちなみに、.gitconfigのaliasはこんなかんじにしている。

[alias]
    a = add
    br = branch
    cl = clone
    cm = commit
    co = checkout
    ga = log --graph --all --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
    gr = log --graph --date=short --decorate=short --pretty=format:'%Cgreen%h %Creset%cd %Cblue%cn %Cred%d %Creset%s'
    l = log
    ph = push
    st = status
[color]
    diff = auto
    status = auto
    branch = auto
    grep = auto

SHA2017 CTF round Teaser NoComment..Again writeup

問題文

There might be something hiddenq in this file, can you find it?

.exeが与えられる。

解く

動かしてみると、PerlApp Utilityのライセンスが評価版で期限が切れたという旨のメッセージが出る。 これはPCの時間をn日前にすることで回避できる。この問題を落とした直後はこのようなメッセージは出なかった。

f:id:TAKEmaru:20170628044608p:plain

CTFが終わった直後は以下のように動作した。正しいパスワードをいれればいいっぽい?

f:id:TAKEmaru:20170628070904p:plain

また、IDAのString ViewでPealApp::hogeという文字列が散見されるのでPerl製のプログラムだと分かる。scriptnameというあやしい文字列があったので、その付近を見ていく。

f:id:TAKEmaru:20170628070646p:plain

このGraph Viewのすぐしたに"-e#line1"という文字列をstackに積んでいた。Pealのeオプションはワンライナーを実行するので、Perlのコードがこの付近でスタックに出てきそうだと当たりをつける。

f:id:TAKEmaru:20170628070701p:plain

0x40549AでSHA_Teaser_bin100.plというファイル名へのアドレスがスタックに積まれた。

f:id:TAKEmaru:20170628070746p:plain

0x4054C7でPerlのソースへのアドレスがスタックに積まれた。

f:id:TAKEmaru:20170628070630p:plain

これのPerlスクリプトをHex Viewから取り出すと以下のようになる。

#!/usr/bin/perl
 
print "\n[*] SHA2017 CTF Teaser BIN 100\n".
      "      No comment... again...\n\n";
 
$secret = "SuPeRS3cr3tsTuFf?!";
 
print "[*] What is the secret? ";
$answer = <STDIN>
chomp($answer);
 
if ($answer eq $secret) {
  print "\n[*] Yes, that is correct! However that was not the goal of this challen
    ge.\n".
        "    Did you know that compiled code does not contain any comments?\n";
} else {
    print "\n[*] Isn't that cute...but it is WRONG!\n";
}
 
#  W e l l ,    w e l l,    t h e r e    i s    a    c o m m e n t    a g a i n  
    . . . .
#
# .----------------. .----------------. .----------------. .----------------. 
#| .--------------. | .--------------. | .--------------. | .--------------. |
#| |  _________   | | |   _____      | | |      __      | | |    ______    | |
#| | |_   ___  |  | | |  |_   _|     | | |     /  \     | | |  .' ___  |   | |
#| |   | |_  \_|  | | |    | |       | | |    / /\ \    | | | / .'   \_|   | |
#| |   |  _|      | | |    | |   _   | | |   / ____ \   | | | | |    ____  | |
#| |  _| |_       | | |   _| |__/ |  | | | _/ /    \ \_ | | | \ `.___]  _| | |
#| | |_____|      | | |  |________|  | | ||____|  |____|| | |  `._____.'   | |
#| |              | | |              | | |              | | |              | |
#| '--------------' | '--------------' | '--------------' | '--------------' |
# '----------------' '----------------' '----------------' '----------------' 
# .----------------. .----------------. .----------------. .----------------.
#| .--------------. | .--------------. | .--------------. | .--------------. |
#| |       __     | | |     ____     | | |    ______    | | |     ____     | |
#| |     .' _/    | | |   .'    '.   | | |  .' ____ '.  | | |   .' __ '.   | |
#| |     | |      | | |  |  .--.  |  | | |  | (____) |  | | |   | (__) |   | |
#| |    < <       | | |  | |    | |  | | |  '_.____. |  | | |   .`____'.   | |
#| |     | |_     | | |  |  `--'  |  | | |  | \____| |  | | |  | (____) |  | |
#| |     `.__\    | | |   '.____.'   | | |   \______,'  | | |  `.______.'  | |
#| |              | | |              | | |              | | |              | |
#| '--------------' | '--------------' | '--------------' | '--------------' |
# '----------------' '----------------' '----------------' '----------------' 
# .----------------. .----------------. .----------------. .----------------.
#| .--------------. | .--------------. | .--------------. | .--------------. |
#| |   ______     | | |     ____     | | |   _    _     | | |   _______    | |
#| |  |_   _ \    | | |   .'    '.   | | |  | |  | |    | | |  |  ___  |   | |
#| |    | |_) |   | | |  |  .--.  |  | | |  | |__| |_   | | |  |_/  / /    | |
#| |    |  __'.   | | |  | |    | |  | | |  |____   _|  | | |      / /     | |
#| |   _| |__) |  | | |  |  `--'  |  | | |      _| |_   | | |     / /      | |
#| |  |_______/   | | |   '.____.'   | | |     |_____|  | | |    /_/       | |
#| |              | | |              | | |              | | |              | |
#| '--------------' | '--------------' | '--------------' | '--------------' |
# '----------------' '----------------' '----------------' '----------------' 
# .----------------. .----------------. .----------------. .----------------.
#| .--------------. | .--------------. | .--------------. | .--------------. |
#| |      __      | | |      __      | | |     __       | | |    ______    | |
#| |     /  \     | | |     /  \     | | |    /  |      | | |  .' ____ '.  | |
#| |    / /\ \    | | |    / /\ \    | | |    `| |      | | |  | (____) |  | |
#| |   / ____ \   | | |   / ____ \   | | |     | |      | | |  '_.____. |  | |
#| | _/ /    \ \_ | | | _/ /    \ \_ | | |    _| |_     | | |  | \____| |  | |
#| ||____|  |____|| | ||____|  |____|| | |   |_____|    | | |   \______,'  | |
#| |              | | |              | | |              | | |              | |
#| '--------------' | '--------------' | '--------------' | '--------------' |
# '----------------' '----------------' '----------------' '----------------' 
# .----------------. .----------------. .----------------. .----------------.
#| .--------------. | .--------------. | .--------------. | .--------------. |
#| |    ______    | | |   _______    | | |   _______    | | |     ______   | |
#| |   / ____ `.  | | |  |  ___  |   | | |  |  _____|   | | |   .' ___  |  | |
#| |   `'  __) |  | | |  |_/  / /    | | |  | |____     | | |  / .'   \_|  | |
#| |   _  |__ '.  | | |      / /     | | |  '_.____''.  | | |  | |         | |
#| |  | \____) |  | | |     / /      | | |  | \____) |  | | |  \ `.___.'\  | |
#| |   \______.'  | | |    /_/       | | |   \______.'  | | |   `._____.'  | |
#| |              | | |              | | |              | | |              | |
#| '--------------' | '--------------' | '--------------' | '--------------' |
# '----------------' '----------------' '----------------' '----------------' 
# .----------------. .----------------. .----------------. .----------------.
#| .--------------. | .--------------. | .--------------. | .--------------. |
#| |     ______   | | |   _______    | | |     ____     | | |      __      | |
#| |   .' ___  |  | | |  |  ___  |   | | |   .'    '.   | | |     /  \     | |
#| |  / .'   \_|  | | |  |_/  / /    | | |  |  .--.  |  | | |    / /\ \    | |
#| |  | |         | | |      / /     | | |  | |    | |  | | |   / ____ \   | |
#| |  \ `.___.'\  | | |     / /      | | |  |  `--'  |  | | | _/ /    \ \_ | |
#| |   `._____.'  | | |    /_/       | | |   '.____.'   | | ||____|  |____|| |
#| |              | | |              | | |              | | |              | |
#| '--------------' | '--------------' | '--------------' | '--------------' |
# '----------------' '----------------' '----------------' '----------------' 
# .----------------. .----------------. .----------------. .----------------.
#| .--------------. | .--------------. | .--------------. | .--------------. |
#| |  _________   | | |     __       | | |     ______   | | |   _    _     | |
#| | |_   ___  |  | | |    /  |      | | |   .' ___  |  | | |  | |  | |    | |
#| |   | |_  \_|  | | |    `| |      | | |  / .'   \_|  | | |  | |__| |_   | |
#| |   |  _|  _   | | |     | |      | | |  | |         | | |  |____   _|  | |
#| |  _| |___/ |  | | |    _| |_     | | |  \ `.___.'\  | | |      _| |_   | |
#| | |_________|  | | |   |_____|    | | |   `._____.'  | | |     |_____|  | |
#| |              | | |              | | |              | | |              | |
#| '--------------' | '--------------' | '--------------' | '--------------' |
# '----------------' '----------------' '----------------' '----------------' 
# .----------------. .----------------. .----------------. .----------------. 
#| .--------------. | .--------------. | .--------------. | .--------------. |
#| |     ____     | | |      __      | | |     ____     | | |    ______    | |
#| |   .' __ '.   | | |     /  \     | | |   .'    '.   | | |   / ____ `.  | |
#| |   | (__) |   | | |    / /\ \    | | |  |  .--.  |  | | |   `'  __) |  | |
#| |   .`____'.   | | |   / ____ \   | | |  | |    | |  | | |   _  |__ '.  | |
#| |  | (____) |  | | | _/ /    \ \_ | | |  |  `--'  |  | | |  | \____) |  | |
#| |  `.______.'  | | ||____|  |____|| | |   '.____.'   | | |   \______.'  | |
#| |              | | |              | | |              | | |              | |
#| '--------------' | '--------------' | '--------------' | '--------------' |
# '----------------' '----------------' '----------------' '----------------' 
# .----------------. .----------------. .----------------. .----------------. 
#| .--------------. | .--------------. | .--------------. | .--------------. |
#| |      __      | | |  ________    | | |    _____     | | |     ______   | |
#| |     /  \     | | | |_   ___ `.  | | |   / ___ `.   | | |   .' ___  |  | |
#| |    / /\ \    | | |   | |   `. \ | | |  |_/___) |   | | |  / .'   \_|  | |
#| |   / ____ \   | | |   | |    | | | | |   .'____.'   | | |  | |         | |
#| | _/ /    \ \_ | | |  _| |___.' / | | |  / /____     | | |  \ `.___.'\  | |
#| ||____|  |____|| | | |________.'  | | |  |_______|   | | |   `._____.'  | |
#| |              | | |              | | |              | | |              | |
#| '--------------' | '--------------' | '--------------' | '--------------' |
# '----------------' '----------------' '----------------' '----------------'  
# .----------------. .----------------. 
#| .--------------. | .--------------. |
#| |    ______    | | |     __       | |
#| |  .' ____ \   | | |    \_ `.     | |
#| |  | |____\_|  | | |      | |     | |
#| |  | '____`'.  | | |       > >    | |
#| |  | (____) |  | | |     _| |     | |
#| |  '.______.'  | | |    /__.'     | |
#| |              | | |              | |
#| '--------------' | '--------------' | 

アスキーアートがflagだった。FLAG{098B047AA19375CC70AE1C48A03AD2C6}

SHA2017 CTF round Teaser Website Attack writeup

SHA2017 CTF round Teaser Website Attack (Network) のwriteup。ひさしぶりにCTFをやった。

問題文

Our website received an attack in 2013, we managed to capture the attack in this pcap. Can you find out if we leaked some sensitive information?

website-attack.pcapが与えられる。

decode as

IPAという見慣れないプロトコルのパケットがあるが、中身を見ると明らかにHTTPなのでwiresharkDecode As...でHTTPとして認識させる。

f:id:TAKEmaru:20170621031102p:plain

通信内容

WiresharkExport Objects → HTTPでHTTPのリクエストをざっと見てみる。

f:id:TAKEmaru:20170621045208p:plain

action=search&words=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&sort=stockの後のリクエストはwordsパラメータが少しずつ変化しているのでBlind SQL Injectionっぽい通信だなと分かる。

通信の流れ

  1. GET /
  2. GET bootstrap.min.css
  3. GET /?action=search&words=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&sort=stock
  4. redirect GET /?action=display&what=e4146d4252bafb3b38212df186497a7479d5e95af4796e7573a65e6849952032e4146d4252bafb3b38212df186497a7479d5e95af4796e7573a65e6849952032e4146d4252bafb3b38212df186497a7479d5e95af4796e7573a65e6849952032e4146d4252bafb3b38212df186497a7479d5e95af4796e7573a65e6849952032e4146d4252bafb3b38212df186497a74799edb6fda5b44
  5. GET /?action=display&what=af7d6f4240be9a2d31252290ef5b7e797dd7fc3be66d6d6766b5375a79b84d428964052355a9f53759403fe18b416f7067d9e948e17d7d147eae52605cf4505f947c0c3e33dc8a5d593424f58928484157f7c33bf0747c7112976d406bb14136eb1105
  6. 5のwhatパラメータを少しずつ変えながらGETリクエストを投げ続けている。

Our website received an attack in 2013?

2013年にRC4に対する脆弱性(JVNDB-2013-001910 - TLS プロトコルおよび SSL プロトコルで使用される RC4 アルゴリズムにおける平文回復攻撃を実行される脆弱性)が発表されていた。ので、RC4が関係していると考える。

RC4 , Is it possible to find the key if we know the plaintext and ciphertext? - Cryptography Stack Exchangeによると、RC4のciphertext Cはplaintext PとkeyであるRC4(key)のXORで求められる。

f:id:TAKEmaru:20170621163826p:plain

よって、RC4(key)が同一であった場合、Pは2つ目のplaintext P'、2つ目のciphertext C'を用いて以下のように表せる。

f:id:TAKEmaru:20170621031035p:plain

これを用いてリクエスト内容を復号化する。このような式になるのはRC4がXORを用いて暗号化しているからだ。

これ以外の2013年のメジャーな攻撃にはTLSに対するCBCモードPadding oracle attackであるLucky Thirteen Attackがあったが今回はTLSに関係ない。

解く

RC4のものと思われるchipertextを、Wiresharkでexportしたファイル名から取り出し、sql文にdecryptする。

コード

# coding: UTF-8

import os

files = os.listdir('./exports')


def decrypt(encrypted):
    p2 = ""
    for i in range(len(encrypted) / 64 + 1):
        c = encrypted[i * 64: (i + 1) * 64]
        c2 = "e4146d4252bafb3b38212df186497a7479d5e95af4796e7573a65e6849952032"
        p = "4141414141414141414141414141414141414141414141414141414141414141"
        for j in range(len(c) / 2):
            begin = j * 2
            end = (j + 1) * 2
            c_i = ord(c[begin: end].decode('hex'))
            c2_i = ord(c2[begin: end].decode('hex'))
            p_i = ord(p[begin: end].decode('hex'))
            p2 = p2 + chr(c_i ^ c2_i ^ p_i)

    return p2

for filename in files:
    with open('./exports/' + filename, 'r') as f:
        data = f.read()
        # レスポンスが HTTP/1.1 200 OKのときだけ
        if data.find('hyper') < data.find('Traditional'):
            print decrypt(filename[23:])

実行結果

flagテーブルからSELECTしているSQL文とSQLテーブルからSELECTしているSQL文が混在している。

$ python solve.py
...
(CASE WHEN (SELECT SUBSTR(flag,28,1)  FROM secret_flag LIMIT 0,1) = '1' THEN stock ELSE price END)

(CASE WHEN (SELECT SUBSTR(flag,2,1)  FROM secret_flag LIMIT 0,1) = 'l' THEN stock ELSE price END)

(CASE WHEN (SELECT SUBSTR(flag,9,1)  FROM secret_flag LIMIT 0,1) = '7' THEN stock ELSE price END)

(CASE WHEN (SELECT SUBSTR(flag,8,1)  FROM secret_flag LIMIT 0,1) = '0' THEN stock ELSE price END)

(CASE WHEN (SELECT SUBSTR(sql,3,1) FROM  SQLITE_MASTER LIMIT 1,1) = 'E' THEN stock ELSE price END)

(CASE WHEN (SELECT SUBSTR(sql,35,1) FROM  SQLITE_MASTER LIMIT 1,1) = 't' THEN stock ELSE price END

(CASE WHEN (SELECT SUBSTR(sql,34,1) FROM  SQLITE_MASTER LIMIT 1,1) = 'x' THEN stock ELSE price END
...

この結果を踏まえて、flagテーブルを操作しているSQL文から一文字ずつflagを抜くことを考える。

コード

# coding: UTF-8

import os

files = os.listdir('./exports')


def decrypt(encrypted):
    p2 = ""
    for i in range(len(encrypted) / 64 + 1):
        c = encrypted[i * 64: (i + 1) * 64]
        c2 = "e4146d4252bafb3b38212df186497a7479d5e95af4796e7573a65e6849952032"
        p = "4141414141414141414141414141414141414141414141414141414141414141"
        for j in range(len(c) / 2):
            begin = j * 2
            end = (j + 1) * 2
            c_i = ord(c[begin: end].decode('hex'))
            c2_i = ord(c2[begin: end].decode('hex'))
            p_i = ord(p[begin: end].decode('hex'))
            p2 = p2 + chr(c_i ^ c2_i ^ p_i)

    return p2

flag = [' ' for _ in range(40)]

for filename in files:
    with open('./exports/' + filename, 'r') as f:
        data = f.read()
        # レスポンスが HTTP/1.1 200 OKのときだけ
        if data.find('hyper') < data.find('Traditional'):

            for filename in files:
                with open('./exports/' + filename, 'r') as f:
                    fc = f.read()
                    if fc.find('hyper') < fc.find('Traditional'):
                        decrypted = decrypt(filename[23:])

                        if decrypted.find('flag') != -1:
                            flag_i = decrypted.find("'") + 1
                            if flag_i != 0:
                                flag_c = decrypted[flag_i]

                            substr_position_i = decrypted.find(',') + 1
                            if substr_position_i != 0:
                                substr_position = decrypted[substr_position_i:substr_position_i + 2]
                                if substr_position[1] == ',':
                                    substr_position = substr_position[0]

                            if flag_i != 0:
                                flag[int(substr_position)] = flag_c


print ''.join(flag)

実行結果

$ python solve.py
 flag{7307e3ee8da198ca4a7f9b1f8b018d8e} 

参考資料