BUUCTF NewStarCTF新知识记录
前言
作为一名网络安全的小白,CTF的菜鸡,BUUCTF最近举办了NewStarCTF活动,注释说比较适合入门,自己也做了下,同时也是尝试做MISC的题目,记录下接触到的新的知识。
一、eazyxor
知识:os.urandom(size)
size: 它是字符串随机字节的大小
返回值: 此方法返回一个字符串,该字符串表示适合加密用途的随机字节。
如:
import os
key=os.urandom(1)
print(key)
print(key[0])
输出:
b'\xdd'
221
from os import urandom
from secret import flag
key = urandom(1)
def xor(plaintext, key):
ret = []
for i in range(len(plaintext)):
ret.append(plaintext[i] ^ key[0])
return bytes(ret)
ciphertext = xor(flag, key)
print(ciphertext.hex())
output:9b919c9a8685cd8fa294c8a28c88cc89cea2ce9c878480
这是一道异或加密题,一个数进行两次异或,结果等于本神,所以只需要对异或得到的结果再异或。因为这里加密的key[0]是随机的,所以需要对key[0]进行爆破,找出flag值。
解密如下:
from os import urandom
import binascii
key = urandom(1)
def xor(plaintext, key):
ret = []
for i in range(len(plaintext)):
ret.append(plaintext[i] ^ key)
return bytes(ret)
global i
try:
for i in range(150, 255):
s = binascii.a2b_hex('9b919c9a8685cd8fa294c8a28c88cc89cea2ce9c878480')
ciphertext = xor(s, i)
print(ciphertext.decode(), i)
except UnicodeDecodeError:
print('错误', i)
二、RSA_begin
from Crypto.Util.number import *
from secret import flag
assert len(flag) % 5 == 0
cnt = len(flag) // 5
flags = [flag[cnt * i:cnt * (i + 1)] for i in range(5)]
# Try to implement your RSA with primes p and q
def level1(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 0x10001
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'p = {p}')
print(f'q = {q}')
# But how can we attack the RSA when we didn't know the primes?
def level2(message):
m = bytes_to_long(message)
p = getPrime(64)
q = getPrime(64)
n = p * q
e = 0x10001
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'n = {n}')
# Different e may cause danger?
def level3(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
e = 3
n = p * q
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'n = {n}')
# So is there anything wrong with RSA as shown below?
def level4(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
d = getPrime(64)
e = inverse(d, (p - 1) * (q - 1))
n = p * q
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'e = {e}')
print(f'n = {n}')
# What about different n? Just have a try with the hint!
def level5(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
n = p * p * q
e = 0x10001
d = inverse(e, p * (p - 1) * (q - 1))
assert m < n
c = pow(m, e, n)
hint = pow(d, e, n)
print(f'c = {c}')
print(f'hint = {hint}')
print(f'n = {n}')
# phi和n有公因数p
# demod(phi)=1
# hint=d^emodn
# n=p*p*q
# (de)^emod(n)=(kphi+1)^emodn
# hint*e^emodn=(kphi+1)^emodn
# p是n和\(hint * e ^ e - 1\)的最大公约数
# hint*e^e-1=kphi phi=p*(p-1)*(q-1)
print('Level 1:')
level1(flags[0])
print('Level 2:')
level2(flags[1])
print('Level 3:')
level3(flags[2])
print('Level 4:')
level4(flags[3])
print('Level 5:')
level5(flags[4])
这里将一个flag分成了5块,分别使用了RSA进行加密,所以要分五块进行分别解密
level1:
def level1(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 0x10001
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'p = {p}')
print(f'q = {q}')
c = 22160015525054597533062795679117215923801827397299805735087138192137742945881204146337349060934854888054628153923021387981306839951210090523829296521835965212118849043671673133979884712755090374758002677916820953359774554825569218497687506468472278309097929775388010403607769802840990547048001743970754496905
p = 6962443023774446497102092246794613339314677593117417573764609329949026862782472380488956732038459060928443992561763464365758383525259954798321350043810351
q = 9631855759661411029901156175243744760977799976661519182223576693685069000499866459636568713055906075171480855575061732016121299027658733834671035383233163
质数p和q都给了,直接解密即可
level2:
def level2(message):
m = bytes_to_long(message)
p = getPrime(64)
q = getPrime(64)
n = p * q
e = 0x10001
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'n = {n}')
c = 17250922799297131008803303235771955129
n = 134097988095851988085603926250918812377
p和q比较小,只有64位,可以尝试下factor质数分解直接爆破n。
[factordb.com![icon-
default.png?t=M85B](https://csdnimg.cn/release/blog_editor_html/release2.2.0/ckeditor/plugins/CsdnLink/icons/icon-
default.png?t=M85B)http://factordb.com/index.php?](http://factordb.com/index.php?
"factordb.com")爆出p为10094271714305059493,q为13284562957208247589,可以直接解密了
level3:
def level3(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
e = 3
n = p * q
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'n = {n}')
e只有3,明显可以使用低指数加密攻击,因为e很小的话,明文m的e次方=密文c+kn,可以直接通过对c+kn开e次方根,返回能开e次方根的结果。
脚本如下:
import gmpy2
import libnum
c = 2776571135646565181849912433877522437622755332262910824866791711
n = 85793694792655420934945863688968944466300304898903354212780512650924132933351787673979641944071634528676901506049360194331553838080226562532784448832916022442020751986591703547743056267118831445759258041047213294368605599719242059474324548598203039032847591828382166845797857139844445858881218318006747115157
def de(c, e, n):
k = 0
while True:
mm = c + n * k
result, flag = gmpy2.iroot(mm, e)
if flag:
return result
k += 1
e = 3
m = de(c, e, n)
print(m)
print(libnum.n2s(int(m)).decode())
level4:
def level4(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
d = getPrime(64)
e = inverse(d, (p - 1) * (q - 1))
n = p * q
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'e = {e}')
print(f'n = {n}')
output:
c = 68588738085497640698861260094482876262596289469248772328560280530093163764972313090939471997156632421517452790632223565521726590730640805290182026911025142051864898712501214753986865172996090706657535814234291235489829621372021092488300236623525366939477695283380634188510950335639019458758643273802572617191
e = 51999725233581619348238930320668315462087635295211755849675812266270026439521805156908952855288255992098479180003264827305694330542325533165867427898010879823017054891520626992724274019277478717788189662456052796449734904215067032681345261878977193341769514961038309763898052908572726913209883965288047452751
n = 68816697240190744603903822351423855593899797203703723038363240057913366227564780805815565183450516726498872118491739132110437976570592602837245705802946829337567674506561850972973663435358068441037127926802688722648016352967768929007662772115485020718202683004813042834036078650571763978066558718285783045969
看e的结果,e一般为65535这些,这里的e很大,可以使用渐进式分数的方法把d爆出来。
脚本:
import gmpy2
def transform(x, y): # 使用辗转相处将分数 x/y 转为连分数的形式
res = []
while y:
res.append(x // y)
x, y = y, x % y
return res
def continued_fraction(sub_res):
numerator, denominator = 1, 0
for i in sub_res[::-1]: # 从sublist的后面往前循环
denominator, numerator = numerator, i * numerator + denominator
return denominator, numerator # 得到渐进分数的分母和分子,并返回
# 求解每个渐进分数
def sub_fraction(x, y):
res = transform(x, y)
res = list(map(continued_fraction, (res[0:i] for i in range(1, len(res))))) # 将连分数的结果逐一截取以求渐进分数
return res
def get_pq(a, b, c): # 由p+q和pq的值通过维达定理来求解p和q
par = gmpy2.isqrt(b * b - 4 * a * c) # 由上述可得,开根号一定是整数,因为有解
x1, x2 = (-b + par) // (2 * a), (-b - par) // (2 * a)
return x1, x2
def wienerAttack(e, n):
for (d, k) in sub_fraction(e, n): # 用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if k == 0: # 可能会出现连分数的第一个为0的情况,排除
continue
if (e * d - 1) % k != 0: # ed=1 (mod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
continue
phi = (e * d - 1) // k # 这个结果就是 φ(n)
px, qy = get_pq(1, n - phi + 1, n)
if px * qy == n:
p, q = abs(int(px)), abs(int(qy)) # 可能会得到两个负数,负负得正未尝不会出现
d = gmpy2.invert(e, (p - 1) * (q - 1)) # 求ed=1 (mod φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
return d
print("该方法不适用")
e = 51999725233581619348238930320668315462087635295211755849675812266270026439521805156908952855288255992098479180003264827305694330542325533165867427898010879823017054891520626992724274019277478717788189662456052796449734904215067032681345261878977193341769514961038309763898052908572726913209883965288047452751
n = 68816697240190744603903822351423855593899797203703723038363240057913366227564780805815565183450516726498872118491739132110437976570592602837245705802946829337567674506561850972973663435358068441037127926802688722648016352967768929007662772115485020718202683004813042834036078650571763978066558718285783045969
d = wienerAttack(e, n)
print("d=", d)
得到d= 12966126097163765179,便可以直接解密出明文
level5:
def level5(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
n = p * p * q
e = 0x10001
d = inverse(e, p * (p - 1) * (q - 1))
assert m < n
c = pow(m, e, n)
hint = pow(d, e, n)
print(f'c = {c}')
print(f'hint = {hint}')
print(f'n = {n}')
# phi和n有公因数p
# demod(phi)=1
# hint=d^emodn
# n=p*p*q
# hint*e^e-1=kphi phi=p*(p-1)*(q-1)
# (de)^emod(n)=(kphi+1)^emodn
# hint*e^emodn=(kphi+1)^emodn
# p是n和(hint * e ^ e - 1)的最大公约数
把n做了变式,这里的n=ppq,然后给了一个hint,肯定是要利用hint将p或q求出来,通过简单的式子推导,猜测p是n和(hint*e^e-1)的最大公约数,为了印证自己的请求,自己造一个明文然后进行相同的加密看看是不是如此。
from Crypto.Util.number import getPrime, inverse, bytes_to_long, long_to_bytes
from gmpy2 import gmpy2
message=b'flag{RSA_is_Good_to_Study}'
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
n = p * p * q
e = 65537
d = inverse(e, p * (p - 1) * (q - 1))
c = pow(m, e, n)
hint = pow(d, e, n)
k = gmpy2.gcd(hint*pow(e,e)-1, n)
print(k==p)
output:True
果真如此
解密:
from Crypto.Util.number import inverse, long_to_bytes
c = 17250922799297131008803303235771955129
n = 134097988095851988085603926250918812377
p = 10094271714305059493
q = 13284562957208247589
e = 65537
d = inverse(e, (p - 1) * (q - 1))
m = long_to_bytes(pow(c, d, n))
print(m)
三、Yesec no drumsticks
知识: LSB隐写(最低有效位隐写)
LSB隐写一般发生在PNG图片中,因为PNG图片是无损压缩,在压缩过程中修改的信息不会被损坏。一般PNG图像像数由RGB红绿蓝三色组成,每个颜色占用8位,取值是0x00-0xFF,一共有256的三次方种颜色,人类的眼睛有好多种颜色是无法区分的。而LSB隐写就是将RGB中的最低二进制位修改,人的眼睛却无法发现这种颜色变化,因此可以修改携带信息。
直接解密脚本:
try:
from PIL import Image
from PIL import ImageFile
except:
import os
os.system('pip install Pillow')
from PIL import Image
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES=True
def full_eight(str):
return str.zfill(8)
def get_text_bin(strr):
string=""
s_text=strr.encode()
for i in range(len(s_text)):
string=string+full_eight(bin(s_text[i]).replace('0b',''))
return string
def mod(x,y):
return x%y
def tell_you_bad(str1,str2,str3):
im=Image.open(str1)
width=im.size[0]
height=im.size[1]
count=0
key=get_text_bin(str2)
keylen=len(key)
for h in range(0,height):
for w in range(0,width):
pixel=im.getpixel((w,h))
a=pixel[0]
b=pixel[1]
c=pixel[2]
if count==keylen:
break
a=a-mod(a,2)+int(key[count])
count+=1
if count==keylen:
im.putpixel((w,h),(a,b,c))
break
b=b-mod(b,2)+int(key[count])
count+=1
if count==keylen:
im.putpixel((w,h),(a,b,c))
break
c=c-mod(c,2)+int(key[count])
count+=1
if count==keylen:
im.putpixel((w,h),(a,b,c))
break
if count%3==0:
im.putpixel((w,h),(a,b,c))
im.save(str3)
def tell_you_fun(le,str1):
a=""
b=""
im = Image.open(str1)
lenth = le*8
width = im.size[0]
height = im.size[1]
count = 0
for h in range(0, height):
for w in range(0, width):
pixel = im.getpixel((w, h))
if count%3==0:
count+=1
b=b+str((mod(int(pixel[0]),2)))
if count ==lenth:
break
if count%3==1:
count+=1
b=b+str((mod(int(pixel[1]),2)))
if count ==lenth:
break
if count%3==2:
count+=1
b=b+str((mod(int(pixel[2]),2)))
if count ==lenth:
break
if count == lenth:
break
st=""
for i in range(0,len(b),8):
stra = int(b[i:i+8],2)
st+=chr(stra)
return st
def main():
print("加密(1) OR 解密(2):",end=' ')
choice=int(input())
if choice==1:
try:
print("[+]加密图片:",end=' ')
old=input()
print("[+]加密文字(以#号结束):",end=' ')
kkk=input()
print("[+]加密图片保存重命名:",end=' ')
new=input()
tell_you_bad(old,kkk,new)
print("[+]Fun Picture done!")
except:
print("[-]未找到此图片,请检查图片名和路径")
if choice==2:
le = 30
try:
print("[+]解密图片:",end=' ')
new = input()
word=tell_you_fun(le,new).split('#')
print('[+]Picture Tell You: ',word[0])
except:
print("[-]未找到此图片,请检查图片名和路径")
if __name__=="__main__":
main()
四、EzSnake
前面下载附件,发现是个jar贪吃蛇小游戏,丢入winhex看看。
50 4B 03 04的文件头格式,是zip压缩包,改成压缩包就看到了源代码,因为要分数大于114才能有flag,所以直接看分数大于114的那一段代码。
if (this.score >= 114) {
this.isStart = false;
final File tf = new File("./" + Data.o0o0o0o00 + Data.oo000o0o0o + Data.o0o0o0o00o0o + Data.o0o0o00oo0o0);
InputStream is = null;
FileOutputStream os = null;
try {
is = GamePanel.class.getResourceAsStream("/statics/1919810/114514");
os = new FileOutputStream(tf);
if (!tf.exists()) {
tf.createNewFile();
}
byte[] b = new byte[1024];
byte[] tmp = new byte[1024];
for(int data = is.read(b); data != -1; data = is.read(b)) {
for(int i = 0; i < data; ++i) {
tmp[i] = (byte)(b[i] ^ 88);
}
os.write(tmp, 0, data);
}
JFrame jf = new JFrame("114514");
jf.setSize(600, 600);
jf.setResizable(false);
jf.setLocationRelativeTo((Component)null);
JPanel jp = new JPanel() {
protected void paintComponent(Graphics g) {
try {
Image bg = ImageIO.read(tf);
g.drawImage(bg, 0, 0, this.getWidth(), this.getHeight(), (ImageObserver)null);
} catch (IOException var6) {
var6.printStackTrace();
} finally {
tf.delete();
}
}
};
jp.setVisible(true);
jf.add(jp);
jf.setVisible(true);
} catch (Exception var22) {
var22.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException var21) {
var21.printStackTrace();
}
}
if (os != null) {
try {
os.close();
} catch (IOException var20) {
var20.printStackTrace();
}
}
}
}
从源代码可以看出,当分数大于114时,创建了一个新的File文件,并且从static里面的静态文件中读取了文件流,解题的关键就在于读取文件流时,对字节进行了异或88加密,可以尝试对静态文件进行异或88解密,得到二维码图片,增加定位码,即可得到flag。
[CyberChefThe Cyber Swiss Army Knife - a web app for encryption, encoding,
compression and data
analysishttps://gchq.github.io/CyberChef/](https://gchq.github.io/CyberChef/
"CyberChef")
for(int data = is.read(b); data != -1; data = is.read(b)) {
for(int i = 0; i < data; ++i) {
tmp[i] = (byte)(b[i] ^ 88);
}
五、Pyre
之前在广东攻防大赛也有过这样的题,这次又忘了,记录下。
下载py脚本:
[Download PyInstaller Extractor from SourceForge.netExtract contents of a
Windows executable file created by
pyinstallerhttps://sourceforge.net/projects/pyinstallerextractor/files/dist/pyinstxtractor.py/download?use_mirror=udomain](https://sourceforge.net/projects/pyinstallerextractor/files/dist/pyinstxtractor.py/download?use_mirror=udomain
"Download PyInstaller Extractor from
SourceForge.net")1、首先使用下载的脚本会生成一共extracted文件夹,文件夹中有pyre和struct文件,因为pyinstaller将py文件生成exe时,会将文件头去掉,因此需要复原文件头。
从E3开始,抽取struct文件第一行到E3的十六进制数值,插入到pyre的E3前面,结果如下
随后将得到的pyre添加后缀pyc,使用python的uncompyle6将pyc文件反编译,输出py文件。
flag = ''
encode = 'REla{PSF!!fg}!Y_SN_1_0U'
table = [7, 8, 1, 2, 4, 5, 13, 16, 20, 21, 0, 3, 22, 19, 6, 12, 11, 18, 9, 10, 15, 14, 17]
def enc(input):
tmp = ''
for i in range(len(input)):
tmp += input[table[i]]
return tmp
if __name__ == '__main__':
print('Please input your flag:')
flag = input()
if len(flag) != 23:
print('Length Wrong!!')
else:
final = enc(flag)
if final == encode:
print('Wow,you get the right flag!!')
else:
print('Sorry,Your input is Wrong')
对以上进行解密即可,只是将字母调换了顺序
encode = 'REla{PSF!!fg}!Y_SN_1_0U'
table = [7, 8, 1, 2, 4, 5, 13, 16, 20, 21, 0, 3, 22, 19, 6, 12, 11, 18, 9, 10, 15, 14, 17]
# encode = enc(flag)
d = {}
result=''
def enc(input):
tmp = ''
for i in range(len(input)):
d[table[i]] = encode[i]
enc(encode)
for index in sorted(d):
result+=d[index]
print(result)
总结
这是第一次尝试接触图片隐写等MISC,发现还挺有趣的,以后可以多尝试学习下。
文章标题:BUUCTF NewStarCTF新知识记录
文章链接:https://aiwin.fun/index.php/archives/700/
最后编辑:2024 年 1 月 5 日 21:09 By Aiwin
许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)