[PingCTF2022] guess what - S1gMa & Shumu
前言
本题来自PingCTF2022 - guess what,早上12点被树木喊起来对超极长的代码审计和写
题面
本题是到交互题,题面给了
代码按照顺序给出:
from src.common import *
from src.pow import NcPowser
from src.part1 import part1_, part1
from src.part2 import part2_, part2
from src.part3 import part3_, part3
def main():
part1_()
input("Press enter to continue...")
for i in range(2, 18):
part1(i)
part2_()
input("Press enter to continue...")
for i in range(2, 6):
part2(6)
part3_()
input("Press enter to continue...")
part3()
if __name__ == '__main__':
if (SHOULD_USE_POW):
nc = NcPowser()
if nc.pow():
main()
else:
print("Wrong answer")
exit(0)
else:
main()
from time import time
import itertools
import random
from Crypto.Util.number import bytes_to_long
from progress.bar import ChargingBar
from time import *
import os
SHOULD_USE_POW = True
SHOULD_USE_ANNOYING_ANIMATIONS = True
intro_dictionary = "AB"
mid_dictionary = "ABCD"
def brrr_the_strings(strings):
print('PRINTING...')
for i in range(len(strings)):
print(strings[i])
print('DONE PRINTING')
from src.common import *
def part1_():
print("Hi, this is my game :)")
print("I will give you some sTrInGs, and you will have to tell me, which one is missing, seems easy, right? :D")
print("Let's try it out!")
def part1(l):
if (SHOULD_USE_ANNOYING_ANIMATIONS):
for i in ChargingBar("Loading sTrInGs", max=16, check_tty=False).iter(range(16)):
sleep(0.1)
strings = ["".join(x)
for x in itertools.product(intro_dictionary, repeat=l)]
indexToRemove = bytes_to_long(os.urandom(32)) % len(strings)
removedString = strings[indexToRemove]
strings.remove(removedString)
random.shuffle(strings)
brrr_the_strings(strings)
print("Which one is missing?")
guess = input("> ")
if guess == removedString:
print("Correct!")
else:
print("Wrong!!!!! Cmon, you can do it!")
exit(0)
from src.common import *
def part2_():
print("You are doing great! Now, let's try something harder!")
print("I will give you AGAIN some StRiNgS, and you will have to tell me, which one is missing, seems still doable, right? :D")
print("But I need you to hurry this time, so you will have to guess the missing string in 5 seconds.")
print("Let's try it out!")
def part2(l):
if (SHOULD_USE_ANNOYING_ANIMATIONS):
for i in ChargingBar("Loading StRiNgS", max=32, check_tty=False).iter(range(32)):
sleep(0.1)
strings = ["".join(x) for x in itertools.product(mid_dictionary, repeat=l)]
indexToRemove = bytes_to_long(os.urandom(32)) % len(strings)
removedString = strings[indexToRemove]
strings.remove(removedString)
random.shuffle(strings)
brrr_the_strings(strings)
print("Which one is missing?")
guess = input("> ")
if guess == removedString:
print("Correct!")
else:
print("Wrong!!!!! Cmon, you can do it!")
exit(0)
from src.common import *
from flag import flag
assert(len(flag) == 2**(2**2))
def part3_():
print("Ok. This is kinda spooky. This time I will show you that I know everything, and you will have to prove me wrong in order to get the flag.")
def part3():
real_flag = flag[5:][:-1]
if (SHOULD_USE_ANNOYING_ANIMATIONS):
for i in ChargingBar("Loading flags", max=64, check_tty=False).iter(range(64)):
sleep(0.1)
flags = ["".join(x) for x in itertools.permutations(real_flag)]
flags.remove(real_flag)
random.shuffle(flags)
brrr_the_strings(flags)
print("If you are so smart, then you should be able to give the flag in 15 seconds!")
start = time()
guess = input("> ")
end = time()
if guess == flag and end - start <= 15:
print("Correct! Here is your flag: " + flag)
else:
print("Well, at least I can rest. GL")
exit(0)
import hashlib
import secrets
class NcPowser:
def __init__(self, difficulty=20, prefix_length=17):
self.difficulty = difficulty
self.prefix_length = prefix_length
def get_challenge(self):
prefix = secrets.token_hex(self.prefix_length)
rest = secrets.token_hex(self.difficulty - self.prefix_length)
return prefix, rest
def pow(self):
prefix, rest = self.get_challenge()
print(
f"sha256(\"{prefix} + {'?'*(len(rest))}\") == \"{hashlib.sha256((prefix + rest).encode()).hexdigest()}\"")
answer = input("> ")
if hashlib.sha256((prefix + answer).encode()).hexdigest() == hashlib.sha256((prefix + rest).encode()).hexdigest():
return True
else:
return False
def solve_pow(self, prefix, result, unknown_count):
from itertools import product
possibilities = product("0123456789abcdef", repeat=unknown_count)
for ans in possibilities:
answer = "".join(ans)
if hashlib.sha256((prefix + answer).encode()).hexdigest() == result:
return answer
if __name__ == '__main__':
print("Solving PoW...")
nc = NcPowser()
# sha256("dd32ded3ce6a9c864b5b2a0c364003b409 + ??????") == "e46f470c74eff9629dd828c0bfada1ff87bbeede19cdcd3fbcac8684a07b1384"
prefix = "dd32ded3ce6a9c864b5b2a0c364003b409"
result = "e46f470c74eff9629dd828c0bfada1ff87bbeede19cdcd3fbcac8684a07b1384"
unknown_count = 6
solution = nc.solve_pow(prefix, result, unknown_count)
print(f"Solution: {solution}")
exit(0)
审计代码:
main.py
def main():
part1_()
input("Press enter to continue...")
for i in range(2, 18):
part1(i)
part2_()
input("Press enter to continue...")
for i in range(2, 6):
part2(6)
part3_()
input("Press enter to continue...")
part3()
显然是按照顺序进行闯关,不过这里要注意循环中读进去的参数(对后面很重要)。
common.py
intro_dictionary = "AB"
mid_dictionary = "ABCD"
这里给出的是后面两个
pow.py
sha256("dd32ded3ce6a9c864b5b2a0c364003b409 + ??????") =="e46f470c74eff9629dd828c0bfada1ff87bbeede19cdcd3fbcac8684a07b1384"
prefix = "dd32ded3ce6a9c864b5b2a0c364003b409"
这里就可以看出,第一步是sha256的爆破,6位的掩码爆破,差不多要跑十分钟(后面错一次就要跑这一次出题人坏b)
EXP
p = remote("guess_what.ctf.knping.pl",20000)
str=p.recvuntil(b'> ')
key=str[8:-84].decode()
h=str[58:-4].decode()
print(key)
print(h)
for i in range(16777216):
tmp = key + hex(i).zfill(6)[2:8]
if hashlib.sha256(tmp.encode('utf-8')).hexdigest() == h:
print(tmp)
break
print(tmp[-6:])
p.sendline(tmp[-6:])
p.recvuntil(b'Press enter to continue...')
p.sendline()
part1.py
strings = ["".join(x) for x in itertools.product(intro_dictionary, repeat=l)]
这里是字符串的生成规则,
removedString = strings[indexToRemove]
strings.remove(removedString)
random.shuffle(strings)
然后将其获取到的字符串数组打乱,随机删去一个数,然后猜删了什么。
EXP
EXP在分析完题目后就非常好写了,只需要讲排列组合列出来对生成的字符串进行比对然后
for l in range(2,18):
p.recvuntil(b'PRINTING...\n')
keyword=p.recvuntil(b'DONE PRINTING\n',drop=True)
keyword=keyword[:-1].decode()
keywordlist=keyword.split("\n")
#print(keyword)
#print(keywordlist)
for e in itertools.product('AB', repeat=len(keywordlist[0])):
s = ''.join(e)
if s not in keywordlist:
p.sendline(s)
p.recvuntil(b'Press enter to continue...')
p.sendline()
对输出字符串进行切割利用 split("\n") 进行转为数组(群里大佬教的,长见识了),然后根据题意利用循坏改变
part2.py
strings = ["".join(x) for x in itertools.product(mid_dictionary, repeat=l)]
思路和
EXP
for l in range(2,6):
p.recvuntil(b'PRINTING...\n')
keyword=p.recvuntil(b'DONE PRINTING\n',drop=True)
keyword=keyword[:-1].decode()
keywordlist=keyword.split("\n")
#print(keyword)
#print(keywordlist)
for e in itertools.product('ABCD', repeat=len(keywordlist[0])):
s = ''.join(e)
if s not in keywordlist:
p.sendline(s)
p.recvuntil(b'Press enter to continue...')
p.sendline()
part3.py
assert(len(flag) == 2**(2**2))
real_flag = flag[5:][:-1]
flags = ["".join(x) for x in itertools.permutations(real_flag)]
flags.remove(real_flag)
random.shuffle(flags)
brrr_the_strings(flags)
if guess == flag and end- start <= 15:
以上是关键代码,意思分别是告诉
EXP
看到全排列就知道有
p.recvuntil(b'PRINTING...\n')
keyword=p.recvuntil(b'DONE PRINTING\n',drop=True)
keyword=keyword[:-1].decode()
keywordlist=keyword.split("\n")
flags = ["".join(x) for x in itertools.permutations('i8a9eF2d4n')]
keywordlist.sort()
flags.sort()
p.recvuntil(b'> \n')
for i in range(0,10**10):
if keywordlist[i] != flags[i]:
p.sendline("ping{"+flags[i]+"}")
break
p.interactive()
完整EXP
import itertools
from pwn import *
import hashlib
context.log_level='debug'
p = remote("guess_what.ctf.knping.pl",20000)
str=p.recvuntil(b'> ')
key=str[8:-84].decode()
h=str[58:-4].decode()
print(key)
print(h)
for i in range(16777216):
tmp = key + hex(i).zfill(6)[2:8]
if hashlib.sha256(tmp.encode('utf-8')).hexdigest() == h:
print(tmp)
break
print(tmp[-6:])
p.sendline(tmp[-6:])
p.recvuntil(b'Press enter to continue...')
p.sendline()
#part1
for l in range(2,18):
p.recvuntil(b'PRINTING...\n')
keyword=p.recvuntil(b'DONE PRINTING\n',drop=True)
keyword=keyword[:-1].decode()
keywordlist=keyword.split("\n")
#print(keyword)
#print(keywordlist)
for e in itertools.product('AB', repeat=len(keywordlist[0])):
s = ''.join(e)
if s not in keywordlist:
p.sendline(s)
p.recvuntil(b'Press enter to continue...')
p.sendline()
#part2
for l in range(2,6):
p.recvuntil(b'PRINTING...\n')
keyword=p.recvuntil(b'DONE PRINTING\n',drop=True)
keyword=keyword[:-1].decode()
keywordlist=keyword.split("\n")
#print(keyword)
#print(keywordlist)
for e in itertools.product('ABCD', repeat=len(keywordlist[0])):
s = ''.join(e)
if s not in keywordlist:
p.sendline(s)
p.recvuntil(b'Press enter to continue...')
p.sendline()
#getflag
p.recvuntil(b'PRINTING...\n')
keyword=p.recvuntil(b'DONE PRINTING\n',drop=True)
keyword=keyword[:-1].decode()
keywordlist=keyword.split("\n")
flags = ["".join(x) for x in itertools.permutations('i8a9eF2d4n')]
keywordlist.sort()
flags.sort()
p.recvuntil(b'> \n')
for i in range(0,10**10):
if keywordlist[i] != flags[i]:
p.sendline("ping{"+flags[i]+"}")
break
p.interactive()
结语
以上便是本题的所有内容了,总体来说不是很难,主要考验代码审计能力和优化能力,是挺有意思的一道题~
感谢大家阅读,求求点点关注哦~
会不断更新各个比赛的wp、题目分享、出题笔记、学习笔记等。
碎碎念
阳了真的真的真的真的好难受呜呜呜呜呜呜。。。。。。咩。。。。。。。。。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通