[原创] Amateurs CTF 2023 逆向WP
2023-7-21 13:48:48 Author: bbs.pediy.com(查看原文) 阅读量:9 收藏

23/7/20

Amateurs CTF 2023 逆向题解共10题

也不算难,但是真的很抽象……

volcano

ELF64

Inspired by recent "traumatic" events.

nc amt.rs 31010

在IDA中分析,要求输入3个数 b, v, p 满足

  • b == 18476 + 22890 k
  • 17 <= sum_number_of_1(binary( v )) <= 26
  • p > 1 && p % 2 == 1

满足条件后再进行如下检测,其中 N = 4919,函数已重命名

1

2

3

4

v4 = cnt_digits(v);

if ( v4 == cnt_digits(b)

  && (v5 = sum_digits(v), v5 == sum_digits(b))

  && (v6 = fast_pow(N, v, p), v6 == fast_pow(N, b, p)) )

  • bv 在十进制下位数相同
  • bv 在十进制下各位和相同
  • 4919 ^ b = 4919 ^ v (mod p)

p = 4919可满足最后一个条件,按 k = 1~100查询 b 的各位和与位数,构造得
b = 1254536 = 54 * 22890 + 18476,
v = 1048562 = 1111_1111_1111_1111_0010b
就可以过关。

amateursCTF{yep_th0se_l00k_th3_s4me_to_m3!_:clueless:}

headache

ELF64

Ugh... my head hurts...

Flag is amateursCTF{[a-zA-Z0-9_]+}

在IDA中分析,发现这个函数,感觉是用异或做的壳。

1

2

3

4

5

6

7

8

9

10

11

12

__int64 __fastcall sub_401290(_BYTE *s)

{

  _DWORD *v2;

  if ( (*s ^ s[25]) != 86 )

    return 0LL;

  v2 = &loc_4012A4;

  do

    *v2++ ^= 0xEA228DE6;

  while ( *(v2 - 1) != 0xEA228DE6 );

  return ((__int64 (*)(void))loc_4012A4)();

}

使用脚本还原后发现还有一层异或套壳,再解密发现还有一层,感到不太对劲。
观察了一下发现套壳的代码全部位于一段地址区间内,头部执行完一些逻辑之后跳转到尾部,
尾部对头尾区间内的数据异或解密,再跳转到新解密的代码的头部,如此往复,计算了一下大概有百来层。

头部长这样(rdi是函数输入的第一个参数即我们的输入)

    mov     r15b, [rdi+19h]
    xor     r15b, [rdi]
    cmp     r15b, 56h
    jz      loc_404374
    xor     eax, eax
    retn
loc_4012A4: 
    ; 后面属于下一个区间

尾部长这样

loc_404374:
    mov     eax, 0EA228DE6h
    lea     rsi, loc_4012A4
loc_404381:
    xor     [rsi], eax
    add     rsi, 4
    cmp     [rsi-4], eax
    jnz     short loc_404381
    call    loc_4012A4
    retn

首先写IDA脚本还原这段地址区间的代码。
尾部的开头总是4个0x00标记上个区间的结尾,然后跟一个 mov eax, __MAGIC
用脚本提取之后需要nop掉加密代码并把call换成jmp,便于我们F5。

这里有一点很坑,在begin和end小于128时,字节码由call变成了call near,短了4个字节。

patch.py

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

from ida_bytes import get_bytes, patch_bytes

def xor_patch(BEGIN, END, MAGIC):

  buf = get_bytes(BEGIN, END - BEGIN)

  proc = bytes(buf[i] ^ MAGIC[i % len(MAGIC)] for i in range(len(buf)))

  patch_bytes(BEGIN, proc)

  print(f"0x{BEGIN:x}: patched {END - BEGIN} bytes")

begin = 0x4012a4

end = 0x404394

while True:

  patch_bytes(end - 8, b'\xe9')

  patch_bytes(end - 3, b'\x90')

  end -= 36

  if end <= begin:

    break

  magic = get_bytes(end + 5, 4)

  xor_patch(begin, end, magic)

  patch_bytes(end, b'\x90' * 28)

  begin += 24

  if end - begin < 128:

    begin -= 4

使用F5观察函数,大概有400多行,且所有的要求全为三元组 s[x] ^ s[y] = z

1

2

3

4

5

6

7

8

9

