题目如下:

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. 关于private子网访问s3时报错:Connect timeout on endpoint URL

    今天在使用private私有子网,通过aws s3命令访问s3时,报如下错误: [qq_5201351@private ~]$ aws s3 ls Connect timeout on endpoin ...

  2. vulnhub靶场之THE PLANETS: EARTH

    准备: 攻击机:虚拟机kali.本机win10. 靶机:THE PLANETS: EARTH,网段地址我这里设置的桥接,所以与本机电脑在同一网段,下载地址:https://download.vulnh ...

  3. Linux-->vi和vim编辑器的基本操作

    vim编辑器介绍 vi或者vim就是对linux下的文本进行编辑的一种编辑器比如说a.cpp文件这种 Linux会内置vi文本编辑器 Vim可以简单的认为vi的增强版 Linux是区分大小写的! 用法 ...

  4. Maven中POM文件总体配置说明

    POM文件总体配置说明 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://w ...

  5. java集合框架复习----(1)

    文章目录 1 .集合框架思维导图 一.什么是集合 二.collection接口 1 .集合框架思维导图 一.什么是集合 存放在java.util.*.是一个存放对象的容器. 存放的是对象的引用,不是对 ...

  6. Pycharm和IDEA利用Git操作Github仓库

    1. Git Bash 选择一个本地代码仓库文件夹:D:/Github_Code/新建文件夹,然后在此目录打开git bash 依次进行: git init //首次需执行,之后可不用 git add ...

  7. go基础语法50问,来看看你的go基础合格了吗?

    目录 1.使用值为 nil 的 slice.map会发生啥 2.访问 map 中的 key,需要注意啥 3.string 类型的值可以修改吗 4.switch 中如何强制执行下一个 case 代码块 ...

  8. Pyhton基础部分:6、与用户交互、基础运算符

    目录 一.python数据类型 1.布尔值bool 2.元组tuple 3.集合set 二.与用户交互 1.获取用户输入 2.输出内部信息 3.语言环境差异 三.格式化输入 1.代码实现 2.注意事项 ...

  9. TKK: 更新 TKK 失败,请检查网络连接 idea翻译错误-IDEA翻译失败-Translation用不了

    IDEA 提示:更新 TKK 失败,请检查网络连接 解决方法: 1.进入 C:\Windows\System32\drivers\etc 找到 hosts文件修改 注意:如果用记事本打开不能修改,则修 ...

  10. perl reverse函数

    转载至  Perl - 列表 - reverse 操作 reverse(逆转)操作将输入的一串列表(可能是数组)按相反的顺序返回. my @arr=("Head_PMA1",&qu ...