阿里云CTF misc随写

随便玩玩

猫星球

脑王题,做的大脑升级了

Konano/CatWatermark (github.com)

给了项目,水印写在图像内,原始图像下载

Desktop Wallpapers Earth planet Africa Space 11500×11500 (1zoom.me)

原图另一个链接:https://eoimages.gsfc.nasa.gov/images/imagerecords/77000/77085/marble_east_vir_2012023_lrg.jpg

(这两个图片相近)

两张图片xor后获得一个明显的猫脸变换的图片

审给的脚本,

可以发现

图片[1]-阿里云CTF misc随写-魔法少女雪殇

水印的位置是在变换后的中间部分,已知图像长宽为11500,那么图像中间也就是5700,5700的位置

并且他的dx,dy的范围是随机的,大概在1150-11500之间,非常之大

图片[2]-阿里云CTF misc随写-魔法少女雪殇

此时随机生成private后带入函数中。

图片[3]-阿里云CTF misc随写-魔法少女雪殇

加密脚本根据他写的其实iterations并没有实际使用,实际上只运行了一次(此处贴个常规猫脸变换,对比一下就知道问题所在了

def arnold(img, shuffle_times, a, b):
    r, c, d = img.shape # 高,宽,通道个数
    p = np.zeros(img.shape, np.uint8)
    for times in range(shuffle_times):  
        for i in range(r):
            for j in range(c):
                x = (i + b * j) % r
                y = (a * i + (a * b + 1) * j) % c
                p[x, y, :] = img[i, j, :]
        img = np.copy(p) 
    return p 

综上所述,我们获得了三个信息,

第一,变换次数恒为1,

第二,数据较大,并且图片也很大,盲目爆破不现实

第三,思考一种办法能够提前获知dx或者dy,这样可以减少爆破数量和时间。

根据分析Arnold算法的关系,

图片[4]-阿里云CTF misc随写-魔法少女雪殇

当_x=0的时候。_y=_y

_x=1时,_y=_y+dx

也就是说,第0行元素不变,根据这个公式来编写即可计算我们所需要的dx值。

import sys
import numpy as np
from PIL import Image,ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

img = np.array(Image.open('1234.png').convert('L'))
width,height = img.shape
np.set_printoptions(threshold=np.inf)

def arnold_cat_map_rev_single_row(row, offset_x, width):
    new_row = np.zeros(row.shape, dtype=np.uint8)
    for y in range(width):
        _y = (y - offset_x * (row.size - y)) % width
        new_row[_y] = row[y]
    return new_row

def compute_dx(first_row, second_row, width):
    best_dx = None
    best_diff = float('inf')

    for dx in range(5700, width): #从图像中间开始
        # Apply the Arnold's cat map reverse transformation to the second row
        second_row_transformed = arnold_cat_map_rev_single_row(second_row, dx, width)
        
        # Compute the difference between the first row and the transformed second row
        diff = np.abs(first_row - second_row_transformed).sum()

        # If the difference is smaller than the best difference found so far, update the best dx value
        if diff < best_diff:
            best_dx = dx -1
            best_diff = diff

    return best_dx

row0 = img[0,:]
row1 = img[1,:]

# Compute the width of the image (or the watermark)
width = len(row0)

# Calculate the dx value
dx = compute_dx(row0, row1, width)

print(dx)

图片[5]-阿里云CTF misc随写-魔法少女雪殇

可以计算出dx值为5809,此时拥有其中一个值我们无脑爆破最后一个dy就好了,体力活。

import os
import sys

import numpy as np
from PIL import Image, ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

def arnold_cat_map(image, key=(1, 2, 1)):
    """
    Implements Arnold's cat map transformation on an image.
    """
    height, width, *_ = image.shape
    offset_x, offset_y, iterations = key

    new_image = np.zeros(image.shape, dtype=np.uint8)

    for x in range(height):
        for y in range(width):
            _x, _y = x, y
            _y = (_y + offset_x * _x) % width
            _x = (_x + offset_y * _y) % height
            new_image[_x, _y] = image[x, y]
    return new_image

def arnold_cat_map_rev(image, key=(1, 2, 1)):
    """
    Implements Arnold's cat map transformation on an image (reverse).
    """
    height, width, *_ = image.shape
    offset_x, offset_y, iterations = key

    new_image = np.zeros(image.shape, dtype=np.uint8)
    
    for x in range(height):
        for y in range(width):
            _x, _y = x, y
            _x = (_x - offset_y * _y) % height
            _y = (_y - offset_x * _x) % width
            new_image[_x, _y] = image[x, y]
    return new_image

def extract_watermark(original_image_path, watermarked_image_path, output_image_path, private_key):
    """
    Extracts a text watermark from a watermarked image using the Arnold's cat map transformation.
    """

    # Open the original image
    original_image = np.array(Image.open(original_image_path).convert("RGB"))

    # Open the watermarked image
    watermarked_image = np.array(Image.open(watermarked_image_path).convert("RGB"))
    assert watermarked_image.shape == original_image.shape

    # Extract the watermark from the watermarked image
    original_image ^= watermarked_image
    transformed_image = arnold_cat_map(original_image, private_key)
    transformed_image[transformed_image > 0] = 255
    transformed_image = 255 - transformed_image

    # Save the extracted watermark
    Image.fromarray(np.uint8(transformed_image)).save(output_image_path)

def try_arnold_dy(original_image_path, watermarked_image_path, output_image_path, arnold_dx, arnold_rd):
    original_image = np.array(Image.open(original_image_path).convert("RGB"))
    watermarked_image = np.array(Image.open(watermarked_image_path).convert("RGB"))

    height, width, *_ = original_image.shape
    for arnold_dy in range(2800, 3000):
        private_key = (arnold_dx, arnold_dy, arnold_rd)
        extracted_watermark = arnold_cat_map(original_image ^ watermarked_image, private_key)
        extracted_watermark[extracted_watermark > 0] = 255
        extracted_watermark = 255 - extracted_watermark

        # 将尝试的结果保存为文件,文件名包含尝试的 arnold_dy 值
        output_filename = f"{output_image_path}_arnold_dy_{arnold_dy}.png"
        Image.fromarray(np.uint8(extracted_watermark)).save(output_filename)
        print(f"Saved {output_filename} with arnold_dy = {arnold_dy}")

# 输入参数检查
if len(sys.argv) != 4:
    print("Usage: brute_force_arnold_dy.py original_image watermarked_image output_image")
    sys.exit(1)

original_image_path = sys.argv[1]
watermarked_image_path = sys.argv[2]
output_image_path = sys.argv[3]

arnold_dx = 5809
arnold_rd = 1

try_arnold_dy(original_image_path, watermarked_image_path, output_image_path, arnold_dx, arnold_rd)

图片[6]-阿里云CTF misc随写-魔法少女雪殇

最后找到最终值 5809 2901 1为最终flag

flag图

图片[7]-阿里云CTF misc随写-魔法少女雪殇

OOBdetection

邪道做法,先做第一层爆破

# -*- coding: utf-8 -*-
from pwn import *
import hashlib
import string
import itertools
from tqdm import tqdm

#开启日志
context.log_level = 'debug'

sh = remote("47.98.209.191", 1337)


def crack():
    temp = sh.recvuntil(b"sha256(XXX +")
    temp = sh.recvline()
    target_hash = temp.split(b" == ")[1].strip()
    suffix = temp[1:15]
    print(temp, target_hash, suffix)
    target_hash = target_hash.decode('utf-8')
    suffix = suffix.decode('utf-8')
    charset = "0123456789abcdef"
    total_combinations = len(charset) ** 6
    for combination in tqdm(itertools.product(charset, repeat=6), desc="Cracking progress", total=total_combinations):
        prefix = ''.join(combination)
        test_string = prefix + suffix
        test_string_bytes = bytes.fromhex(test_string)
        test_hash = hashlib.sha256(test_string_bytes).hexdigest()
        if test_hash == target_hash:
            print("XXX = " + prefix)
            return prefix

passwd = crack()

assert passwd is not None
sh.sendline(passwd.encode())

sh.interactive()

然后内容是根据运算内容判断是否存在溢出和异常,常规做法是转lark然后跑

我又不懂编译原理,答案是GPT帮我判断

图片[8]-阿里云CTF misc随写-魔法少女雪殇

写好prompt多次调教后提高准确性

图片[9]-阿里云CTF misc随写-魔法少女雪殇

最后弄个脚本就行了,这里就不放了,省的我钱没了

懂得都懂带带弟弟

正挺好,非预期

import('../flag')

消失的声波

minimoden直接解

图片[10]-阿里云CTF misc随写-魔法少女雪殇

获得个oss桶,根据官网规则,直接拼接url访问

https://iot2023.oss-cn-hangzhou.aliyuncs.com/OpYdCuMtkQ8Yjhm2

下载文件是个macos的程序,不逆向直接strings怎么说

图片[11]-阿里云CTF misc随写-魔法少女雪殇

关键信息拿到了,直接连就完事了

Python Link SDK (aliyun.com)

import sys
from linkkit import linkkit
import threading
import traceback
import inspect
import time
import logging

# config log
__log_format = '%(asctime)s-%(process)d-%(thread)d - %(name)s:%(module)s:%(funcName)s - %(levelname)s - %(message)s'
# logging.basicConfig(format=__log_format)

lk = linkkit.LinkKit(
    host_name="cn-shanghai",
    product_key="a1eAwsBKddO",
    device_name="ncApIY2XV9NUIY4VpbGk",
    device_secret="04845e512ead208b2437d970a154d69e")
# lk.config_mqtt(endpoint="iot-cn-6ja******.mqtt.iothub.aliyuncs.com")

lk.enable_logger(logging.DEBUG)


def on_device_dynamic_register(rc, value, userdata):
    if rc == 0:
        print("dynamic register device success, value:" + value)
    else:
        print("dynamic register device fail, message:" + value)


def on_connect(session_flag, rc, userdata):
    print("on_connect:%d,rc:%d" % (session_flag, rc))
    pass


def on_disconnect(rc, userdata):
    print("on_disconnect:rc:%d,userdata:" % rc)


def on_topic_message(topic, payload, qos, userdata):
    print("on_topic_message:" + topic + " payload:" + str(payload) + " qos:" + str(qos))
    pass


def on_subscribe_topic(mid, granted_qos, userdata):
    print("on_subscribe_topic mid:%d, granted_qos:%s" %
          (mid, str(','.join('%s' % it for it in granted_qos))))
    pass


def on_unsubscribe_topic(mid, userdata):
    print("on_unsubscribe_topic mid:%d" % mid)
    pass


def on_publish_topic(mid, userdata):
    print("on_publish_topic mid:%d" % mid)


lk.on_device_dynamic_register = on_device_dynamic_register
lk.on_connect = on_connect
lk.on_disconnect = on_disconnect
lk.on_topic_message = on_topic_message
lk.on_subscribe_topic = on_subscribe_topic
lk.on_unsubscribe_topic = on_unsubscribe_topic
lk.on_publish_topic = on_publish_topic


lk.config_device_info("Eth|03ACDEFF0032|Eth|03ACDEFF0031")
lk.config_mqtt(port=1883, protocol="MQTTv311", transport="TCP",secure="TLS")
lk.connect_async()
lk.start_worker_loop()

while True:
    try:
        msg = input()
    except KeyboardInterrupt:
        sys.exit()
    else:
        if msg == "1":
            lk.disconnect()
        elif msg == "2":
            lk.connect_async()
        elif msg == "3":
            rc, mid = lk.subscribe_topic(lk.to_full_topic("user/get"))
            if rc == 0:
                print("subscribe topic success:%r, mid:%r" % (rc, mid))
            else:
                print("subscribe topic fail:%d" % rc)
        elif msg == "4":
            rc, mid = lk.unsubscribe_topic(lk.to_full_topic("user/get"))
            if rc == 0:
                print("unsubscribe topic success:%r, mid:%r" % (rc, mid))
            else:
                print("unsubscribe topic fail:%d" % rc)
        elif msg == "5":
            #发送{"id":"flag"}到/user/get
            rc, mid = lk.publish_topic(lk.to_full_topic("user/update"), "{\"id\":\"flag\"}")
            if rc == 0:
                print("publish topic success:%r, mid:%r" % (rc, mid))
            else:
                print("publish topic fail:%d" % rc)
        elif msg == "8":
            ret = lk.dump_user_topics()
            print("user topics:%s", str(ret))
        elif msg == "9":
            lk.destruct()
            print("destructed")
        else:
            sys.exit()

最后获得flag

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情

    暂无评论内容