10

11

12

13

14

int __fastcall check(_BYTE *s)

{

  if ( (*s ^ s[25]) != 86 )

    return 0;

  if ( (s[14] ^ s[45]) != 29 )

    return 0;

  if ( (s[33] ^ s[34]) != 5 )

    return 0;

  if ( (s[40] ^ s[52]) != 5 )

    return 0;

  if ( (s[12] ^ s[56]) != 5 )

    return 0;

}

建立图 G

  • vertex(i) : 代表某个 s[i]
  • edge(i, j) : 权重为 s[i] ^ s[j]

其中 vertex[0] = 'a' 已知,只需遍历图就可以得出整个字串。
当然首先要用正则提取一下 x , y , z

solve.py

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

import re

pat1 = re.compile(r'\(\(s\[(\d+)\] \^ s\[(\d+)\]\) \!\= (\d+)\)')

pat2 = re.compile(r'\(s\[(\d+)\] \!\= s\[(\d+)\]\)')

flag = bytearray(b'amateurs' + b'\x00' * 53)

graph = [{} for i in range(61)]

with open("src.c", "r") as fp:

    src = fp.read()

    a = pat1.findall(src)

    for ai in a:

        x, y, w = int(ai[0]), int(ai[1]), int(ai[2])

        if y in graph[x]:

            if graph[x][y] != w:

                print(f"{x} {y} what??")

                exit()

            else:

                continue

        graph[x][y] = graph[y][x] = w

        print(f"{x} <-{w}-> {y}")

    b = pat2.findall(src)

    for bi in b:

        x, y = int(bi[0]), int(bi[1])

        if y in graph[x]:

            if graph[x][y] != w:

                print(f"{x} {y} what??")

                exit()

            else:

                continue

        graph[x][y] = graph[y][x] = 0

        print(f"{x} <-{0}-> {y}")

    changed = True

    while changed:

        changed = False

        for i in range(61):

            if flag[i] != 0:

                for j in graph[i].keys():

                    if flag[j] != 0:

                        continue

                    else:

                        flag[j] = flag[i] ^ graph[i][j]

                        changed = True

    print(flag)

amateursCTF{i_h4v3_a_spli77ing_headache_1_r3qu1re_m04r_sl33p}

rusteze

ELF64

Get rid of all your Rust rust with this brand new Rust-eze™ de-ruster.

Flag is amateursCTF{[a-zA-Z0-9_]+}

Rust编写的程序,先将输入和既定数组异或,再循环左移2位,最后和另一个既定数组比较。

1

2

3

4

5

6

7

for ( i = 0LL; i < 38; ++i )

{

  v15 = e1[i] ^ input_str[i];

  v23 = __ROL1__(v15, 2);

  v15 = v23;

  s[i] = v23;

}

solve.py

1

2

3

4

5

6

7

8

9

10

11

12

13

e1 = [39, 151, 87, 225, 169, 117, 102, 62, 27, 99, -29, -96, 5, 115, 89, -5, 10, 67, -113, -

      32, -70, -64, 84, -103, 6, -65, -97, 47, -60, -86, -90, 116, 30, -35, -105, 34, -19, -59]

e2 = [25, -21, -40, 86, 51, 0, 80, 53, 97, -36, -106, 111, -75, 13, -92, 122, 85, -24, -2,

      86, -105, -34, -99, -81, -44, 71, -81, -63, - 62, 106, 90, -84, -79, -94, -118, 89, 82, -30]

def ror2(x):

    x = x & 255

    y = x & 3

    return ((y << 6) + (x >> 2)) & 255

flag = "".join(chr(ror2(y) ^ (x & 255)) for x, y in zip(e1, e2))

print(flag)

amateursCTF{h0pe_y0u_w3r3nt_t00_ru5ty}

rusteze 2

PE64

My boss said Linux binaries wouldn't reach enough customers so I was forced to make a Windows version.

Flag is amateursCTF{[a-zA-Z0-9_]+}

Rust写的程序,但链接在Windows平台,可读性非常差。然而其逻辑基本与上题相同,按上题相同的解法可得字串'sorry_this_isnt_the_flag_this_time.'。
输入该字串,程序输出'Correct!',但并没有flag。

观察到加密函数中有语句(byte_7FF6BCD60000为一串乱码)

1

2

3

