首页 > 其他分享 >[PingCTF2022] 题目分享 - S1gMa

[PingCTF2022] 题目分享 - S1gMa

时间:2022-12-18 03:33:07浏览次数:47  
标签:PingCTF2022 keywordlist 题目 keyword S1gMa import print recvuntil strings

前言

本题来自PingCTF2022 - guess what,早上12点被树木喊起来对超极长的代码审计和写 \(exp\) ,俩人之间干到下午 \(6\) 点,对着一个不存在的错误 \(debug\) 了 \(4\) 个小时,然后世一血就没了(悲),于是决定记录下来>_<

题面

本题是到交互题,题面给了 \(6\) 个脚本:\(main.py\)、\(common.py\)、\(part1.py\)、\(part2.py\)、\(part3.py\)、\(pow.py\).

代码按照顺序给出:

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"

这里给出的是后面两个 \(part\) 用来生成字符串的字典。

pow.py

sha256("dd32ded3ce6a9c864b5b2a0c364003b409 + ??????") =="e46f470c74eff9629dd828c0bfada1ff87bbeede19cdcd3fbcac8684a07b1384"
	prefix = "dd32ded3ce6a9c864b5b2a0c364003b409"

这里就可以看出,第一步是sha256的爆破,6位的字典,差不多要跑十分钟

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)]

这里是字符串的生成规则,\(itertools.product\) 指的是对字典里的字符串进行排列组合取数,取数规则就是 \(repert\)。

removedString = strings[indexToRemove]
strings.remove(removedString)
random.shuffle(strings)

然后将其获取到的字符串数组打乱,随机删去一个数,然后猜删了什么。

EXP

EXP在分析完题目后就非常好写了,只需要讲排列组合列出来对生成的字符串进行比对然后 \(sendline\) 即可。

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") 进行转为数组(群里大佬教的,长见识了),然后根据题意利用循坏改变 \(repert\) 的值一层一层爆破即可

part2.py

strings = ["".join(x) for x in itertools.product(mid_dictionary, repeat=l)]

思路和 \(part1\) 同理,惟独改变的是字典

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:

以上是关键代码,意思分别是告诉 \(flag\) 为 \(16\) 位字符串,同时在交互时只保留中心 \(flag\),然后对其进行全排列、乱序、删数,然后必须在15秒内猜到。

EXP

看到全排列就知道有 \(10^{10}\) 个可能性,而且 \(15s\) 的限制,不得不对代码进行优化,因此考虑现生成完整的全排列数列,然后利用 \(sort()\) 同时对两个数组进行排序,然后一一比对遇到不一样的既找到答案,如果不这样干的话将会最多比对 \(C_{10^{10}}^{2}\) 次,时间必然会超时。

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、题目分享、出题笔记、学习笔记等。

碎碎念

阳了真的真的真的真的好难受呜呜呜呜呜呜。。。。。。咩。。。。。。。。。

标签:PingCTF2022,keywordlist,题目,keyword,S1gMa,import,print,recvuntil,strings
From: https://www.cnblogs.com/S1gMa/p/16989935.html

相关文章

  • 群论类题目
    先证一下一些相关的定理。轨道-稳定子定理即:$|G^x|\times|G(x)|=|G|$其中$G$为置换群,$x$为任意元素。$proof:$根据置换群定义:$\varphi(g,\varphi(p,x))=\varphi(......
  • 弗洛伊德算法-考试题目用
    对带权有向图可用v1可以从v0开始写,都可以如下图 填好表格将第一行和第一列填入下一个表,判断第一行或第一列有无穷的,则这个元素的列或行的值填原来的,同时对角线填原理......
  • CISP中关于PING的一个题目
    CISP练习题中,关于PING的一个题目:这个题目应该答案是B:TTLBriefTTL是IP协议包中的一个值,它告诉网络​​路由器​​包在网络中的时间是否太长而应被丢弃。有很多原因使包在一......
  • PTA6-8题目集
    一、前言1、题目集6-8难度适中,电信计费给出了相应类图,根据类图构建好大致框架后只需填充一些方法、完善类的功能就行,其他题目难度不大,但需要注意题目要求。这三次题......
  • 6-8次PTA题目集(电信计费)
     前言电信计费系列题目虽然难度相对于多边形系列有所下降,但涉及知识点很广,主要如下:1、容器的使用2、抛出异常3、抽象类4、继承与多态5、正则表达式6、类和对象电......
  • 6-8次PTA题目集总结
    一,前言1,第六次作业(针对电信计费)本次作业主要是对类的操作,题目已经给出类图,类图里面已经给了各种方法以及属性。此次作业涉及了座机的计费方式,市内省内省外怎么计费。难度......
  • BLOG-3对之前发布的6-8次PTA题目集(电信计费)总结
     for(Useru:users)前言:总结之前所涉及到的知识点、题量、难度等情况_____________________________________________________________________________________......
  • 对6-8次PTA题目集总结
    前言:通过了前几次的大作业的洗礼后,来到这一次的大作业,感觉还是比较简单的。没有什么无从下手的地方,仿照着PTA给出的类图很快就有个初步模板了,后面自己慢慢调试就可以解......
  • C/C++《程序设计与算法综合实践》备选题目
    C/C++《程序设计与算法综合实践》备选题目《程序设计与算法综合实践》期末大作业题目及评分标准有如下情况之一者,为不及格。(1)未能完成所选题目评分标准的最低要求。(2)......
  • 蓝桥杯校赛题目以及解析
    题目一输入一个字符串,求它包含多少个单词。单词间以一个或者多个空格分开。第一个单词前,最后一个单词后也可能有0到多个空格。比如:"abc   xyz"包含两个单词,"ab  c......