文章目录
- 2023NewStarCTF
- Web
- 泄露的秘密
- Begin of Upload
- ErrorFlask
- Begin of HTTP
- 考点:对请求头的考察
- Begin of PHP
- R!C!E
- Login
- Crypto
- babyrsa:
- 考点:多素数rsa
- Vigenere
- small_d
- 考点:低解密指数攻击
- Baby_xor
- 考点:异或
- Affine
- 考点:仿射密码
- BabyEncoding
- babyaes
- 考点:异或 aes中cbc模式
2023NewStarCTF
Web
泄露的秘密
www.zip
Begin of Upload
打开源码 找到限制是在前端
我们抓包 上传正常后缀的文件 比如jpg结尾
但是这样传上去服务器是无法解析的
所以我们进行抓包 然后在bp中修改后缀名
将我们上传的后缀jpg在请求包中改为php 服务器就可以解析我们的语句了
一句话木马:
然后进入到我们上传的这个文件中就可以rce啦
http://13878733-90ba-4e0c-9a0a-4ce861aaa1bf.node4.buuoj.cn:81/upload/aaa_b.php?b=system(%27cat%20/fllll4g%27);
ErrorFlask
python中flask启的框架
输入非法字符,报错
在报错中找到源代码
Begin of HTTP
考点:对请求头的考察
POST /" />Begin of PHP
源码开套:
<?phperror_reporting(0);highlight_file(__FILE__);if(isset($_GET['key1']) && isset($_GET['key2'])){echo "=Level 1=
";if($_GET['key1'] !== $_GET['key2'] && md5($_GET['key1']) == md5($_GET['key2'])){$flag1 = True;}else{die("nope,this is level 1");}}if($flag1){echo "=Level 2=
";if(isset($_POST['key3'])){if(md5($_POST['key3']) === sha1($_POST['key3'])){$flag2 = True;}}else{die("nope,this is level 2");}}if($flag2){echo "=Level 3=
";if(isset($_GET['key4'])){if(strcmp($_GET['key4'],file_get_contents("/flag")) == 0){$flag3 = True;}else{die("nope,this is level 3");}}}if($flag3){echo "=Level 4=
";if(isset($_GET['key5'])){if(!is_numeric($_GET['key5']) && $_GET['key5'] > 2023){$flag4 = True;}else{die("nope,this is level 4");}}}if($flag4){echo "=Level 5=
";extract($_POST);foreach($_POST as $var){if(preg_match("/[a-zA-Z0-9]/",$var)){die("nope,this is level 5");}}if($flag5){echo file_get_contents("/flag");}else{die("nope,this is level 5");}}首先进入第一关:
利用数组特性绕过
if($_GET['key1'] !== $_GET['key2'] && md5($_GET['key1']) == md5($_GET['key2']))
因为md5不能对数组进行加密,所以设置数据类型均为数组的时候就会全部返回null,使得两值相等
payload:
?key1[]=11&key2[]=22
第二关:
同理,数组绕过,返回值均为null 相等
第三关:
strcmp漏洞 如果输入错误数据类型无法比较报错,执行的结果也是返回0
所以使用数组报错
第四关:
is_numeric
字符串绕过2024a
同样使用数组绕过对数字的验证第五关:
高度重视两个函数:
extract
: 给设定的变量赋值
foreach
: 遍历键值对中的值我们传的值只要设置为符号就可以绕过foreach中的匹配,但是当我们成功绕过我们发现,啊咋多了个flag5
这也没有之前的设置啊
然后查询了一些extract函数
发现其赋值的特性,所以在post中对flag5给个初始值就行,默认为true。
R!C!E
首先是md5的绕过 这个密码比较特殊 网上一搜就有了
114514
这里一般使用python来爆破(当时偷懒了直接网上搜的
import hashlibdef crack(pre):for i in range(0,999999):if(hashlib.md5(str(i).encode("UTF-8")).hexdigest())[0:6] == str(pre):print(i)breakcrack("c4d038")
然后就可以进行rce了
过滤了好多:
if(!preg_match("/flag|system|pass|cat|ls/i",$code))
使用exec代替system
使用tac代替cat
使用通配符* 代替flag
payload:
password=114514&e[v.a.l=echo(exec('tac /f*'));
在传参的时候 变量名会将不合法的字符转化为下划线 只会转换一次 其中不合法字符包括
[
和.
所以让第一个转换为下划线 后面的点号就不会被改变避大坑:一定要加一个echo!!!!一开始忘记让它回显了555 也可以
var_dump()
法二:
var_dump(file_get_contents($_POST['_']))$_=
Login
有注册界面 先尝试一下普通用户进去的样子 否则注册界面写出来就没有什么意义了
在交互模式中退出方法
ctrl+c
或者ctrl+d
在bin目录中一般存放的是可执行命令
退出之后我们思考为什么一进来就是在chat程序中 肯定是前面有人输入其他命令了嘛
我们点击向上键 可以看到内容
但是个人感觉没什么大用哈哈
还是得爆破密码
在bp中跑一下 admin的密码是000000
Crypto
babyrsa:
考点:多素数rsa
首先yafu分解得到10个素数
import gmpy2from Crypto.Util.number import *e = 65537n = 17290066070594979571009663381214201320459569851358502368651245514213538229969915658064992558167323586895088933922835353804055772638980251328261c = 14322038433761655404678393568158537849783589481463521075694802654611048898878605144663750410655734675423328256213114422929994037240752995363595P = [2217990919,4278428893, 2804303069, 3654864131, 2923072267, 2338725373, 2706073949, 2970591037, 2370292207, 2463878387, 3939901243, 2794985117, 3207148519, 4093178561, 3831680819]phi = 1for i in range(len(P)):phi *= P[i]-1d = gmpy2.invert(e,phi)m = pow(c,d,n)print(long_to_bytes(m))#b'flag{us4_s1ge_t0_cal_phI}'
Vigenere
还是推荐:https://www.guballa.de/vigenere-solver
然后对于题目中的描述 我搜了一些是法语:不可破译的语言
那么我们在破解的时候选择French
此外想补充一下可以根据开头密文和部分已知明文flag进行匹配推测密钥
small_d
考点:低解密指数攻击
源码:
from secret import flagfrom Crypto.Util.number import *p = getPrime(1024)q = getPrime(1024)d = getPrime(32)e = inverse(d, (p-1)*(q-1))n = p*qm = bytes_to_long(flag)c = pow(m,e,n)print(c)print(e)print(n)# c = 6755916696778185952300108824880341673727005249517850628424982499865744864158808968764135637141068930913626093598728925195859592078242679206690525678584698906782028671968557701271591419982370839581872779561897896707128815668722609285484978303216863236997021197576337940204757331749701872808443246927772977500576853559531421931943600185923610329322219591977644573509755483679059951426686170296018798771243136530651597181988040668586240449099412301454312937065604961224359235038190145852108473520413909014198600434679037524165523422401364208450631557380207996597981309168360160658308982745545442756884931141501387954248# e = 8614531087131806536072176126608505396485998912193090420094510792595101158240453985055053653848556325011409922394711124558383619830290017950912353027270400567568622816245822324422993074690183971093882640779808546479195604743230137113293752897968332220989640710311998150108315298333817030634179487075421403617790823560886688860928133117536724977888683732478708628314857313700596522339509581915323452695136877802816003353853220986492007970183551041303875958750496892867954477510966708935358534322867404860267180294538231734184176727805289746004999969923736528783436876728104351783351879340959568183101515294393048651825# n = 19873634983456087520110552277450497529248494581902299327237268030756398057752510103012336452522030173329321726779935832106030157682672262548076895370443461558851584951681093787821035488952691034250115440441807557595256984719995983158595843451037546929918777883675020571945533922321514120075488490479009468943286990002735169371404973284096869826357659027627815888558391520276866122370551115223282637855894202170474955274129276356625364663165723431215981184996513023372433862053624792195361271141451880123090158644095287045862204954829998614717677163841391272754122687961264723993880239407106030370047794145123292991433
下面是直接调用的板子:
import gmpy2from Crypto.Util.number import *# numerator(n):分子, denominator(d):分母def t_cf(n, d):# 将分数 x/y 转为连分数的形式res = []while d:res.append(n // d)n, d = d, n % dreturn resdef cf(sub_res):# 得到渐进分数的分母和分子n, d = 1, 0for i in sub_res[::-1]:# 从后面往前循环d, n = n, i * n + dreturn d, ndef list_fraction(x, y): # 列出每个渐进分数res = t_cf(x, y)res = list(map(cf, (res[0:i] for i in range(1, len(res)))))# 将连分数的结果逐一截取以求渐进分数return resdef 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, x2def wienerAttack(e, n):print(2)for (d, k) in list_fraction(e, n):# 用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数if k == 0:# 可能会出现连分数的第一个为0的情况,排除continueif (e * d - 1) % k != 0:# ed=1 (mod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)continuephi = (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)的乘法逆元dreturn dprint("求解d失败")print(1)e = 8614531087131806536072176126608505396485998912193090420094510792595101158240453985055053653848556325011409922394711124558383619830290017950912353027270400567568622816245822324422993074690183971093882640779808546479195604743230137113293752897968332220989640710311998150108315298333817030634179487075421403617790823560886688860928133117536724977888683732478708628314857313700596522339509581915323452695136877802816003353853220986492007970183551041303875958750496892867954477510966708935358534322867404860267180294538231734184176727805289746004999969923736528783436876728104351783351879340959568183101515294393048651825n = 19873634983456087520110552277450497529248494581902299327237268030756398057752510103012336452522030173329321726779935832106030157682672262548076895370443461558851584951681093787821035488952691034250115440441807557595256984719995983158595843451037546929918777883675020571945533922321514120075488490479009468943286990002735169371404973284096869826357659027627815888558391520276866122370551115223282637855894202170474955274129276356625364663165723431215981184996513023372433862053624792195361271141451880123090158644095287045862204954829998614717677163841391272754122687961264723993880239407106030370047794145123292991433d = wienerAttack(e, n)print(d)c = 6755916696778185952300108824880341673727005249517850628424982499865744864158808968764135637141068930913626093598728925195859592078242679206690525678584698906782028671968557701271591419982370839581872779561897896707128815668722609285484978303216863236997021197576337940204757331749701872808443246927772977500576853559531421931943600185923610329322219591977644573509755483679059951426686170296018798771243136530651597181988040668586240449099412301454312937065604961224359235038190145852108473520413909014198600434679037524165523422401364208450631557380207996597981309168360160658308982745545442756884931141501387954248m = pow(c,d,n)print(long_to_bytes(m))
Wiener攻击学习:
需要学习连分数相关知识 Michael J.Wiener的论文
参考:https://cryptohack.gitbook.io/cryptobook/untitled/low-private-component-attacks/wieners-attack
使用wiener攻击的前提条件
- d相比于n足够小 满足范围 d < 13n4 \frac{1}{3}\sqrt[4]{n}314n
- q < p < 2q
- e < phi(n)
获得d的原理:
ed = 1 mod phi(n)
即有 ed = k phi(n) + 1
又因为phi(n) = (p-1)(q-1) = pq - (p+q) + 1 约等于 pq = n
此外在ed式子中1所占值太小 可以忽略
即有
ed = kn
=> e n= k d\frac{e}{n}=\frac{k}{d} ne=dk因为e和n已知 利用连分数的展开方法去尝试爆破d的值
那么连分数又是什么 又该如何获得连分数呢?
首先我们对== 43 19\frac{43}{19} 1943==进行连分数展开
- 4319 \frac{43}{19}1943 = 2 + 519 \frac{5}{19}195
需要把分数部分的分子转化为1
- 4319 \frac{43}{19}1943 = 2 + 1 19 5 \frac{1}{\frac{19}{5}}5191 = 2 + 1 3 + 45\frac{1}{3+\frac{4}{5}}3+541 = 2 + 1 3 + 1 5 4\frac{1}{3+\frac{1}{\frac{5}{4}}}3+4511 = 2 + 1 3 + 1 1 + 14 \frac{1}{3+\frac{1}{1+\frac{1}{4}}}3+1+4111
如果对 1 4\frac{1}{4} 41 再进行转化的话余数为0 所以展开到此结束
那么如此繁琐的算法 我们解题的时候肯定不能手算吧 所以一种方法是上面我们自己写代码实现这种算法 基于欧几里得思想 遇到余数为0停止
另一种稍微简单的方法是直接使用sagemath 因为这里面的函数自带解连分数的功能
#sagee=43n=19cf = continued_fraction(e/n)print(cf)convergents = cf.convergents()print(convergents)#[2; 3, 1, 4]#[2, 7/3, 9/4, 43/19]
转换为分数的形式 每一次都是抛弃套娃的最后一次计算 比如第二次就是为 2 + 1 3\frac{1}{3} 31 = 7 3\frac{7}{3} 37
同理带入到我们要解决的问题中 我们想要获得的k和d 就存在这其中一次的结果中
所以对每组进行尝试即可
简易版exp:
#sagee = 8614531087131806536072176126608505396485998912193090420094510792595101158240453985055053653848556325011409922394711124558383619830290017950912353027270400567568622816245822324422993074690183971093882640779808546479195604743230137113293752897968332220989640710311998150108315298333817030634179487075421403617790823560886688860928133117536724977888683732478708628314857313700596522339509581915323452695136877802816003353853220986492007970183551041303875958750496892867954477510966708935358534322867404860267180294538231734184176727805289746004999969923736528783436876728104351783351879340959568183101515294393048651825n = 19873634983456087520110552277450497529248494581902299327237268030756398057752510103012336452522030173329321726779935832106030157682672262548076895370443461558851584951681093787821035488952691034250115440441807557595256984719995983158595843451037546929918777883675020571945533922321514120075488490479009468943286990002735169371404973284096869826357659027627815888558391520276866122370551115223282637855894202170474955274129276356625364663165723431215981184996513023372433862053624792195361271141451880123090158644095287045862204954829998614717677163841391272754122687961264723993880239407106030370047794145123292991433cf = continued_fraction(e/n)# print(cf)convergents = cf.convergents()# print(convergents)reald = 1for kd in convergents:k = kd.numerator() #获取分子d = kd.denominator() #获取分母if pow(2,e*d,n) == 2: #判断我们的d能否在这个加密体系中对2加密之后成功还原为2 还原成功则证明解密正确reald = dprint(d)breakc = 6755916696778185952300108824880341673727005249517850628424982499865744864158808968764135637141068930913626093598728925195859592078242679206690525678584698906782028671968557701271591419982370839581872779561897896707128815668722609285484978303216863236997021197576337940204757331749701872808443246927772977500576853559531421931943600185923610329322219591977644573509755483679059951426686170296018798771243136530651597181988040668586240449099412301454312937065604961224359235038190145852108473520413909014198600434679037524165523422401364208450631557380207996597981309168360160658308982745545442756884931141501387954248from Crypto.Util.number import *print(long_to_bytes(int(pow(c,d,n))))#2357048593#b'flag{learn_some_continued_fraction_technique#dc16885c}'
Baby_xor
考点:异或
源码:
from secret import *ciphertext = []for f in flag:ciphertext.append(f ^ key)print(bytes(ciphertext).hex())# e9e3eee8f4f7bffdd0bebad0fcf6e2e2bcfbfdf6d0eee1ebd0eabbf5f6aeaeaeaeaeaef2
真服 一个简单的异或把我难住了 去cryptohack怒刷xor题目
flag = "e9e3eee8f4f7bffdd0bebad0fcf6e2e2bcfbfdf6d0eee1ebd0eabbf5f6aeaeaeaeaeaef2"flag = bytes.fromhex(flag)from pwn import xor#相当于对key进行暴力猜测for i in range(256):if b'f' or b'F' in xor(flag,i):print(xor(flag,i))#flag{x0r_15_symm3try_and_e4zy!!!!!!}
Affine
考点:仿射密码
源码:
from flag import flag, keymodulus = 256ciphertext = []for f in flag:ciphertext.append((key[0]*f + key[1]) % modulus)print(bytes(ciphertext).hex())# dd4388ee428bdddd5865cc66aa5887ffcca966109c66edcca920667a88312064
很明显在加密程序中存在两个位置量 key[0] 和 key[1] 作为密钥a和b
其实仿射密码的本质还是解方程得到a和b
关于仿射密码参考:https://blog.csdn.net/jayq1/article/details/130474510" />import gmpy2#key = '****CENSORED***************'#密钥 censored 被遮盖flag = 'flag{*******CENSORED********}'#部分明文data = "dd4388ee428bdddd5865cc66aa5887ffcca966109c66edcca920667a88312064" #密文待解密encrypted = bytes.fromhex(data) # encrypted = data# print(encrypted)plaindelta = ord(flag[2]) - ord(flag[1])print(plaindelta)cipherdalte = encrypted[2] - encrypted[1]print(cipherdalte)modulus = 256a = gmpy2.invert(plaindelta, modulus) * cipherdalte % modulusb = (encrypted[1] - a * ord(flag[1])) % modulusprint(a,b)# a = 17# b = 23a_inv = gmpy2.invert(a, modulus)result = ""for c in encrypted:result += chr((c - b) * a_inv % modulus)print(result)#flag{4ff1ne_c1pher_i5_very_3azy}#------------------算法解释--------------------------# #对密文进行操作# # range(0,len(cip),2):生成一个从 0 开始、以 2 为步长、不超过 data 长度的整数序列。这个序列中的每一个整数 i,都是 data 中两个字符的起始位置。# # data[i:i+2]:对于这个序列中的每一个整数 i,取 data 中从 i 开始、长度为 2 的子串。这样就可以把 data 中的所有字符两两分组。# # int(data[i:i+2],16):对于这个子串,使用 int 函数将其转换为一个 16 进制的整数。# # for i in range(0,len(data),2):将上述操作应用到 range 函数生成的整数序列中的每一个整数 i,得到一个整数列表。# # 最终的结果就是 encrypted 列表,它包含了 data 中所有字符两两分组后转换成的 16 进制整数。例如,如果 data 是字符串 "805eed80cbbccb94c36413275780ec94a857dfec8da8ca94a8c313a8ccf9",则 encrypted 是一个整数列表,它的值为 [128, 94, 237, 128, 203, 188, 185, 76, 195, 100, 19, 39, 87, 128, 236, 148, 168, 87, 223, 236, 141, 168, 202, 148, 168, 195, 19, 168, 204, 249]。# #去计算密钥key# print(encrypted[0])# plaindelta = (ord(flag[1]) - ord(flag[0]))#提取部分已知明文# cipherdalte = (encrypted[1] - encrypted[0]) #提取已知密文# print(cipherdalte)# #原来的加密公式# #y1 = (a * x1 + b) (mod 256)# #y2 = (a * x2 + b) (mod 256)# #故有# #y1 - y2 = a*(x1-x2)(mod 256) # #想要求a# #即为# #plaindelta = x1-x2# #cipherdalta = y1-y2# #cipherdalte = a * plaindelta(mod 256)!!取模只是让最后的结果在0~251范围之间!!# #计算公式中的a,引入初等数论里面的内容# #ci = a * p % 256# #ci / p = a % 256# #在初等数论中 除相当于乘逆元# #故 ci * invert(p,256)= a (% 256) # #所以%251只是一个对于整体结果的一个限制,在公式推到的时候可以忽略存在# a = gmpy2.invert(plaindelta, 257) * cipherdalte %256#数据全部放在0~251之间# #通过a计算b# b = (encrypted[0] - a * ord(flag[0]))%256#数据全部放在0~251之间# a_inv = gmpy2.invert(a, 257)# result = ""# for c in encrypted:# result += chr(((c-b) * a_inv) % 256)# print(result)
BabyEncoding
part 1 of flag: ZmxhZ3tkYXp6bGluZ19lbmNvZGluZyM0ZTBhZDQ=flag{dazzling_encoding#4e0ad4f0ca08d1e1d0f10c0c7afe422fea7c55192c992036ef623372601ff3a}part 2 of flag: MYYGGYJQHBSDCZJRMQYGMMJQMMYGGN3BMZSTIMRSMZSWCNY=part 3 of flag: =8S4U,3DR8SDY,C`S-F5F-C(S,S<R-C`Q9F8S87T`
前两部分赛博厨子直接出
最后一个部分采用Unencode编码 去使用bugku的工具https://ctf.bugku.com/tools
babyaes
考点:异或 aes中cbc模式
emm怎么讲嘞 虽然这个题叫做aes 但是真实的考点还是想考查大家的异或能力
附上源码:
from Crypto.Cipher import AESimport osfrom flag import flagfrom Crypto.Util.number import *def pad(data):return data + b"".join([b'\x00' for _ in range(0, 16 - len(data))])def main():flag_ = pad(flag)key = os.urandom(16) * 2 #32字节iv = os.urandom(16) #16字节print(bytes_to_long(key) ^ bytes_to_long(iv) ^ 1)aes = AES.new(key, AES.MODE_CBC, iv)enc_flag = aes.encrypt(flag_)print(enc_flag)if __name__ == "__main__":main()# 3657491768215750635844958060963805125333761387746954618540958489914964573229# b'>]\xc1\xe5\x82/\x02\x7ft\xf1B\x8d\n\xc1\x95i'
先带大家分析一下这个题目啊,首先是因为要进行cbc的块加密模式嘛,所以把flag进行pad操作,如果不满足16位就在后面填充
\x00
然后获得key和iv:
key
: 随机生成加密的16个字节 重复2次 拼接
iv
: 随机生成加密的16个字节然后利用到异或的知识对泄露的内容进行利用:
首先将泄露的数据与1异或 就会得到key和iv的异或值
然后我们在编码体系中看一下
bytes_to_long
和long_to_bytes
函数的实现方法都是针对单一字节独自操作的 就是对整体没有影响
然后我们写一个小demo测试一下当两个不等位的数据进行异或的对齐方式:
test1 = 0b1110011test2 = 0b11# print(bin(test1 ^ test2)) #0b1110000#结果显示是低位对齐进行异或
所以key和iv进行异或 因为key是iv的两倍 所以对于key的高16位字节没有影响
所以我们就可以直接得到key的组成:
key = long_to_bytes(data)[:16]*2
然后将
key ^ (key ^ iv) = iv
完整exp:
import osfrom Crypto.Util.number import *from Crypto.Cipher import AESdata = 3657491768215750635844958060963805125333761387746954618540958489914964573229data = data ^ 1key = long_to_bytes(data)[:16]*2iv = bytes_to_long(key) ^ dataiv = long_to_bytes(iv)aes = AES.new(key, AES.MODE_CBC, iv)c = b'>]\xc1\xe5\x82/\x02\x7ft\xf1B\x8d\n\xc1\x95i'dec_flag = aes.decrypt(c)print(len(key),len(iv))print("flag=",dec_flag)#flag= b'firsT_cry_Aes\x00\x00\x00'
我是哈皮,祝您每天嗨皮!我们下期再见~