v14 = e1[idx] ^ s[idx]

if ( idx < 30 )

  byte_7FF6BCD60000[idx] ^= v14;

假定输入的是flag的开头'amateursCTF',将byte_7FF6BCD60000, e1, s异或之后可得'sorry_this'。因此猜测如果输入上面得到的字串,该全局变量处就是flag。

debug得

amateursCTF{d0n3_4nd_deRust3d}

trick question

pyc

Which one do you hate more: decompiling pycs or reading Python bytecode disassembly? Just kidding that's a trick question.

Run with Python version 3.10.

Flag is amateursCTF{[a-zA-Z0-9_]+}

先用pycdc嗦一下,发现是个套娃:
把一串b64加密的python代码提取出来再exec。

1

2

3

b64decode = lambda x: id.__self__.__dict__['exec'](id.__self__.__dict__['__import__']('base64').b64decode(x))

b64decode('A'.join(x[::-1]))

这段代码解密如下,发现还是个套娃……

1

2

3

4

5

6

7

8

check = lambda:None

code = type(check.__code__)(1, 0, 0, 6, 5, 67, b'|\x00d\x00d\x01\x85\x02\x19\x00d\x02k\x03r\x0et\x00j\x01j\x02d\x03\x19\x00S\x00|\x00d\x04\x19\x00d\x05k\x03r\x1at\x00j\x01j\x02d\x03\x19\x00S\x00|\x00d\x01d\x04\x85\x02\x19\x00}\x00t\x00j\x01j\x02d\x06\x19\x00|\x00\x83\x01d\x07k\x03r0t\x00j\x01j\x02d\x03\x19\x00S\x00g\x00}\x01t\x00j\x01j\x02d\x08\x19\x00|\x00\x83\x01D\x00]\r\\\x02}\x02}\x03|\x03d\tk\x02rG|\x01\xa0\x03|\x02\xa1\x01\x01\x00q:|\x01g\x00d\n\xa2\x01k\x03rTt\x00j\x01j\x02d\x03\x19\x00S\x00|\x00\xa0\x04\xa1\x00\xa0\x05d\x0b\xa1\x01}\x00|\x00d\x0c\x19\x00d\x00d\x00d\x04\x85\x03\x19\x00d\rk\x03rlt\x00j\x01j\x02d\x03\x19\x00S\x00|\x00d\x0e\x19\x00d\x0c\x19\x00|\x00d\x0e\x19\x00d\x0e\x19\x00\x17\x00|\x00d\x0e\x19\x00d\x0f\x19\x00\x18\x00|\x00d\x0e\x19\x00d\x0e\x19\x00|\x00d\x0e\x19\x00d\x0f\x19\x00\x17\x00|\x00d\x0e\x19\x00d\x0c\x19\x00\x18\x00|\x00d\x0e\x19\x00d\x0f\x19\x00|\x00d\x0e\x19\x00d\x0c\x19\x00\x17\x00|\x00d\x0e\x19\x00d\x0e\x19\x00\x18\x00f\x03d\x10k\x03r\xa9t\x00j\x01j\x02d\x03\x19\x00S\x00t\x00j\x01j\x02d\x11\x19\x00d\x12\x83\x01\xa0\x06|\x00d\x0f\x19\x00\xa1\x01\xa0\x07\xa1\x00d\x13k\x03r\xc0t\x00j\x01j\x02d\x03\x19\x00S\x00t\x00j\x01j\x02d\x11\x19\x00d\x14\x83\x01}\x04|\x04\xa0\x08|\x00d\x0f\x19\x00\xa1\x01\x01\x00t\x00j\x01j\x02d\x15\x19\x00|\x00d\x16\x19\x00\x83\x01|\x00d\x16<\x00|\x04\xa0\t|\x00d\x16\x19\x00\xa1\x01\x01\x00|\x00d\x16\x19\x00g\x00d\x17\xa2\x01k\x03r\xf0t\x00j\x01j\x02d\x03\x19\x00S\x00|\x00d\x18\x19\x00d\x19\x17\x00d\x1ak\x03r\xfet\x00j\x01j\x02d\x03\x19\x00S\x00t\x00j\x01j\x02d\x1b\x19\x00\xa0\n|\x00d\x1c\x19\x00d\x0cd\x18\x85\x02\x19\x00d\x1d\xa1\x02|\x04\xa0\x0bd\x0cd\x1e\xa1\x02A\x00d\x1fk\x03\x90\x01r\x1dt\x00j\x01j\x02d\x03\x19\x00S\x00t\x00j\x01j\x02d\x1b\x19\x00\xa0\n|\x00d\x1c\x19\x00d\x18d \x85\x02\x19\x00d\x1d\xa1\x02|\x04\xa0\x0bd\x0cd\x1e\xa1\x02A\x00d!k\x03\x90\x01r<t\x00j\x01j\x02d\x03\x19\x00S\x00t\x00j\x01j\x02d\x1b\x19\x00\xa0\n|\x00d\x1c\x19\x00d d\x01\x85\x02\x19\x00d"\x17\x00d\x1d\xa1\x02|\x04\xa0\x0bd\x0cd\x1e\xa1\x02A\x00d#k\x03\x90\x01r]t\x00j\x01j\x02d\x03\x19\x00S\x00d\x0c}\x05|\x00d$\x19\x00D\x00]\x0b}\x02|\x05d%9\x00}\x05|\x05|\x027\x00}\x05\x90\x01qct\x00j\x01j\x02d&\x19\x00|\x05\x83\x01d\'k\x03\x90\x01r\x80t\x00j\x01j\x02d\x03\x19\x00S\x00t\x00j\x01j\x02d(\x19\x00S\x00', (None, 12, 'amateursCTF{', 'False', -1, '}', 'len', 42, 'enumerate', '_', (7, 11, 13, 20, 23, 35), b'_', 0, b'sn0h7YP', 1, 2, (160, 68, 34), '__import__', 'hashlib', '4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a', 'random', 'list', 3, (49, 89, 102, 109, 108, 52), 4, b'freebie', b'0ffreebie', 'int', 5, 'little', 4294967295, 4227810561, 8, 825199122, b'\x00', 4277086886, 6, 128, 'hex', '0x29ee69af2f3', 'True', 'Did you know? pycdc can decompile marshaled code objects. Just make sure you mention the right version!'), ('id', '__self__', '__dict__', 'append', 'encode', 'split', 'sha256', 'hexdigest', 'seed', 'shuffle', 'from_bytes', 'randint'), ('input', 'underscores', 'i', 'x', 'r', 'c'), '', 'check', 3, b'\x10\x01\x0c\x01\x0c\x01\x0c\x01\x0c\x01\x14\x02\x0c\x01\x04\x02\x18\x01\x08\x01\n\x01\x02\x80\x0c\x01\x0c\x01\x0e\x02\x16\x01\x0c\x01n\x03\x0c\x01"\x02\x0c\x01\x10\x02\x0e\x01\x18\x01\x0e\x01\x10\x02\x0c\x01\x10\x02\x0c\x012\x02\x0c\x012\x02\x0c\x016\x02\x0c\x01\x04\x02\x0c\x01\x08\x01\x0c\x01\x16\x02\x0c\x01\x0c\x02', (), ())

