题目如下:

from random import randint
from base64 import b64encode def validate(password: str) -> bool:
if len(password) != 49:
return False key = ['vs'.join(str(randint(7, 9)) for _ in range(ord(i))) + 'vs' for i in password[::-2]]
gate = [118, 140, 231, 176, 205, 480, 308, 872, 702, 820, 1034, 1176, 1339, 1232, 1605, 1792, 782, 810, 1197, 880,
924, 1694, 2185, 2208, 2775]
if [randint(a, b[0]) for a, b in enumerate(zip(gate, key), 1) if len(b[1]) != 3 * (b[0] + 7 * a) // a]:
return False hammer = {str(a): password[a] + password[a + len(password) // 2] for a in range(1, len(password) // 2, 2)}
block = b'c3MxLnRkMy57XzUuaE83LjVfOS5faDExLkxfMTMuR0gxNS5fTDE3LjNfMTkuMzEyMS5pMzIz'
if b64encode(b'.'.join([((b + a).encode()) for a, b in hammer.items()])) != block:
return False return True if __name__ == "__main__":
passwd = input('Please validate your ID using your password\n> ')
if validate(passwd):
print('Access Granted: You now have gained access to the View Source Flag Vault!')
else:
print('Access Denied :(')

源码分析:

这个题的难点就在于代码过于python化,核心内容全是推导式。读懂代码这题就迎刃而解了。(也不是,如果解题代码写的不对,得到的也是错误flag,没错,说的就是本人这个弱鸡)

如果你也没读懂代码,那就跟着我思路来看看,或许会让你豁然开朗。如果你也明白了代码里的含义,可以跳过这个部分看看解题代码,我猜你只是需要这个。废话不多说,开搞。

先看 if __name__ == "__main__" 发现比较重要的是调用了validate函数,把我们输入的结果passwd传了进去。

对validate函数审计,因为我们是要破解,所以应当,也应该如此的从后面开始看。我们一眼就发现密文block,然后在 if 判断出告诉了我们block是base64加密后的结果,我们果断解一下。

content = 'c3MxLnRkMy57XzUuaE83LjVfOS5faDExLkxfMTMuR0gxNS5fTDE3LjNfMTkuMzEyMS5pMzIz'
print(base64.b64decode(content))

得到结果:

ss1.td3.{_5.hO7.5_9._h11.L_13.GH15._L17.3_19.3121.i323

继续对 if 分析,我们发现有一个列表推导式:[((b + a).encode()) for a, b in hammer.items()]

for 前面的内容是属于 for 循环里面的内容,所以这里意思是把 hammer 字典中 key 和 value 分别赋值给 a 和 b ,注意啊,这里 b 和 a 都是字符串,因此它们相加是拼接到一起。这里的encode的作用不大,使用默认的编码格式。

然后就把 a 和 b 生成的新字符串加到 前面的字符串中,然后以 点 分隔。

通过以上,我们就知道了hammer字典中用哪些内容,于是写个代码提取一下:

cipertext = 'ss1.td3.{_5.hO7.5_9._h11.L_13.GH15._L17.3_19.3121.i323'
print(len(cipertext)) flag_key = {}
j = 1
for i in cipertext.split('.'):
flag_key.update({j: i[0:2]})
j += 2
print(flag_key)

得到结果:

{1: 'ss', 3: 'td', 5: '{_', 7: 'hO', 9: '5_', 11: '_h', 13: 'L_', 15: 'GH', 17: '_L', 19: '3_', 21: '31', 23: 'i3'}

然后对hammer字典分析,发现是个字典推导式:

key = str(a)

value = password[a] + password[a + len(password) // 2]

for 循环从数字0 开始 每次步长为 2 ,结束为 password 的长度,看一下代码就知道 len(password) = 49

对以上分析我们就知道了 password[1] = s,pasword[25] = s,pasword[3] = t,,pasword[27] = d  以此类推,写个代码:

key_1 = ''
key_2 = ''
key_content = ''
# b_lenth=[375, 231, 252, 153, 144, 261, 153, 348, 255, 267, 303, 315, 330, 285, 342, 357, 159, 156, 210, 153, 153, 252, 306, 297, 354]
for a in flag_key:
key_1 += flag_key[a][0:1]
key_2 += flag_key[a][1:2] key_content += key_1 + ' ' + key_2
print(key_content)
#print(len(key_content))

得到结果flag1:

st{h5_LG_33i  sd_O_h_HL_13

继续对源码分析,在 if 处又发现一个列表推导式:[randint(a, b[0]) for a, b in enumerate(zip(gate, key), 1) if len(b[1]) != 3 * (b[0] + 7 * a) // a]

复杂推导式的读取原则是:从左边开始的第一个 for 开始读,属于 for 左边的为最后结果,右边则属于 for 循环里的下级内容。(自己总结的,如果不理解可以去百度了解更多)

enumerate函数的链接:Python enumerate() 函数 | 菜鸟教程 (runoob.com)

zip函数的链接:Python zip() 函数 | 菜鸟教程 (runoob.com)

这里一开始我也琢磨了好久,后面才知道 a 是从 1 开始的整数数列,通式为 n = n + 1 , 而 b[0] 就是 gate列表的内容,因为 zip 函数返回的是元组并且赋值给的 b,而 b[0] 正是元组中的第一个参数gate的值,因为 if 是在 for 里面的,所以 b [0] 动态变化。

通过上述我们知道,出题人要求我们使得 len(b[1]) = 3 * (b[0] + 7 * a) // a,而 b[1] 的内容 则是key列表的内容,写个代码提取一下:

gate = [118, 140, 231, 176, 205, 480, 308, 872, 702, 820, 1034, 1176, 1339, 1232, 1605, 1792, 782, 810, 1197, 880,924, 1694, 2185, 2208, 2775]
b_lenth = []
for i in range(len(gate)):
b_lenth.append(3 * (gate[i] + 7 * (i + 1)) // (i + 1))
print(b_lenth)

得到结果:

# b_lenth=[375, 231, 252, 153, 144, 261, 153, 348, 255, 267, 303, 315, 330, 285, 342, 357, 159, 156, 210, 153, 153, 252, 306, 297, 354]

我们继续对代码分析,发现了一个更加复杂的列表推导式 key,我们在前面总结了复杂推导式的读取原则,但是你应该明白这里的列表推导式的 “第一个” for 应该为 最后一个。

所以我们在前面的复杂推导式读取原则应该加上:如果遇到的第一个 for 是在小括号中,则往后找第一个 for

这里的 key 列表推导式我们展开来写,如下:

#假设password = ’ vsctf‘
password = 'vsctf'
key = ['vs'.join(str(7) for _ in range(ord(i))) + 'vs' for i in password[::2]]
print(key[0])
print(len(key[0])) key1 = []
content = ''
for i in password[::2]:
for _ in range(ord(i)):
content += 'vs'.join(str(7)) #因为这里join中传入参数不是序列,所以前面的'vs'不起作用,则 content = '7'
content += 'vs'
key1.append(content)
break #如果要和上述列表推导式相等,这里break实际没有
print(key1[0])
print(len(key1[0]))

得到结果如下:

发现是一模一样的,如果你这里感到奇怪,那一定是对 join 函数的不理解:Python join()方法 | 菜鸟教程 (runoob.com)

通过对key 列表推导式分析发现,它是取 password 中 偶数索引的内容,然后将内容转换为ascii码数值,作为循环的次数,每次循环取7 到 9中随机一个整数加 ’vs' 字符串。

所以我们只要知道有多少个整数,就知道了这个ASCII码数值是多少,从而知道 password 中偶数索引的内容。

因为前面我们已经算出了b[1]长度,又因为这里 for 每次都加上 'vs' 两个字符串,所以在原来的基础上扩大了3倍,因此只要 把 b_lenth 列表中每个元素除于 3 再转换即得flag2内容。

# b_lenth=[375, 231, 252, 153, 144, 261, 153, 348, 255, 267, 303, 315, 330, 285, 342, 357, 159, 156, 210, 153, 153, 252, 306, 297, 354]

key = ''
for i in b_lenth:
# print(chr(i // 3))
key += chr(i // 3)
print(key)

结果如下:

最后我们从flag2后面开始拿一个,从flag1 前面拿一个,然后重复前面,即得最终flag

综上所述,我们可以写出完整解题脚本。

完整解题代码:

#coding:utf-8
import base64 content = 'c3MxLnRkMy57XzUuaE83LjVfOS5faDExLkxfMTMuR0gxNS5fTDE3LjNfMTkuMzEyMS5pMzIz'
cipertext = base64.b64decode(content)
print(len(cipertext)) flag_key = {}
j = 1
for i in cipertext.split('.'):
flag_key.update({j: i[0:2]})
j += 2
print(flag_key) flag = ''
k = -1
for a in flag_key:
flag += chr(b_lenth[k] // 3) + flag_key[a][0:1] #偶数索引内容 + 奇数索引内容,从password字符串的索引0开始,到索引23结束
k += -1
if len(flag) == 24:
print(flag)
k = -13
for b in flag_key:
flag += chr(b_lenth[k] // 3) + flag_key[b][1:2] #从索引24开始,到索引48结束
k += -1
flag += chr(b_lenth[0] // 3) #加上索引49
break
print(flag)

得到flag:

VSCTF的Recovery的更多相关文章

  1. POJ 2255. Tree Recovery

    Tree Recovery Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 11939   Accepted: 7493 De ...

  2. Android手机刷recovery

    以前觉得android刷机是件很麻烦的事,现在倒不觉得了.  只要手机刷入第三方的recovery,一切都好办了,无论是root还是刷google play.  recovery开源的有两大阵营,tw ...

  3. Change the Target Recovery Time of a Database (SQL Server) 间接-checkpoints flushcache flushcache-message

    Change the Target Recovery Time of a Database (SQL Server) 间接checkpoints   flushcache flushcache-mes ...

  4. SQL Server恢复软件 Stellar Phoenix sql recovery

    SQL Server恢复软件 Stellar Phoenix sql recovery http://www.stellarinfo.com/ http://www.stellarinfo.com/ ...

  5. SQL Server恢复软件SysTools SQL Recovery/SysTools SQL Server Recovery Manager

    SQL Server恢复软件SysTools SQL Recovery/SysTools SQL Server Recovery Manager http://www.systoolsgroup.co ...

  6. SQL Server通过File Header Page来进行Crash Recovery

    SQL Server通过File Header Page来进行Crash Recovery 看了盖总的一篇文章 http://www.eygle.com/archives/2008/11/oracle ...

  7. DataBase异常状态:Recovery Pending,Suspect,估计Recovery的剩余时间

    一,RECOVERY PENDING状态 今天修改了SQL Server的Service Account的密码,然后重启SQL Server的Service,发现有db处于Recovery Pendi ...

  8. Oracle Recovery 01 - 常规恢复之完全恢复

    背景:这里提到的常规恢复指的是数据库有完备可用的RMAN物理备份. 实验环境:RHEL6.4 + Oracle 11.2.0.4 DG primary. 一.常规恢复之完全恢复:不丢失数据 1.1 单 ...

  9. Oracle Recovery 02 - 常规恢复之不完全恢复

    背景:这里提到的常规恢复指的是数据库有完备可用的RMAN物理备份. 实验环境:RHEL6.4 + Oracle 11.2.0.4 单实例. 二.常规恢复之不完全恢复:部分数据丢失 2.1 重做日志文件 ...

  10. 故障恢复和恢复模式(Crash Recovery & Recovery Models)

    数据库的恢复模型是否影响故障恢复,在简单恢复模式里,你是否会丢失事务?在今天的文章里我想谈下这点,详细讨论下. 恢复模式(Recovery Models) 对于这个问题的最简单的答案是不会:恢复模型不 ...

随机推荐

  1. 改善C#程序的方法-2 使用TryParse

    一 使用TryParse,而不是Parse 除string外的所有基元类型,都有两个将string类型转型为其本身类型的方法:Parse 和 TryParse. 以double类型为例,这两个方法最简 ...

  2. 条件期望:Conditional Expectation 举例详解之入门之入门之草履虫都说听懂了

    我知道有很多人理解不了 "条件期望" (Conditional Expectation) 这个东西,有的时候没看清把随机变量看成事件,把 \(\sigma\)-algebra 看成 ...

  3. 修改-Python函数-2

    一.导入 $$f ( x , y ) = 2 x + 3 y$$ 上面括号里面的就是数学公式里的自变量,自变量就相当于函数里的参数. 二.为什么要有参数 如果一个大楼里有两种尺寸不一的窗户,显然在没有 ...

  4. Go_gin权限验证

    权限管理 Casbin是用于Golang项目的功能强大且高效的开源访问控制库. 1. 特征 Casbin的作用: 以经典{subject, object, action}形式或您定义的自定义形式实施策 ...

  5. Java递归查找层级文件夹下特定内容的文件

    递归查找文件 引言 或许是文件太多,想找某个文件又忘记放哪了;又或者是项目改造,需要将外部调用接口进行改造,项目太多,又无法排查.那么怎么快速找到自己想要的内容就是一件值得思考的事情了. 根据特定内容 ...

  6. Centos7使用sendEmail-v1.56发送邮件

    Centos7使用sendEmail-v1.56发送邮件 注意:Centos7默认使用perl5.16,而sendEmail-v.1.56要求使用perl5.10.否则会报以下错误.所以需要下载并安装 ...

  7. resultMap处理字段和属性的映射关系

    1.resultMap处理字段和属性的映射关系 若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射 <!-- resultMap:设置自定义映射 属性: id:表示自定 ...

  8. 微粒群算法PSO 01背包问题 python

    import random import math import matplotlib.pyplot as plt import numpy as np import time def init(b_ ...

  9. Tauri-Vue3桌面端聊天室|tauri+vite3仿微信|tauri聊天程序EXE

    基于tauri+vue3.js+vite3跨桌面端仿微信聊天实例TauriVue3Chat. tauri-chat 运用最新tauri+vue3+vite3+element-plus+v3layer等 ...

  10. pinpoint:查看hbase表和修改数据过期时间

    先做个记录,监控数据量过大时可以设置表的数据过期时间来清理数据. 1. 查找本地数据表大小 [root@ZWZF-CWY-LZY-12 ~]# cd /home/pinpoint/hbase/data ...