Pwnable之[Toddler's Bottle](三)--UAF
2018-03-30 11:42
316 查看
Pwnable之[Toddler’s Bottle]–UAF
UAF,use-after-free顾名思义,就是释放过内存的重利用。
根据操作系统里的内存分配就知道,当分配给的一个代码释放后,如果再申请的内存大小<=原先内存的时候,内存的指针会优先指向前面被free的内存,即分配原先的内存,从而产生迷途指针。
通过迷途指针进行操作时,会错误地按照释放前的偏移逻辑去访问新内容。
详细可看这个解析:
https://en.wikipedia.org/wiki/Dangling_pointer
查看代码:
#include <fcntl.h> #include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> using namespace std; class Human{//父类Human private: virtual void give_shell(){ system("/bin/sh");//虚函数getshell } protected: int age; string name; public: virtual void introduce(){//虚函数输出信息 cout << "My name is " << name << endl; cout << "I am " << age << " years old" << endl; } }; class Man: public Human{//继承Human public: Man(string name, int age){ this->name = name; this->age = age; } virtual void introduce(){ Human::introduce();//继承虚函数并调用 cout << "I am a nice guy!" << endl; } }; class Woman: public Human{ public: Woman(string name, int age){ this->name = name; this->age = age; } virtual void introduce(){ Human::introduce();//继承虚函数并调用 cout << "I am a cute girl!" << endl; } }; int main(int argc, char* argv[]){ Human* m = new Man("Jack", 25); Human* w = new Woman("Jill", 21); size_t len; char* data; unsigned int op; while(1){ cout << "1. use\n2. after\n3. free\n"; cin >> op; switch(op){ case 1: m->introduce(); w->introduce(); break; case 2: len = atoi(argv[1]);//将第一个参数变为整数 data = new char[len]; read(open(argv[2], O_RDONLY), data, len); //以只读的形式打开第二个参数,并给返回0,即将第二个参数执行完的第一个参数个长度的数据放入标准输入。 cout << "your data is allocated" << endl; break; case 3: delete m; delete w; break; default: break; } } return 0; }
查看代码后可以很直观地发现一个UAF漏洞,选择3.free将man与woman释放掉,再选择2.after新开内存,这里触发UAF漏洞,给新开内存写入give_shell()地址,再次调用1就会使这里的内存调用give_shell()的地址,就能成功拿到shell去读flag。
再用IDA分析下具体该怎么利用。
F5打开发现Women和Man分配的空间长度为0x18也就是24。
对应的是这个代码
然后双击man进入man的构造函数。
再跟进0ff_401570.
在这里发现了give_shell()和introduce()。
说明Man继承Human即使是私有的give_shell()虚函数也会被继承。
gdb看一下内存里0x401570的情况
看来地址没有随机化。
调试下看下内存,当执行玩构造函数后。
可以看到m和w对应的虚函数的首地址
发现woman的地址0x40150在内存0xfe1090。
可以清晰地看到,women类对象在内存中按8字节对齐,依次保存了虚表指针(指向类Women的虚函数表0x401550),继承自基类的成员age( 0x15 = 21),继承自积累的成员name(字符串地址为0xfe1078,前往该地址查看内容即为“Jill”)。
那么Man的虚函数表0x4015470也是类似的。
验证了8位后再看这个women的虚函数地址:
在前面分析中我们知道0x40117a代表shell(),0x401376代表introduce()。
那么系统如果要调用introduce()是不是要0x401550+8?
在IDA查看:
当输入为1时的 w->introduce();
发现调用introduce()就是v13+8和v14+8
而v13和v14刚好是前面由Human类开辟的空间。
OK,验证完毕。只要给把这+8去掉就是shell()的地址位置了。
也就是输入的是0x401550-8=0x401548。当被调用introduce()时+8就是shell()的位置了。
或者用man的也行,0x401570-8=0x401568
构造payload:
python -c 'print "\x48\x15\x40\x00\x00\x00\x00\x00"' > /tmp/jaken #python -c 'print "\x68\x15\x40\x00\x00\x00\x00\x00"' > /tmp/jaken1223
然后这里有个问题,因为它连续delete了m和w。所以2最好新建两次,防止指向NULL报错。
3->2->2->1 :
还是太菜了,一个简单的uaf就写这么多0 0…
涉及知识总结:
1》UAF漏洞的利用:(1)先搞出来一个迷途指针
(2)精心构造数据填充被释放的内存区域
(3)再次使用该指针,让填充的数据使eip发生跳转。
2》UAF的条件:
(1)用户可以控制该对象的大小
(2)用户空间可以对该对象写入数据
如果碰巧这块问题内存新分配的数据是比如C++中的类,那这块内存堆对上可能散落着各种函数指针,只要用shellcode的地址覆盖其中一个函数指针,就能够达成执行任意指令。
3》malloc函数做了那些事情。
https://blog.csdn.net/qq_32635069/article/details/74838187
https://blog.csdn.net/u011974987/article/details/52290724
//缩小,缩小的那一部分数据会丢失
//扩大,(连续的)
1.如果当前内存段后面有需要的内存空间,直接扩展这段内存空间,realloc返回原指针
2.如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据库释放掉,返回新的内存地址
3.如果申请失败,返回NULL,原来的指针仍然有效.
这也就是UAF产生的原因。
内存注意细节。
1.不能多次释放; 2.释放完之后(指针仍然有值),给指针置NULL,标志释放完成; 3.内存泄露(p重新赋值之后,再free,并没有真正释放内存);
4》虚函数表的结构
1.虚函数,一旦一个类使用了虚函数,编译器便会为这个类建立一张vtable。子类继承父类vtable中所有项,当子类有同名函数时,修改vtable同名函数地址,改为指向子类的函数地址,子类有新的虚函数时,在vtable中添加。
2.同时,私有函数无法继承,但如果私有函数是虚函数,vtable中会有相应的函数地址,所有子类可以通过手段得到父类的虚私有函数。
3.每一个类只有唯一的一个vtable,不是每个对象都有一个vtable,恰恰是每个同一个类的对象都有一个指针,这个指针指向该类的vtable(当然,前提是这个类包含虚函数)。那么,每个对象只额外增加了一个指针的大小,一般说来是4字节。
4.在类对象的内存布局中,首先是该类的vtable指针,然后才是对象数据。
在通过对象指针调用一个虚函数时,编译器生成的代码将先获取对象类的vtable指针,然后调用vtable中对应的项。
参考文章:
https://www.jianshu.com/p/04e963d7e0bc
http://blog.sina.com.cn/s/blog_e23a215b0102whgy.html
https://www.cnblogs.com/p4nda/p/7149870.html
https://blog.csdn.net/qq_20307987/article/details/51511230
相关文章推荐
- pwnable.kr [Toddler's Bottle] - uaf
- pwnable 笔记 Toddler's Bottle - uaf
- pwnable 笔记 Toddler's Bottle - bof
- pwnable 笔记 Toddler's Bottle - mistake
- pwnable.kr [Toddler's Bottle] - blackjack
- Pwnable之[Toddler's Bottle](二)
- pwnable 笔记 Toddler's Bottle - leg
- pwnable 笔记 Toddler's Bottle - lotto
- pwnable 笔记 Toddler's Bottle - flag
- pwnable.kr [Toddler's Bottle] - lotto
- pwnable 笔记 Toddler's Bottle - passcode
- pwnable.kr [Toddler's Bottle] - cmd1
- pwnable 笔记 Toddler's Bottle - random
- pwnable.kr [Toddler's Bottle] -fd
- pwnable.kr [Toddler's Bottle] - cmd2
- pwnable 笔记 Toddler's Bottle - input
- pwnable.kr [Toddler's Bottle] - collision
- pwnable 笔记 Toddler's Bottle - shellshock
- pwnable.kr [Toddler's Bottle] - bof
- pwnable 笔记 Toddler's Bottle - cmd1