check = type(check)(code, {'id': id})

if check(input("Enter the flag: ")):

    print("Correct!")

else:

    print("Incorrect.")

用dis去读这个代码对象的python字节码,解密要求如下:

输入为'amateurs{FLAG}',其中FLAG长度为42,由7段组成:0000000_111_2_333333_44_55555555555_666666

  • FLAG[0]是sn0h7YP的倒转 PY7h0ns
  • FLAG[1]是方程组,解得 ar3
  • FLAG[2]的hash值给定,枚举得 4
  • FLAG[3]由b'4'为种子shuffle,观察得 f4m1lY
  • FLAG[4]直接给出为 0f
  • FLAG[5]种子不变,产生随机数异或,解得 N0Nv3nom0us
  • FLAG[6]给定其作为128进制数的值,得 Sn4kes

amateursCTF{PY7h0ns_ar3_4_f4m1lY_0f_N0Nv3nom0us_Sn4kes}

蟒蛇是一种无毒蛇。

Data Structures and Algorithms

ELF64(Coredump)

I was doing some homework for my Data Structures and Algorithms class, but my program unexpectedly crashed when I entered in my flag. Could you help me get it back?

Here's the coredump and the binary, I'll even toss in the header file. Can't give out the source code though, how do I know you won't cheat off me?

使用IDA分析,建立结构体如下:(data为1字节数据,7字节padding)

1

2

3

4

5

6

7

8

9

10

00 listnode_t      struc

00 data            db 8 dup(?)

08 ptr             dq ?                    ; offset

10 listnode_t      ends

00 list_t          struc

00 len             dd ?

04 pad             dd ?

08 head            dq ?                    ; offset

10 list_t          ends

阅读源程序可知,作者将输入字串提取成链表,但混淆数据时竟将指针异或导致段错误。

将coredump使用gdb调试,发现core格式错误无法调试。
考虑到其一定包含内存的镜像,直接用rehex打开寻找堆的位置。
具体方法是搜索后跟7个0x00字节的'a'字节,即b'\x61\x00\x00\x00\x00\x00\x00\x00'。

amateursCTF{l1nk3d_bY_4n_xor}

jvm

java

I heard my professor talking about some "Java Virtual Machine" and its weird gimmicks, so I took it upon myself to complete one. It wasn't even that hard? I don't know why he was complaining about it so much.

Compiled with openjdk 11.0.19.

Run with java JVM code.jvm.

jd-gui嗦一下,发现是用java搓的虚拟机。

  • 位宽: 8
  • 寄存器: 4
  • 栈: 1024
  • 字节码: 1指令 + 0~2数据
  • 代码混淆:如果字节码无法识别,就对 b , b1 , b2 解密
    • b = b0 ^ b10 ^ b20
    • b1 = b10
    • b2 = b0
字节码 解释
0,1,2,3 swap(reg[i0],reg[i1])
8 reg[i1]+=i2
9 reg[i1]+=reg[i2]
12 reg[i1]-=i2
13 reg[i1]-=reg[i2]
16 reg[i1]*=i2
17 reg[i1]*=reg[i2]
20 r[i1]/=i2
21 r[i1]/=r[i2]
24 r[i1]%=i2
25 r[i1]%=r[i2]
28 r[i1]<<=i2
29 r[i1]<<=r[i2]
31 r[i1]=read()
32 push(read())
33 write(r[i1])
34 write(pop())
41 jmp i2 if !r[i1]
42 jmp i2 if r[i1]
43 jmp i1
52 push(r[i1])
53 r[i1]=pop()
54 push(i1)
127 quit()

disas.py

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

with open("code.jvm", "rb") as fp:

    with open("asm.txt", "w") as fq:

        program = bytearray(fp.read())

        idx = 0

        while idx < len(program):

            b0 = program[idx]

            b1 = program[idx + 1] if idx + 1 < len(program) else 0xff

            b2 = program[idx + 2] if idx + 2 < len(program) else 0xff

            if b0 == 0 or b0 == 1 or b0 == 2 or b0 == 3:

                fq.write(f"{idx}    swap r{b0} r{b1}\n")

                idx += 2

            elif b0 == 8:

                fq.write(f"{idx}    add r{b1}, {b2}\n")

                idx += 3

            elif b0 == 9:

                fq.write(f"{idx}    add r{b1}, r{b2}\n")

                idx += 3

            elif b0 == 12:

                fq.write(f"{idx}    sub r{b1}, {b2}\n")

                idx += 3

            elif b0 == 13:

                fq.write(f"{idx}    sub r{b1}, r{b2}\n")

                idx += 3

            elif b0 == 16:

                fq.write(f"{idx}    mul r{b1}, {b2}\n")

                idx += 3

            elif b0 == 17:

                fq.write(f"{idx}    mul r{b1}, r{b2}\n")

                idx += 3

            elif b0 == 20:

                fq.write(f"{idx}    div r{b1}, {b2}\n")

                idx += 3

            elif b0 == 21:

                fq.write(f"{idx}    div r{b1}, r{b2}\n")

                idx += 3

            elif b0 == 24:

                fq.write(f"{idx}    mod r{b1}, {b2}\n")

                idx += 3

            elif b0 == 25:

                fq.write(f"{idx}    mod r{b1}, r{b2}\n")

                idx += 3

            elif b0 == 28:

                fq.write(f"{idx}    shl r{b1}, {b2}\n")

                idx += 3

            elif b0 == 29:

                fq.write(f"{idx}    shl r{b1}, r{b2}\n")

                idx += 3

            elif b0 == 31:

                fq.write(f"{idx}    read r{b1}\n")

                idx += 2

            elif b0 == 32:

                fq.write(f"{idx}    rdpush\n")

                idx += 1

            elif b0 == 33:

                fq.write(f"{idx}    write r{b1}\n")

                idx += 2

            elif b0 == 34:

                fq.write(f"{idx}    wrpop\n")

                idx += 1

            elif b0 == 41:

                fq.write(f"{idx}    jmp {b2} if !r{b1}\n")

                idx += 3

            elif b0 == 42:

                fq.write(f"{idx}    jmp {b2} if r{b1}\n")

                idx += 3

            elif b0 == 43:

                fq.write(f"{idx}    jmp {b1}\n")

                idx += 2

            elif b0 == 52:

                fq.write(f"{idx}    push r{b1}\n")

                idx += 2

            elif b0 == 53:

                fq.write(f"{idx}    pop r{b1}\n")

                idx += 2

            elif b0 == 54:

                fq.write(f"{idx}    push {b1}\n")

                idx += 2

            elif b0 == 127:

                fq.write(f"{idx}    quit\n")

                idx += 1

            else:

                if idx + 1 < len(program) and idx + 2 < len(program):

                    program[idx] = b0 ^ b1 ^ b2

                    program[idx + 1] = b1

                    program[idx + 2] = b0

                else:

                    print("what??")

                    exit()

代码逻辑很简单,得

amateursCTF{wh4t_d0_yoU_m34n_j4v4_isnt_A_vm?}

JVM,指用Java做的VM。

flagchecker

scratch3.0

I was making this simple flag checker in Scratch, but my friend logged into my account and messed up all my variable names :(. Can you help me recover my flag please?

You should run on Turbowarp for better performance.

Turbowarp打开发现积木团在一起,变量名也混淆过。

Scratch的项目sb3其实是个zip,由资源文件和积木json文件压缩得到。

首先需要把变量名全部改好看点。

recover.py

1

2

3

4

5

6

7

8

9

10

import re

pat1 = re.compile(r"(DAT_694206942069420(0*))")

def rename(s):

    return 'Name' + str(len(s.groups()[1]))

with open("./project.json", "r") as fp:

    buf = re.sub(pat1, rename, fp.read())

    with open("./recovered/project.json", "w") as fq:

        fq.write(buf)

再上Turbowarp发现还是不太好读,但注意到一个积木中的常数0x9e3779b9,应该是TEA加密。
只要把某块积木拆开就可以下断点,debug了一下发现确实如此。

solve.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

dump = [239, 202, 230, 114, 17, 147, 199, 39, 182, 230, 119, 248, 78, 246, 224, 46, 99, 164, 112, 134, 30, 216, 53, 194, 60, 75, 223,

        122, 67, 202, 207, 56, 16, 128, 216, 142, 248, 16, 27, 202, 119, 105, 158, 232, 251, 201, 158, 69, 242, 193, 90, 191, 63, 96, 38, 164]

key = [69420, 1412141, 1936419188, 1953260915]

def decrypt(v, k):

    total, delta = 0xc6ef3720, 0x9e3779b9

    v0, v1 = int(v[0].hex(), 16), int(v[1].hex(), 16)

    k0, k1, k2, k3 = k[0], k[1], k[2], k[3]

    for i in range(32):

        v1 = (v1 - (((v0 << 4) + k2) ^ (v0 + total)

              ^ ((v0 >> 5) + k3))) & 0xffffffff

        v0 = (v0 - (((v1 << 4) + k0) ^ (v1 + total)

              ^ ((v1 >> 5) + k1))) & 0xffffffff

        total = (total - delta) & 0xffffffff

    return bytes.fromhex(hex(v0)[2:]) + bytes.fromhex(hex(v1)[2:])

flag = b''

for idx in range(0, len(dump), 8):

    flag += decrypt([bytes(dump[idx:idx+4]), bytes(dump[idx+4:idx+8])], key)

print(flag)

amateursCTF{screw_scratch_llvm_we_code_by_hand_1a89c87b}

你说的对,但是Scratch是麻省理工学院的“终身幼儿园团队”在2007年发布的一种图形化编程工具。

emoji

emojicode

I apologize in advance.

Flag is amateursCTF{[a-z0-9_]+}

Compiled using the latest version of emojicode

Note that there may be multiple inputs that cause the program to print ✅. The correct input (and the flag) has sha256 hash 53cf379fa8fd802fd2f99b2aa395fe8b19b066ab5e2ff49e44633ce046c346c4.

emojicode,源码就特别抽象,需要对着官网的文档搜索查询。

过关要求:

输入的每个字符按二进制顺序排成新的字符串,
abcd->61626364->1100001_1100010_1100011_1100100
然后将其顺序填入16*16的棋盘中,不够用0补齐。

在每一行和每一列中,统计所有连续的'1'的块的长度记为列表,
1101101110 中有3个连续块,长度为 [2, 2, 3]

输入flag,使得每一行每一列的连续块长度列表恰为给定值。

爆破感觉很麻烦,因为开头是'amateursCTF'已经给定了,所以打算像扫雷一样玩一下。

最后长这样:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

1100001110110111

0000111101001100

1011110101111001

0111001110000111

0101001000110111

1011110111110100

0110100111100111

0111111100010110

0101110110011011

1111011101100111

1110011101111111

0100111011101110

0111100011100100

1100101101111111

0110111010011101

0111000111111101

光是得到比特串还不够,因为flag可能是数字也可能是字母、符号(长度可能是6也可能是7),
所以还是需要爆破(enc为得到的比特串)。

solve.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

keys = '0123456789_abcdefghijklmnopqrstuvwxyz'

def crack(s, i):

    if i == len(enc):

        s = 'amateursCTF{' + s + '}'

        if sha256(bytes(s, 'utf-8')).hexdigest() == sha256hex:

            print(s)

    if i + 6 <= len(enc):

        x6 = chr(int(enc[i : i + 6], 2))

        if x6 in keys:

            crack(s + x6, i + 6)

    if i + 7 <= len(enc):

        x7 = chr(int(enc[i : i + 7], 2))

        if x7 in keys:

            crack(s + x7, i + 7)

crack('', 0)

amateursCTF{7his_belongs_ins1de_mi5c}

misc都没这个抽象……

jsrev

javascript/html

Someone wanted this, so I delivered. Have fun!

jsrev.amt.rs

用three.js写的玩球的游戏,阅读源码发现这样一段

1

2

3

4

5

6

7

8

9

let positions = [];

await fetch('./coords.json')

    .then((response) => response.json())

    .then((data) => { positions = data; });

for (let i = 0; i < positions.length; i++) {

    const [x, y, z] = positions[i];

    spheres[i].collider.set(new THREE.Vector3(x, y, z), SPHERE_RADIUS);

}

爬取coords.json发现是三元组组成的列表,每个三元组代表一个初始球的坐标。
又观察到coords.json按y轴大小拍好了序,y轴相同的点排在了一块。

于是怀疑这些点排成了flag,并且从xOz看,y轴的一片就代表了一个字符。

用pyplot绘制散点图,注意xOz要给一点偏移,不然所有的点从上往下看全部团一块了看不清。

solve.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

import matplotlib.pyplot as pyplot

import numpy

coords = []

with open('coords.json', 'r') as fp:

  coords = eval(fp.read())

x = numpy.array([(c[2]-(c[1]-34)*10) for c in coords])

y = numpy.array([-c[0] for c in coords])

z = numpy.array([0 for c in coords])

ax = pyplot.subplot(projection='3d')

ax.set_title('jsrev')

ax.scatter(x, y, z, c='b')

ax.set_xlabel('x')

ax.set_ylabel('y')

ax.set_zlabel('z')

pyplot.axis('scaled')

pyplot.show()

jsrev.png

amateursCTF{asK_4nD_thr33_5h4ll_r3c31v3}

完结撒花

CTF训练营-Web篇

上传的附件:
  • volcano (14.13kb,1次下载)
  • trick-question.pyc (5.54kb,1次下载)
  • rusteze-2.exe (193.00kb,1次下载)
  • rusteze (4.23MB,1次下载)
  • jvm.zip (1.97kb,1次下载)
  • jsrev.zip (9.54kb,1次下载)
  • headache (26.14kb,1次下载)
  • flagchecker.sb3 (64.14kb,1次下载)
  • emoji.zip (0.96kb,1次下载)
  • DataStructuresAndAlgorithms.zip (814.80kb,1次下载)

文章来源: https://bbs.pediy.com/thread-278109.htm
如有侵权请联系:admin#unsafe.sh